iodine 0.0.4 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/iodine/http.rb +24 -11
- data/lib/iodine/http/hpack.rb +5 -5
- data/lib/iodine/http/http1.rb +168 -165
- data/lib/iodine/http/http2.rb +1 -1
- data/lib/iodine/http/rack_support.rb +7 -6
- data/lib/iodine/http/request.rb +1 -1
- data/lib/iodine/http/response.rb +1 -3
- data/lib/iodine/http/session.rb +2 -1
- data/lib/iodine/http/websocket_client.rb +1 -1
- data/lib/iodine/http/websocket_handler.rb +1 -1
- data/lib/iodine/http/websockets.rb +2 -2
- data/lib/iodine/io.rb +4 -2
- data/lib/iodine/protocol.rb +3 -0
- data/lib/iodine/version.rb +1 -1
- metadata +1 -1
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 9451f837296bd4fecf60df42fd3cfee80d80ef0f
         | 
| 4 | 
            +
              data.tar.gz: af0d8bf95137d229b23ee74aef4825f50a9aafa0
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 6039e844f9b6658326a70fbadc12ebd61007b466a546100670382ce119d2d0bbf193497fc2c40a4fc36945352ad94e2f83f3a1c4f5f1a6a3c88f6c0719bf3dd1
         | 
| 7 | 
            +
              data.tar.gz: 608df34266557cb59300dd08321c3dcef74a67c7f7f0eec343ae77ac823f440d2108b40008e8c744fd753199ca73b0dffdfb96fc9bad4a2ce1aaac362319d093
         | 
    
        data/lib/iodine/http.rb
    CHANGED
    
    | @@ -68,14 +68,15 @@ module Iodine | |
| 68 68 | 
             
            	#
         | 
| 69 69 | 
             
            	# See {Iodine::Http::WebsocketHandler} for a good starting point or inherit {Iodine::Http::WebsocketHandler} in your handler.
         | 
| 70 70 | 
             
            	#
         | 
| 71 | 
            -
            	 | 
| 71 | 
            +
            	module Http
         | 
| 72 | 
            +
            		public
         | 
| 72 73 | 
             
            		# Sets or gets the Http callback.
         | 
| 73 74 | 
             
            		#
         | 
| 74 75 | 
             
            		# An Http callback is a Proc like object that answers to `call(request, response)` and returns either:
         | 
| 75 76 | 
             
            		# `true`:: the response has been set by the callback and can be managed (including any streaming) by the server.
         | 
| 76 77 | 
             
            		# `false`:: the request shouldn't be answered or resource not found (error 404 will be sent as a response).
         | 
| 77 78 | 
             
            		# String:: the String will be appended to the response and the response sent.
         | 
| 78 | 
            -
            		def  | 
| 79 | 
            +
            		def on_http handler = nil, &block
         | 
| 79 80 | 
             
            			@http_app = handler || block if handler || block
         | 
| 80 81 | 
             
            			@http_app
         | 
| 81 82 | 
             
            		end
         | 
| @@ -84,29 +85,38 @@ module Iodine | |
| 84 85 | 
             
            		# A Websockets callback is a Proc like object that answers to `call(request)` and returns either:
         | 
| 85 86 | 
             
            		# `false`:: the request shouldn't be answered or resource not found (error 404 will be sent as a response).
         | 
| 86 87 | 
             
            		# Websocket Handler:: a Websocket handler is an object that is expected to answer `on_message(data)` and `on_close`. See {} for more data.
         | 
| 87 | 
            -
            		def  | 
| 88 | 
            +
            		def on_websocket handler = nil, &block
         | 
| 88 89 | 
             
            			@websocket_app = handler || block if handler || block
         | 
| 89 90 | 
             
            			@websocket_app
         | 
| 90 91 | 
             
            		end
         | 
| 91 92 |  | 
| 92 93 | 
             
            		# Sets the session token for the Http server (String). Defaults to the name of the script + '_id'.
         | 
| 93 | 
            -
            		def  | 
| 94 | 
            +
            		def session_token= token
         | 
| 94 95 | 
             
            			@session_token = token
         | 
| 95 96 | 
             
            		end
         | 
| 96 97 | 
             
            		# Sets the session token for the Http server (String). Defaults to the name of the script.
         | 
| 97 | 
            -
            		def  | 
| 98 | 
            +
            		def session_token
         | 
| 98 99 | 
             
            			@session_token
         | 
| 99 100 | 
             
            		end
         | 
| 100 101 |  | 
| 101 102 | 
             
            		# Sets whether Iodine will allow connections to the experiemntal Http2 protocol. Defaults to false unless the `http2` command line flag is present.
         | 
| 102 | 
            -
            		def  | 
| 103 | 
            +
            		def http2= allow
         | 
| 103 104 | 
             
            			@http2 = allow && true
         | 
| 104 105 | 
             
            		end
         | 
| 105 106 | 
             
            		# Returns true if Iodine will require that new connection be encrypted.
         | 
| 106 | 
            -
            		def  | 
