tp2 0.13.4 → 0.14
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 +4 -0
- data/TODO.md +0 -2
- data/examples/app.rb +1 -1
- data/lib/tp2/http1_connection.rb +21 -11
- data/lib/tp2/logger.rb +64 -35
- 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: '08812ae0e3200972086172f46db095a85dd81ab9f92a19f3545cb7e08425e722'
|
4
|
+
data.tar.gz: 204dcb6bc431cccddcd2a1aee3416235bb495359f99cc0afaa96cd56353d51f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49f58f9c2e4650816583633323a9f9fd38d5028ca27a3c024694baad38fbd656ec360b6cb6189876e7cb9d0574e8c340d2d71796086135225057e9367ebe7ef7
|
7
|
+
data.tar.gz: 1f9cc001a6613a89b094d07ebeadf884f377840d302080c269de4cb8f635220a18e358efe3a078936acc069479202eb17a7f47b5842fb5a150256891ce7819d0
|
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
|
@@ -43,16 +46,23 @@ module TP2
|
|
43
46
|
@app.call(request)
|
44
47
|
persist_connection?(headers)
|
45
48
|
rescue ProtocolError => e
|
46
|
-
|
47
|
-
|
49
|
+
@logger&.error(
|
50
|
+
message: 'Protocol error, closing connection',
|
51
|
+
error: e
|
52
|
+
)
|
48
53
|
false
|
49
54
|
rescue SystemCallError => e
|
50
|
-
|
51
|
-
|
55
|
+
@logger&.error(
|
56
|
+
message: 'I/O error, closing connection',
|
57
|
+
error: e
|
58
|
+
)
|
52
59
|
false
|
53
60
|
rescue StandardError => e
|
54
|
-
|
55
|
-
|
61
|
+
@logger&.error(
|
62
|
+
message: 'Internal error while serving request, abandoning connection',
|
63
|
+
error: e
|
64
|
+
)
|
65
|
+
|
56
66
|
if request && !@done
|
57
67
|
respond(request, 'Internal server error', ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
|
58
68
|
end
|
@@ -116,7 +126,7 @@ module TP2
|
|
116
126
|
else
|
117
127
|
@machine.send(@fd, formatted_headers, formatted_headers.bytesize, SEND_FLAGS)
|
118
128
|
end
|
119
|
-
@logger&.
|
129
|
+
@logger&.info(request: request, response_headers: headers)
|
120
130
|
@done = true
|
121
131
|
@response_headers = headers
|
122
132
|
end
|
@@ -155,7 +165,7 @@ module TP2
|
|
155
165
|
@machine.send(@fd, data, data.bytesize, SEND_FLAGS)
|
156
166
|
return if @done || !done
|
157
167
|
|
158
|
-
@logger&.
|
168
|
+
@logger&.info(request: request, response_headers: @response_headers)
|
159
169
|
@done = true
|
160
170
|
end
|
161
171
|
|
@@ -167,7 +177,7 @@ module TP2
|
|
167
177
|
@machine.send(@fd, EMPTY_CHUNK, EMPTY_CHUNK_LEN, SEND_FLAGS)
|
168
178
|
return if @done
|
169
179
|
|
170
|
-
@logger&.
|
180
|
+
@logger&.info(request, request, response_headers: @response_headers)
|
171
181
|
@done = true
|
172
182
|
end
|
173
183
|
|
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,83 @@ 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
|
+
{
|
71
|
+
level: level.to_s,
|
72
|
+
ts: (t = Time.now; t.to_i),
|
73
|
+
ts_s: t.iso8601,
|
74
|
+
message: o[:message] || 'HTTP request done',
|
75
|
+
client_ip: request.forwarded_for || '?',
|
76
|
+
http_method: request_headers[':method'].upcase,
|
77
|
+
user_agent: request_headers['user-agent'],
|
78
|
+
uri: full_uri(request_headers),
|
79
|
+
status: response_headers[':status'] || '200'
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def make_hash_entry(level, hash)
|
84
|
+
{
|
85
|
+
level: level.to_s,
|
86
|
+
ts: (t = Time.now; t.to_i),
|
87
|
+
ts_s: t.iso8601
|
88
|
+
}
|
89
|
+
.merge(hash)
|
61
90
|
end
|
62
91
|
|
63
92
|
def full_uri(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
|