puma 1.6.3 → 2.0.0.b1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- data/History.txt +13 -0
- data/Manifest.txt +18 -0
- data/Rakefile +6 -0
- data/docs/config.md +0 -0
- data/docs/nginx.md +85 -0
- data/examples/puma/keystore.jks +0 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/ext/puma_http11/extconf.rb +3 -0
- data/ext/puma_http11/io_buffer.c +146 -0
- data/ext/puma_http11/mini_ssl.c +189 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +289 -0
- data/ext/puma_http11/puma_http11.c +6 -0
- data/lib/puma/accept_nonblock.rb +23 -0
- data/lib/puma/binder.rb +253 -0
- data/lib/puma/cli.rb +212 -114
- data/lib/puma/client.rb +28 -3
- data/lib/puma/configuration.rb +11 -4
- data/lib/puma/const.rb +12 -1
- data/lib/puma/delegation.rb +11 -0
- data/lib/puma/events.rb +18 -0
- data/lib/puma/io_buffer.rb +7 -0
- data/lib/puma/java_io_buffer.rb +45 -0
- data/lib/puma/minissl.rb +124 -0
- data/lib/puma/puma_http11.bundle +0 -0
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/reactor.rb +4 -1
- data/lib/puma/server.rb +67 -144
- data/lib/puma/thread_pool.rb +5 -2
- data/puma.gemspec +5 -5
- data/test/test_integration.rb +53 -2
- data/test/test_iobuffer.rb +38 -0
- data/test/test_puma_server.rb +12 -7
- data/tools/jungle/README.md +54 -0
- data/tools/jungle/puma +332 -0
- data/tools/jungle/run-puma +3 -0
- metadata +24 -5
data/lib/puma/configuration.rb
CHANGED
@@ -68,7 +68,8 @@ module Puma
|
|
68
68
|
end
|
69
69
|
|
70
70
|
unless @options[:quiet]
|
71
|
-
|
71
|
+
logger = @options[:logger] || STDOUT
|
72
|
+
app = Rack::CommonLogger.new(app, logger)
|
72
73
|
end
|
73
74
|
|
74
75
|
return app
|
@@ -155,6 +156,11 @@ module Puma
|
|
155
156
|
@options[:binds] << url
|
156
157
|
end
|
157
158
|
|
159
|
+
# Set the environment in which the Rack's app will run.
|
160
|
+
def environment(environment)
|
161
|
+
@options[:environment] = environment
|
162
|
+
end
|
163
|
+
|
158
164
|
# Code to run before doing a restart. This code should
|
159
165
|
# close logfiles, database connections, etc.
|
160
166
|
#
|
@@ -209,9 +215,10 @@ module Puma
|
|
209
215
|
@options[:state] = path.to_s
|
210
216
|
end
|
211
217
|
|
212
|
-
#
|
213
|
-
|
214
|
-
|
218
|
+
# *Cluster mode only* How many worker processes to run.
|
219
|
+
#
|
220
|
+
def workers(count)
|
221
|
+
@options[:workers] = count.to_i
|
215
222
|
end
|
216
223
|
end
|
217
224
|
end
|
data/lib/puma/const.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'rack'
|
2
2
|
|
3
3
|
module Puma
|
4
|
+
class UnsupportedOption < RuntimeError
|
5
|
+
end
|
6
|
+
|
4
7
|
|
5
8
|
# Every standard HTTP code mapped to the appropriate message. These are
|
6
9
|
# used so frequently that they are placed directly in Puma for easy
|
@@ -25,7 +28,9 @@ module Puma
|
|
25
28
|
# too taxing on performance.
|
26
29
|
module Const
|
27
30
|
|
28
|
-
PUMA_VERSION = VERSION = "
|
31
|
+
PUMA_VERSION = VERSION = "2.0.0.b1".freeze
|
32
|
+
|
33
|
+
FAST_TRACK_KA_TIMEOUT = 0.2
|
29
34
|
|
30
35
|
# The default number of seconds for another request within a persistent
|
31
36
|
# session.
|
@@ -47,11 +52,17 @@ module Puma
|
|
47
52
|
|
48
53
|
PUMA_TMP_BASE = "puma".freeze
|
49
54
|
|
55
|
+
# Indicate that we couldn't parse the request
|
56
|
+
ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
|
57
|
+
|
50
58
|
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
51
59
|
ERROR_404_RESPONSE = "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\nNOT FOUND".freeze
|
52
60
|
|
53
61
|
CONTENT_LENGTH = "CONTENT_LENGTH".freeze
|
54
62
|
|
63
|
+
# Indicate that there was an internal error, obviously.
|
64
|
+
ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
|
65
|
+
|
55
66
|
# A common header for indicating the server is too busy. Not used yet.
|
56
67
|
ERROR_503_RESPONSE = "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
57
68
|
|
data/lib/puma/events.rb
CHANGED
@@ -29,6 +29,10 @@ module Puma
|
|
29
29
|
@stdout.puts str
|
30
30
|
end
|
31
31
|
|
32
|
+
def write(str)
|
33
|
+
@stdout.write str
|
34
|
+
end
|
35
|
+
|
32
36
|
# Write +str+ to +@stderr+
|
33
37
|
#
|
34
38
|
def error(str)
|
@@ -67,4 +71,18 @@ module Puma
|
|
67
71
|
Events.new StringIO.new, StringIO.new
|
68
72
|
end
|
69
73
|
end
|
74
|
+
|
75
|
+
class PidEvents < Events
|
76
|
+
def log(str)
|
77
|
+
super "[#{$$}] #{str}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def write(str)
|
81
|
+
super "[#{$$}] #{str}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def error(str)
|
85
|
+
super "[#{$$}] #{str}"
|
86
|
+
end
|
87
|
+
end
|
70
88
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
# Conservative native JRuby/Java implementation of IOBuffer
|
4
|
+
# backed by a ByteArrayOutputStream and conversion between
|
5
|
+
# Ruby String and Java bytes
|
6
|
+
module Puma
|
7
|
+
class JavaIOBuffer < java.io.ByteArrayOutputStream
|
8
|
+
field_reader :buf
|
9
|
+
end
|
10
|
+
|
11
|
+
class IOBuffer
|
12
|
+
BUF_DEFAULT_SIZE = 4096
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@buf = JavaIOBuffer.new(BUF_DEFAULT_SIZE)
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset
|
19
|
+
@buf.reset
|
20
|
+
end
|
21
|
+
|
22
|
+
def <<(str)
|
23
|
+
bytes = str.to_java_bytes
|
24
|
+
@buf.write(bytes, 0, bytes.length)
|
25
|
+
end
|
26
|
+
|
27
|
+
def append(*strs)
|
28
|
+
strs.each { |s| self << s; }
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
String.from_java_bytes @buf.to_byte_array
|
33
|
+
end
|
34
|
+
|
35
|
+
alias_method :to_str, :to_s
|
36
|
+
|
37
|
+
def used
|
38
|
+
@buf.size
|
39
|
+
end
|
40
|
+
|
41
|
+
def capacity
|
42
|
+
@buf.buf.length
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/puma/minissl.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module Puma::MiniSSL
|
2
|
+
class Socket
|
3
|
+
def initialize(socket, engine)
|
4
|
+
@socket = socket
|
5
|
+
@engine = engine
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_io
|
9
|
+
@socket
|
10
|
+
end
|
11
|
+
|
12
|
+
def readpartial(size)
|
13
|
+
while true
|
14
|
+
output = @engine.read
|
15
|
+
return output if output
|
16
|
+
|
17
|
+
data = @socket.readpartial(size)
|
18
|
+
@engine.inject(data)
|
19
|
+
output = @engine.read
|
20
|
+
|
21
|
+
return output if output
|
22
|
+
|
23
|
+
while neg_data = @engine.extract
|
24
|
+
@socket.write neg_data
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_nonblock(size)
|
30
|
+
while true
|
31
|
+
output = @engine.read
|
32
|
+
return output if output
|
33
|
+
|
34
|
+
data = @socket.read_nonblock(size)
|
35
|
+
|
36
|
+
@engine.inject(data)
|
37
|
+
output = @engine.read
|
38
|
+
|
39
|
+
return output if output
|
40
|
+
|
41
|
+
while neg_data = @engine.extract
|
42
|
+
@socket.write neg_data
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def write(data)
|
48
|
+
need = data.size
|
49
|
+
|
50
|
+
while true
|
51
|
+
wrote = @engine.write data
|
52
|
+
enc = @engine.extract
|
53
|
+
|
54
|
+
if enc
|
55
|
+
@socket.syswrite enc
|
56
|
+
end
|
57
|
+
|
58
|
+
need -= wrote
|
59
|
+
|
60
|
+
return data.size if need == 0
|
61
|
+
|
62
|
+
data = data[need..-1]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
alias_method :syswrite, :write
|
67
|
+
|
68
|
+
def flush
|
69
|
+
@socket.flush
|
70
|
+
end
|
71
|
+
|
72
|
+
def close
|
73
|
+
@socket.close
|
74
|
+
end
|
75
|
+
|
76
|
+
def peeraddr
|
77
|
+
@socket.peeraddr
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Context
|
82
|
+
attr_accessor :key, :cert, :verify_mode
|
83
|
+
end
|
84
|
+
|
85
|
+
VERIFY_NONE = 0
|
86
|
+
VERIFY_PEER = 1
|
87
|
+
|
88
|
+
#if defined?(JRUBY_VERSION)
|
89
|
+
#class Engine
|
90
|
+
#def self.server(key, cert)
|
91
|
+
#new(key, cert)
|
92
|
+
#end
|
93
|
+
#end
|
94
|
+
#end
|
95
|
+
|
96
|
+
class Server
|
97
|
+
def initialize(socket, ctx)
|
98
|
+
@socket = socket
|
99
|
+
@ctx = ctx
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_io
|
103
|
+
@socket
|
104
|
+
end
|
105
|
+
|
106
|
+
def accept
|
107
|
+
io = @socket.accept
|
108
|
+
engine = Engine.server @ctx.key, @ctx.cert
|
109
|
+
|
110
|
+
Socket.new io, engine
|
111
|
+
end
|
112
|
+
|
113
|
+
def accept_nonblock
|
114
|
+
io = @socket.accept_nonblock
|
115
|
+
engine = Engine.server @ctx.key, @ctx.cert
|
116
|
+
|
117
|
+
Socket.new io, engine
|
118
|
+
end
|
119
|
+
|
120
|
+
def close
|
121
|
+
@socket.close
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
Binary file
|
Binary file
|
data/lib/puma/reactor.rb
CHANGED
@@ -59,13 +59,16 @@ module Puma
|
|
59
59
|
|
60
60
|
# The client doesn't know HTTP well
|
61
61
|
rescue HttpParserError => e
|
62
|
+
c.write_400
|
62
63
|
c.close
|
64
|
+
|
63
65
|
sockets.delete c
|
64
66
|
|
65
67
|
@events.parse_error @server, c.env, e
|
66
|
-
|
67
68
|
rescue StandardError => e
|
69
|
+
c.write_500
|
68
70
|
c.close
|
71
|
+
|
69
72
|
sockets.delete c
|
70
73
|
end
|
71
74
|
end
|
data/lib/puma/server.rb
CHANGED
@@ -8,9 +8,16 @@ require 'puma/null_io'
|
|
8
8
|
require 'puma/compat'
|
9
9
|
require 'puma/reactor'
|
10
10
|
require 'puma/client'
|
11
|
+
require 'puma/binder'
|
12
|
+
require 'puma/delegation'
|
13
|
+
require 'puma/accept_nonblock'
|
11
14
|
|
12
15
|
require 'puma/puma_http11'
|
13
16
|
|
17
|
+
unless Puma.const_defined? "IOBuffer"
|
18
|
+
require 'puma/io_buffer'
|
19
|
+
end
|
20
|
+
|
14
21
|
require 'socket'
|
15
22
|
|
16
23
|
module Puma
|
@@ -19,6 +26,7 @@ module Puma
|
|
19
26
|
class Server
|
20
27
|
|
21
28
|
include Puma::Const
|
29
|
+
extend Puma::Delegation
|
22
30
|
|
23
31
|
attr_reader :thread
|
24
32
|
attr_reader :events
|
@@ -42,7 +50,6 @@ module Puma
|
|
42
50
|
@events = events
|
43
51
|
|
44
52
|
@check, @notify = IO.pipe
|
45
|
-
@ios = [@check]
|
46
53
|
|
47
54
|
@status = :stop
|
48
55
|
|
@@ -56,34 +63,18 @@ module Puma
|
|
56
63
|
@persistent_timeout = PERSISTENT_TIMEOUT
|
57
64
|
@persistent_check, @persistent_wakeup = IO.pipe
|
58
65
|
|
66
|
+
@binder = Binder.new(events)
|
59
67
|
@first_data_timeout = FIRST_DATA_TIMEOUT
|
60
68
|
|
61
|
-
@unix_paths = []
|
62
|
-
|
63
|
-
@proto_env = {
|
64
|
-
"rack.version".freeze => Rack::VERSION,
|
65
|
-
"rack.errors".freeze => events.stderr,
|
66
|
-
"rack.multithread".freeze => true,
|
67
|
-
"rack.multiprocess".freeze => false,
|
68
|
-
"rack.run_once".freeze => false,
|
69
|
-
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
70
|
-
|
71
|
-
# Rack blows up if this is an empty string, and Rack::Lint
|
72
|
-
# blows up if it's nil. So 'text/plain' seems like the most
|
73
|
-
# sensible default value.
|
74
|
-
"CONTENT_TYPE".freeze => "text/plain",
|
75
|
-
|
76
|
-
"QUERY_STRING".freeze => "",
|
77
|
-
SERVER_PROTOCOL => HTTP_11,
|
78
|
-
SERVER_SOFTWARE => PUMA_VERSION,
|
79
|
-
GATEWAY_INTERFACE => CGI_VER
|
80
|
-
}
|
81
|
-
|
82
|
-
@envs = {}
|
83
|
-
|
84
69
|
ENV['RACK_ENV'] ||= "development"
|
85
70
|
end
|
86
71
|
|
72
|
+
attr_accessor :binder
|
73
|
+
|
74
|
+
forward :add_tcp_listener, :@binder
|
75
|
+
forward :add_ssl_listener, :@binder
|
76
|
+
forward :add_unix_listener, :@binder
|
77
|
+
|
87
78
|
# On Linux, use TCP_CORK to better control how the TCP stack
|
88
79
|
# packetizes our stream. This improves both latency and throughput.
|
89
80
|
#
|
@@ -106,81 +97,6 @@ module Puma
|
|
106
97
|
end
|
107
98
|
end
|
108
99
|
|
109
|
-
# Tell the server to listen on host +host+, port +port+.
|
110
|
-
# If +optimize_for_latency+ is true (the default) then clients connecting
|
111
|
-
# will be optimized for latency over throughput.
|
112
|
-
#
|
113
|
-
# +backlog+ indicates how many unaccepted connections the kernel should
|
114
|
-
# allow to accumulate before returning connection refused.
|
115
|
-
#
|
116
|
-
def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
|
117
|
-
s = TCPServer.new(host, port)
|
118
|
-
if optimize_for_latency
|
119
|
-
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
120
|
-
end
|
121
|
-
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
122
|
-
s.listen backlog
|
123
|
-
@ios << s
|
124
|
-
s
|
125
|
-
end
|
126
|
-
|
127
|
-
def inherit_tcp_listener(host, port, fd)
|
128
|
-
s = TCPServer.for_fd(fd)
|
129
|
-
@ios << s
|
130
|
-
s
|
131
|
-
end
|
132
|
-
|
133
|
-
def add_ssl_listener(host, port, ctx, optimize_for_latency=true, backlog=1024)
|
134
|
-
s = TCPServer.new(host, port)
|
135
|
-
if optimize_for_latency
|
136
|
-
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
137
|
-
end
|
138
|
-
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
139
|
-
s.listen backlog
|
140
|
-
|
141
|
-
ssl = OpenSSL::SSL::SSLServer.new(s, ctx)
|
142
|
-
env = @proto_env.dup
|
143
|
-
env[HTTPS_KEY] = HTTPS
|
144
|
-
@envs[ssl] = env
|
145
|
-
|
146
|
-
@ios << ssl
|
147
|
-
s
|
148
|
-
end
|
149
|
-
|
150
|
-
def inherited_ssl_listener(fd, ctx)
|
151
|
-
s = TCPServer.for_fd(fd)
|
152
|
-
@ios << OpenSSL::SSL::SSLServer.new(s, ctx)
|
153
|
-
s
|
154
|
-
end
|
155
|
-
|
156
|
-
# Tell the server to listen on +path+ as a UNIX domain socket.
|
157
|
-
#
|
158
|
-
def add_unix_listener(path, umask=nil)
|
159
|
-
@unix_paths << path
|
160
|
-
|
161
|
-
# Let anyone connect by default
|
162
|
-
umask ||= 0
|
163
|
-
|
164
|
-
begin
|
165
|
-
old_mask = File.umask(umask)
|
166
|
-
s = UNIXServer.new(path)
|
167
|
-
@ios << s
|
168
|
-
ensure
|
169
|
-
File.umask old_mask
|
170
|
-
end
|
171
|
-
|
172
|
-
s
|
173
|
-
end
|
174
|
-
|
175
|
-
def inherit_unix_listener(path, fd)
|
176
|
-
@unix_paths << path
|
177
|
-
|
178
|
-
s = UNIXServer.for_fd fd
|
179
|
-
@ios << s
|
180
|
-
|
181
|
-
s
|
182
|
-
end
|
183
|
-
|
184
100
|
def backlog
|
185
101
|
@thread_pool and @thread_pool.backlog
|
186
102
|
end
|
@@ -200,19 +116,23 @@ module Puma
|
|
200
116
|
|
201
117
|
@status = :run
|
202
118
|
|
203
|
-
@thread_pool = ThreadPool.new(@min_threads,
|
119
|
+
@thread_pool = ThreadPool.new(@min_threads,
|
120
|
+
@max_threads,
|
121
|
+
IOBuffer) do |client, buffer|
|
204
122
|
process_now = false
|
205
123
|
|
206
124
|
begin
|
207
125
|
process_now = client.eagerly_finish
|
208
126
|
rescue HttpParserError => e
|
127
|
+
client.write_400
|
209
128
|
client.close
|
129
|
+
|
210
130
|
@events.parse_error self, client.env, e
|
211
131
|
rescue IOError
|
212
132
|
client.close
|
213
133
|
else
|
214
134
|
if process_now
|
215
|
-
process_client client
|
135
|
+
process_client client, buffer
|
216
136
|
else
|
217
137
|
client.set_timeout @first_data_timeout
|
218
138
|
@reactor.add client
|
@@ -239,7 +159,7 @@ module Puma
|
|
239
159
|
def handle_servers
|
240
160
|
begin
|
241
161
|
check = @check
|
242
|
-
sockets = @ios
|
162
|
+
sockets = [check] + @binder.ios
|
243
163
|
pool = @thread_pool
|
244
164
|
|
245
165
|
while @status == :run
|
@@ -249,8 +169,13 @@ module Puma
|
|
249
169
|
if sock == check
|
250
170
|
break if handle_check
|
251
171
|
else
|
252
|
-
|
253
|
-
|
172
|
+
begin
|
173
|
+
if io = sock.accept_nonblock
|
174
|
+
c = Client.new io, @binder.env(sock)
|
175
|
+
pool << c
|
176
|
+
end
|
177
|
+
rescue SystemCallError
|
178
|
+
end
|
254
179
|
end
|
255
180
|
end
|
256
181
|
rescue Errno::ECONNABORTED
|
@@ -261,14 +186,14 @@ module Puma
|
|
261
186
|
end
|
262
187
|
end
|
263
188
|
|
189
|
+
graceful_shutdown if @status == :stop || @status == :restart
|
264
190
|
@reactor.clear! if @status == :restart
|
265
191
|
|
266
192
|
@reactor.shutdown
|
267
|
-
graceful_shutdown if @status == :stop
|
268
193
|
ensure
|
269
194
|
unless @status == :restart
|
270
|
-
@
|
271
|
-
@
|
195
|
+
@check.close
|
196
|
+
@binder.close
|
272
197
|
end
|
273
198
|
end
|
274
199
|
end
|
@@ -298,19 +223,21 @@ module Puma
|
|
298
223
|
# indicates that it supports keep alive, wait for another request before
|
299
224
|
# returning.
|
300
225
|
#
|
301
|
-
def process_client(client)
|
226
|
+
def process_client(client, buffer)
|
302
227
|
begin
|
303
228
|
close_socket = true
|
304
229
|
|
305
230
|
while true
|
306
|
-
case handle_request(client)
|
231
|
+
case handle_request(client, buffer)
|
307
232
|
when false
|
308
233
|
return
|
309
234
|
when :async
|
310
235
|
close_socket = false
|
311
236
|
return
|
312
237
|
when true
|
313
|
-
|
238
|
+
buffer.reset
|
239
|
+
|
240
|
+
unless client.reset(@status == :run)
|
314
241
|
close_socket = false
|
315
242
|
client.set_timeout @persistent_timeout
|
316
243
|
@reactor.add client
|
@@ -325,10 +252,14 @@ module Puma
|
|
325
252
|
|
326
253
|
# The client doesn't know HTTP well
|
327
254
|
rescue HttpParserError => e
|
255
|
+
client.write_400
|
256
|
+
|
328
257
|
@events.parse_error self, client.env, e
|
329
258
|
|
330
259
|
# Server error
|
331
260
|
rescue StandardError => e
|
261
|
+
client.write_500
|
262
|
+
|
332
263
|
@events.unknown_error self, e, "Read"
|
333
264
|
|
334
265
|
ensure
|
@@ -389,7 +320,7 @@ module Puma
|
|
389
320
|
# was one. This is an optimization to keep from having to look
|
390
321
|
# it up again.
|
391
322
|
#
|
392
|
-
def handle_request(req)
|
323
|
+
def handle_request(req, lines)
|
393
324
|
env = req.env
|
394
325
|
client = req.io
|
395
326
|
|
@@ -434,6 +365,9 @@ module Puma
|
|
434
365
|
|
435
366
|
cork_socket client
|
436
367
|
|
368
|
+
line_ending = LINE_END
|
369
|
+
colon = COLON
|
370
|
+
|
437
371
|
if env[HTTP_VERSION] == HTTP_11
|
438
372
|
allow_chunked = true
|
439
373
|
keep_alive = env[HTTP_CONNECTION] != CLOSE
|
@@ -444,13 +378,10 @@ module Puma
|
|
444
378
|
# the response header.
|
445
379
|
#
|
446
380
|
if status == 200
|
447
|
-
|
381
|
+
lines << HTTP_11_200
|
448
382
|
else
|
449
|
-
|
450
|
-
|
451
|
-
client.write " "
|
452
|
-
client.write HTTP_STATUS_CODES[status]
|
453
|
-
client.write "\r\n"
|
383
|
+
lines.append "HTTP/1.1 ", status.to_s, " ",
|
384
|
+
HTTP_STATUS_CODES[status], line_ending
|
454
385
|
|
455
386
|
no_body = status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
456
387
|
end
|
@@ -462,21 +393,15 @@ module Puma
|
|
462
393
|
# Same optimization as above for HTTP/1.1
|
463
394
|
#
|
464
395
|
if status == 200
|
465
|
-
|
396
|
+
lines << HTTP_10_200
|
466
397
|
else
|
467
|
-
|
468
|
-
|
469
|
-
client.write " "
|
470
|
-
client.write HTTP_STATUS_CODES[status]
|
471
|
-
client.write "\r\n"
|
398
|
+
lines.append "HTTP/1.0 ", status.to_s, " ",
|
399
|
+
HTTP_STATUS_CODES[status], line_ending
|
472
400
|
|
473
401
|
no_body = status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
474
402
|
end
|
475
403
|
end
|
476
404
|
|
477
|
-
colon = COLON
|
478
|
-
line_ending = LINE_END
|
479
|
-
|
480
405
|
headers.each do |k, vs|
|
481
406
|
case k
|
482
407
|
when CONTENT_LENGTH2
|
@@ -490,51 +415,49 @@ module Puma
|
|
490
415
|
end
|
491
416
|
|
492
417
|
vs.split(NEWLINE).each do |v|
|
493
|
-
|
494
|
-
client.write colon
|
495
|
-
client.write v
|
496
|
-
client.write line_ending
|
418
|
+
lines.append k, colon, v, line_ending
|
497
419
|
end
|
498
420
|
end
|
499
421
|
|
500
422
|
if no_body
|
501
|
-
|
423
|
+
lines << line_ending
|
424
|
+
client.syswrite lines.to_s
|
502
425
|
return keep_alive
|
503
426
|
end
|
504
427
|
|
505
428
|
if include_keepalive_header
|
506
|
-
|
429
|
+
lines << CONNECTION_KEEP_ALIVE
|
507
430
|
elsif !keep_alive
|
508
|
-
|
431
|
+
lines << CONNECTION_CLOSE
|
509
432
|
end
|
510
433
|
|
511
434
|
if content_length
|
512
|
-
|
513
|
-
client.write content_length.to_s
|
514
|
-
client.write line_ending
|
435
|
+
lines.append CONTENT_LENGTH_S, content_length.to_s, line_ending
|
515
436
|
chunked = false
|
516
437
|
elsif allow_chunked
|
517
|
-
|
438
|
+
lines << TRANSFER_ENCODING_CHUNKED
|
518
439
|
chunked = true
|
519
440
|
end
|
520
441
|
|
521
|
-
|
442
|
+
lines << line_ending
|
443
|
+
|
444
|
+
client.syswrite lines.to_s
|
522
445
|
|
523
446
|
res_body.each do |part|
|
524
447
|
if chunked
|
525
|
-
client.
|
526
|
-
client.
|
527
|
-
client.
|
528
|
-
client.
|
448
|
+
client.syswrite part.bytesize.to_s(16)
|
449
|
+
client.syswrite line_ending
|
450
|
+
client.syswrite part
|
451
|
+
client.syswrite line_ending
|
529
452
|
else
|
530
|
-
client.
|
453
|
+
client.syswrite part
|
531
454
|
end
|
532
455
|
|
533
456
|
client.flush
|
534
457
|
end
|
535
458
|
|
536
459
|
if chunked
|
537
|
-
client.
|
460
|
+
client.syswrite CLOSE_CHUNKED
|
538
461
|
client.flush
|
539
462
|
end
|
540
463
|
|