| 107 | 
            +
            		def http2
         | 
| 107 108 | 
             
            			@http2
         | 
| 108 109 | 
             
            		end
         | 
| 109 110 |  | 
| 111 | 
            +
            		# # Sets whether Iodine will allow connections to the experiemntal Http2 protocol. Defaults to false unless the `http2` command line flag is present.
         | 
| 112 | 
            +
            		# def self.message_buffer_size= size
         | 
| 113 | 
            +
            		# 	@message_buffer_size = size
         | 
| 114 | 
            +
            		# end
         | 
| 115 | 
            +
            		# # Returns true if Iodine will require that new connection be encrypted.
         | 
| 116 | 
            +
            		# def self.message_buffer_size
         | 
| 117 | 
            +
            		# 	@message_buffer_size
         | 
| 118 | 
            +
            		# end
         | 
| 119 | 
            +
             | 
| 110 120 | 
             
            		# Creates a websocket client within a new task (non-blocking).
         | 
| 111 121 | 
             
            		# 
         | 
| 112 122 | 
             
            		# Make sure to setup all the callbacks (as needed) prior to starting the connection. See {::Iodine::Http::WebsocketClient.connect}
         | 
| @@ -132,22 +142,25 @@ module Iodine | |
| 132 142 | 
             
            		#       #if running from irb:
         | 
| 133 143 | 
             
            		#       exit
         | 
| 134 144 | 
             
            		#     
         | 
| 135 | 
            -
            		def  | 
| 145 | 
            +
            		def ws_connect url, options={}, &block
         | 
| 136 146 | 
             
            			::Iodine.run { ::Iodine::Http::WebsocketClient.connect url, options, &block }
         | 
| 137 147 | 
             
            		end
         | 
| 138 148 |  | 
| 149 | 
            +
            		protected
         | 
| 150 | 
            +
             | 
| 139 151 | 
             
            		@http2 = (ARGV.index('http2') && true)
         | 
| 140 152 |  | 
| 141 153 | 
             
            		@websocket_app = @http_app = NOT_IMPLEMENTED = Proc.new { |i,o| false }
         | 
| 142 154 | 
             
            		@session_token = "#{File.basename($0, '.*')}_uuid"
         | 
| 155 | 
            +
            		extend self
         | 
| 143 156 | 
             
            	end
         | 
| 144 157 |  | 
| 145 158 | 
             
            	@queue.tap do |q|
         | 
| 146 159 | 
             
            		arr =[];
         | 
| 147 160 | 
             
            		arr << q.pop until q.empty?;
         | 
| 148 | 
            -
            		run { Iodine.ssl_protocols = { 'h2' => Iodine::Http::Http2, 'http/1.1' => Iodine::Http } if @ssl && @ssl_protocols.empty? && ::Iodine::Http.http2 }
         | 
| 161 | 
            +
            		run { ::Iodine.ssl_protocols = { 'h2' => ::Iodine::Http::Http2, 'http/1.1' => ::Iodine::Http::Http1 } if @ssl && @ssl_protocols.empty? && ::Iodine::Http.http2 }
         | 
| 149 162 | 
             
            		run do
         | 
| 150 | 
            -
            			if Iodine.protocol == ::Iodine::Http && ::Iodine::Http.on_http == ::Iodine::Http::NOT_IMPLEMENTED && ::Iodine::Http.on_websocket == ::Iodine::Http::NOT_IMPLEMENTED
         | 
| 163 | 
            +
            			if Iodine.protocol == ::Iodine::Http::Http1 && ::Iodine::Http.on_http == ::Iodine::Http::NOT_IMPLEMENTED && ::Iodine::Http.on_websocket == ::Iodine::Http::NOT_IMPLEMENTED
         | 
| 151 164 | 
             
            				::Iodine.protocol = :http_not_initialized
         | 
| 152 165 | 
             
            				q << arr.shift until arr.empty?
         | 
| 153 166 | 
             
            				run { Process.kill("INT", 0) }
         | 
| @@ -156,4 +169,4 @@ module Iodine | |
| 156 169 | 
             
            		q << arr.shift until arr.empty?
         | 
| 157 170 | 
             
            	end
         | 
| 158 171 | 
             
            end
         | 
| 159 | 
            -
            Iodine.protocol = ::Iodine::Http
         | 
| 172 | 
            +
            Iodine.protocol = ::Iodine::Http::Http1
         | 
    
        data/lib/iodine/http/hpack.rb
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 2 |  | 
| 3 3 | 
             
            module Iodine
         | 
| 4 | 
            -
            	 | 
| 4 | 
            +
            	module Http
         | 
| 5 5 | 
             
            		class Http2 < ::Iodine::Protocol
         | 
| 6 6 | 
             
            			class HPACK
         | 
| 7 7 | 
             
            				class IndexTable
         | 
| @@ -145,10 +145,10 @@ module Iodine | |
| 145 145 | 
             
            					buffer
         | 
| 146 146 | 
             
            				rescue
         | 
