shuttlebay 0.1.4
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 +7 -0
- data/Cargo.lock +416 -0
- data/Cargo.toml +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +191 -0
- data/exe/shuttlebay +12 -0
- data/ext/shuttlebay/Cargo.toml +31 -0
- data/ext/shuttlebay/extconf.rb +22 -0
- data/ext/shuttlebay/src/codec.rs +477 -0
- data/ext/shuttlebay/src/headers.rs +34 -0
- data/ext/shuttlebay/src/lib.rs +94 -0
- data/ext/shuttlebay/src/link.rs +78 -0
- data/lib/rackup/handler/shuttlebay.rb +26 -0
- data/lib/shuttlebay/attached.rb +139 -0
- data/lib/shuttlebay/capybara.rb +8 -0
- data/lib/shuttlebay/engine.rb +23 -0
- data/lib/shuttlebay/handler.rb +152 -0
- data/lib/shuttlebay/logger.rb +28 -0
- data/lib/shuttlebay/process_control.rb +33 -0
- data/lib/shuttlebay/railtie.rb +11 -0
- data/lib/shuttlebay/runtime.rb +83 -0
- data/lib/shuttlebay/server.rb +199 -0
- data/lib/shuttlebay/stream.rb +56 -0
- data/lib/shuttlebay/tunnel.rb +95 -0
- data/lib/shuttlebay/version.rb +5 -0
- data/lib/shuttlebay/worker.rb +166 -0
- data/lib/shuttlebay.rb +4 -0
- metadata +187 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4038d15bb64a686fa2abc8662520807bbdedc3ac6d3796688ccb57c4acef8aaa
|
|
4
|
+
data.tar.gz: 1bfb6f3eb2456f5b3b16117a45d5daa97f9c58e08420bf53c888d023b3f28210
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 472a1f07635596621b47dfd053f6ffacb4d579def811317a90160cff2d9c52c547ee562f6b2071f2e559fe1228e9a18ecccb3fcf8457d2cb3fcdc6707f67bf1b
|
|
7
|
+
data.tar.gz: ffba9bc614e3b26f51db20e397d44cb640ca32a6ca58bda9e7c33f26eb4d9627991ee99e981cab37f9c047d2162980505f3aa1448695e229fd80a2f3b7ba3d37
|
data/Cargo.lock
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "aho-corasick"
|
|
7
|
+
version = "1.1.5"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"memchr",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "bindgen"
|
|
16
|
+
version = "0.72.1"
|
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
|
+
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"bitflags",
|
|
21
|
+
"cexpr",
|
|
22
|
+
"clang-sys",
|
|
23
|
+
"itertools",
|
|
24
|
+
"proc-macro2",
|
|
25
|
+
"quote",
|
|
26
|
+
"regex",
|
|
27
|
+
"rustc-hash",
|
|
28
|
+
"shlex",
|
|
29
|
+
"syn 2.0.119",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "bitflags"
|
|
34
|
+
version = "2.13.2"
|
|
35
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
36
|
+
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
|
37
|
+
|
|
38
|
+
[[package]]
|
|
39
|
+
name = "bytes"
|
|
40
|
+
version = "1.12.1"
|
|
41
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
+
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
|
43
|
+
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "cexpr"
|
|
46
|
+
version = "0.6.0"
|
|
47
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
48
|
+
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
|
49
|
+
dependencies = [
|
|
50
|
+
"nom",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[[package]]
|
|
54
|
+
name = "cfg-if"
|
|
55
|
+
version = "1.0.5"
|
|
56
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
57
|
+
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
|
58
|
+
|
|
59
|
+
[[package]]
|
|
60
|
+
name = "clang-sys"
|
|
61
|
+
version = "1.9.1"
|
|
62
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
63
|
+
checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
|
|
64
|
+
dependencies = [
|
|
65
|
+
"glob",
|
|
66
|
+
"libc",
|
|
67
|
+
"libloading",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[[package]]
|
|
71
|
+
name = "either"
|
|
72
|
+
version = "1.18.0"
|
|
73
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
74
|
+
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
|
|
75
|
+
|
|
76
|
+
[[package]]
|
|
77
|
+
name = "glob"
|
|
78
|
+
version = "0.3.4"
|
|
79
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
80
|
+
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
|
81
|
+
|
|
82
|
+
[[package]]
|
|
83
|
+
name = "itertools"
|
|
84
|
+
version = "0.13.0"
|
|
85
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
86
|
+
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
87
|
+
dependencies = [
|
|
88
|
+
"either",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[[package]]
|
|
92
|
+
name = "itoa"
|
|
93
|
+
version = "1.0.18"
|
|
94
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
95
|
+
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
96
|
+
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "lazy_static"
|
|
99
|
+
version = "1.5.1"
|
|
100
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
101
|
+
checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb"
|
|
102
|
+
|
|
103
|
+
[[package]]
|
|
104
|
+
name = "libc"
|
|
105
|
+
version = "0.2.190"
|
|
106
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
107
|
+
checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
|
|
108
|
+
|
|
109
|
+
[[package]]
|
|
110
|
+
name = "libloading"
|
|
111
|
+
version = "0.8.9"
|
|
112
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
113
|
+
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
|
114
|
+
dependencies = [
|
|
115
|
+
"cfg-if",
|
|
116
|
+
"windows-link",
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
[[package]]
|
|
120
|
+
name = "magnus"
|
|
121
|
+
version = "0.9.2"
|
|
122
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
123
|
+
checksum = "b9068710e00d21be762edca59b9c4c8946dbdb29820d4f76ad559afea07982c9"
|
|
124
|
+
dependencies = [
|
|
125
|
+
"bytes",
|
|
126
|
+
"magnus-macros",
|
|
127
|
+
"rb-sys",
|
|
128
|
+
"rb-sys-env",
|
|
129
|
+
"seq-macro",
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
[[package]]
|
|
133
|
+
name = "magnus-macros"
|
|
134
|
+
version = "0.9.1"
|
|
135
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
136
|
+
checksum = "9dafde2eeb66f090e0f326112323eb8324b12cb32b5d1210e541d3df6b0ce77e"
|
|
137
|
+
dependencies = [
|
|
138
|
+
"proc-macro2",
|
|
139
|
+
"quote",
|
|
140
|
+
"syn 2.0.119",
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
[[package]]
|
|
144
|
+
name = "memchr"
|
|
145
|
+
version = "2.8.3"
|
|
146
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
147
|
+
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
148
|
+
|
|
149
|
+
[[package]]
|
|
150
|
+
name = "minimal-lexical"
|
|
151
|
+
version = "0.2.1"
|
|
152
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
153
|
+
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|
154
|
+
|
|
155
|
+
[[package]]
|
|
156
|
+
name = "mothership-docking-protocol"
|
|
157
|
+
version = "0.2.0"
|
|
158
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
159
|
+
checksum = "aaec5966e84e1a3c65b5846bd89c0c0621de6c75362f9c669636449fdf5a4b99"
|
|
160
|
+
dependencies = [
|
|
161
|
+
"serde",
|
|
162
|
+
"serde_json",
|
|
163
|
+
"thiserror",
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
[[package]]
|
|
167
|
+
name = "nom"
|
|
168
|
+
version = "7.1.3"
|
|
169
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
170
|
+
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
|
171
|
+
dependencies = [
|
|
172
|
+
"memchr",
|
|
173
|
+
"minimal-lexical",
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
[[package]]
|
|
177
|
+
name = "proc-macro2"
|
|
178
|
+
version = "1.0.107"
|
|
179
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
180
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
181
|
+
dependencies = [
|
|
182
|
+
"unicode-ident",
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
[[package]]
|
|
186
|
+
name = "quote"
|
|
187
|
+
version = "1.0.47"
|
|
188
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
189
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
190
|
+
dependencies = [
|
|
191
|
+
"proc-macro2",
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
[[package]]
|
|
195
|
+
name = "rb-sys"
|
|
196
|
+
version = "0.9.130"
|
|
197
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
198
|
+
checksum = "02faf625bb10ba893e3ae620f19c9fb1b5f8fcae0fe4eb86bb3f2230fad75edb"
|
|
199
|
+
dependencies = [
|
|
200
|
+
"rb-sys-build",
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
[[package]]
|
|
204
|
+
name = "rb-sys-build"
|
|
205
|
+
version = "0.9.130"
|
|
206
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
207
|
+
checksum = "05be6f9c86fe5482808162826f6c047d093b3670582296c770de1d94f8066694"
|
|
208
|
+
dependencies = [
|
|
209
|
+
"bindgen",
|
|
210
|
+
"lazy_static",
|
|
211
|
+
"proc-macro2",
|
|
212
|
+
"quote",
|
|
213
|
+
"regex",
|
|
214
|
+
"shell-words",
|
|
215
|
+
"syn 2.0.119",
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
[[package]]
|
|
219
|
+
name = "rb-sys-env"
|
|
220
|
+
version = "0.2.3"
|
|
221
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
222
|
+
checksum = "cca7ad6a7e21e72151d56fe2495a259b5670e204c3adac41ee7ef676ea08117a"
|
|
223
|
+
|
|
224
|
+
[[package]]
|
|
225
|
+
name = "regex"
|
|
226
|
+
version = "1.13.1"
|
|
227
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
228
|
+
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
|
229
|
+
dependencies = [
|
|
230
|
+
"aho-corasick",
|
|
231
|
+
"memchr",
|
|
232
|
+
"regex-automata",
|
|
233
|
+
"regex-syntax",
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
[[package]]
|
|
237
|
+
name = "regex-automata"
|
|
238
|
+
version = "0.4.18"
|
|
239
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
240
|
+
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
|
241
|
+
dependencies = [
|
|
242
|
+
"aho-corasick",
|
|
243
|
+
"memchr",
|
|
244
|
+
"regex-syntax",
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
[[package]]
|
|
248
|
+
name = "regex-syntax"
|
|
249
|
+
version = "0.8.11"
|
|
250
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
251
|
+
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
252
|
+
|
|
253
|
+
[[package]]
|
|
254
|
+
name = "rustc-hash"
|
|
255
|
+
version = "2.1.3"
|
|
256
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
257
|
+
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
|
258
|
+
|
|
259
|
+
[[package]]
|
|
260
|
+
name = "seq-macro"
|
|
261
|
+
version = "0.3.6"
|
|
262
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
263
|
+
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
|
|
264
|
+
|
|
265
|
+
[[package]]
|
|
266
|
+
name = "serde"
|
|
267
|
+
version = "1.0.229"
|
|
268
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
269
|
+
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
|
270
|
+
dependencies = [
|
|
271
|
+
"serde_core",
|
|
272
|
+
"serde_derive",
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
[[package]]
|
|
276
|
+
name = "serde_core"
|
|
277
|
+
version = "1.0.229"
|
|
278
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
279
|
+
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
|
280
|
+
dependencies = [
|
|
281
|
+
"serde_derive",
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
[[package]]
|
|
285
|
+
name = "serde_derive"
|
|
286
|
+
version = "1.0.229"
|
|
287
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
288
|
+
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
289
|
+
dependencies = [
|
|
290
|
+
"proc-macro2",
|
|
291
|
+
"quote",
|
|
292
|
+
"syn 3.0.6",
|
|
293
|
+
]
|
|
294
|
+
|
|
295
|
+
[[package]]
|
|
296
|
+
name = "serde_json"
|
|
297
|
+
version = "1.0.151"
|
|
298
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
299
|
+
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
|
300
|
+
dependencies = [
|
|
301
|
+
"itoa",
|
|
302
|
+
"memchr",
|
|
303
|
+
"serde",
|
|
304
|
+
"serde_core",
|
|
305
|
+
"zmij",
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
[[package]]
|
|
309
|
+
name = "shell-words"
|
|
310
|
+
version = "1.1.1"
|
|
311
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
312
|
+
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
|
313
|
+
|
|
314
|
+
[[package]]
|
|
315
|
+
name = "shlex"
|
|
316
|
+
version = "1.3.0"
|
|
317
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
318
|
+
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
319
|
+
|
|
320
|
+
[[package]]
|
|
321
|
+
name = "shuttlebay"
|
|
322
|
+
version = "0.1.0"
|
|
323
|
+
dependencies = [
|
|
324
|
+
"magnus",
|
|
325
|
+
"mothership-docking-protocol",
|
|
326
|
+
"rb-sys",
|
|
327
|
+
"serde_json",
|
|
328
|
+
"state-machines",
|
|
329
|
+
]
|
|
330
|
+
|
|
331
|
+
[[package]]
|
|
332
|
+
name = "state-machines"
|
|
333
|
+
version = "0.30.0"
|
|
334
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
335
|
+
checksum = "f2bc4296143483a9608a37ba4cf9955b42b8e2154dc58917df4f9cbe8120ebb4"
|
|
336
|
+
dependencies = [
|
|
337
|
+
"state-machines-core",
|
|
338
|
+
"state-machines-macro",
|
|
339
|
+
]
|
|
340
|
+
|
|
341
|
+
[[package]]
|
|
342
|
+
name = "state-machines-core"
|
|
343
|
+
version = "0.11.0"
|
|
344
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
345
|
+
checksum = "54b3da777f8a61919bdb3c6e9e445f2995f851b967e5d73b5949290dccfdcddd"
|
|
346
|
+
|
|
347
|
+
[[package]]
|
|
348
|
+
name = "state-machines-macro"
|
|
349
|
+
version = "0.30.0"
|
|
350
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
351
|
+
checksum = "edf80d0c48518c7fb7fbb1dd4dda13a8dc189c1f15ac435545b351d5f2c276b1"
|
|
352
|
+
dependencies = [
|
|
353
|
+
"proc-macro2",
|
|
354
|
+
"quote",
|
|
355
|
+
"syn 2.0.119",
|
|
356
|
+
]
|
|
357
|
+
|
|
358
|
+
[[package]]
|
|
359
|
+
name = "syn"
|
|
360
|
+
version = "2.0.119"
|
|
361
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
362
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
363
|
+
dependencies = [
|
|
364
|
+
"proc-macro2",
|
|
365
|
+
"quote",
|
|
366
|
+
"unicode-ident",
|
|
367
|
+
]
|
|
368
|
+
|
|
369
|
+
[[package]]
|
|
370
|
+
name = "syn"
|
|
371
|
+
version = "3.0.6"
|
|
372
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
373
|
+
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
|
|
374
|
+
dependencies = [
|
|
375
|
+
"proc-macro2",
|
|
376
|
+
"quote",
|
|
377
|
+
"unicode-ident",
|
|
378
|
+
]
|
|
379
|
+
|
|
380
|
+
[[package]]
|
|
381
|
+
name = "thiserror"
|
|
382
|
+
version = "2.0.21"
|
|
383
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
384
|
+
checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e"
|
|
385
|
+
dependencies = [
|
|
386
|
+
"thiserror-impl",
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
[[package]]
|
|
390
|
+
name = "thiserror-impl"
|
|
391
|
+
version = "2.0.21"
|
|
392
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
393
|
+
checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
|
|
394
|
+
dependencies = [
|
|
395
|
+
"proc-macro2",
|
|
396
|
+
"quote",
|
|
397
|
+
"syn 3.0.6",
|
|
398
|
+
]
|
|
399
|
+
|
|
400
|
+
[[package]]
|
|
401
|
+
name = "unicode-ident"
|
|
402
|
+
version = "1.0.26"
|
|
403
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
404
|
+
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
|
405
|
+
|
|
406
|
+
[[package]]
|
|
407
|
+
name = "windows-link"
|
|
408
|
+
version = "0.2.1"
|
|
409
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
410
|
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
411
|
+
|
|
412
|
+
[[package]]
|
|
413
|
+
name = "zmij"
|
|
414
|
+
version = "1.0.23"
|
|
415
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
416
|
+
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
data/Cargo.toml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[workspace]
|
|
2
|
+
members = ["ext/shuttlebay"]
|
|
3
|
+
resolver = "3"
|
|
4
|
+
|
|
5
|
+
[workspace.package]
|
|
6
|
+
edition = "2024"
|
|
7
|
+
rust-version = "1.99"
|
|
8
|
+
license = "MIT"
|
|
9
|
+
repository = "https://github.com/seuros/shuttlebay"
|
|
10
|
+
|
|
11
|
+
[profile.release]
|
|
12
|
+
lto = true
|
|
13
|
+
codegen-units = 1
|
|
14
|
+
opt-level = 3
|
|
15
|
+
strip = true
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abdelkader Boudih
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Shuttlebay
|
|
2
|
+
|
|
3
|
+
Run a Rack app as a [Mothership](https://github.com/seuros/mothership) `[[bays.http]]` bay.
|
|
4
|
+
|
|
5
|
+
Mothership is the HTTP server. Shuttlebay is the Ruby side: it preloads your
|
|
6
|
+
app, forks workers, and runs the app on threads that receive requests over
|
|
7
|
+
Unix-socket docking links. A native engine (Rust, via magnus) decodes the
|
|
8
|
+
docking protocol and builds the Rack env, so Ruby never parses HTTP.
|
|
9
|
+
|
|
10
|
+
```mermaid
|
|
11
|
+
flowchart TD
|
|
12
|
+
client([client]) -- HTTP --> mothership["mothership<br/>static, gzip, routing, limits, request ids…"]
|
|
13
|
+
mothership -- "docking protocol v3<br/>over a Unix socket" --> master["shuttlebay master<br/>config.ru preloaded: Rails, Sinatra, Roda, Padrino, …"]
|
|
14
|
+
master --> w0["worker 0<br/>threads, one docking link each"]
|
|
15
|
+
master --> w1["worker 1<br/>threads"]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# Gemfile
|
|
22
|
+
gem "shuttlebay"
|
|
23
|
+
gem "guardship" # the mothership binary (or: cargo install mothership)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```toml
|
|
27
|
+
# ship-manifest.toml
|
|
28
|
+
[mothership.bind]
|
|
29
|
+
http = "0.0.0.0:3000"
|
|
30
|
+
|
|
31
|
+
[[mothership.static_dirs]] # public/ served by mothership, not Rails
|
|
32
|
+
path = "./public"
|
|
33
|
+
prefix = "/"
|
|
34
|
+
|
|
35
|
+
[[bays.http]]
|
|
36
|
+
name = "web"
|
|
37
|
+
command = "bundle"
|
|
38
|
+
args = ["exec", "shuttlebay", "config.ru"]
|
|
39
|
+
workers = "auto" # default: sized from CPUs and memory; 0 = single process
|
|
40
|
+
threads = 5
|
|
41
|
+
routes = [{ bind = "http", pattern = "/.*" }]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
bundle exec mothership
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## What Mothership does instead of Rack middleware
|
|
49
|
+
|
|
50
|
+
| Concern | Before (app server + middleware) | With Shuttlebay |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| HTTP parsing, keep-alive, slow clients | App server reactor | Mothership |
|
|
53
|
+
| Static files | `ActionDispatch::Static` / nginx | `[[mothership.static_dirs]]` |
|
|
54
|
+
| `send_file` / `Rack::Sendfile` | X-Sendfile + nginx | `to_path` bodies streamed from disk by Mothership |
|
|
55
|
+
| Compression | `Rack::Deflater` / nginx | `compression = true` |
|
|
56
|
+
| Request id, queue time | `ActionDispatch::RequestId`, LB | `X-Request-Id`, `X-Request-Start: t=<µs>` |
|
|
57
|
+
| Real client IP | `RemoteIp` + proxy config | `REMOTE_ADDR` resolved (Forwarded / PROXY protocol) |
|
|
58
|
+
| Load balancing to workers | kernel accept / `wait_for_less_busy_worker` | idle-thread checkout; queueing in Mothership |
|
|
59
|
+
| Request body buffering | App server | Mothership reads the whole body before claiming a thread |
|
|
60
|
+
|
|
61
|
+
When Rails is loaded, the bundled Railtie removes `Rack::Sendfile`, since the
|
|
62
|
+
engine offloads `to_path` bodies itself.
|
|
63
|
+
|
|
64
|
+
## Hooks
|
|
65
|
+
|
|
66
|
+
`config.ru` loads in the master before forking, so register hooks there:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
Shuttlebay.before_fork { ActiveRecord::Base.connection_pool.disconnect! }
|
|
70
|
+
Shuttlebay.on_worker_boot { |index| Rails.logger.info("worker #{index} up") }
|
|
71
|
+
Shuttlebay.on_worker_shutdown { |index| Rails.logger.info("worker #{index} drained") }
|
|
72
|
+
Shuttlebay.out_of_band { GC.start } # runs when a worker has no request in flight
|
|
73
|
+
run Rails.application
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Fibers
|
|
77
|
+
|
|
78
|
+
With a fiber scheduler, each worker runs its `threads` docking links as fibers
|
|
79
|
+
on one reactor thread instead of as threads. A request waiting on I/O parks
|
|
80
|
+
its fiber, and the reactor serves the other links meanwhile.
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# config.ru (Gemfile: gem "async")
|
|
84
|
+
require "async"
|
|
85
|
+
Shuttlebay.fiber_scheduler { Async::Scheduler.new }
|
|
86
|
+
run Rails.application
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The block runs once per worker, after fork, on the reactor thread; any
|
|
90
|
+
`Fiber::Scheduler` works. `threads` in the manifest still counts docking links
|
|
91
|
+
per worker, so Mothership needs no change, but a parked fiber costs no thread,
|
|
92
|
+
so it can go much higher (64, 256).
|
|
93
|
+
|
|
94
|
+
- Fibers are not preempted: a request burning CPU stalls every other request
|
|
95
|
+
on its reactor. Size `workers` for CPU and `threads` for I/O wait.
|
|
96
|
+
`Shuttlebay.fiber_scheduler(reactors: 4) { … }` splits a worker's links
|
|
97
|
+
across 4 reactor threads, which Ruby does preempt, so one busy request
|
|
98
|
+
stalls a quarter of the links instead of all of them.
|
|
99
|
+
- Only I/O through Ruby's scheduler yields: Ruby sockets (`Net::HTTP`, Redis
|
|
100
|
+
clients without hiredis), `pg`, `sleep`. C extensions doing their own
|
|
101
|
+
blocking I/O (`mysql2`, libcurl gems) stall the reactor.
|
|
102
|
+
- Rails: set `config.active_support.isolation_level = :fiber` and size the
|
|
103
|
+
Active Record pool to at least `threads`.
|
|
104
|
+
- Hijacked connections (WebSockets) still relay on their own threads.
|
|
105
|
+
- Async's I/O hooks use `IO::Buffer`, so Ruby prints a one-time experimental
|
|
106
|
+
warning to stderr; `Warning[:experimental] = false` silences it.
|
|
107
|
+
|
|
108
|
+
## Development server and system tests
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
bin/rails server -u shuttlebay # or: rackup -s shuttlebay
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
# test/application_system_test_case.rb
|
|
116
|
+
require "shuttlebay/capybara"
|
|
117
|
+
Capybara.server = :shuttlebay
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Both run the app in their own process and start a Mothership that attaches to
|
|
121
|
+
it, so development and tests go through the same HTTP stack as production. The
|
|
122
|
+
binary comes from `MOTHERSHIP_BIN`, the `guardship` gem, or `PATH`. The
|
|
123
|
+
`Threads` option (`rackup -O Threads=5`) or `MS_BAY_THREADS` sets the thread
|
|
124
|
+
count; Mothership sizes its link pool from what the app reports when it docks.
|
|
125
|
+
|
|
126
|
+
For HTTPS, run Mothership with a `tls` bind and the app as a docking upstream
|
|
127
|
+
(see the Mothership README) instead of the attached server.
|
|
128
|
+
|
|
129
|
+
## Behaviour
|
|
130
|
+
|
|
131
|
+
- **Responses:** HEAD, 1xx, 204 and 304 send no body. `to_ary` bodies go out
|
|
132
|
+
in one write. `each` bodies flush per chunk (SSE, `ActionController::Live`).
|
|
133
|
+
Rack 3 streaming bodies (`call`) are supported. A `to_path` body with status
|
|
134
|
+
200 is served by Mothership from disk.
|
|
135
|
+
- **Thread handoff:** after the response's last byte, the thread closes the
|
|
136
|
+
body and runs `rack.response_finished` callbacks, then sends `Ready`.
|
|
137
|
+
Mothership routes the next request to that link only after `Ready`, so a
|
|
138
|
+
request never waits behind Rails' after-response work.
|
|
139
|
+
- **Errors:** an exception before the head goes out becomes a 500. After the
|
|
140
|
+
head, the link is closed and the response aborts.
|
|
141
|
+
`rack.response_finished` callbacks run after every response.
|
|
142
|
+
- **WebSockets:** run ActionCable on [orbitcast](https://github.com/seuros/orbitcast),
|
|
143
|
+
which holds the sockets in Rust and speaks ActionCable's protocol, so no
|
|
144
|
+
Ruby thread sits on a connection. Every other WebSocket (Faye,
|
|
145
|
+
`websocket-driver`, custom protocols, or ActionCable's built-in server in
|
|
146
|
+
development and system tests) goes through `rack.hijack`: the app answers on
|
|
147
|
+
the hijacked IO, Mothership relays the upgraded connection, and the thread
|
|
148
|
+
goes back to serving while the relay runs on threads of its own.
|
|
149
|
+
- **Early hints:** not offered (`rack.early_hints` is absent). Mothership's
|
|
150
|
+
HTTP stack cannot send 1xx responses yet; Rails falls back to `Link`
|
|
151
|
+
headers on the final response.
|
|
152
|
+
- **Large uploads:** bodies over 1 MiB are spooled to an unlinked temp file
|
|
153
|
+
(`rack.input` is a `Tempfile`). Memory stays bounded however large uploads
|
|
154
|
+
are allowed to be.
|
|
155
|
+
- **Signals:** on `TERM`, workers stop accepting, drop idle links, and finish
|
|
156
|
+
in-flight requests within `MS_BAY_DRAIN_TIMEOUT` (Mothership passes the
|
|
157
|
+
bay's `request_timeout`). A dead worker is re-forked with backoff.
|
|
158
|
+
- **Deploys:** `kill -USR1` the mothership process to roll the bay to new code
|
|
159
|
+
with no dropped requests (phased restart).
|
|
160
|
+
- **TLS:** terminated by Mothership on a `tls` bind; the app sees
|
|
161
|
+
`rack.url_scheme = "https"`.
|
|
162
|
+
- **Logs:** JSON lines on stdout, which Mothership passes through.
|
|
163
|
+
|
|
164
|
+
## Environment (set by Mothership)
|
|
165
|
+
|
|
166
|
+
`MS_SOCKET_PATH`, `MS_SHIP`, `MS_BAY_WORKERS`, `MS_BAY_THREADS`, `MS_BAY_DRAIN_TIMEOUT`.
|
|
167
|
+
|
|
168
|
+
## Development
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
bundle install
|
|
172
|
+
bundle exec rake compile # builds lib/shuttlebay/shuttlebay.{bundle,so}
|
|
173
|
+
bundle exec rake test
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The engine crate (`ext/shuttlebay`) shares the wire format with Mothership
|
|
177
|
+
through `mothership-docking-protocol` and is never published to crates.io.
|
|
178
|
+
Platform gems: `bundle exec rake platforms:build` (rb-sys-dock, Docker).
|
|
179
|
+
|
|
180
|
+
Releases: release-please tags the version, CI builds the platform and source
|
|
181
|
+
gems and attaches them to the GitHub release. Push them to rubygems.org from a
|
|
182
|
+
workstation:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
bundle exec rake 'gems:fetch[v0.1.0]' # download the release's gems into pkg/
|
|
186
|
+
bundle exec rake 'gems:push[v0.1.0]' # fetch, then gem push each one
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
data/exe/shuttlebay
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../lib/shuttlebay/logger'
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
require_relative '../lib/shuttlebay/runtime'
|
|
8
|
+
Shuttlebay::Server.from_env(ARGV).run
|
|
9
|
+
rescue StandardError, ScriptError => e
|
|
10
|
+
Shuttlebay::Logger.new.error('shuttlebay stopped', **Shuttlebay::Logger.error_fields(e))
|
|
11
|
+
exit 1
|
|
12
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "shuttlebay"
|
|
3
|
+
version = "0.1.4" # x-release-please-version
|
|
4
|
+
edition.workspace = true
|
|
5
|
+
rust-version.workspace = true
|
|
6
|
+
license.workspace = true
|
|
7
|
+
repository.workspace = true
|
|
8
|
+
authors = ["Abdelkader Boudih <oss@seuros.com>"]
|
|
9
|
+
description = "Ruby engine for Mothership HTTP bays: docking protocol codec and Rack env builder"
|
|
10
|
+
publish = false
|
|
11
|
+
|
|
12
|
+
[lib]
|
|
13
|
+
crate-type = ["cdylib"]
|
|
14
|
+
|
|
15
|
+
[lints.rust]
|
|
16
|
+
unsafe_code = "deny"
|
|
17
|
+
unused_must_use = "warn"
|
|
18
|
+
|
|
19
|
+
[lints.clippy]
|
|
20
|
+
all = { level = "warn", priority = -1 }
|
|
21
|
+
pedantic = { level = "warn", priority = -1 }
|
|
22
|
+
nursery = { level = "warn", priority = -1 }
|
|
23
|
+
unwrap_used = "deny"
|
|
24
|
+
expect_used = "deny"
|
|
25
|
+
|
|
26
|
+
[dependencies]
|
|
27
|
+
magnus = { version = "0.9", features = ["bytes"] }
|
|
28
|
+
rb-sys = "0.9.124"
|
|
29
|
+
mothership-docking-protocol = "0.2.0"
|
|
30
|
+
serde_json = "1.0"
|
|
31
|
+
state-machines = "0.30"
|