Class: Iodine::Http
- Defined in:
- lib/iodine/http.rb,
lib/iodine/http/hpack.rb,
lib/iodine/http/http1.rb,
lib/iodine/http/http2.rb,
lib/iodine/http/request.rb,
lib/iodine/http/session.rb,
lib/iodine/http/response.rb,
lib/iodine/http/websockets.rb,
lib/iodine/http/rack_support.rb,
lib/iodine/http/websocket_client.rb,
lib/iodine/http/websocket_handler.rb
Overview
The Http class allows the creation of Http and Websocket servers using Iodine.
To start an Http server, simply require ‘iodine/http` (which isn’t required by default) and set up your Http callback. i.e.:
require 'iodine/http'
Iodine::Http.on_http { |request, response| 'Hello World!' }
exit # only if running from irb
To start a Websocket server, require ‘iodine/http` (which isn’t required by default), create a Websocket handling Class and set up your Websocket callback. i.e.:
require 'iodine/http'
class WSChatServer
def initialize nickname, response
@nickname = nickname || "unknown"
@response = response
# @response.io # => Http Protocol
end
def on_open
# only now is the response.io pointing at the Websocket Protocol
@io = @response.io
@io.broadcast "#{@nickname} has joined the chat!"
@io << "Welcome #{@nickname}, you have joined the chat!"
end
def data
@io.broadcast "#{@nickname} >> #{data}"
@io << ">> #{data}"
end
def on_broadcast data
# the http response can also be used to send websocket data.
@response << data
end
def on_close
@io.broadcast "#{@nickname} has left the chat!"
end
end
Iodine::Http.on_websocket { |request, response| WSChatServer.new request.params[:name], response}
See WebsocketHandler for a good starting point or inherit WebsocketHandler in your handler.
Defined Under Namespace
Modules: Rack, SessionManager Classes: Http2, Request, Response, WebsocketClient, WebsocketHandler, Websockets
Instance Attribute Summary
Attributes inherited from Protocol
Class Method Summary collapse
-
.http2 ⇒ Object
Returns true if Iodine will require that new connection be encrypted.
-
.http2=(allow) ⇒ Object
Sets whether Iodine will allow connections to the experiemntal Http2 protocol.
-
.on_http(handler = nil, &block) ⇒ Object
Sets or gets the Http callback.
-
.on_websocket(handler = nil, &block) ⇒ Object
Sets or gets the Websockets callback.
-
.session_token ⇒ Object
Sets the session token for the Http server (String).
-
.session_token=(token) ⇒ Object
Sets the session token for the Http server (String).
-
.ws_connect(url, options = {}, &block) ⇒ Object
Creates a websocket client within a new task (non-blocking).
Instance Method Summary collapse
- #on_message(data) ⇒ Object
- #on_open ⇒ Object
- #send_response(response) ⇒ Object
- #stream_response(response, finish = false) ⇒ Object
Methods inherited from Protocol
#call, #close, #closed?, each, #id, #initialize, #on_close, #on_shutdown, #ping, #read, #set_timeout, #ssl?, #timeout?, #write
Constructor Details
This class inherits a constructor from Iodine::Protocol
Class Method Details
.http2 ⇒ Object
Returns true if Iodine will require that new connection be encrypted.
106 107 108 |
# File 'lib/iodine/http.rb', line 106 def self.http2 @http2 end |
.http2=(allow) ⇒ Object
Sets whether Iodine will allow connections to the experiemntal Http2 protocol. Defaults to false unless the ‘http2` command line flag is present.
102 103 104 |
# File 'lib/iodine/http.rb', line 102 def self.http2= allow @http2 = allow && true end |
.on_http(handler = nil, &block) ⇒ Object
Sets or gets the Http callback.
An Http callback is a Proc like object that answers to ‘call(request, response)` and returns either:
- ‘true`
-
the response has been set by the callback and can be managed (including any streaming) by the server.
- ‘false`
-
the request shouldn’t be answered or resource not found (error 404 will be sent as a response).
- String
-
the String will be appended to the response and the response sent.
78 79 80 81 |
# File 'lib/iodine/http.rb', line 78 def self.on_http handler = nil, &block @http_app = handler || block if handler || block @http_app end |
.on_websocket(handler = nil, &block) ⇒ Object
Sets or gets the Websockets callback.
A Websockets callback is a Proc like object that answers to ‘call(request)` and returns either:
- ‘false`
-
the request shouldn’t be answered or resource not found (error 404 will be sent as a response).
- Websocket Handler
-
a Websocket handler is an object that is expected to answer ‘on_message(data)` and `on_close`. See {} for more data.
87 88 89 90 |
# File 'lib/iodine/http.rb', line 87 def self.on_websocket handler = nil, &block @websocket_app = handler || block if handler || block @websocket_app end |
.session_token ⇒ Object
Sets the session token for the Http server (String). Defaults to the name of the script.
97 98 99 |
# File 'lib/iodine/http.rb', line 97 def self.session_token @session_token end |
.session_token=(token) ⇒ Object
Sets the session token for the Http server (String). Defaults to the name of the script + ‘_id’.
93 94 95 |
# File 'lib/iodine/http.rb', line 93 def self.session_token= token @session_token = token end |
.ws_connect(url, options = {}, &block) ⇒ Object
Creates a websocket client within a new task (non-blocking).
Make sure to setup all the callbacks (as needed) prior to starting the connection. See Iodine::Http::WebsocketClient.connect
i.e.:
require 'iodine/http'
# don't start the server
Iodine.protocol = :timer
= {}
[:on_open] = Proc.new { write "Hello there!"}
[:on_message] = Proc.new do |data|
puts ">> #{data}";
write "Bye!";
# It's possible to update the callback midstream.
{|data| puts "-- Goodbye message: #{data}"; close;}
end
# After closing we will call `Iodine.signal_exit` to signal Iodine to finish up.
[:on_close] = Proc.new { puts "disconnected"; Iodine.signal_exit }
Iodine::Http.ws_connect "ws://echo.websocket.org",
#if running from irb:
exit
135 136 137 |
# File 'lib/iodine/http.rb', line 135 def self.ws_connect url, ={}, &block ::Iodine.run { ::Iodine::Http::WebsocketClient.connect url, , &block } end |
Instance Method Details
#on_message(data) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/iodine/http/http1.rb', line 9 def data return if @refuse_requests @http2_pri_review ||= ( ::Iodine::Http.http2 && ::Iodine::Http::Http2.pre_handshake(self, data) && (return true) ) || true data = ::StringIO.new data until data.eof? request = (@request ||= ::Iodine::Http::Request.new(self)) unless request[:method] l = data.gets.strip next if l.empty? request[:method], request[:query], request[:version] = l.split(/[\s]+/, 3) return (Iodine.warn('Protocol Error, closing connection.') && close) unless request[:method] =~ HTTP_METHODS_REGEXP request[:version] = (request[:version] || '1.1'.freeze).match(/[\d\.]+/)[0] request[:time_recieved] = Time.now end until request[:headers_complete] || (l = data.gets).nil? if l.include? ':' # n = l.slice!(0, l.index(':')); l.slice! 0 # n.strip! ; n.downcase!; n.freeze # request[n] ? (request[n].is_a?(Array) ? (request[n] << l) : request[n] = [request[n], l ]) : (request[n] = l) l = l.strip.split(/:[\s]?/, 2) l[0].strip! ; l[0].downcase!; 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]) elsif l =~ /^[\r]?\n/ request[:headers_complete] = true else #protocol error Iodine.warn 'Protocol Error, closing connection.' return close end end until request[:body_complete] && request[:headers_complete] if request['transfer-coding'.freeze] == 'chunked'.freeze # ad mid chunk logic here if @parser[:length].to_i == 0 chunk = data.gets return false unless chunk @parser[:length] = chunk.to_i(16) return (Iodine.warn('Protocol Error, closing connection.') && close) unless @parser[:length] request[:body_complete] = true && break if @parser[:length] == 0 @parser[:act_length] = 0 request[:body] ||= '' end chunk = data.read(@parser[:length] - @parser[:act_length]) return false unless chunk request[:body] << chunk @parser[:act_length] += chunk.bytesize (@parser[:act_length] = @parser[:length] = 0) && (data.gets) if @parser[:act_length] >= @parser[:length] elsif request['content-length'.freeze] && request['content-length'.freeze].to_i != 0 request[:body] ||= '' packet = data.read(request['content-length'.freeze].to_i - request[:body].bytesize) return false unless packet request[:body] << packet request[:body_complete] = true if request['content-length'.freeze].to_i - request[:body].bytesize <= 0 elsif request['content-type'.freeze] Iodine.warn 'Body type protocol error.' unless request[:body] line = data.gets return false unless line (request[:body] ||= '') << line request[:body_complete] = true if line =~ EOHEADERS else request[:body_complete] = true end end (@request = ::Iodine::Http::Request.new(self)) && ( (::Iodine::Http.http2 && ::Iodine::Http::Http2.handshake(request, self, data)) || dispatch(request, data) ) if request.delete :body_complete end end |
#on_open ⇒ Object
3 4 5 6 7 8 |
# File 'lib/iodine/http/http1.rb', line 3 def on_open set_timeout 1 @refuse_requests = false @bytes_sent = 0 @parser = {} end |
#send_response(response) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/iodine/http/http1.rb', line 77 def send_response response return false if response.headers.frozen? request = response.request headers = response.headers body = response.extract_body headers['content-length'.freeze] ||= body.to_s.bytesize keep_alive = response.keep_alive 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) keep_alive = true headers['connection'.freeze] ||= 'Keep-Alive'.freeze headers['keep-alive'.freeze] ||= "timeout=#{(@timeout ||= 3).to_s}" else headers['connection'.freeze] ||= 'close'.freeze end send_headers response return log_finished(response) if request.head? (response.bytes_written += (write(body) || 0)) && (body.frozen? || body.clear) if body close unless keep_alive log_finished response end |
#stream_response(response, finish = false) ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/iodine/http/http1.rb', line 101 def stream_response response, finish = false unless response.headers.frozen? response['transfer-encoding'.freeze] = 'chunked' response.headers['connection'.freeze] = 'close'.freeze send_headers response @refuse_requests = true end return if response.request.head? body = response.extract_body response.bytes_written += stream_data(body) if body || finish if finish response.bytes_written += stream_data('') unless body.nil? log_finished response end (body.frozen? || body.clear) if body true end |