--- /dev/null
+#--\r
+#\r
+# Author:: Francis Cianfrocca (gmail: blackhedd)\r
+# Homepage:: http://rubyeventmachine.com\r
+# Date:: 15 Nov 2006\r
+# \r
+# See EventMachine and EventMachine::Connection for documentation and\r
+# usage examples.\r
+#\r
+#----------------------------------------------------------------------------\r
+#\r
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.\r
+# Gmail: blackhedd\r
+# \r
+# This program is free software; you can redistribute it and/or modify\r
+# it under the terms of either: 1) the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2 of the\r
+# License, or (at your option) any later version; or 2) Ruby's License.\r
+# \r
+# See the file COPYING for complete licensing information.\r
+#\r
+#---------------------------------------------------------------------------\r
+#\r
+#\r
+\r
+module EventMachine\r
+ module Protocols\r
+\r
+ # === Usage\r
+ #\r
+ # class RequestHandler < EM::P::HeaderAndContentProtocol\r
+ # def receive_request headers, content\r
+ # p [:request, headers, content]\r
+ # end\r
+ # end\r
+ #\r
+ # EM.run{\r
+ # EM.start_server 'localhost', 80, RequestHandler\r
+ # }\r
+ #\r
+ #--\r
+ # Originally, this subclassed LineAndTextProtocol, which in\r
+ # turn relies on BufferedTokenizer, which doesn't gracefully\r
+ # handle the transitions between lines and binary text.\r
+ # Changed 13Sep08 by FCianfrocca.\r
+ class HeaderAndContentProtocol < Connection\r
+ include LineText2\r
+\r
+ ContentLengthPattern = /Content-length:\s*(\d+)/i\r
+\r
+ def initialize *args\r
+ super\r
+ init_for_request\r
+ end\r
+\r
+ def receive_line line\r
+ case @hc_mode\r
+ when :discard_blanks\r
+ unless line == ""\r
+ @hc_mode = :headers\r
+ receive_line line\r
+ end\r
+ when :headers\r
+ if line == ""\r
+ raise "unrecognized state" unless @hc_headers.length > 0\r
+ if respond_to?(:receive_headers)\r
+ receive_headers @hc_headers\r
+ end\r
+ # @hc_content_length will be nil, not 0, if there was no content-length header.\r
+ if @hc_content_length.to_i > 0\r
+ set_binary_mode @hc_content_length\r
+ else\r
+ dispatch_request\r
+ end\r
+ else\r
+ @hc_headers << line\r
+ if ContentLengthPattern =~ line\r
+ # There are some attacks that rely on sending multiple content-length\r
+ # headers. This is a crude protection, but needs to become tunable.\r
+ raise "extraneous content-length header" if @hc_content_length\r
+ @hc_content_length = $1.to_i\r
+ end\r
+ if @hc_headers.length == 1 and respond_to?(:receive_first_header_line)\r
+ receive_first_header_line line\r
+ end\r
+ end\r
+ else\r
+ raise "internal error, unsupported mode"\r
+ end\r
+ end\r
+\r
+ def receive_binary_data text\r
+ @hc_content = text\r
+ dispatch_request\r
+ end\r
+\r
+ def dispatch_request\r
+ if respond_to?(:receive_request)\r
+ receive_request @hc_headers, @hc_content\r
+ end\r
+ init_for_request\r
+ end\r
+ private :dispatch_request\r
+\r
+ def init_for_request\r
+ @hc_mode = :discard_blanks\r
+ @hc_headers = []\r
+ # originally was @hc_headers ||= []; @hc_headers.clear to get a performance\r
+ # boost, but it's counterproductive because a subclassed handler will have to\r
+ # call dup to use the header array we pass in receive_headers.\r
+\r
+ @hc_content_length = nil\r
+ @hc_content = ""\r
+ end\r
+ private :init_for_request\r
+\r
+ # Basically a convenience method. We might create a subclass that does this\r
+ # automatically. But it's such a performance killer.\r
+ def headers_2_hash hdrs\r
+ self.class.headers_2_hash hdrs\r
+ end\r
+\r
+ class << self\r
+ def headers_2_hash hdrs\r
+ hash = {}\r
+ hdrs.each {|h|\r
+ if /\A([^\s:]+)\s*:\s*/ =~ h\r
+ tail = $'.dup\r
+ hash[ $1.downcase.gsub(/-/,"_").intern ] = tail\r
+ end\r
+ }\r
+ hash\r
+ end\r
+ end\r
+\r
+ end\r
+ end\r
+end\r