Class: Reel::Connection

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

Overview

A connection to the HTTP server

Defined Under Namespace

Classes: StateError

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.



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

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

  reset_request
  @response_state = :header
end

Instance Attribute Details

#buffer_sizeObject (readonly)

Returns the value of attribute buffer_size.



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

def buffer_size
  @buffer_size
end

#parserObject (readonly)

Returns the value of attribute parser.



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

def parser
  @parser
end

#socketObject (readonly)

Raw access to the underlying socket

Raises:



163
164
165
# File 'lib/reel/connection.rb', line 163

def socket
  @socket
end

Instance Method Details

#alive?Boolean

Is the connection still active?

Returns:

  • (Boolean)


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

def alive?; @keepalive; end

#attached?Boolean

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

Returns:

  • (Boolean)


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

def attached?; @attached; end

#closeObject

Close the connection

Raises:



141
142
143
144
145
146
# File 'lib/reel/connection.rb', line 141

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

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

#current_requestObject



49
50
51
# File 'lib/reel/connection.rb', line 49

def current_request
  @current_request
end

#detachObject

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



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

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



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

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

#finish_responseObject

Finish the response and reset the response state to header

Raises:



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

def finish_response
  raise StateError, "not in body state" if @response_state != :chunked_body
  @writer.finish_response
  @response_state = :header
end

#hijack_socketObject

Hijack the socket from the connection



149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/reel/connection.rb', line 149

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_state != :ready && @response_state != :header
    raise StateError, "connection is not in a hijackable state"
  end

  @request_state = @response_state = :hijacked
  socket  = @socket
  @socket = nil
  socket
end

#readpartial(size = @buffer_size) ⇒ Object

Raises:



44
45
46
47
# File 'lib/reel/connection.rb', line 44

def readpartial(size = @buffer_size)
  raise StateError, "can't read in the '#{@request_state}' request state" unless @request_state == :ready
  @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
68
# File 'lib/reel/connection.rb', line 54

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

  req = @parser.current_request
  @request_state = :ready
  @keepalive = false if req[CONNECTION] == CLOSE || req.version == HTTP_VERSION_1_0
  @current_request = req

  req
rescue IOError, Errno::ECONNRESET, Errno::EPIPE
  # The client is disconnected
  @request_state = :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



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
# File 'lib/reel/connection.rb', line 85

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

  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
    response = Response.new(response, headers, body)
  when Response
  else raise TypeError, "invalid response: #{response.inspect}"
  end

  @writer.handle_response(response)

  # Enable streaming mode
  if response.chunked? and response.body.nil?
    @response_state = :chunked_body
  end
rescue IOError, Errno::ECONNRESET, Errno::EPIPE
  # The client disconnected early
  @keepalive = false
ensure
  if @keepalive
    reset_request(:ready)
  else
    @socket.close unless @socket.closed?
    reset_request(:closed)
  end
end

#write(chunk) ⇒ Object Also known as: <<

Write body chunks directly to the connection

Raises:



127
128
129
130
# File 'lib/reel/connection.rb', line 127

def write(chunk)
  raise StateError, "not in chunked body mode" unless @response_state == :chunked_body
  @writer.write(chunk)
end