| 147 147 | 
             
            					puts "HPACK failure data dump:"
         | 
| 148 | 
            -
            					puts "buffer: #{buffer} - #{buffer.encoding}"
         | 
| 149 | 
            -
            					puts " | 
| 150 | 
            -
            					puts " | 
| 151 | 
            -
            					puts "packed #{pack_string( | 
| 148 | 
            +
            					puts "buffer: #{buffer} - #{buffer.encoding}" if buffer
         | 
| 149 | 
            +
            					puts "name: #{name} - #{name.encoding}"  if name.is_a? String
         | 
| 150 | 
            +
            					puts "value: #{value} - #{value.encoding}"  if value.is_a? String
         | 
| 151 | 
            +
            					puts "packed #{pack_string(name)} - #{pack_string(value)}" if value
         | 
| 152 152 | 
             
            					raise
         | 
| 153 153 | 
             
            				end
         | 
| 154 154 | 
             
            				def extract_number data, prefix, prefix_length
         | 
    
        data/lib/iodine/http/http1.rb
    CHANGED
    
    | @@ -1,195 +1,198 @@ | |
| 1 1 | 
             
            module Iodine
         | 
| 2 | 
            -
            	 | 
| 3 | 
            -
            		 | 
| 4 | 
            -
            			 | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
            					 | 
| 16 | 
            -
             | 
| 17 | 
            -
            						 | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
            					until request[:headers_complete] || (l = data.gets).nil?
         | 
| 25 | 
            -
            						if l.include? ':'
         | 
| 26 | 
            -
            							# n = l.slice!(0, l.index(':')); l.slice! 0
         | 
| 27 | 
            -
            							# n.strip! ; n.downcase!; n.freeze
         | 
| 28 | 
            -
            							# request[n] ? (request[n].is_a?(Array) ? (request[n] << l) : request[n] = [request[n], l ]) : (request[n] = l)
         | 
| 29 | 
            -
            							l = l.strip.split(/:[\s]?/, 2)
         | 
| 30 | 
            -
            							l[0].strip! ; l[0].downcase!;
         | 
| 31 | 
            -
            							request[l[0]] ? (request[l[0]].is_a?(Array) ? (request[l[0]] << l[1]) : request[l[0]] = [request[l[0]], l[1] ]) : (request[l[0]] = l[1])
         | 
| 32 | 
            -
            						elsif l =~ /^[\r]?\n/
         | 
| 33 | 
            -
            							request[:headers_complete] = true
         | 
| 34 | 
            -
            						else
         | 
| 35 | 
            -
            							#protocol error
         | 
| 36 | 
            -
            							Iodine.warn 'Protocol Error, closing connection.'
         | 
| 37 | 
            -
            							return close
         | 
| 2 | 
            +
            	module Http
         | 
| 3 | 
            +
            		class Http1 < ::Iodine::Protocol
         | 
| 4 | 
            +
            			def on_open
         | 
| 5 | 
            +
            				set_timeout 1
         | 
| 6 | 
            +
            				@refuse_requests = false
         | 
| 7 | 
            +
            				@bytes_sent = 0
         | 
| 8 | 
            +
            				@parser = {}
         | 
| 9 | 
            +
            			end
         | 
| 10 | 
            +
            			def on_message data
         | 
| 11 | 
            +
            					return if @refuse_requests
         | 
| 12 | 
            +
            					@http2_pri_review ||= ( ::Iodine::Http.http2 && ::Iodine::Http::Http2.pre_handshake(self, data) && (return true) ) || true
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            					data = ::StringIO.new data
         | 
| 15 | 
            +
            					until data.eof?
         | 
| 16 | 
            +
            						request = (@request ||= ::Iodine::Http::Request.new(self))
         | 
| 17 | 
            +
            						unless request[:method]
         | 
| 18 | 
            +
            							l = data.gets.strip
         | 
| 19 | 
            +
            							next if l.empty?
         | 
| 20 | 
            +
            							request[:method], request[:query], request[:version] = l.split(/[\s]+/, 3)
         | 
| 21 | 
            +
            							return (Iodine.warn('Protocol Error, closing connection.') && close) unless request[:method] =~ HTTP_METHODS_REGEXP
         | 
| 22 | 
            +
            							request[:version] = (request[:version] || '1.1'.freeze).match(/[\d\.]+/)[0]
         | 
| 23 | 
            +
            							request[:time_recieved] = Time.now
         | 
| 38 24 | 
             
            						end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
            								 | 
| 25 | 
            +
            						until request[:headers_complete] || (l = data.gets).nil?
         | 
| 26 | 
            +
            							if l.include? ':'
         | 
| 27 | 
            +
            								# n = l.slice!(0, l.index(':')); l.slice! 0
         | 
| 28 | 
            +
            								# n.strip! ; n.downcase!; n.freeze
         | 
| 29 | 
            +
            								# request[n] ? (request[n].is_a?(Array) ? (request[n] << l) : request[n] = [request[n], l ]) : (request[n] = l)
         | 
