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)