tp2 0.13.3 → 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 +8 -0
- data/TODO.md +0 -2
- data/examples/app.rb +1 -1
- data/lib/tp2/http1_connection.rb +26 -12
- 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
|
@@ -42,13 +45,24 @@ module TP2
|
|
42
45
|
request = Qeweney::Request.new(headers, self)
|
43
46
|
@app.call(request)
|
44
47
|
persist_connection?(headers)
|
45
|
-
rescue ProtocolError
|
46
|
-
|
47
|
-
|
48
|
+
rescue ProtocolError => e
|
49
|
+
@logger&.error(
|
50
|
+
message: 'Protocol error, closing connection',
|
51
|
+
error: e
|
52
|
+
)
|
53
|
+
false
|
54
|
+
rescue SystemCallError => e
|
55
|
+
@logger&.error(
|
56
|
+
message: 'I/O error, closing connection',
|
57
|
+
error: e
|
58
|
+
)
|
48
59
|
false
|
49
60
|
rescue StandardError => e
|
50
|
-
|
51
|
-
|
61
|
+
@logger&.error(
|
62
|
+
message: 'Internal error while serving request, abandoning connection',
|
63
|
+
error: e
|
64
|
+
)
|
65
|
+
|
52
66
|
if request && !@done
|
53
67
|
respond(request, 'Internal server error', ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
|
54
68
|
end
|
@@ -112,7 +126,7 @@ module TP2
|
|
112
126
|
else
|
113
127
|
@machine.send(@fd, formatted_headers, formatted_headers.bytesize, SEND_FLAGS)
|
114
128
|
end
|
115
|
-
@logger&.
|
129
|
+
@logger&.info(request: request, response_headers: headers)
|
116
130
|
@done = true
|
117
131
|
@response_headers = headers
|
118
132
|
end
|
@@ -151,7 +165,7 @@ module TP2
|
|
151
165
|
@machine.send(@fd, data, data.bytesize, SEND_FLAGS)
|
152
166
|
return if @done || !done
|
153
167
|
|
154
|
-
@logger&.
|
168
|
+
@logger&.info(request: request, response_headers: @response_headers)
|
155
169
|
@done = true
|
156
170
|
end
|
157
171
|
|
@@ -163,7 +177,7 @@ module TP2
|
|
163
177
|
@machine.send(@fd, EMPTY_CHUNK, EMPTY_CHUNK_LEN, SEND_FLAGS)
|
164
178
|
return if @done
|
165
179
|
|
166
|
-
@logger&.
|
180
|
+
@logger&.info(request, request, response_headers: @response_headers)
|
167
181
|
@done = true
|
168
182
|
end
|
169
183
|
|
@@ -231,7 +245,7 @@ module TP2
|
|
231
245
|
break if line.nil? || line.empty?
|
232
246
|
|
233
247
|
m = line.match(RE_HEADER_LINE)
|
234
|
-
raise ProtocolError,
|
248
|
+
raise ProtocolError, "Invalid header: #{line[0..2047].inspect}" if !m
|
235
249
|
|
236
250
|
headers[m[1].downcase] = m[2]
|
237
251
|
end
|
@@ -244,7 +258,7 @@ module TP2
|
|
244
258
|
return nil if !line
|
245
259
|
|
246
260
|
m = line.match(RE_REQUEST_LINE)
|
247
|
-
raise ProtocolError,
|
261
|
+
raise ProtocolError, "Invalid request line: #{line[0..2047].inspect}" if !m
|
248
262
|
|
249
263
|
{
|
250
264
|
':method' => m[1].downcase,
|
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
|