| 30 | 
            +
            								l = l.strip.split(/:[\s]?/, 2)
         | 
| 31 | 
            +
            								l[0].strip! ; l[0].downcase!;
         | 
| 32 | 
            +
            								request[l[0]] ? (request[l[0]].is_a?(Array) ? (request[l[0]] << l[1]) : request[l[0]] = [request[l[0]], l[1] ]) : (request[l[0]] = l[1])
         | 
| 33 | 
            +
            							elsif l =~ /^[\r]?\n/
         | 
| 34 | 
            +
            								request[:headers_complete] = true
         | 
| 35 | 
            +
            							else
         | 
| 36 | 
            +
            								#protocol error
         | 
| 37 | 
            +
            								Iodine.warn 'Protocol Error, closing connection.'
         | 
| 38 | 
            +
            								return close
         | 
| 39 | 
            +
            							end
         | 
| 40 | 
            +
            						end
         | 
| 41 | 
            +
            						until request[:body_complete] && request[:headers_complete]
         | 
| 42 | 
            +
            							if request['transfer-coding'.freeze] == 'chunked'.freeze
         | 
| 43 | 
            +
            								# ad mid chunk logic here
         | 
| 44 | 
            +
            								if @parser[:length].to_i == 0
         | 
| 45 | 
            +
            									chunk = data.gets
         | 
| 46 | 
            +
            									return false unless chunk
         | 
| 47 | 
            +
            									@parser[:length] = chunk.to_i(16)
         | 
| 48 | 
            +
            									return (Iodine.warn('Protocol Error, closing connection.') && close) unless @parser[:length]
         | 
| 49 | 
            +
            									request[:body_complete] = true && break if @parser[:length] == 0
         | 
| 50 | 
            +
            									@parser[:act_length] = 0
         | 
| 51 | 
            +
            									request[:body] ||= ''
         | 
| 52 | 
            +
            								end
         | 
| 53 | 
            +
            								chunk = data.read(@parser[:length] - @parser[:act_length])
         | 
| 45 54 | 
             
            								return false unless chunk
         | 
| 46 | 
            -
            								 | 
| 47 | 
            -
            								 | 
| 48 | 
            -
            								 | 
| 49 | 
            -
             | 
| 55 | 
            +
            								request[:body] << chunk
         | 
| 56 | 
            +
            								@parser[:act_length] += chunk.bytesize
         | 
| 57 | 
            +
            								(@parser[:act_length] = @parser[:length] = 0) && (data.gets) if @parser[:act_length] >= @parser[:length]
         | 
| 58 | 
            +
            							elsif request['content-length'.freeze] && request['content-length'.freeze].to_i != 0
         | 
| 50 59 | 
             
            								request[:body] ||= ''
         | 
| 60 | 
            +
            								packet = data.read(request['content-length'.freeze].to_i - request[:body].bytesize)
         | 
| 61 | 
            +
            								return false unless packet
         | 
| 62 | 
            +
            								request[:body] << packet
         | 
| 63 | 
            +
            								request[:body_complete] = true if request['content-length'.freeze].to_i - request[:body].bytesize <= 0
         | 
| 64 | 
            +
            							elsif request['content-type'.freeze]
         | 
| 65 | 
            +
            								Iodine.warn 'Body type protocol error.' unless request[:body]
         | 
| 66 | 
            +
            								line = data.gets
         | 
| 67 | 
            +
            								return false unless line
         | 
| 68 | 
            +
            								(request[:body] ||= '') << line
         | 
| 69 | 
            +
            								request[:body_complete] = true if line =~ EOHEADERS
         | 
| 70 | 
            +
            							else
         | 
| 71 | 
            +
            								request[:body_complete] = true
         | 
| 51 72 | 
             
            							end
         | 
| 52 | 
            -
            							chunk = data.read(@parser[:length] - @parser[:act_length])
         | 
| 53 | 
            -
            							return false unless chunk
         | 
| 54 | 
            -
            							request[:body] << chunk
         | 
| 55 | 
            -
            							@parser[:act_length] += chunk.bytesize
         | 
| 56 | 
            -
            							(@parser[:act_length] = @parser[:length] = 0) && (data.gets) if @parser[:act_length] >= @parser[:length]
         | 
| 57 | 
            -
            						elsif request['content-length'.freeze] && request['content-length'.freeze].to_i != 0
         | 
| 58 | 
            -
            							request[:body] ||= ''
         | 
| 59 | 
            -
            							packet = data.read(request['content-length'.freeze].to_i - request[:body].bytesize)
         | 
| 60 | 
            -
            							return false unless packet
         | 
| 61 | 
            -
            							request[:body] << packet
         | 
| 62 | 
            -
            							request[:body_complete] = true if request['content-length'.freeze].to_i - request[:body].bytesize <= 0
         | 
| 63 | 
            -
            						elsif request['content-type'.freeze]
         | 
| 64 | 
            -
            							Iodine.warn 'Body type protocol error.' unless request[:body]
         | 
| 65 | 
            -
            							line = data.gets
         | 
