Class: Reel::Connection

Inherits:
Object
  • Object
show all
Includes:
ConnectionMixin, HTTPVersionsMixin
Defined in:
lib/reel/connection.rb

Overview

A connection to the HTTP server

Constant Summary collapse

CONNECTION =
'Connection'.freeze
TRANSFER_ENCODING =
'Transfer-Encoding'.freeze
KEEP_ALIVE =
'Keep-Alive'.freeze
CLOSE =
'close'.freeze
BUFFER_SIZE =

Attempt to read this much data

16384

Constants included from HTTPVersionsMixin

HTTPVersionsMixin::DEFAULT_HTTP_VERSION, HTTPVersionsMixin::HTTP_VERSION_1_0, HTTPVersionsMixin::HTTP_VERSION_1_1

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ConnectionMixin

#remote_host, #remote_ip

Constructor Details

#initialize(socket, buffer_size = nil) ⇒ Connection

Returns a new instance of Connection.



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/reel/connection.rb', line 21

def initialize(socket, buffer_size = nil)
  @attached    = true
  @socket      = socket
  @keepalive   = true
  @buffer_size = buffer_size || BUFFER_SIZE
  @parser      = Request::Parser.new(self)
  @request_fsm = Request::StateMachine.new(@socket)

  reset_request
  @response_state = :headers
end

Instance Attribute Details

#buffer_sizeObject (readonly)

Returns the value of attribute buffer_size.



19
20
21
# File 'lib/reel/connection.rb', line 19

def buffer_size
  @buffer_size
end

#current_requestObject (readonly)

Returns the value of attribute current_request.



14
15
16
# File 'lib/reel/connection.rb', line 14

def current_request
  @current_request
end

#parserObject (readonly)

Returns the value of attribute parser.



14
15
16
# File 'lib/reel/connection.rb', line 14

def parser
  @parser
end

#request_stateObject

Returns the value of attribute request_state.



15
16
17
# File 'lib/reel/connection.rb', line 15

def request_state
  @request_state
end

#response_stateObject

Returns the value of attribute response_state.



15
16
17
# File 'lib/reel/connection.rb', line 15

def response_state
  @response_state
end

#socketObject (readonly)

Raw access to the underlying socket

Raises:



155
156
157
# File 'lib/reel/connection.rb', line 155

def socket
  @socket
end

Instance Method Details

#alive?Boolean

Is the connection still active?

Returns:

  • (Boolean)


34
# File 'lib/reel/connection.rb', line 34

def alive?; @keepalive; end

#attached?Boolean

Is the connection still attached to a Reel::Server?

Returns:

  • (Boolean)


37
# File 'lib/reel/connection.rb', line 37

def attached?; @attached; end

#closeObject

Close the connection

Raises:



132
133
134
135
136
137
# File 'lib/reel/connection.rb', line 132

def close
  raise StateError, "socket has been hijacked from this connection" unless @socket

  @keepalive = false
  @socket.close unless @socket.closed?
end

#detachObject

Detach this connection from the Reel::Server and manage it independently



40
41
42
43
# File 'lib/reel/connection.rb', line 40

def detach
  @attached = false
  self
end

#each_requestObject

Enumerate the requests from this connection, since we might receive many if the client is using keep-alive



71
72
73
74
75
76
77
78
79
80
# File 'lib/reel/connection.rb', line 71

def each_request
  while req = request
    yield req

    # Websockets upgrade the connection to the Websocket protocol
    # Once we have finished processing a Websocket, we can't handle
    # additional requests
    break if req.websocket?
  end
end

#hijack_socketObject

Hijack the socket from the connection



140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/reel/connection.rb', line 140

def hijack_socket
  # FIXME: this doesn't do a great job of ensuring we can hijack the socket
  # in its current state. Improve the state detection.
  if @request_fsm != :ready && @response_state != :headers
    raise StateError, "connection is not in a hijackable state"
  end

  @request_fsm.transition :hijacked
  @response_state = :hijacked
  socket  = @socket
  @socket = nil
  socket
end

#readpartial(size = @buffer_size) ⇒ Object



45
46
47
48
49
50
51
# File 'lib/reel/connection.rb', line 45

def readpartial(size = @buffer_size)
  unless @request_fsm.state == :headers || @request_fsm.state == :body
    raise StateError, "can't read in the '#{@request_fsm.state}' request state"
  end

  @parser.readpartial(size)
end

#requestObject

Read a request object from the connection



54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/reel/connection.rb', line 54

def request
  raise StateError, "already processing a request" if current_request

  req = @parser.current_request
  @request_fsm.transition :headers
  @keepalive = false if req[CONNECTION] == CLOSE || req.version == HTTP_VERSION_1_0
  @current_request = req

  req
rescue IOError, Errno::ECONNRESET, Errno::EPIPE
  @request_fsm.transition :closed
  @keepalive = false
  nil
end

#respond(response, headers_or_body = {}, body = nil) ⇒ Object

Send a response back to the client Response can be a symbol indicating the status code or a Reel::Response



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/reel/connection.rb', line 84

def respond(response, headers_or_body = {}, body = nil)
  raise StateError, "not in header state" if @response_state != :headers

  if headers_or_body.is_a? Hash
    headers = headers_or_body
  else
    headers = {}
    body = headers_or_body
  end

  if @keepalive
    headers[CONNECTION] = KEEP_ALIVE
  else
    headers[CONNECTION] = CLOSE
  end

  case response
  when Symbol, Fixnum, Integer
    response = Response.new(response, headers, body)
  when Response
  else raise TypeError, "invalid response: #{response.inspect}"
  end

  if current_request
    current_request.handle_response(response)
  else
    raise RequestError
  end

  # Enable streaming mode
  if response.chunked? and response.body.nil?
    @response_state = :chunked_body
  elsif @keepalive
    reset_request
  else
    @current_request = nil
    @parser.reset
    @request_fsm.transition :closed
  end
rescue IOError, SystemCallError, RequestError
  # The client disconnected early, or there is no request
  @keepalive = false
  @request_fsm.transition :closed
  @parser.reset
  @current_request = nil
end