tp2 0.11.3 → 0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +196 -0
- data/CHANGELOG.md +4 -0
- data/TODO.md +6 -4
- data/bin/tp2 +19 -18
- data/lib/tp2/http1_connection.rb +26 -58
- data/lib/tp2/logger.rb +39 -2
- data/lib/tp2/version.rb +1 -1
- data/lib/tp2.rb +4 -4
- data/test/helper.rb +6 -0
- data/test/test_http1_adapter.rb +0 -6
- data/test/test_server.rb +26 -7
- data/tp2.gemspec +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3f8224d39ef6a0c94d38882aa4fbde1692de6e777803060eb5adc96e56f3504
|
4
|
+
data.tar.gz: 572011895d001d3f132416ad669191fc75291d7c0b0c01fbf5dce619da6bf22c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8faf10f9c9da6ef061c3203cd4795d0d9fa01a2ecf7ce788abd89fbdc251a126f760c08424e41b4a5e0a7f10c2ecd7a64b56b152b441049637a9a6ac3f34db4b
|
7
|
+
data.tar.gz: 111856fa3064194dc93a98c197adeb9c512c232bc8ca4aea73b9f24542a866360f086cb2c1ba94cee068df3bb8967fdb565104b58604a7c8c3555f6184f07700
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.2
|
3
|
+
RubyInterpreters:
|
4
|
+
- ruby
|
5
|
+
Exclude:
|
6
|
+
- "**/*.gemspec"
|
7
|
+
- "test/**/*.rb"
|
8
|
+
- "examples/**/*.rb"
|
9
|
+
- "Gemfile*"
|
10
|
+
# Style/LambdaCall:
|
11
|
+
# Enabled: false
|
12
|
+
# Style/ModuleFunction:
|
13
|
+
# Enabled: false
|
14
|
+
# Style/RegexpLiteral:
|
15
|
+
# Enabled: false
|
16
|
+
|
17
|
+
# Naming/MemoizedInstanceVariableName:
|
18
|
+
# Enabled: false
|
19
|
+
|
20
|
+
# Style/Alias:
|
21
|
+
# EnforcedStyle: prefer_alias_method
|
22
|
+
|
23
|
+
# Style/SpecialGlobalVars:
|
24
|
+
# Enabled: false
|
25
|
+
|
26
|
+
# Style/ClassAndModuleChildren:
|
27
|
+
# Enabled: false
|
28
|
+
|
29
|
+
# Metrics/AbcSize:
|
30
|
+
# Enabled: false
|
31
|
+
|
32
|
+
# Style/MixinUsage:
|
33
|
+
# Enabled: false
|
34
|
+
|
35
|
+
# Style/MultilineBlockChain:
|
36
|
+
# Enabled: false
|
37
|
+
|
38
|
+
# Lint/RescueException:
|
39
|
+
# Enabled: false
|
40
|
+
|
41
|
+
# Lint/InheritException:
|
42
|
+
# Enabled: false
|
43
|
+
|
44
|
+
Style/NumericPredicate:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
# Style/TrivialAccessors:
|
48
|
+
# Enabled: false
|
49
|
+
|
50
|
+
# Lint/MissingSuper:
|
51
|
+
# Enabled: false
|
52
|
+
|
53
|
+
# Style/GlobalVars:
|
54
|
+
# Exclude:
|
55
|
+
# - lib/polyphony/auto_run.rb
|
56
|
+
# - lib/polyphony/extensions/core.rb
|
57
|
+
# - examples/**/*.rb
|
58
|
+
|
59
|
+
Layout/HashAlignment:
|
60
|
+
Enabled: false
|
61
|
+
EnforcedColonStyle: table
|
62
|
+
EnforcedHashRocketStyle: table
|
63
|
+
|
64
|
+
# Naming/AccessorMethodName:
|
65
|
+
# Exclude:
|
66
|
+
# - lib/polyphony/extensions/fiber.rb
|
67
|
+
# - examples/**/*.rb
|
68
|
+
|
69
|
+
# Naming/MethodName:
|
70
|
+
# Exclude:
|
71
|
+
# - test/test_signal.rb
|
72
|
+
|
73
|
+
# Lint/SuppressedException:
|
74
|
+
# Exclude:
|
75
|
+
# - examples/**/*.rb
|
76
|
+
|
77
|
+
Metrics/MethodLength:
|
78
|
+
Max: 14
|
79
|
+
# Exclude:
|
80
|
+
# - lib/polyphony/extensions/io.rb
|
81
|
+
# - lib/polyphony/extensions/fiber.rb
|
82
|
+
# - lib/polyphony/extensions/thread.rb
|
83
|
+
# - lib/polyphony/adapters/open3.rb
|
84
|
+
# - test/**/*.rb
|
85
|
+
# - examples/**/*.rb
|
86
|
+
|
87
|
+
# Metrics/ModuleLength:
|
88
|
+
# Exclude:
|
89
|
+
# - lib/polyphony/core/global_api.rb
|
90
|
+
# - examples/**/*.rb
|
91
|
+
|
92
|
+
# Metrics/ClassLength:
|
93
|
+
# Exclude:
|
94
|
+
# - lib/polyphony/extensions/io.rb
|
95
|
+
# - lib/polyphony/extensions/fiber.rb
|
96
|
+
# - lib/polyphony/extensions/object.rb
|
97
|
+
# - lib/polyphony/extensions/thread.rb
|
98
|
+
# - test/**/*.rb
|
99
|
+
# - examples/**/*.rb
|
100
|
+
|
101
|
+
# Metrics/CyclomaticComplexity:
|
102
|
+
# Exclude:
|
103
|
+
# - lib/polyphony/extensions/fiber.rb
|
104
|
+
|
105
|
+
# Metrics/PerceivedComplexity:
|
106
|
+
# Exclude:
|
107
|
+
# - lib/polyphony/extensions/fiber.rb
|
108
|
+
|
109
|
+
# Style/RegexpLiteral:
|
110
|
+
# Enabled: false
|
111
|
+
|
112
|
+
Style/RescueModifier:
|
113
|
+
Enabled: false
|
114
|
+
# Style/Documentation:
|
115
|
+
# Exclude:
|
116
|
+
# - test/**/*.rb
|
117
|
+
# - examples/**/*.rb
|
118
|
+
# - lib/polyphony/adapters/**/*.rb
|
119
|
+
|
120
|
+
# Style/FormatString:
|
121
|
+
# Exclude:
|
122
|
+
# - test/**/*.rb
|
123
|
+
# - examples/**/*.rb
|
124
|
+
|
125
|
+
# Style/FormatStringToken:
|
126
|
+
# Exclude:
|
127
|
+
# - test/**/*.rb
|
128
|
+
# - examples/**/*.rb
|
129
|
+
|
130
|
+
Naming/MethodParameterName:
|
131
|
+
Enabled: false
|
132
|
+
|
133
|
+
# Security/MarshalLoad:
|
134
|
+
# Exclude:
|
135
|
+
# - examples/**/*.rb
|
136
|
+
|
137
|
+
# Lint/ShadowedArgument:
|
138
|
+
# Exclude:
|
139
|
+
# - lib/polyphony/extensions/fiber.rb
|
140
|
+
|
141
|
+
# Style/HashEachMethods:
|
142
|
+
# Enabled: true
|
143
|
+
|
144
|
+
# Style/HashTransformKeys:
|
145
|
+
# Enabled: true
|
146
|
+
|
147
|
+
# Style/HashTransformValues:
|
148
|
+
# Enabled: true
|
149
|
+
|
150
|
+
# Layout/EmptyLinesAroundAttributeAccessor:
|
151
|
+
# Enabled: true
|
152
|
+
|
153
|
+
# Layout/SpaceAroundMethodCallOperator:
|
154
|
+
# Enabled: true
|
155
|
+
|
156
|
+
# Lint/DeprecatedOpenSSLConstant:
|
157
|
+
# Enabled: true
|
158
|
+
|
159
|
+
# Lint/MixedRegexpCaptureTypes:
|
160
|
+
# Enabled: true
|
161
|
+
|
162
|
+
# Lint/RaiseException:
|
163
|
+
# Enabled: true
|
164
|
+
|
165
|
+
# Lint/StructNewOverride:
|
166
|
+
# Enabled: true
|
167
|
+
|
168
|
+
Style/NegatedIf:
|
169
|
+
Enabled: false
|
170
|
+
# Style/NegatedWhile:
|
171
|
+
# Enabled: false
|
172
|
+
|
173
|
+
# Style/CombinableLoops:
|
174
|
+
# Enabled: false
|
175
|
+
|
176
|
+
# Style/InfiniteLoop:
|
177
|
+
# Enabled: false
|
178
|
+
|
179
|
+
# Style/RedundantReturn:
|
180
|
+
# Enabled: false
|
181
|
+
|
182
|
+
# Style/ExponentialNotation:
|
183
|
+
# Enabled: true
|
184
|
+
|
185
|
+
# Style/RedundantRegexpCharacterClass:
|
186
|
+
# Enabled: true
|
187
|
+
|
188
|
+
# Style/RedundantRegexpEscape:
|
189
|
+
# Enabled: true
|
190
|
+
|
191
|
+
# Style/SlicingWithRange:
|
192
|
+
# Enabled: true
|
193
|
+
|
194
|
+
# Style/RaiseArgs:
|
195
|
+
# Exclude:
|
196
|
+
# - lib/polyphony/extensions/fiber.rb
|
data/CHANGELOG.md
CHANGED
data/TODO.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
- Add failing tests for request bombs (requests with lots of bytes)
|
2
|
-
|
3
|
-
-
|
4
|
-
|
5
|
-
- Add limits for:
|
2
|
+
- Add test for `ProtocolError` handling
|
3
|
+
- Add limits with tests for:
|
6
4
|
- total request size (pre body)
|
7
5
|
- line size
|
8
6
|
- request method size
|
@@ -15,3 +13,7 @@
|
|
15
13
|
|
16
14
|
HEADER_RE = /^\s([^\s^\:]{1, #{MAX_HEADER_KEY_BYTES}}).../
|
17
15
|
```
|
16
|
+
|
17
|
+
- Benchmark parsing with UM::Stream (current approach), against parsing with strscan
|
18
|
+
|
19
|
+
- Add pluggable logging. Move default logging format outside and pass a callable.
|
data/bin/tp2
CHANGED
@@ -1,46 +1,47 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require 'tp2'
|
5
|
+
require 'optparse'
|
6
6
|
|
7
7
|
opts = {
|
8
8
|
banner: true,
|
9
|
-
|
9
|
+
logger: true
|
10
10
|
}
|
11
11
|
|
12
12
|
parser = OptionParser.new do |o|
|
13
|
-
o.banner =
|
13
|
+
o.banner = 'Usage: tp2 [options]'
|
14
14
|
|
15
|
-
o.on(
|
15
|
+
o.on('-c', '--config CONFIG_FILE', String, 'TP2 config file to use (default: app.rb)') do
|
16
16
|
opts[:app_type] = :tp2
|
17
17
|
opts[:app_location] = it
|
18
|
-
|
19
|
-
o.on(
|
18
|
+
end
|
19
|
+
o.on('-r', '--rack RACK_FILE', String, 'Rack config file to use (default: config.ru)') do
|
20
20
|
opts[:app_type] = :rack
|
21
21
|
opts[:app_location] = it
|
22
|
-
|
23
|
-
o.on(
|
22
|
+
end
|
23
|
+
o.on('-d', '--dir PATH', String, 'Static files at path') do
|
24
24
|
opts[:app_type] = :static
|
25
25
|
opts[:app_location] = it
|
26
|
-
|
26
|
+
end
|
27
27
|
|
28
|
-
o.on(
|
28
|
+
o.on('-b', '--bind BIND', String,
|
29
|
+
'Bind address (default: http://0.0.0.0:1234). You can specify this flag multiple times to bind to multiple addresses.') do
|
29
30
|
opts[:bind] ||= []
|
30
31
|
opts[:bind] << it
|
31
|
-
|
32
|
+
end
|
32
33
|
|
33
|
-
o.on(
|
34
|
+
o.on('-s', '--silent', 'Silent mode') do
|
34
35
|
opts[:banner] = nil
|
35
|
-
opts[:
|
36
|
-
|
36
|
+
opts[:logger] = nil
|
37
|
+
end
|
37
38
|
|
38
|
-
o.on(
|
39
|
+
o.on('-h', '--help', 'Show this help message') do
|
39
40
|
puts o
|
40
41
|
exit
|
41
42
|
end
|
42
43
|
|
43
|
-
o.on(
|
44
|
+
o.on('-v', '--version', 'Show version') do
|
44
45
|
puts "TP2 version #{TP2::VERSION}"
|
45
46
|
exit
|
46
47
|
end
|
@@ -59,7 +60,7 @@ opts[:location] = ARGV.shift || '.'
|
|
59
60
|
def check_app_file(opts, fn, type)
|
60
61
|
fn = File.join(opts[:location], fn)
|
61
62
|
return false if !File.file?(fn)
|
62
|
-
|
63
|
+
|
63
64
|
opts[:app_type] = type
|
64
65
|
opts[:app_location] = File.expand_path(fn)
|
65
66
|
end
|
data/lib/tp2/http1_connection.rb
CHANGED
@@ -20,7 +20,7 @@ module TP2
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def run
|
23
|
-
|
23
|
+
loop do
|
24
24
|
@done = nil
|
25
25
|
@response_headers = nil
|
26
26
|
persist = serve_request
|
@@ -28,9 +28,8 @@ module TP2
|
|
28
28
|
end
|
29
29
|
rescue UM::Terminate, SystemCallError
|
30
30
|
# we're done, we don't care about broken socket
|
31
|
-
rescue StandardError
|
32
|
-
@opts[:
|
33
|
-
@opts[:log]&.log(e.backtrace.join("\n"))
|
31
|
+
rescue StandardError => e
|
32
|
+
@opts[:logger]&.call(e, nil)
|
34
33
|
ensure
|
35
34
|
@machine.close_async(@fd)
|
36
35
|
end
|
@@ -45,7 +44,7 @@ module TP2
|
|
45
44
|
persist_connection?(headers)
|
46
45
|
rescue ProtocolError
|
47
46
|
# TODO: add hook for testing protocol errors
|
48
|
-
|
47
|
+
false
|
49
48
|
end
|
50
49
|
|
51
50
|
def get_body(req)
|
@@ -60,7 +59,7 @@ module TP2
|
|
60
59
|
read(1 << 20, nil, false)
|
61
60
|
end
|
62
61
|
|
63
|
-
def get_body_chunk(req,
|
62
|
+
def get_body_chunk(req, _buffered_only = false)
|
64
63
|
headers = req.headers
|
65
64
|
content_length = headers['content-length']
|
66
65
|
if content_length
|
@@ -105,7 +104,7 @@ module TP2
|
|
105
104
|
else
|
106
105
|
@machine.send(@fd, formatted_headers, formatted_headers.bytesize, SEND_FLAGS)
|
107
106
|
end
|
108
|
-
|
107
|
+
@opts[:logger]&.call(request, headers)
|
109
108
|
@done = true
|
110
109
|
@response_headers = headers
|
111
110
|
end
|
@@ -120,7 +119,6 @@ module TP2
|
|
120
119
|
def send_headers(request, headers, empty_response: false, chunked: true)
|
121
120
|
formatted_headers = format_headers(headers, !empty_response, http1_1?(request) && chunked)
|
122
121
|
request.tx_incr(formatted_headers.bytesize)
|
123
|
-
#
|
124
122
|
@machine.send(@fd, formatted_headers, formatted_headers.bytesize, SEND_FLAGS)
|
125
123
|
@response_headers = headers
|
126
124
|
end
|
@@ -143,10 +141,10 @@ module TP2
|
|
143
141
|
|
144
142
|
request.tx_incr(data.bytesize)
|
145
143
|
@machine.send(@fd, data, data.bytesize, SEND_FLAGS)
|
146
|
-
if done
|
147
|
-
|
148
|
-
|
149
|
-
|
144
|
+
return if @done || !done
|
145
|
+
|
146
|
+
@opts[:logger]&.call(request, @response_headers)
|
147
|
+
@done = true
|
150
148
|
end
|
151
149
|
|
152
150
|
# Finishes the response to the current request. If no headers were sent,
|
@@ -155,10 +153,10 @@ module TP2
|
|
155
153
|
def finish(request)
|
156
154
|
request.tx_incr(EMPTY_CHUNK_LEN)
|
157
155
|
@machine.send(@fd, EMPTY_CHUNK, EMPTY_CHUNK_LEN, SEND_FLAGS)
|
158
|
-
if
|
159
|
-
|
160
|
-
|
161
|
-
|
156
|
+
return if @done
|
157
|
+
|
158
|
+
@opts[:logger]&.call(request, @response_headers)
|
159
|
+
@done = true
|
162
160
|
end
|
163
161
|
|
164
162
|
def respond_with_static_file(req, path, opts, cache_headers)
|
@@ -170,17 +168,17 @@ module TP2
|
|
170
168
|
opts[:headers] = cache_headers
|
171
169
|
end
|
172
170
|
|
173
|
-
maxlen = opts[:max_len] ||
|
171
|
+
maxlen = opts[:max_len] || 65_536
|
174
172
|
buf = String.new(capacity: maxlen)
|
175
173
|
headers_sent = nil
|
176
|
-
|
174
|
+
loop do
|
177
175
|
res = @machine.read(fd, buf, maxlen, 0)
|
178
176
|
if res < maxlen && !headers_sent
|
179
177
|
return respond(req, buf, opts[:headers])
|
180
178
|
elsif res == 0
|
181
179
|
return finish(req)
|
182
180
|
end
|
183
|
-
|
181
|
+
|
184
182
|
if !headers_sent
|
185
183
|
send_headers(req, opts[:headers])
|
186
184
|
headers_sent = true
|
@@ -199,34 +197,8 @@ module TP2
|
|
199
197
|
|
200
198
|
private
|
201
199
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
request_headers = request.headers
|
206
|
-
uri = full_uri(request_headers)
|
207
|
-
|
208
|
-
str = format(
|
209
|
-
"%<client_ip>s %<method>s %<uri>s %<status>s %<tx>d",
|
210
|
-
client_ip: request.forwarded_for || '?',
|
211
|
-
method: request_headers[':method'].upcase,
|
212
|
-
uri: uri,
|
213
|
-
status: @status,
|
214
|
-
tx: request_headers[':tx']
|
215
|
-
)
|
216
|
-
@opts[:log].log(str)
|
217
|
-
end
|
218
|
-
|
219
|
-
def full_uri(headers)
|
220
|
-
format(
|
221
|
-
"%s://%s%s",
|
222
|
-
headers['x_forwarded_proto'] || 'http',
|
223
|
-
headers['host'],
|
224
|
-
headers[':path']
|
225
|
-
)
|
226
|
-
end
|
227
|
-
|
228
|
-
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i.freeze
|
229
|
-
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i.freeze
|
200
|
+
RE_REQUEST_LINE = %r{^([a-z]+)\s+([^\s]+)\s+(http/[0-9.]{1,3})}i
|
201
|
+
RE_HEADER_LINE = /^([a-z0-9-]+):\s+(.+)/i
|
230
202
|
MAX_REQUEST_LINE_LEN = 1 << 14 # 16KB
|
231
203
|
MAX_HEADER_LINE_LEN = 1 << 10 # 1KB
|
232
204
|
MAX_CHUNK_SIZE_LEN = 16
|
@@ -236,11 +208,9 @@ module TP2
|
|
236
208
|
|
237
209
|
def persist_connection?(headers)
|
238
210
|
connection = headers['connection']&.downcase
|
239
|
-
if headers[':protocol'] == 'http/1.1'
|
240
|
-
|
241
|
-
|
242
|
-
return connection && connection != 'close'
|
243
|
-
end
|
211
|
+
return connection != 'close' if headers[':protocol'] == 'http/1.1'
|
212
|
+
|
213
|
+
connection && connection != 'close'
|
244
214
|
end
|
245
215
|
|
246
216
|
def parse_headers
|
@@ -248,7 +218,7 @@ module TP2
|
|
248
218
|
headers = get_request_line(buf)
|
249
219
|
return nil if !headers
|
250
220
|
|
251
|
-
|
221
|
+
loop do
|
252
222
|
line = @stream.get_line(buf, MAX_HEADER_LINE_LEN)
|
253
223
|
break if line.nil? || line.empty?
|
254
224
|
|
@@ -276,7 +246,7 @@ module TP2
|
|
276
246
|
end
|
277
247
|
|
278
248
|
def get_body_chunked_encoding(headers)
|
279
|
-
buf = String.new(capacity:
|
249
|
+
buf = String.new(capacity: 65_536)
|
280
250
|
while read_chunk(headers, buf)
|
281
251
|
end
|
282
252
|
|
@@ -285,9 +255,7 @@ module TP2
|
|
285
255
|
|
286
256
|
def read(len, buf = nil, raise_on_eof = true)
|
287
257
|
str = @stream.get_string(buf, len)
|
288
|
-
if !str && raise_on_eof
|
289
|
-
raise ProtocolError, 'Missing data'
|
290
|
-
end
|
258
|
+
raise ProtocolError, 'Missing data' if !str && raise_on_eof
|
291
259
|
|
292
260
|
str
|
293
261
|
end
|
@@ -314,7 +282,7 @@ module TP2
|
|
314
282
|
request.headers[':protocol'] == 'http/1.1'
|
315
283
|
end
|
316
284
|
|
317
|
-
INTERNAL_HEADER_REGEXP =
|
285
|
+
INTERNAL_HEADER_REGEXP = /^:/
|
318
286
|
|
319
287
|
# Formats response headers into an array. If empty_response is true(thy),
|
320
288
|
# the response status code will default to 204, otherwise to 200.
|
data/lib/tp2/logger.rb
CHANGED
@@ -2,15 +2,52 @@
|
|
2
2
|
|
3
3
|
module TP2
|
4
4
|
class Logger
|
5
|
-
def initialize(machine, fd =
|
5
|
+
def initialize(machine, fd = $stdout.fileno, **opts)
|
6
6
|
@machine = machine
|
7
7
|
@fd = fd
|
8
8
|
@opts = opts
|
9
9
|
end
|
10
10
|
|
11
11
|
def log(str)
|
12
|
-
str = format(
|
12
|
+
str = format(
|
13
|
+
"%<stamp>s %<msg>s\n",
|
14
|
+
stamp: Time.now.strftime('%Y-%m-%d %H:%M:%S.%3N'),
|
15
|
+
msg: str
|
16
|
+
)
|
13
17
|
@machine.write_async(@fd, str)
|
14
18
|
end
|
19
|
+
|
20
|
+
def call(request, _response_headers)
|
21
|
+
if request.is_a?(Exception)
|
22
|
+
e = request
|
23
|
+
log("Error: #{e.inspect}: #{e.backtrace.inspect}")
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
log(format_request_log_line(request))
|
28
|
+
end
|
29
|
+
|
30
|
+
def format_request_log_line(request)
|
31
|
+
request_headers = request.headers
|
32
|
+
uri = full_uri(request_headers)
|
33
|
+
|
34
|
+
format(
|
35
|
+
'%<client_ip>s %<method>s %<uri>s %<status>s %<tx>d',
|
36
|
+
client_ip: request.forwarded_for || '?',
|
37
|
+
method: request_headers[':method'].upcase,
|
38
|
+
uri: uri,
|
39
|
+
status: @status,
|
40
|
+
tx: request_headers[':tx']
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def full_uri(headers)
|
45
|
+
format(
|
46
|
+
'%<scheme>s://%<host>s%<path>s',
|
47
|
+
scheme: headers['x_forwarded_proto'] || 'http',
|
48
|
+
host: headers['host'],
|
49
|
+
path: headers[':path']
|
50
|
+
)
|
51
|
+
end
|
15
52
|
end
|
16
53
|
end
|
data/lib/tp2/version.rb
CHANGED
data/lib/tp2.rb
CHANGED
@@ -22,7 +22,7 @@ module TP2
|
|
22
22
|
" / \\ https://github.com/noteflakes/tp2\n" +
|
23
23
|
"⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺\n"
|
24
24
|
)
|
25
|
-
|
25
|
+
|
26
26
|
class << self
|
27
27
|
def run(opts = nil, &app)
|
28
28
|
if @in_run
|
@@ -37,7 +37,7 @@ module TP2
|
|
37
37
|
machine = UM.new
|
38
38
|
|
39
39
|
machine.puts(TP2::BANNER) if opts[:banner]
|
40
|
-
opts[:
|
40
|
+
opts[:logger] = opts[:logger] && TP2::Logger.new(machine, **opts)
|
41
41
|
|
42
42
|
server = Server.new(machine, opts, &app)
|
43
43
|
|
@@ -50,7 +50,7 @@ module TP2
|
|
50
50
|
|
51
51
|
def config(opts = nil, &app)
|
52
52
|
return @config if !opts && !app
|
53
|
-
|
53
|
+
|
54
54
|
@config = opts || {}
|
55
55
|
@config[:app] = app if app
|
56
56
|
end
|
@@ -66,7 +66,7 @@ module TP2
|
|
66
66
|
# waits for signal from queue, then terminates given fiber
|
67
67
|
# to be done
|
68
68
|
def watch_for_int_signal(machine, queue, fiber)
|
69
|
-
|
69
|
+
machine.shift(queue)
|
70
70
|
machine.schedule(fiber, UM::Terminate.new)
|
71
71
|
end
|
72
72
|
end
|
data/test/helper.rb
CHANGED
data/test/test_http1_adapter.rb
CHANGED
data/test/test_server.rb
CHANGED
@@ -25,19 +25,25 @@ class ServerTest < Minitest::Test
|
|
25
25
|
def setup
|
26
26
|
@machine = UM.new
|
27
27
|
@port = 10000 + rand(30000)
|
28
|
-
@
|
29
|
-
@
|
30
|
-
|
31
|
-
rescue STOP
|
32
|
-
ensure
|
33
|
-
@server_done = true
|
34
|
-
end
|
28
|
+
@opts = { bind: "127.0.0.1:#{@port}" }
|
29
|
+
@server = TP2::Server.new(@machine, @opts) { @app&.call(it) }
|
30
|
+
@f_server = @machine.spin { run_server }
|
35
31
|
|
32
|
+
# let server spin and listen to incoming connections
|
36
33
|
@machine.sleep(0.01)
|
34
|
+
|
37
35
|
@client_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
38
36
|
@machine.connect(@client_fd, '127.0.0.1', @port)
|
39
37
|
end
|
40
38
|
|
39
|
+
def run_server
|
40
|
+
@server.run
|
41
|
+
rescue STOP
|
42
|
+
# ignore
|
43
|
+
ensure
|
44
|
+
@server_done = true
|
45
|
+
end
|
46
|
+
|
41
47
|
def teardown
|
42
48
|
@machine.close(@client_fd) rescue nil
|
43
49
|
@machine.schedule(@f_server, STOP.new)
|
@@ -240,4 +246,17 @@ class ServerTest < Minitest::Test
|
|
240
246
|
body = @bodies.shift
|
241
247
|
assert_equal 'barbaz', body
|
242
248
|
end
|
249
|
+
|
250
|
+
def test_logging
|
251
|
+
reqs = []
|
252
|
+
@opts[:logger] = ->(req, _h) { reqs << req }
|
253
|
+
@app = ->(req) { req.respond('Hello, world!', {}) }
|
254
|
+
|
255
|
+
write_http_request "GET / HTTP/1.0\r\n\r\n"
|
256
|
+
response = read_client_side
|
257
|
+
expected = "HTTP/1.1 200\r\nContent-Length: 13\r\n\r\nHello, world!"
|
258
|
+
assert_equal(expected, response)
|
259
|
+
|
260
|
+
assert_equal 1, reqs.size
|
261
|
+
end
|
243
262
|
end
|
data/tp2.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.required_ruby_version = '>= 3.4'
|
21
21
|
s.executables = ['tp2']
|
22
22
|
|
23
|
-
s.add_dependency 'uringmachine', '~> 0.
|
23
|
+
s.add_dependency 'uringmachine', '~> 0.15'
|
24
24
|
s.add_dependency 'qeweney', '~> 0.21'
|
25
25
|
s.add_dependency 'rack', '~> 3.1.15'
|
26
26
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tp2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.12'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - "~>"
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: '0.
|
18
|
+
version: '0.15'
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - "~>"
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: '0.
|
25
|
+
version: '0.15'
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: qeweney
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -60,6 +60,7 @@ extra_rdoc_files:
|
|
60
60
|
files:
|
61
61
|
- ".github/workflows/test.yml"
|
62
62
|
- ".gitignore"
|
63
|
+
- ".rubocop.yml"
|
63
64
|
- CHANGELOG.md
|
64
65
|
- Gemfile
|
65
66
|
- README.md
|