| 66 | 
            -
            							return false unless line
         | 
| 67 | 
            -
            							(request[:body] ||= '') << line
         | 
| 68 | 
            -
            							request[:body_complete] = true if line =~ EOHEADERS
         | 
| 69 | 
            -
            						else
         | 
| 70 | 
            -
            							request[:body_complete] = true
         | 
| 71 73 | 
             
            						end
         | 
| 74 | 
            +
            						(@request = ::Iodine::Http::Request.new(self)) && ( (::Iodine::Http.http2 && ::Iodine::Http::Http2.handshake(request, self, data)) || dispatch(request, data) ) if request.delete :body_complete
         | 
| 72 75 | 
             
            					end
         | 
| 73 | 
            -
             | 
| 74 | 
            -
            				end
         | 
| 75 | 
            -
            		end
         | 
| 76 | 
            +
            			end
         | 
| 76 77 |  | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 78 | 
            +
            			def send_response response
         | 
| 79 | 
            +
            				return false if response.headers.frozen?
         | 
| 79 80 |  | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 81 | 
            +
            				request = response.request
         | 
| 82 | 
            +
            				headers = response.headers
         | 
| 83 | 
            +
            				body = response.extract_body
         | 
| 83 84 |  | 
| 84 | 
            -
             | 
| 85 | 
            +
            				headers['content-length'.freeze] ||= body.to_s.bytesize
         | 
| 85 86 |  | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 87 | 
            +
            				keep_alive = response.keep_alive
         | 
| 88 | 
            +
            				if (request[:version].to_f > 1 && request['connection'.freeze].nil?) || request['connection'.freeze].to_s =~ /ke/i || (headers['connection'.freeze] && headers['connection'.freeze] =~ /^ke/i)
         | 
| 89 | 
            +
            					keep_alive = true
         | 
| 90 | 
            +
            					headers['connection'.freeze] ||= 'keep-alive'.freeze
         | 
| 91 | 
            +
            					headers['keep-alive'.freeze] ||= "timeout=#{(@timeout ||= 3).to_s}"
         | 
| 92 | 
            +
            				else
         | 
| 93 | 
            +
            					headers['connection'.freeze] ||= 'close'.freeze
         | 
| 94 | 
            +
            				end
         | 
| 94 95 |  | 
| 95 | 
            -
            			send_headers response
         | 
| 96 | 
            -
            			return log_finished(response) if request.head?
         | 
| 97 | 
            -
            			(response.bytes_written += (write(body) || 0)) && (body.frozen? || body.clear) if body
         | 
| 98 | 
            -
            			close unless keep_alive
         | 
| 99 | 
            -
            			log_finished response
         | 
| 100 | 
            -
            		end
         | 
| 101 | 
            -
            		def stream_response response, finish = false
         | 
| 102 | 
            -
            			unless response.headers.frozen?
         | 
| 103 | 
            -
            				response['transfer-encoding'.freeze] = 'chunked'
         | 
| 104 | 
            -
            				response.headers['connection'.freeze] = 'close'.freeze
         | 
| 105 96 | 
             
            				send_headers response
         | 
| 106 | 
            -
            				 | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
            			body = response.extract_body
         | 
| 110 | 
            -
            			response.bytes_written += stream_data(body) if body || finish
         | 
| 111 | 
            -
            			if finish
         | 
| 112 | 
            -
            				response.bytes_written += stream_data('') unless body.nil?
         | 
| 97 | 
            +
            				return log_finished(response) if request.head?
         | 
| 98 | 
            +
            				(response.bytes_written += (write(body) || 0)) && (body.frozen? || body.clear) if body
         | 
| 99 | 
            +
            				close unless keep_alive
         | 
| 113 100 | 
             
            				log_finished response
         | 
| 114 101 | 
             
            			end
         | 
| 115 | 
            -
            			 | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 102 | 
            +
            			def stream_response response, finish = false
         | 
| 103 | 
            +
            				unless response.headers.frozen?
         | 
| 104 | 
            +
            					response['transfer-encoding'.freeze] = 'chunked'
         | 
| 105 | 
            +
            					response.headers['connection'.freeze] = 'close'.freeze
         | 
| 106 | 
            +
            					send_headers response
         | 
| 107 | 
            +
            					@refuse_requests = true
         | 
| 108 | 
            +
            				end
         | 
| 109 | 
            +
            				return if response.request.head?
         | 
| 110 | 
            +
            				body = response.extract_body
         | 
| 111 | 
            +
            				response.bytes_written += stream_data(body) if body || finish
         | 
| 112 | 
            +
            				if finish
         | 
| 113 | 
            +
            					response.bytes_written += stream_data('') unless body.nil?
         | 
| 114 | 
            +
            					log_finished response
         | 
| 115 | 
            +
            					close unless response.keep_alive
         | 
| 116 | 
            +
            				end
         | 
| 117 | 
            +
            				(body.frozen? || body.clear) if body
         | 
| 118 | 
            +
            				true
         | 
| 119 | 
            +
            			end
         | 
