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
CLOSE =
'close'.freeze
VERSION =
"HTTP/1.1".freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream) ⇒ HTTP11

Returns a new instance of HTTP11.



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

def initialize(stream)
	super(stream, CRLF)
	
	@persistent = true
	@count = 0
end

Instance Attribute Details

#count ⇒ Object (readonly)

Returns the value of attribute count.



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

def count
  @count
end

Instance Method Details

#call(request) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/async/http/protocol/http11.rb', line 97

def call(request)
	@count += 1
	
	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
	Async.logger.debug(self) {"Connection failed with EOFError after #{@count} requests."}
	return nil
end

#multiplex ⇒ Object

Only one simultaneous connection at a time.



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

def multiplex
	1
end

#persistent?(headers) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/async/http/protocol/http11.rb', line 68

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

#read_request ⇒ Object



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

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_response ⇒ Object



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

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

#receive_requests(task: Task.current) ⇒ Object

Server loop.



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

def receive_requests(task: Task.current)
	while true
		request = Request.new(*read_request)
		@count += 1
		
		response = yield request
		
		response.version ||= request.version
		
		write_response(response.version, response.status, response.headers, response.body)
		
		request.finish
		
		unless persistent?(request.headers) and persistent?(response.headers)
			@persistent = 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)


51
52
53
# File 'lib/async/http/protocol/http11.rb', line 51

def reusable?
	@persistent
end

#version ⇒ Object



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

def version
	VERSION
end

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



111
112
113
114
115
116
117
118
119
120
121
# File 'lib/async/http/protocol/http11.rb', line 111

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



141
142
143
144
145
146
147
148
149
# File 'lib/async/http/protocol/http11.rb', line 141

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