tp2 0.13.4 → 0.14.1
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/CHANGELOG.md +8 -0
- data/TODO.md +0 -2
- data/examples/app.rb +1 -1
- data/lib/tp2/http1_connection.rb +26 -11
- data/lib/tp2/logger.rb +66 -35
- data/lib/tp2/request_extensions.rb +3 -1
- data/lib/tp2/server.rb +3 -3
- data/lib/tp2/version.rb +1 -1
- data/lib/tp2.rb +2 -2
- data/test/test_server.rb +22 -2
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c99469ae66a0fa3a184c3196d2e7978a5ae1cdac9b55820a4badc176030033a
|
4
|
+
data.tar.gz: 4e4f7646de8efdc89c23af826f2502c0aabc5ef8e7773272e4596ccf1c106822
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3dc15dc21f2d199cf3d807cc99e82d2f624269862e9adbf49e0d1bcd29bf00046f6c90bdb9523decfa9ad78377278e2e56a0cb8b6cc30f990273aa9349fe5bed
|
7
|
+
data.tar.gz: c236ef22298ffca5c23a432dfa31e83c243821b0c95d1705eeb109d0fd9312669e43551a9ca64d90e5f16abd9ec593f77416aa04306ab86d675e06a6890effb5
|
data/CHANGELOG.md
CHANGED
data/TODO.md
CHANGED
data/examples/app.rb
CHANGED
data/lib/tp2/http1_connection.rb
CHANGED
@@ -5,7 +5,7 @@ require 'stringio'
|
|
5
5
|
|
6
6
|
module TP2
|
7
7
|
class HTTP1Connection
|
8
|
-
attr_reader :fd
|
8
|
+
attr_reader :fd, :response_headers
|
9
9
|
|
10
10
|
def initialize(machine, fd, opts, &app)
|
11
11
|
@machine = machine
|
@@ -29,7 +29,10 @@ module TP2
|
|
29
29
|
rescue UM::Terminate
|
30
30
|
# server is terminated, do nothing
|
31
31
|
rescue StandardError => e
|
32
|
-
@logger&.
|
32
|
+
@logger&.error(
|
33
|
+
message: 'Uncaught error while running connection',
|
34
|
+
error: e
|
35
|
+
)
|
33
36
|
ensure
|
34
37
|
@machine.close_async(@fd)
|
35
38
|
end
|
@@ -40,19 +43,27 @@ module TP2
|
|
40
43
|
return false if !headers
|
41
44
|
|
42
45
|
request = Qeweney::Request.new(headers, self)
|
46
|
+
request.start_stamp = monotonic_clock
|
43
47
|
@app.call(request)
|
44
48
|
persist_connection?(headers)
|
45
49
|
rescue ProtocolError => e
|
46
|
-
|
47
|
-
|
50
|
+
@logger&.error(
|
51
|
+
message: 'Protocol error, closing connection',
|
52
|
+
error: e
|
53
|
+
)
|
48
54
|
false
|
49
55
|
rescue SystemCallError => e
|
50
|
-
|
51
|
-
|
56
|
+
@logger&.error(
|
57
|
+
message: 'I/O error, closing connection',
|
58
|
+
error: e
|
59
|
+
)
|
52
60
|
false
|
53
61
|
rescue StandardError => e
|
54
|
-
|
55
|
-
|
62
|
+
@logger&.error(
|
63
|
+
message: 'Internal error while serving request, abandoning connection',
|
64
|
+
error: e
|
65
|
+
)
|
66
|
+
|
56
67
|
if request && !@done
|
57
68
|
respond(request, 'Internal server error', ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
|
58
69
|
end
|
@@ -116,7 +127,7 @@ module TP2
|
|
116
127
|
else
|
117
128
|
@machine.send(@fd, formatted_headers, formatted_headers.bytesize, SEND_FLAGS)
|
118
129
|
end
|
119
|
-
@logger&.
|
130
|
+
@logger&.info(request: request, response_headers: headers)
|
120
131
|
@done = true
|
121
132
|
@response_headers = headers
|
122
133
|
end
|
@@ -155,7 +166,7 @@ module TP2
|
|
155
166
|
@machine.send(@fd, data, data.bytesize, SEND_FLAGS)
|
156
167
|
return if @done || !done
|
157
168
|
|
158
|
-
@logger&.
|
169
|
+
@logger&.info(request: request, response_headers: @response_headers)
|
159
170
|
@done = true
|
160
171
|
end
|
161
172
|
|
@@ -167,7 +178,7 @@ module TP2
|
|
167
178
|
@machine.send(@fd, EMPTY_CHUNK, EMPTY_CHUNK_LEN, SEND_FLAGS)
|
168
179
|
return if @done
|
169
180
|
|
170
|
-
@logger&.
|
181
|
+
@logger&.info(request, request, response_headers: @response_headers)
|
171
182
|
@done = true
|
172
183
|
end
|
173
184
|
|
@@ -207,6 +218,10 @@ module TP2
|
|
207
218
|
@machine.close_async(@fd)
|
208
219
|
end
|
209
220
|
|
221
|
+
def monotonic_clock
|
222
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
223
|
+
end
|
224
|
+
|
210
225
|
private
|
211
226
|
|
212
227
|
RE_REQUEST_LINE = %r{^([a-z]+)\s+([^\s]+)\s+(http/[0-9.]{1,3})}i
|
data/lib/tp2/logger.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module TP2
|
4
6
|
class Logger
|
5
7
|
def initialize(machine, fd = $stdout.fileno, **opts)
|
@@ -8,56 +10,85 @@ module TP2
|
|
8
10
|
@opts = opts
|
9
11
|
end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def info(o)
|
14
|
+
call(:INFO, o)
|
15
|
+
end
|
16
|
+
|
17
|
+
def warn(o)
|
18
|
+
call(:WARN, o)
|
19
|
+
end
|
20
|
+
|
21
|
+
def error(o)
|
22
|
+
call(:ERROR, o)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# @param level <Symbol> log level
|
28
|
+
# @param o <Hash> hash
|
29
|
+
def call(level, o)
|
30
|
+
emit(make_entry(level, o))
|
15
31
|
rescue StandardError => e
|
32
|
+
puts 'Uncaught error while emitting log entry:'
|
16
33
|
p e: e
|
17
34
|
p e.backtrace
|
18
35
|
exit
|
19
36
|
end
|
20
37
|
|
21
|
-
|
22
|
-
|
23
|
-
def log(str)
|
24
|
-
str = format(
|
25
|
-
"%<stamp>s %<msg>s\n",
|
26
|
-
stamp: Time.now.strftime('%Y-%m-%d %H:%M:%S.%3N'),
|
27
|
-
msg: str
|
28
|
-
)
|
29
|
-
@machine.write_async(@fd, str)
|
38
|
+
def emit(entry)
|
39
|
+
@machine.write_async(@fd, "#{entry.to_json}\n")
|
30
40
|
end
|
31
41
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
format_request_log_line(o, h)
|
38
|
-
when String
|
39
|
-
o
|
42
|
+
def make_entry(level, o)
|
43
|
+
if o[:request]
|
44
|
+
make_request_entry(level, o)
|
45
|
+
elsif o[:error]
|
46
|
+
make_error_entry(level, o)
|
40
47
|
else
|
41
|
-
o
|
48
|
+
make_hash_entry(level, o)
|
42
49
|
end
|
43
50
|
end
|
44
51
|
|
45
|
-
def
|
46
|
-
|
52
|
+
def make_error_entry(level, o)
|
53
|
+
err = o[:error]
|
54
|
+
{
|
55
|
+
level: level.to_s,
|
56
|
+
ts: (t = Time.now; t.to_i),
|
57
|
+
ts_s: t.iso8601
|
58
|
+
}
|
59
|
+
.merge(o)
|
60
|
+
.merge(
|
61
|
+
error: "#{err.class}: #{err.message}",
|
62
|
+
backtrace: err.backtrace
|
63
|
+
)
|
47
64
|
end
|
48
65
|
|
49
|
-
def
|
66
|
+
def make_request_entry(level, o)
|
67
|
+
request = o[:request]
|
50
68
|
request_headers = request.headers
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
69
|
+
response_headers = o[:response_headers]
|
70
|
+
elapsed = request.adapter.monotonic_clock - request.start_stamp
|
71
|
+
{
|
72
|
+
level: level.to_s,
|
73
|
+
ts: (t = Time.now; t.to_i),
|
74
|
+
ts_s: t.iso8601,
|
75
|
+
message: o[:message] || 'HTTP request done',
|
76
|
+
client_ip: request.forwarded_for || '?',
|
77
|
+
http_method: request_headers[':method'].upcase,
|
78
|
+
user_agent: request_headers['user-agent'],
|
79
|
+
uri: full_uri(request_headers),
|
80
|
+
status: response_headers[':status'] || '200',
|
81
|
+
elapsed: elapsed
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def make_hash_entry(level, hash)
|
86
|
+
{
|
87
|
+
level: level.to_s,
|
88
|
+
ts: (t = Time.now; t.to_i),
|
89
|
+
ts_s: t.iso8601
|
90
|
+
}
|
91
|
+
.merge(hash)
|
61
92
|
end
|
62
93
|
|
63
94
|
def full_uri(headers)
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Qeweney::Request
|
4
|
+
attr_accessor :start_stamp
|
5
|
+
|
4
6
|
def respond_with_static_file(path, etag, last_modified, opts)
|
5
7
|
cache_headers = (etag || last_modified) ? {
|
6
8
|
'etag' => etag,
|
7
|
-
'last-modified' => last_modified
|
9
|
+
'last-modified' => last_modified
|
8
10
|
} : {}
|
9
11
|
|
10
12
|
adapter.respond_with_static_file(self, path, opts, cache_headers)
|
data/lib/tp2/server.rb
CHANGED
@@ -17,7 +17,7 @@ module TP2
|
|
17
17
|
|
18
18
|
def self.tp2_app(_machine, opts)
|
19
19
|
if opts[:app_location]
|
20
|
-
opts[:logger]&.
|
20
|
+
opts[:logger]&.info(message: 'Loading web app', location: opts[:app_location])
|
21
21
|
require opts[:app_location]
|
22
22
|
|
23
23
|
opts.merge!(TP2.config)
|
@@ -65,7 +65,7 @@ module TP2
|
|
65
65
|
@accept_fibers << @machine.spin { accept_incoming(fd) }
|
66
66
|
end
|
67
67
|
bind_string = bind_info.map { it.join(':') }.join(', ')
|
68
|
-
@opts[:logger]&.
|
68
|
+
@opts[:logger]&.info(message: "Listening on #{bind_string}")
|
69
69
|
|
70
70
|
# map fibers
|
71
71
|
@connection_fiber_map = {}
|
@@ -121,7 +121,7 @@ module TP2
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def graceful_shutdown
|
124
|
-
@opts[:logger]&.
|
124
|
+
@opts[:logger]&.info(message: 'Shutting down gracefully...')
|
125
125
|
|
126
126
|
# stop listening
|
127
127
|
close_all_server_fds
|
data/lib/tp2/version.rb
CHANGED
data/lib/tp2.rb
CHANGED
@@ -19,7 +19,7 @@ module TP2
|
|
19
19
|
" o\n" +
|
20
20
|
" \\|/ TP2 - a modern web server for Ruby apps\n" +
|
21
21
|
" / \\ \n" +
|
22
|
-
" /
|
22
|
+
" / x \\ https://github.com/noteflakes/tp2\n" +
|
23
23
|
"⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺\n"
|
24
24
|
)
|
25
25
|
|
@@ -37,7 +37,7 @@ module TP2
|
|
37
37
|
machine = opts[:machine] || UM.new
|
38
38
|
machine.puts(opts[:banner]) if opts[:banner]
|
39
39
|
|
40
|
-
opts[:logger]&.
|
40
|
+
opts[:logger]&.info(message: "Running TP2 #{TP2::VERSION}, UringMachine #{UM::VERSION}, Ruby #{RUBY_VERSION}")
|
41
41
|
|
42
42
|
server = Server.new(machine, opts, &app)
|
43
43
|
|
data/test/test_server.rb
CHANGED
@@ -247,17 +247,37 @@ class ServerTest < Minitest::Test
|
|
247
247
|
assert_equal 'barbaz', body
|
248
248
|
end
|
249
249
|
|
250
|
+
class TestLogger
|
251
|
+
attr_reader :entries
|
252
|
+
|
253
|
+
def initialize
|
254
|
+
@entries = []
|
255
|
+
end
|
256
|
+
|
257
|
+
def info(o)
|
258
|
+
@entries << o.merge(level: :INFO)
|
259
|
+
end
|
260
|
+
|
261
|
+
def error(o)
|
262
|
+
@entries << o.merge(level: :ERROR)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
250
266
|
def test_logging
|
251
267
|
skip
|
252
268
|
reqs = []
|
253
|
-
@opts[:logger] =
|
254
|
-
@app = ->(req) { req.respond('Hello, world!', {}) }
|
269
|
+
@opts[:logger] = TestLogger.new
|
270
|
+
@app = ->(req) { reqs << req; req.respond('Hello, world!', {}) }
|
255
271
|
|
256
272
|
write_http_request "GET / HTTP/1.0\r\n\r\n"
|
257
273
|
response = read_client_side
|
258
274
|
expected = "HTTP/1.1 200\r\nContent-Length: 13\r\n\r\nHello, world!"
|
259
275
|
assert_equal(expected, response)
|
260
276
|
|
277
|
+
entries = @opts[:logger].entries
|
278
|
+
assert_equal 1, entries.size
|
261
279
|
assert_equal 1, reqs.size
|
280
|
+
|
281
|
+
assert_equal reqs.first, entries.first[:request]
|
262
282
|
end
|
263
283
|
end
|