| 118 120 |  | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 121 | 
            +
            			protected
         | 
| 122 | 
            +
             | 
| 123 | 
            +
            			HTTP_METHODS = %w{GET HEAD POST PUT DELETE TRACE OPTIONS CONNECT PATCH}
         | 
| 124 | 
            +
            			HTTP_METHODS_REGEXP = /\A#{HTTP_METHODS.join('|')}/i
         | 
| 125 | 
            +
             | 
| 126 | 
            +
            			def dispatch request, data
         | 
| 127 | 
            +
            				return data.string.clear if @io.closed? || @refuse_requests
         | 
| 128 | 
            +
            				::Iodine::Http::Request.parse request
         | 
| 129 | 
            +
            				#check for server-responses
         | 
| 130 | 
            +
            				case request[:method]
         | 
| 131 | 
            +
            				when 'TRACE'.freeze
         | 
| 132 | 
            +
            					close
         | 
| 133 | 
            +
            					data.string.clear
         | 
| 134 | 
            +
            					return false
         | 
| 135 | 
            +
            				when 'OPTIONS'.freeze
         | 
| 136 | 
            +
            					response = ::Iodine::Http::Response.new request
         | 
| 137 | 
            +
            					response[:Allow] = 'GET,HEAD,POST,PUT,DELETE,OPTIONS'.freeze
         | 
| 138 | 
            +
            					response['access-control-allow-origin'.freeze] = '*'
         | 
| 139 | 
            +
            					response['content-length'.freeze] = 0
         | 
| 140 | 
            +
            					send_response response
         | 
| 141 | 
            +
            					return false
         | 
| 142 | 
            +
            				end
         | 
| 134 143 | 
             
            				response = ::Iodine::Http::Response.new request
         | 
| 135 | 
            -
            				 | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
             | 
| 146 | 
            -
            				else
         | 
| 147 | 
            -
            					ret = self.class.on_http.call(request, response)
         | 
| 148 | 
            -
            					if ret.is_a?(String)
         | 
| 149 | 
            -
            						response << ret
         | 
| 150 | 
            -
            					elsif ret == false
         | 
| 151 | 
            -
            						response.clear && (response.status = 404) && (response <<  ::Iodine::Http::Response::STATUS_CODES[404])
         | 
| 144 | 
            +
            				begin
         | 
| 145 | 
            +
            					if request.websocket?
         | 
| 146 | 
            +
            						@refuse_requests = true
         | 
| 147 | 
            +
            						::Iodine::Http::Websockets.handshake request, response, ::Iodine::Http.on_websocket.call(request, response)
         | 
| 148 | 
            +
            					else
         | 
| 149 | 
            +
            						ret = ::Iodine::Http.on_http.call(request, response)
         | 
| 150 | 
            +
            						if ret.is_a?(String)
         | 
| 151 | 
            +
            							response << ret
         | 
| 152 | 
            +
            						elsif ret == false
         | 
| 153 | 
            +
            							response.clear && (response.status = 404) && (response <<  ::Iodine::Http::Response::STATUS_CODES[404])
         | 
| 154 | 
            +
            						end
         | 
| 152 155 | 
             
            					end
         | 
| 156 | 
            +
            					send_response response
         | 
| 157 | 
            +
            				rescue => e
         | 
| 158 | 
            +
            					Iodine.error e
         | 
| 159 | 
            +
            					send_response ::Iodine::Http::Response.new(request, 500, {},  ::Iodine::Http::Response::STATUS_CODES[500])
         | 
| 153 160 | 
             
            				end
         | 
| 154 | 
            -
            				send_response response
         | 
| 155 | 
            -
            			rescue => e
         | 
| 156 | 
            -
            				Iodine.error e
         | 
| 157 | 
            -
            				send_response ::Iodine::Http::Response.new(request, 500, {},  ::Iodine::Http::Response::STATUS_CODES[500])
         | 
| 158 161 | 
             
            			end
         | 
| 159 | 
            -
            		end
         | 
| 160 162 |  | 
| 161 | 
            -
             | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 163 | 
            +
            			def send_headers response
         | 
| 164 | 
            +
            				return false if response.headers.frozen?
         | 
| 165 | 
            +
            				request = response.request
         | 
| 166 | 
            +
            				headers = response.headers
         | 
| 165 167 |  | 
| 166 | 
            -
             | 
| 168 | 
            +
            				# response['date'.freeze] ||= request[:time_recieved].httpdate
         | 
| 167 169 |  | 
| 168 | 
            -
             | 
| 170 | 
            +
            				out = "HTTP/#{request[:version]} #{response.status} #{::Iodine::Http::Response::STATUS_CODES[response.status] || 'unknown'}\r\n"
         | 
| 169 171 |  | 
| 170 | 
            -
             | 
| 172 | 
            +
            				out << request[:time_recieved].utc.strftime("Date: %a, %d %b %Y %H:%M:%S GMT\r\n".freeze) unless headers['date'.freeze]
         | 
| 171 173 |  | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
             | 
