| Module | ServerSide::HTTP::Server |
| In: |
lib/serverside/http/server.rb
|
The HTTP server is implemented as a simple state-machine with the following states: state_initial - initialize request variables. state_request_line - wait for and parse the request line. state_request_headers - wait for and parse header lines. state_request_body - wait for and parse the request body. state_response - send a response. state_done - the connection is closed.
The server supports persistent connections (if the request is in HTTP 1.1). In that case, after responding to the request the state is changed back to request_line.
| request | [R] | attribute readers |
| response_sent | [R] | attribute readers |
Creates a new server module
# File lib/serverside/http/server.rb, line 23
23: def self.new
24: Module.new do
25: # include the HTTP state machine and everything else
26: include ServerSide::HTTP::Server
27:
28: # define a start method for starting the server
29: def self.start(addr, port)
30: EventMachine::run do
31: EventMachine::start_server addr, port, self
32: end
33: end
34:
35: # invoke the supplied block for application-specific behaviors.
36: yield
37: end
38: end
Handle errors raised while processing a request
# File lib/serverside/http/server.rb, line 73
73: def handle_error(e)
74: # if an error is raised, we send an error response
75: unless @response_sent || @streaming
76: send_response(Response.error(e))
77: end
78: end
receive_data is a callback invoked whenever new data is received on the connection. The incoming data is added to the @in buffer and the state method is invoked.
# File lib/serverside/http/server.rb, line 56
56: def receive_data(data)
57: @in << data
58: send(@state)
59: rescue => e
60: handle_error(e)
61: end
# File lib/serverside/http/server.rb, line 140
140: def send_response(resp)
141: persistent = @request.persistent && resp.persistent?
142: if !persistent
143: resp.headers << CONNECTION_CLOSE
144: end
145: send_data(resp.to_s)
146: @response_sent = true
147: if resp.streaming?
148: start_stream_loop(resp.stream_period, resp.stream_proc)
149: else
150: set_state(persistent ? :state_initial : :state_done)
151: end
152: end
starts implements a periodical timer. The timer is invoked until the supplied block returns false or nil. When the
# File lib/serverside/http/server.rb, line 161
161: def start_stream_loop(period, block)
162: @streaming = true
163: if block.call(self)
164: EventMachine::add_timer(period) {start_stream_loop(period, block)}
165: else
166: set_state(:state_done)
167: end
168: end
state_done closes the connection.
# File lib/serverside/http/server.rb, line 155
155: def state_done
156: close_connection_after_writing
157: end
state_initial creates a new request instance and immediately transitions to the request_line state.
# File lib/serverside/http/server.rb, line 82
82: def state_initial
83: @request = ServerSide::HTTP::Request.new(self)
84: @response_sent = false
85: set_state(:state_request_line)
86: end
state_request_body waits for the request body to arrive and then parses the body. Once the body is parsed, the connection transitions to the response state.
# File lib/serverside/http/server.rb, line 123
123: def state_request_body
124: if @in.size >= @request.content_length
125: @request.parse_body(@in.slice!(0...@request.content_length))
126: set_state(:state_response)
127: end
128: end
state_request_headers parses each header as it arrives. If too many headers are included or a header exceeds the maximum header size, an error is raised.
# File lib/serverside/http/server.rb, line 105
105: def state_request_headers
106: while line = @in.get_line
107: # Check header size
108: if line.size > MAX_HEADER_SIZE
109: raise BadRequestError, "Invalid header size"
110: # If the line empty then we move to the next state
111: elsif line.empty?
112: expecting_body = @request.content_length.to_i > 0
113: set_state(expecting_body ? :state_request_body : :state_response)
114: else
115: @request.parse_header(line)
116: end
117: end
118: end
state_request_line waits for the HTTP request line and parses it once it arrives. If the request line is too big, an error is raised. The request line supplies information including the
# File lib/serverside/http/server.rb, line 91
91: def state_request_line
92: # check request line size
93: if line = @in.get_line
94: if line.size > MAX_REQUEST_LINE_SIZE
95: raise BadRequestError, "Invalid request size"
96: end
97: @request.parse_request_line(line)
98: set_state(:state_request_headers)
99: end
100: end
state_response invokes the handle method. If no response was sent, an error is raised. After the response is sent, the connection is either closed or goes back to the initial state.
# File lib/serverside/http/server.rb, line 133
133: def state_response
134: unless resp = handle(@request)
135: raise "No handler found for this URI (#{@request.url})"
136: end
137: send_response(resp)
138: end