tipi 0.42 → 0.47
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/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +1 -3
- data/CHANGELOG.md +27 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +35 -29
- data/README.md +184 -8
- data/Rakefile +1 -7
- data/benchmarks/bm_http1_parser.rb +45 -21
- data/bin/benchmark +0 -0
- data/bin/h1pd +0 -0
- data/bm.png +0 -0
- data/df/agent.rb +1 -1
- data/df/sample_agent.rb +2 -2
- data/df/server.rb +2 -0
- data/df/server_utils.rb +12 -15
- data/examples/hello.rb +5 -0
- data/examples/hello.ru +3 -3
- data/examples/http_server.js +1 -1
- data/examples/http_server_graceful.rb +1 -1
- data/examples/https_server.rb +41 -18
- data/examples/rack_server_forked.rb +26 -0
- data/examples/rack_server_https_forked.rb +1 -1
- data/examples/websocket_demo.rb +1 -1
- data/lib/tipi/acme.rb +51 -39
- data/lib/tipi/cli.rb +79 -16
- data/lib/tipi/config_dsl.rb +13 -13
- data/lib/tipi/configuration.rb +2 -2
- data/lib/tipi/controller/bare_polyphony.rb +0 -0
- data/lib/tipi/controller/bare_stock.rb +10 -0
- data/lib/tipi/controller/extensions.rb +37 -0
- data/lib/tipi/controller/stock_http1_adapter.rb +15 -0
- data/lib/tipi/controller/web_polyphony.rb +353 -0
- data/lib/tipi/controller/web_stock.rb +635 -0
- data/lib/tipi/controller.rb +12 -0
- data/lib/tipi/digital_fabric/agent.rb +3 -3
- data/lib/tipi/digital_fabric/agent_proxy.rb +11 -5
- data/lib/tipi/digital_fabric/executive.rb +1 -1
- data/lib/tipi/digital_fabric/protocol.rb +1 -1
- data/lib/tipi/digital_fabric/service.rb +12 -8
- data/lib/tipi/handler.rb +2 -2
- data/lib/tipi/http1_adapter.rb +36 -30
- data/lib/tipi/http2_adapter.rb +10 -10
- data/lib/tipi/http2_stream.rb +14 -15
- data/lib/tipi/rack_adapter.rb +2 -2
- data/lib/tipi/response_extensions.rb +1 -1
- data/lib/tipi/supervisor.rb +75 -0
- data/lib/tipi/version.rb +1 -1
- data/lib/tipi/websocket.rb +3 -3
- data/lib/tipi.rb +4 -83
- data/test/coverage.rb +2 -2
- data/test/helper.rb +0 -1
- data/test/test_http_server.rb +14 -14
- data/test/test_request.rb +1 -1
- data/tipi.gemspec +6 -7
- metadata +58 -53
- data/ext/tipi/extconf.rb +0 -13
- data/ext/tipi/http1_parser.c +0 -823
- data/ext/tipi/http1_parser.h +0 -18
- data/ext/tipi/tipi_ext.c +0 -5
- data/security/http1.rb +0 -12
- data/test/test_http1_parser.rb +0 -586
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f49829928be9cc9f944b9749c4092da37f99f42c34b9b07e2e16bceccf0f6e02
|
4
|
+
data.tar.gz: fcdc73b07ec3ad3ad6bb06832b135ca8179c3d1d2939d8065d5b10217edab55c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfba22e88c1bff8f23c828e86274148f5ef895262f2258afb1c30743c67b4e7ff0493b40334dddccc20c418d4dbb3462e68e01a397030681adb4bd1c130d3ab2
|
7
|
+
data.tar.gz: 0765f9c44a88f00d5e45f0d168ade5ae7649682bf61134142ce531603857cbb9c1180dde0f101b69a07d4e57688a47522ab86cb87178074fbcf7d84d949ee40f
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
github: ciconia
|
data/.github/workflows/test.yml
CHANGED
@@ -8,7 +8,7 @@ jobs:
|
|
8
8
|
fail-fast: false
|
9
9
|
matrix:
|
10
10
|
os: [ubuntu-latest]
|
11
|
-
ruby: [2.6, 2.7]
|
11
|
+
ruby: [2.6, 2.7, 3.0]
|
12
12
|
|
13
13
|
name: >-
|
14
14
|
${{matrix.os}}, ${{matrix.ruby}}
|
@@ -25,7 +25,5 @@ jobs:
|
|
25
25
|
POLYPHONY_USE_LIBEV=1 bundle install
|
26
26
|
- name: Show Linux kernel version
|
27
27
|
run: uname -r
|
28
|
-
- name: Compile C-extension
|
29
|
-
run: bundle exec rake compile
|
30
28
|
- name: Run tests
|
31
29
|
run: bundle exec rake test
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
+
## 0.47 2202-02-03
|
2
|
+
|
3
|
+
- Update H1P dependency
|
4
|
+
|
5
|
+
## 0.46 2022-02-01
|
6
|
+
|
7
|
+
- Allow setting valid hosts
|
8
|
+
- Change interface of Qeweney apps to use #run (#15)
|
9
|
+
- Close server listener before terminating connections
|
10
|
+
|
11
|
+
## 0.45 2021-10-25
|
12
|
+
|
13
|
+
- Remove `http_parser.rb` dependency (#14) - thanks @SwagDevOps
|
14
|
+
- Use `argv` argument in `Tipi.opts_from_argv` (#13) - thanks @SwagDevOps
|
15
|
+
- Ignore `ArgumentError` in `#parse_headers`
|
16
|
+
|
17
|
+
## 0.44 2021-09-29
|
18
|
+
|
19
|
+
- Implement compatibility mode for HTTP/1 (WIP)
|
20
|
+
- Add option parsing for CLI tool
|
21
|
+
- Implement supervisor-controller-worker model in CLI tool
|
22
|
+
|
23
|
+
## 0.43 2021-08-20
|
24
|
+
|
25
|
+
- Extract HTTP/1 parser into a separate gem:
|
26
|
+
[H1P](https://github.com/digital-fabric/h1p)
|
27
|
+
|
1
28
|
## 0.42 2021-08-16
|
2
29
|
|
3
30
|
- HTTP/1 parser: disable UTF-8 parsing for all but header values
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,24 +1,36 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../ever
|
3
|
+
specs:
|
4
|
+
ever (0.1)
|
5
|
+
|
6
|
+
PATH
|
7
|
+
remote: ../h1p
|
8
|
+
specs:
|
9
|
+
h1p (0.3)
|
10
|
+
|
1
11
|
PATH
|
2
12
|
remote: ../polyphony
|
3
13
|
specs:
|
4
|
-
polyphony (0.
|
14
|
+
polyphony (0.74)
|
5
15
|
|
6
16
|
PATH
|
7
17
|
remote: ../qeweney
|
8
18
|
specs:
|
9
|
-
qeweney (0.
|
19
|
+
qeweney (0.16)
|
10
20
|
escape_utils (~> 1.2.1)
|
11
21
|
|
12
22
|
PATH
|
13
23
|
remote: .
|
14
24
|
specs:
|
15
|
-
tipi (0.
|
25
|
+
tipi (0.47)
|
16
26
|
acme-client (~> 2.0.8)
|
27
|
+
ever (~> 0.1)
|
17
28
|
extralite (~> 1.2)
|
29
|
+
h1p (~> 0.3)
|
18
30
|
http-2 (~> 0.11)
|
19
31
|
localhost (~> 1.1.4)
|
20
32
|
msgpack (~> 1.4.2)
|
21
|
-
polyphony (~> 0.
|
33
|
+
polyphony (~> 0.71)
|
22
34
|
qeweney (~> 0.14)
|
23
35
|
rack (>= 2.0.8, < 2.3.0)
|
24
36
|
websocket (~> 1.2.8)
|
@@ -26,51 +38,45 @@ PATH
|
|
26
38
|
GEM
|
27
39
|
remote: https://rubygems.org/
|
28
40
|
specs:
|
29
|
-
acme-client (2.0.
|
41
|
+
acme-client (2.0.9)
|
30
42
|
faraday (>= 0.17, < 2.0.0)
|
31
|
-
ansi (1.5.0)
|
32
|
-
builder (3.2.4)
|
33
43
|
cuba (3.9.3)
|
34
44
|
rack (>= 1.6.0)
|
35
45
|
docile (1.4.0)
|
36
46
|
escape_utils (1.2.1)
|
37
|
-
extralite (1.
|
38
|
-
faraday (1.
|
47
|
+
extralite (1.11)
|
48
|
+
faraday (1.9.3)
|
39
49
|
faraday-em_http (~> 1.0)
|
40
50
|
faraday-em_synchrony (~> 1.0)
|
41
51
|
faraday-excon (~> 1.1)
|
42
|
-
faraday-httpclient (~> 1.0
|
52
|
+
faraday-httpclient (~> 1.0)
|
53
|
+
faraday-multipart (~> 1.0)
|
43
54
|
faraday-net_http (~> 1.0)
|
44
|
-
faraday-net_http_persistent (~> 1.
|
55
|
+
faraday-net_http_persistent (~> 1.0)
|
45
56
|
faraday-patron (~> 1.0)
|
46
57
|
faraday-rack (~> 1.0)
|
47
|
-
|
58
|
+
faraday-retry (~> 1.0)
|
48
59
|
ruby2_keywords (>= 0.0.4)
|
49
60
|
faraday-em_http (1.0.0)
|
50
61
|
faraday-em_synchrony (1.0.0)
|
51
62
|
faraday-excon (1.1.0)
|
52
63
|
faraday-httpclient (1.0.1)
|
64
|
+
faraday-multipart (1.0.3)
|
65
|
+
multipart-post (>= 1.2, < 3)
|
53
66
|
faraday-net_http (1.0.1)
|
54
67
|
faraday-net_http_persistent (1.2.0)
|
55
68
|
faraday-patron (1.0.0)
|
56
69
|
faraday-rack (1.0.0)
|
70
|
+
faraday-retry (1.0.3)
|
57
71
|
http-2 (0.11.0)
|
58
|
-
http_parser.rb (0.7.0)
|
59
72
|
json (2.5.1)
|
60
|
-
localhost (1.1.
|
73
|
+
localhost (1.1.9)
|
74
|
+
memory_profiler (1.0.0)
|
61
75
|
minitest (5.11.3)
|
62
|
-
|
63
|
-
ansi
|
64
|
-
builder
|
65
|
-
minitest (>= 5.0)
|
66
|
-
ruby-progressbar
|
67
|
-
msgpack (1.4.2)
|
76
|
+
msgpack (1.4.4)
|
68
77
|
multipart-post (2.1.1)
|
69
78
|
rack (2.2.3)
|
70
|
-
rake (
|
71
|
-
rake-compiler (1.1.1)
|
72
|
-
rake
|
73
|
-
ruby-progressbar (1.11.0)
|
79
|
+
rake (13.0.6)
|
74
80
|
ruby2_keywords (0.0.5)
|
75
81
|
simplecov (0.17.1)
|
76
82
|
docile (~> 1.1)
|
@@ -84,15 +90,15 @@ PLATFORMS
|
|
84
90
|
|
85
91
|
DEPENDENCIES
|
86
92
|
cuba (~> 3.9.3)
|
87
|
-
|
93
|
+
ever!
|
94
|
+
h1p!
|
95
|
+
memory_profiler (~> 1.0.0)
|
88
96
|
minitest (~> 5.11.3)
|
89
|
-
minitest-reporters (~> 1.4.2)
|
90
97
|
polyphony!
|
91
98
|
qeweney!
|
92
|
-
rake (~>
|
93
|
-
rake-compiler (= 1.1.1)
|
99
|
+
rake (~> 13.0.6)
|
94
100
|
simplecov (~> 0.17.1)
|
95
101
|
tipi!
|
96
102
|
|
97
103
|
BUNDLED WITH
|
98
|
-
2.
|
104
|
+
2.3.3
|
data/README.md
CHANGED
@@ -3,27 +3,203 @@
|
|
3
3
|
# Tipi - the All-in-one Web Server for Ruby Apps
|
4
4
|
|
5
5
|
[](http://rubygems.org/gems/tipi)
|
6
|
-
[](https://github.com/digital-fabric/tipi/actions?query=workflow%3ATests)
|
7
7
|
[](https://github.com/digital-fabric/tipi/blob/master/LICENSE)
|
8
8
|
|
9
9
|
## What is Tipi?
|
10
10
|
|
11
11
|
Tipi is an integrated, feature-complete HTTP/S server for Ruby applications.
|
12
|
-
Tipi is built on
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
Tipi is built on top of
|
13
|
+
[Polyphony](https://github.com/digital-fabric/polyphony), a robust,
|
14
|
+
high-performance library for building highly-concurrent applications in Ruby.
|
15
|
+
Tipi can be used to serve any Rack application or set of static files directly
|
16
|
+
without having to employ a reverse-proxy such as Nginx.
|
16
17
|
|
17
18
|
## Features
|
18
19
|
|
19
20
|
* High-performance, highly concurrent web server based on
|
20
21
|
[Polyphony](https://github.com/digital-fabric/polyphony)
|
21
|
-
* Full support for HTTP, HTTP/2, WebSocket protocols
|
22
|
+
* Full support for HTTP/1, HTTP/2, WebSocket protocols
|
22
23
|
* Built-in SSL termination for secure, encrypted connections
|
23
|
-
* Automatic
|
24
|
+
* **Automatic SSL certificates** using ACME providers such as Let's Encrypt (WIP)
|
25
|
+
* Automatic ALPN protocol selection for serving HTTP/2
|
24
26
|
* Request and response body streaming for efficient downloads and uploads
|
25
27
|
* Full support for Rack-based apps
|
26
28
|
|
29
|
+
## Benchmarks
|
30
|
+
|
31
|
+
> Caveat emptor: the following results were obtained with an ad-hoc, manual
|
32
|
+
> process. I am not really familiar with the servers I compared Tipi against,
|
33
|
+
> and I ran them in their default configuration (apart from setting the number
|
34
|
+
> of workers). Take these results with a bunch of salt.
|
35
|
+
|
36
|
+
<img src="bm.png" style="width: 480px">
|
37
|
+
|
38
|
+
| |Tipi|Puma|Falcon|Unicorn|
|
39
|
+
|-|---:|---:|-----:|------:|
|
40
|
+
|HTTP/1.1|138629|34573|40714|7438|
|
41
|
+
|HTTPS/2|56762|n/a|34226|n/a|
|
42
|
+
|
43
|
+
### Methodology
|
44
|
+
|
45
|
+
- All servers ran the same "Hello world" [Rack
|
46
|
+
application](https://github.com/digital-fabric/tipi/blob/master/examples/hello.ru)
|
47
|
+
- Each server was run with 4 forked worker processes:
|
48
|
+
- Tipi: `tipi -w4 -flocalhost:10080:10443 examples/hello.ru`
|
49
|
+
- [Puma](https://github.com/puma/puma): `puma -w 4 examples/hello.ru`
|
50
|
+
- [Falcon](https://github.com/socketry/falcon/): `falcon -n 4 -b http://localhost:9292/ -c examples/hello.ru`
|
51
|
+
- [Unicorn](https://yhbt.net/unicorn/): `unicorn -c u.conf examples/hello.ru`
|
52
|
+
with the configuration file containing the directive `worker_processes 4`
|
53
|
+
- The benchmark results were obtained using `wrk -d60 -t4 -c64 <url>`
|
54
|
+
- All servers were run on Ruby 2.7.2p137
|
55
|
+
- Machine specs: i5-8350U@1.7GHzx8 CPU, 8GB of RAM, running Linux kernel version 5.13.7
|
56
|
+
- Puma does not support HTTP/2.
|
57
|
+
- As far as I could tell Unicorn does not support SSL termination.
|
58
|
+
|
59
|
+
## Running Tipi
|
60
|
+
|
61
|
+
To run Tipi, run the included `tipi` command. Alternatively you can add tipi as
|
62
|
+
a dependency to your Gemfile, then run `bundle exec tipi`. By default
|
63
|
+
|
64
|
+
Tipi can be used to drive Rack apps or alternatively any app using the
|
65
|
+
[Qeweney](https://github.com/digital-fabric/qeweney) request-response interface.
|
66
|
+
|
67
|
+
### Running Rack apps
|
68
|
+
|
69
|
+
Use the `tipi` command to start your app:
|
70
|
+
|
71
|
+
```bash
|
72
|
+
$ bundle exec tipi myapp.ru
|
73
|
+
```
|
74
|
+
|
75
|
+
### Running Qeweney apps
|
76
|
+
|
77
|
+
```bash
|
78
|
+
$ bundle exec tipi myapp.rb
|
79
|
+
```
|
80
|
+
|
81
|
+
The app script file should define an `app` method that returns a proc/lambda
|
82
|
+
taking a single `Qeweney::Request` argument. Here's an example:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# frozen_string_literal: true
|
86
|
+
|
87
|
+
def app
|
88
|
+
->(req) { req.respond('Hello, world!', 'Content-Type' => 'text/plain') }
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
## Setting server listening options
|
93
|
+
|
94
|
+
By default, Tipi serves plain HTTP on port 1234, but you can easily change that
|
95
|
+
by providing command line options as follows:
|
96
|
+
|
97
|
+
### HTTP
|
98
|
+
|
99
|
+
To listen for plain HTTP, use the `-l`/`--listen` option and specify a port
|
100
|
+
number:
|
101
|
+
|
102
|
+
```bash
|
103
|
+
$ bundle exec tipi -l9292 myapp.ru
|
104
|
+
```
|
105
|
+
|
106
|
+
### HTTPS
|
107
|
+
|
108
|
+
To listen for HTTPS connections, use the `-s`/`--secure` option and specify a
|
109
|
+
host name and a port:
|
110
|
+
|
111
|
+
```bash
|
112
|
+
$ bundle exec tipi -sexample.com:9292 myapp.ru
|
113
|
+
```
|
114
|
+
|
115
|
+
### Full service listening
|
116
|
+
|
117
|
+
The Tipi full service listens for both HTTP and HTTPS and supports automatic
|
118
|
+
certificate provisioning. To use the full service, use the `-f`/`--full` option,
|
119
|
+
and specify the domain name, the HTTP port, and the HTTPS port, e.g.:
|
120
|
+
|
121
|
+
```bash
|
122
|
+
$ bundle exec tipi -fmysite.org:10080:10443 myapp.ru
|
123
|
+
|
124
|
+
#If serving multiple domains, you can use * as place holder
|
125
|
+
$ bundle exec tipi -f*:10080:10443 myapp.ru
|
126
|
+
```
|
127
|
+
|
128
|
+
If `localhost` is specified as the domain, Tipi will automatically generate a
|
129
|
+
localhost certificate.
|
130
|
+
|
131
|
+
## Concurrency settings
|
132
|
+
|
133
|
+
By default, the `tipi` command starts a single controller and uses
|
134
|
+
[Polyphony](https://github.com/digital-fabric/polyphony) to run each connection
|
135
|
+
on its own fiber. This means that you will have a single process running on a
|
136
|
+
single thread (on a single CPU core). In order to parallelize your app and
|
137
|
+
employ multiple CPU cores, you can tell Tipi to fork multiple worker processes
|
138
|
+
to run your app. The number of workers is controlled using the `-w`/`--workers`
|
139
|
+
option:
|
140
|
+
|
141
|
+
```bash
|
142
|
+
# fork 4 worker processes
|
143
|
+
$ bundle exec tipi -w4 myapp.ru
|
144
|
+
```
|
145
|
+
|
146
|
+
You can also set Tipi to spawn multiple threads in each worker when in
|
147
|
+
compatibility mode (see below.)
|
148
|
+
|
149
|
+
## Compatibility mode
|
150
|
+
|
151
|
+
> Note: compatibility mode is still being developed, and currently only supports
|
152
|
+
> HTTP/1 connections.
|
153
|
+
|
154
|
+
In some apps, using Polyphony is not possible, due to incompatibilities between
|
155
|
+
it and other third-party dependencies. In order to be able to run these apps,
|
156
|
+
Tipi provides a compatibility mode that does not use Polyphony for concurrency,
|
157
|
+
but instead uses a thread-per-connection concurrency model. You can also fork
|
158
|
+
multiple workers, each running multiple threads, if so desired. Note that the
|
159
|
+
concurrency level is the maximum number workers multiplied by the number of
|
160
|
+
threads per worker:
|
161
|
+
|
162
|
+
```
|
163
|
+
concurrency = worker_count * threads_per_worker
|
164
|
+
```
|
165
|
+
|
166
|
+
To run Tipi in compatibility mode, use the `-c`/`--compatibility` option, e.g.:
|
167
|
+
|
168
|
+
```bash
|
169
|
+
# 4 workers * 8 threads = 32 max concurrency
|
170
|
+
$ bundle exec tipi -c -w4 -t8 myapp.ru
|
171
|
+
```
|
172
|
+
|
173
|
+
## Worker process supervision
|
174
|
+
|
175
|
+
Tipi employs a supervisor-controller-worker process supervision model, which
|
176
|
+
minimizes the memory consumption of forked workers, and which facilitates
|
177
|
+
graceful reloading after updating the application code.
|
178
|
+
|
179
|
+
This supervision model is made of three levels:
|
180
|
+
|
181
|
+
- Supervisor - Starts and stops the controller process
|
182
|
+
- Controller - loads the application code and forks workers
|
183
|
+
- Worker - listens for connections, handles incoming requests
|
184
|
+
|
185
|
+
(If the worker count is 1, the Controller and Worker roles are merged into a
|
186
|
+
single process.)
|
187
|
+
|
188
|
+
This model allows Tipi to fork workers after loading the app code, and use a
|
189
|
+
much simpler way to perform graceful restarts:
|
190
|
+
|
191
|
+
- The supervisor starts a new controller process (which may fork one or more
|
192
|
+
worker processes).
|
193
|
+
- Sleep for a certain amount of time (currently 1 second.)
|
194
|
+
- Stop the old controller process.
|
195
|
+
- Each worker process is gracefully stopped and allowed to finish all pending
|
196
|
+
requests, then shutdown all open connections.
|
197
|
+
|
198
|
+
## Performing a graceful restart
|
199
|
+
|
200
|
+
A graceful restart performed by sending `SIGUSR2` to the supervisor process.
|
201
|
+
|
27
202
|
## Documentation
|
28
203
|
|
29
|
-
Documentation for Tipi is coming soon...
|
204
|
+
Documentation for Tipi's API is coming soon...
|
205
|
+
|
data/Rakefile
CHANGED
@@ -3,13 +3,7 @@
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
require "rake/clean"
|
5
5
|
|
6
|
-
|
7
|
-
Rake::ExtensionTask.new("tipi_ext") do |ext|
|
8
|
-
ext.ext_dir = "ext/tipi"
|
9
|
-
end
|
10
|
-
|
11
|
-
task :recompile => [:clean, :compile]
|
12
|
-
task :default => [:compile, :test]
|
6
|
+
task :default => [:test]
|
13
7
|
|
14
8
|
task :test do
|
15
9
|
exec 'ruby test/run.rb'
|
@@ -4,10 +4,29 @@ require 'bundler/setup'
|
|
4
4
|
|
5
5
|
HTTP_REQUEST = "GET /foo HTTP/1.1\r\nHost: example.com\r\nAccept: */*\r\n\r\n"
|
6
6
|
|
7
|
+
def measure_time_and_allocs
|
8
|
+
4.times { GC.start }
|
9
|
+
GC.disable
|
10
|
+
|
11
|
+
t0 = Time.now
|
12
|
+
a0 = object_count
|
13
|
+
yield
|
14
|
+
t1 = Time.now
|
15
|
+
a1 = object_count
|
16
|
+
[t1 - t0, a1 - a0]
|
17
|
+
ensure
|
18
|
+
GC.enable
|
19
|
+
end
|
20
|
+
|
21
|
+
def object_count
|
22
|
+
count = ObjectSpace.count_objects
|
23
|
+
count[:TOTAL] - count[:FREE]
|
24
|
+
end
|
25
|
+
|
7
26
|
def benchmark_other_http1_parser(iterations)
|
8
27
|
STDOUT << "http_parser.rb: "
|
9
28
|
require 'http_parser.rb'
|
10
|
-
|
29
|
+
|
11
30
|
i, o = IO.pipe
|
12
31
|
parser = Http::Parser.new
|
13
32
|
done = false
|
@@ -16,21 +35,20 @@ def benchmark_other_http1_parser(iterations)
|
|
16
35
|
headers = h
|
17
36
|
headers[':method'] = parser.http_method
|
18
37
|
headers[':path'] = parser.request_url
|
19
|
-
headers[':protocol'] = parser.http_version
|
20
38
|
end
|
21
39
|
parser.on_message_complete = proc { done = true }
|
22
40
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
41
|
+
elapsed, allocated = measure_time_and_allocs do
|
42
|
+
iterations.times do
|
43
|
+
o << HTTP_REQUEST
|
44
|
+
done = false
|
45
|
+
while !done
|
46
|
+
msg = i.readpartial(4096)
|
47
|
+
parser << msg
|
48
|
+
end
|
30
49
|
end
|
31
50
|
end
|
32
|
-
|
33
|
-
puts "#{iterations / (t1 - t0)} ips"
|
51
|
+
puts(format('elapsed: %f, allocated: %d (%f/req), rate: %f ips', elapsed, allocated, allocated.to_f / iterations, iterations / elapsed))
|
34
52
|
end
|
35
53
|
|
36
54
|
def benchmark_tipi_http1_parser(iterations)
|
@@ -40,22 +58,28 @@ def benchmark_tipi_http1_parser(iterations)
|
|
40
58
|
reader = proc { |len| i.readpartial(len) }
|
41
59
|
parser = Tipi::HTTP1Parser.new(reader)
|
42
60
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
61
|
+
elapsed, allocated = measure_time_and_allocs do
|
62
|
+
iterations.times do
|
63
|
+
o << HTTP_REQUEST
|
64
|
+
headers = parser.parse_headers
|
65
|
+
end
|
47
66
|
end
|
48
|
-
|
49
|
-
puts "#{iterations / (t1 - t0)} ips"
|
67
|
+
puts(format('elapsed: %f, allocated: %d (%f/req), rate: %f ips', elapsed, allocated, allocated.to_f / iterations, iterations / elapsed))
|
50
68
|
end
|
51
69
|
|
52
70
|
def fork_benchmark(method, iterations)
|
53
|
-
pid = fork
|
71
|
+
pid = fork do
|
72
|
+
send(method, iterations)
|
73
|
+
rescue Exception => e
|
74
|
+
p e
|
75
|
+
p e.backtrace
|
76
|
+
exit!
|
77
|
+
end
|
54
78
|
Process.wait(pid)
|
55
79
|
end
|
56
80
|
|
57
81
|
x = 500000
|
58
|
-
fork_benchmark(:benchmark_other_http1_parser, x)
|
59
|
-
fork_benchmark(:benchmark_tipi_http1_parser, x)
|
82
|
+
# fork_benchmark(:benchmark_other_http1_parser, x)
|
83
|
+
# fork_benchmark(:benchmark_tipi_http1_parser, x)
|
60
84
|
|
61
|
-
|
85
|
+
benchmark_tipi_http1_parser(x)
|
data/bin/benchmark
CHANGED
File without changes
|
data/bin/h1pd
CHANGED
File without changes
|
data/bm.png
ADDED
Binary file
|
data/df/agent.rb
CHANGED
data/df/sample_agent.rb
CHANGED
@@ -46,7 +46,7 @@ class SampleAgent < DigitalFabric::Agent
|
|
46
46
|
true
|
47
47
|
))
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
end
|
51
51
|
|
52
52
|
def ws_request(req)
|
@@ -82,7 +82,7 @@ class SampleAgent < DigitalFabric::Agent
|
|
82
82
|
true
|
83
83
|
))
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
end
|
87
87
|
|
88
88
|
agent = SampleAgent.new('127.0.0.1', 4411, { path: '/agent' })
|
data/df/server.rb
CHANGED
data/df/server_utils.rb
CHANGED
@@ -64,17 +64,14 @@ def listen_https
|
|
64
64
|
c = IO.read('../../reality/ssl/cacert.pem')
|
65
65
|
certificates = c.scan(CERTIFICATE_REGEXP).map { |p| OpenSSL::X509::Certificate.new(p.first) }
|
66
66
|
ctx = OpenSSL::SSL::SSLContext.new
|
67
|
+
ctx.security_level = 0
|
67
68
|
cert = certificates.shift
|
68
69
|
log "SSL Certificate expires: #{cert.not_after.inspect}"
|
69
70
|
ctx.add_certificate(cert, private_key, certificates)
|
70
|
-
ctx.ciphers = 'ECDH+aRSA'
|
71
|
-
ctx.
|
72
|
-
|
73
|
-
|
74
|
-
OpenSSL::SSL::TLS1_3_VERSION
|
75
|
-
)
|
76
|
-
# ctx.min_version = OpenSSL::SSL::SSL3_VERSION #OpenSSL::SSL::TLS1_VERSION
|
77
|
-
# ctx.max_version = OpenSSL::SSL::TLS1_3_VERSION
|
71
|
+
# ctx.ciphers = 'ECDH+aRSA'
|
72
|
+
ctx.max_version = OpenSSL::SSL::TLS1_3_VERSION
|
73
|
+
ctx.min_version = OpenSSL::SSL::SSL3_VERSION
|
74
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
78
75
|
|
79
76
|
# TODO: further limit ciphers
|
80
77
|
# ref: https://github.com/socketry/falcon/blob/3ec805b3ceda0a764a2c5eb68cde33897b6a35ff/lib/falcon/environments/tls.rb
|
@@ -91,10 +88,10 @@ def listen_https
|
|
91
88
|
server = Polyphony::Net.tcp_listen('0.0.0.0', 10443, opts)
|
92
89
|
id = 0
|
93
90
|
loop do
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
log('Accept HTTPS client connection', client: client)
|
91
|
+
client = server.accept rescue nil
|
92
|
+
next unless client
|
93
|
+
|
94
|
+
# log('Accept HTTPS client connection', client: client)
|
98
95
|
spin("https#{id += 1}") do
|
99
96
|
@service.incr_connection_count
|
100
97
|
Tipi.client_loop(client, opts) { |req| @service.http_request(req) }
|
@@ -104,12 +101,12 @@ def listen_https
|
|
104
101
|
# log("Done with HTTP connection", client: client)
|
105
102
|
@service.decr_connection_count
|
106
103
|
end
|
107
|
-
rescue OpenSSL::SSL::SSLError, SystemCallError, TypeError => e
|
108
|
-
|
104
|
+
# rescue OpenSSL::SSL::SSLError, SystemCallError, TypeError => e
|
105
|
+
# log('HTTPS accept error', error: e)
|
109
106
|
rescue Polyphony::BaseException
|
110
107
|
raise
|
111
108
|
rescue Exception => e
|
112
|
-
log 'HTTPS
|
109
|
+
log 'HTTPS listener error: ', error: e, backtrace: e.backtrace
|
113
110
|
end
|
114
111
|
end
|
115
112
|
end
|
data/examples/hello.rb
ADDED
data/examples/hello.ru
CHANGED
data/examples/http_server.js
CHANGED