| 176 | 
            -
             | 
| 174 | 
            +
            				# unless @headers['connection'] || (@request[:version].to_f <= 1 && (@request['connection'].nil? || !@request['connection'].match(/^k/i))) || (@request['connection'] && @request['connection'].match(/^c/i))
         | 
| 175 | 
            +
            				headers.each {|k,v| out << "#{k.to_s}: #{v}\r\n"}
         | 
| 176 | 
            +
            				out << "cache-control: max-age=0, no-cache\r\n".freeze unless headers['cache-control'.freeze]
         | 
| 177 | 
            +
            				response.extract_cookies.each {|cookie| out << "set-cookie: #{cookie}\r\n"}
         | 
| 178 | 
            +
            				out << "\r\n"
         | 
| 177 179 |  | 
| 178 | 
            -
             | 
| 179 | 
            -
             | 
| 180 | 
            -
             | 
| 181 | 
            -
             | 
| 182 | 
            -
             | 
| 183 | 
            -
             | 
| 184 | 
            -
             | 
| 185 | 
            -
             | 
| 180 | 
            +
            				response.bytes_written += (write(out) || 0)
         | 
| 181 | 
            +
            				out.clear
         | 
| 182 | 
            +
            				headers.freeze
         | 
| 183 | 
            +
            				response.raw_cookies.freeze
         | 
| 184 | 
            +
            			end
         | 
| 185 | 
            +
            			def stream_data data = nil
         | 
| 186 | 
            +
            				 write("#{data.to_s.bytesize.to_s(16)}\r\n#{data.to_s}\r\n") || 0
         | 
| 187 | 
            +
            			end
         | 
| 186 188 |  | 
| 187 | 
            -
             | 
| 188 | 
            -
             | 
| 189 | 
            -
             | 
| 190 | 
            -
             | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 189 | 
            +
            			def log_finished response
         | 
| 190 | 
            +
            				@bytes_sent = 0
         | 
| 191 | 
            +
            				request = response.request
         | 
| 192 | 
            +
            				return if Iodine.logger.nil? || request[:no_log]
         | 
| 193 | 
            +
            				t_n = Time.now
         | 
| 194 | 
            +
            				Iodine.log("#{request[:client_ip]} [#{t_n.utc}] \"#{request[:method]} #{request[:original_path]} #{request[:scheme]}\/#{request[:version]}\" #{response.status} #{response.bytes_written.to_s} #{((t_n - request[:time_recieved])*1000).round(2)}ms\n").clear
         | 
| 195 | 
            +
            			end
         | 
| 193 196 | 
             
            		end
         | 
| 194 197 | 
             
            	end
         | 
| 195 198 | 
             
            end
         | 
    
        data/lib/iodine/http/http2.rb
    CHANGED
    
    
| @@ -1,16 +1,17 @@ | |
| 1 1 | 
             
            module Iodine
         | 
| 2 2 |  | 
| 3 | 
            -
            	 | 
| 3 | 
            +
            	module Http
         | 
| 4 4 | 
             
            		# This (will be) a Rack handler for the Iodine HTTP server.
         | 
| 5 5 | 
             
            		module Rack
         | 
| 6 6 | 
             
            			module_function
         | 
| 7 7 | 
             
            			def run(app, options = {})
         | 
| 8 8 | 
             
            				@app = app
         | 
| 9 9 |  | 
| 10 | 
            -
            				Iodine.protocol ||= Iodine::HTTP
         | 
| 11 10 | 
             
            				Iodine.threads = 18
         | 
| 12 | 
            -
            				 | 
| 13 | 
            -
            				Iodine.protocol | 
| 11 | 
            +
            				Iodine.port = options[:Port]
         | 
| 12 | 
            +
            				Iodine.protocol ||= Iodine::Http::Http1
         | 
| 13 | 
            +
            				@pre_rack_handler = Iodine::Http.on_http
         | 
| 14 | 
            +
            				Iodine::Http.on_http self
         | 
| 14 15 | 
             
            				true
         | 
| 15 16 | 
             
            			end
         | 
| 16 17 | 
             
            			def call request, response
         | 
| @@ -23,10 +24,10 @@ module Iodine | |
| 23 24 | 
             
            				raise "Rack app returned an unexpected value: #{res.to_s}" unless res && res.is_a?(Array)
         | 
| 24 25 | 
             
            				response.status = res[0]
         | 
| 25 26 | 
             
            				response.headers.clear
         | 
| 26 | 
            -
            				response.headers. | 
| 27 | 
            +
            				res[1].each {|k, v| response.headers[k.to_s.downcase] = v }
         | 
| 27 28 | 
             
            				response.body = res[2]
         | 
| 28 29 | 
             
            				response.raw_cookies.clear
         | 
