Class: Async::HTTP::Protocol::HTTP11

Inherits:
IO::Protocol::Line
  • Object
show all
Defined in:
lib/async/http/protocol/http11.rb

Overview

Implements basic HTTP/1.1 request/response.

Direct Known Subclasses

HTTP10

Constant Summary collapse

CRLF =
"\r\n".freeze
KEEP_ALIVE =
'keep-alive'.freeze
CLOSE =
'close'.freeze
VERSION =
"HTTP/1.1".freeze

Instance Method Summary collapse

Constructor Details

#initialize(stream) ⇒ HTTP11

Returns a new instance of HTTP11.



36
37
38
39
40
# File 'lib/async/http/protocol/http11.rb', line 36

def initialize(stream)
  super(stream, CRLF)
  
  @keep_alive = true
end

Instance Method Details

#call(request) ⇒ Object



93
94
95
96
97
98
99
100
101
102
# File 'lib/async/http/protocol/http11.rb', line 93

def call(request)
  request.version ||= self.version
  
  Async.logger.debug(self) {"#{request.method} #{request.path} #{request.headers.inspect}"}
  write_request(request.authority, request.method, request.path, request.version, request.headers, request.body)
  
  return Response.new(*read_response)
rescue EOFError
  return nil
end

#keep_alive?(headers) ⇒ Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/async/http/protocol/http11.rb', line 65

def keep_alive?(headers)
  headers['connection'] != CLOSE
end

#multiplexObject

Only one simultaneous connection at a time.



43
44
45
# File 'lib/async/http/protocol/http11.rb', line 43

def multiplex
  1
end

#read_requestObject



126
127
128
129
130
131
132
# File 'lib/async/http/protocol/http11.rb', line 126

def read_request
  method, path, version = read_line.split(/\s+/, 3)
  headers = read_headers
  body = read_body(headers)
  
  return headers.delete('host'), method, path, version, headers, body
end

#read_responseObject



116
117
118
119
120
121
122
123
124
# File 'lib/async/http/protocol/http11.rb', line 116

def read_response
  version, status, reason = read_line.split(/\s+/, 3)
  headers = read_headers
  body = read_body(headers)
  
  @keep_alive = keep_alive?(headers)
  
  return version, Integer(status), reason, headers, body
end

#receive_requests(task: Task.current) ⇒ Object

Server loop.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/async/http/protocol/http11.rb', line 70

def receive_requests(task: Task.current)
  while true
    request = Request.new(*read_request)
    
    response = yield request
    
    response.version ||= request.version
    
    write_response(response.version, response.status, response.headers, response.body)
    
    request.finish
    
    unless keep_alive?(request.headers) and keep_alive?(response.headers)
      @keep_alive = false
      
      break
    end
    
    # This ensures we yield at least once every iteration of the loop and allow other fibers to execute.
    task.yield
  end
end

#reusable?Boolean

Returns:

  • (Boolean)


47
48
49
# File 'lib/async/http/protocol/http11.rb', line 47

def reusable?
  @keep_alive
end

#versionObject



61
62
63
# File 'lib/async/http/protocol/http11.rb', line 61

def version
  VERSION
end

#write_request(authority, method, path, version, headers, body) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
# File 'lib/async/http/protocol/http11.rb', line 104

def write_request(authority, method, path, version, headers, body)
  @stream.write("#{method} #{path} #{version}\r\n")
  @stream.write("Host: #{authority}\r\n")
  
  write_headers(headers)
  write_body(body)
  
  @stream.flush
  
  return true
end

#write_response(version, status, headers, body) ⇒ Object



134
135
136
137
138
139
140
141
142
# File 'lib/async/http/protocol/http11.rb', line 134

def write_response(version, status, headers, body)
  @stream.write("#{version} #{status}\r\n")
  write_headers(headers)
  write_body(body)
  
  @stream.flush
  
  return true
end