lib/reel/connection.rb in reel-0.1.0 vs lib/reel/connection.rb in reel-0.2.0.pre
- old
+ new
@@ -1,66 +1,82 @@
module Reel
# A connection to the HTTP server
class Connection
class StateError < RuntimeError; end # wrong state for a given operation
- attr_reader :request
+ attr_reader :socket, :parser
# Attempt to read this much data
BUFFER_SIZE = 4096
def initialize(socket)
- @socket = socket
+ @attached = true
+ @socket = socket
@keepalive = true
- @parser = Request::Parser.new
+ @parser = Request::Parser.new
reset_request
@response_state = :header
@body_remaining = nil
end
# Is the connection still active?
def alive?; @keepalive; end
+ # Is the connection still attached to a Reel::Server?
+ def attached?; @attached; end
+
+ # Detach this connection from the Reel::Server and manage it independently
+ def detach
+ @attached = false
+ self
+ end
+
# Reset the current request state
- def reset_request
- @request_state = :header
- @request = nil
+ def reset_request(state = :header)
+ @request_state = state
+ @header_buffer = "" # Buffer headers in case of an upgrade request
@parser.reset
end
- # Read a request object from the connection
- def read_request
- raise StateError, "can't read header" unless @request_state == :header
+ def peer_address
+ @socket.peeraddr(false)
+ end
- begin
- @parser << @socket.readpartial(BUFFER_SIZE) until @parser.headers
- rescue IOError, Errno::ECONNRESET, Errno::EPIPE
- @keepalive = false
- @socket.close unless @socket.closed?
- return
- end
+ def local_address
+ @socket.addr(false)
+ end
- @request_state = :body
+ # Read a request object from the connection
+ def request
+ return if @request_state == :websocket
+ req = Request.read(self)
- headers = {}
- @parser.headers.each do |field, value|
- headers[Http.canonicalize_header(field)] = value
+ case req
+ when Request
+ @request_state = :body
+ @keepalive = false if req['Connection'] == 'close' || req.version == "1.0"
+ @body_remaining = Integer(req['Content-Length']) if req['Content-Length']
+ when WebSocket
+ @request_state = @response_state = :websocket
+ @body_remaining = nil
+ @socket = nil
+ else raise "unexpected request type: #{req.class}"
end
- if headers['Connection']
- @keepalive = false if headers['Connection'] == 'close'
- elsif @parser.http_version == "1.0"
- @keepalive = false
- end
-
- @body_remaining = Integer(headers['Content-Length']) if headers['Content-Length']
- @request = Request.new(@parser.http_method, @parser.url, @parser.http_version, headers, self)
+ req
+ rescue IOError, Errno::ECONNRESET, Errno::EPIPE
+ # The client is disconnected
+ @request_state = :closed
+ @keepalive = false
+ nil
end
# Read a chunk from the request
def readpartial(size = BUFFER_SIZE)
+ raise StateError, "can't read in the `#{@request_state}' state" unless @request_state == :body
+
if @body_remaining and @body_remaining > 0
chunk = @parser.chunk
unless chunk
@parser << @socket.readpartial(size)
chunk = @parser.chunk
@@ -108,14 +124,13 @@
rescue IOError, Errno::ECONNRESET, Errno::EPIPE
# The client disconnected early
@keepalive = false
ensure
if @keepalive
- reset_request
- @request_state = :header
+ reset_request(:header)
else
@socket.close unless @socket.closed?
- @request_state = :closed
+ reset_request(:closed)
end
end
# Write body chunks directly to the connection
def write(chunk)