| 29 | 
            -
            				response.headers[' | 
| 30 | 
            +
            				response.headers['set-cookie'] = response.headers.delete('set-cookie').split("\n").join("\r\nset-cookie: ") if request[:io].is_a?(Iodine::Http::Http1) && response.headers['set-cookie']
         | 
| 30 31 | 
             
            				response.request[:no_log] = true
         | 
| 31 32 | 
             
            				true
         | 
| 32 33 | 
             
            			end
         | 
    
        data/lib/iodine/http/request.rb
    CHANGED
    
    
    
        data/lib/iodine/http/response.rb
    CHANGED
    
    | @@ -1,7 +1,5 @@ | |
| 1 1 | 
             
            module Iodine
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            	class Http < ::Iodine::Protocol
         | 
| 4 | 
            -
             | 
| 2 | 
            +
            	module Http
         | 
| 5 3 | 
             
            		# this class handles Http responses.
         | 
| 6 4 | 
             
            		#
         | 
| 7 5 | 
             
            		# The response can be sent in stages but should complete within the scope of the connecton's message. Please notice that headers and status cannot be changed once the response started sending data.
         | 
    
        data/lib/iodine/http/session.rb
    CHANGED
    
    
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            module Iodine
         | 
| 2 | 
            -
            	 | 
| 2 | 
            +
            	module Http
         | 
| 3 3 | 
             
            		class Websockets < ::Iodine::Protocol
         | 
| 4 4 | 
             
            			# initialize the websocket protocol.
         | 
| 5 5 | 
             
            			def initialize io, handler, request, ws_extentions = nil
         | 
| @@ -159,7 +159,7 @@ module Iodine | |
| 159 159 | 
             
            				else
         | 
| 160 160 | 
             
            					ws_extentions = nil
         | 
| 161 161 | 
             
            				end
         | 
| 162 | 
            -
            				response[' | 
| 162 | 
            +
            				response['sec-websocket-accept'.freeze] = Digest::SHA1.base64digest(request['sec-websocket-key'.freeze] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'.freeze)
         | 
| 163 163 | 
             
            				response.session
         | 
| 164 164 | 
             
            				# Iodine.log "#{@request[:client_ip]} [#{Time.now.utc}] - #{@connection.object_id} Upgraded HTTP to WebSockets.\n"
         | 
| 165 165 | 
             
            				response.finish
         | 
    
        data/lib/iodine/io.rb
    CHANGED
    
    | @@ -26,7 +26,7 @@ module Iodine | |
| 26 26 |  | 
| 27 27 | 
             
            	protected
         | 
| 28 28 |  | 
| 29 | 
            -
            	@port = (ARGV.index('-p') && ARGV[ARGV.index('-p') + 1]) || ENV['PORT'] || 3000
         | 
| 29 | 
            +
            	@port = ((ARGV.index('-p') && ARGV[ARGV.index('-p') + 1]) || ENV['PORT'] || 3000).to_i
         | 
| 30 30 | 
             
            	@bind = (ARGV.index('-ip') && ARGV[ARGV.index('-ip') + 1]) || ENV['IP'] || "0.0.0.0"
         | 
| 31 31 | 
             
            	@ssl = (ARGV.index('ssl') && true) || (@port == 443)
         | 
| 32 32 | 
             
            	@protocol = nil
         | 
| @@ -75,13 +75,15 @@ module Iodine | |
| 75 75 | 
             
            			def on_open
         | 
| 76 76 | 
             
            				@protocol = Iodine.protocol
         | 
| 77 77 | 
             
            				@ssl = Iodine.ssl
         | 
| 78 | 
            +
            				@accept_proc = @protocol.method(:accept)
         | 
| 78 79 | 
             
            			end
         | 
| 79 80 | 
             
            			def call
         | 
| 80 81 | 
             
            				begin
         | 
| 81 82 | 
             
            					n_io = nil
         | 
| 82 83 | 
             
            					loop do
         | 
| 83 84 | 
             
            						n_io = @io.accept_nonblock
         | 
| 84 | 
            -
            						@protocol.accept(n_io, @ssl)
         | 
| 85 | 
            +
            						# @protocol.accept(n_io, @ssl)
         | 
| 86 | 
            +
            						Iodine.run n_io, @ssl, &(@accept_proc)
         | 
| 85 87 | 
             
            					end
         | 
| 86 88 | 
             
            				rescue Errno::EWOULDBLOCK => e
         | 
| 87 89 |  | 
    
        data/lib/iodine/protocol.rb
    CHANGED
    
    | @@ -175,6 +175,9 @@ module Iodine | |
| 175 175 | 
             
            		# Normally you won't need to override this method.
         | 
| 176 176 | 
             
            		def self.accept io, ssl
         | 
| 177 177 | 
             
            			ssl ? SSLConnector.new(io, self) :  self.new(io)
         | 
| 178 | 
            +
            		rescue
         | 
| 179 | 
            +
            			io.close unless io.closed?
         | 
| 180 | 
            +
            			raise
         | 
| 178 181 | 
             
            		end
         | 
| 179 182 | 
             
            		# This methos updates the timeout "watch", signifying the IO was active.
         | 
| 180 183 | 
             
            		def touch
         | 
    
        data/lib/iodine/version.rb
    CHANGED