@rip-lang/server 1.3.125 → 1.4.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.
- package/{docs/READ_VALIDATORS.md → API.md} +41 -119
- package/CONFIG.md +408 -0
- package/README.md +246 -1109
- package/acme/crypto.rip +0 -2
- package/browse.rip +62 -0
- package/control/cli.rip +95 -36
- package/control/lifecycle.rip +67 -1
- package/control/manager.rip +250 -0
- package/control/mdns.rip +3 -0
- package/middleware.rip +1 -1
- package/package.json +14 -11
- package/server.rip +189 -673
- package/serving/config.rip +766 -0
- package/{edge → serving}/forwarding.rip +2 -2
- package/serving/logging.rip +101 -0
- package/{edge → serving}/metrics.rip +29 -1
- package/serving/proxy.rip +99 -0
- package/{edge → serving}/queue.rip +1 -1
- package/{edge → serving}/ratelimit.rip +1 -1
- package/{edge → serving}/realtime.rip +71 -2
- package/{edge → serving}/registry.rip +1 -1
- package/{edge → serving}/router.rip +3 -3
- package/{edge → serving}/runtime.rip +18 -16
- package/{edge → serving}/security.rip +1 -1
- package/serving/static.rip +393 -0
- package/{edge → serving}/tls.rip +3 -7
- package/{edge → serving}/upstream.rip +4 -4
- package/{edge → serving}/verify.rip +16 -16
- package/streams/{tls_clienthello.rip → clienthello.rip} +1 -1
- package/streams/config.rip +8 -8
- package/streams/index.rip +5 -5
- package/streams/router.rip +2 -2
- package/tests/acme.rip +1 -1
- package/tests/config.rip +215 -0
- package/tests/control.rip +1 -1
- package/tests/{runtime_entrypoints.rip → entrypoints.rip} +11 -7
- package/tests/extracted.rip +118 -0
- package/tests/helpers.rip +4 -4
- package/tests/metrics.rip +3 -3
- package/tests/proxy.rip +9 -8
- package/tests/read.rip +1 -1
- package/tests/realtime.rip +3 -3
- package/tests/registry.rip +4 -4
- package/tests/router.rip +27 -27
- package/tests/runner.rip +70 -0
- package/tests/security.rip +4 -4
- package/tests/servers.rip +102 -136
- package/tests/static.rip +2 -2
- package/tests/streams_clienthello.rip +2 -2
- package/tests/streams_index.rip +4 -4
- package/tests/streams_pipe.rip +1 -1
- package/tests/streams_router.rip +10 -10
- package/tests/streams_runtime.rip +4 -4
- package/tests/streams_upstream.rip +1 -1
- package/tests/upstream.rip +2 -2
- package/tests/verify.rip +18 -18
- package/tests/watchers.rip +4 -4
- package/default.rip +0 -435
- package/docs/edge/CONFIG_LIFECYCLE.md +0 -111
- package/docs/edge/CONTRACTS.md +0 -137
- package/docs/edge/EDGEFILE_CONTRACT.md +0 -282
- package/docs/edge/M0B_REVIEW_NOTES.md +0 -102
- package/docs/edge/SCHEDULER.md +0 -46
- package/docs/logo.png +0 -0
- package/docs/logo.svg +0 -13
- package/docs/social.png +0 -0
- package/edge/config.rip +0 -607
- package/edge/static.rip +0 -69
- package/tests/edgefile.rip +0 -165
package/tests/security.rip
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# security.rip — rate limiter, request validation, and request ID tests
|
|
2
2
|
|
|
3
|
-
import { test, eq, ok } from '
|
|
4
|
-
import { createRateLimiter, rateLimitResponse } from '../
|
|
5
|
-
import { validateRequest } from '../
|
|
6
|
-
import { generateRequestId } from '../
|
|
3
|
+
import { test, eq, ok } from './runner.rip'
|
|
4
|
+
import { createRateLimiter, rateLimitResponse } from '../serving/ratelimit.rip'
|
|
5
|
+
import { validateRequest } from '../serving/security.rip'
|
|
6
|
+
import { generateRequestId } from '../serving/forwarding.rip'
|
|
7
7
|
|
|
8
8
|
# --- Request ID ---
|
|
9
9
|
|
package/tests/servers.rip
CHANGED
|
@@ -1,126 +1,110 @@
|
|
|
1
|
-
import { test, eq, ok } from '
|
|
2
|
-
import {
|
|
1
|
+
import { test, eq, ok } from './runner.rip'
|
|
2
|
+
import { normalizeConfig } from '../serving/config.rip'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
getErrors = (fn) ->
|
|
5
|
+
try
|
|
6
|
+
fn()
|
|
7
|
+
[]
|
|
8
|
+
catch e
|
|
9
|
+
e.validationErrors or []
|
|
5
10
|
|
|
6
|
-
test "normalizes hosts into
|
|
7
|
-
config =
|
|
8
|
-
version: 2
|
|
9
|
-
edge: {}
|
|
11
|
+
test "normalizes hosts into concrete site entries", ->
|
|
12
|
+
config = normalizeConfig({
|
|
10
13
|
hosts:
|
|
11
14
|
'*.trusthealth.com':
|
|
12
15
|
cert: '/ssl/trusthealth.com.crt'
|
|
13
16
|
key: '/ssl/trusthealth.com.key'
|
|
14
17
|
root: '/mnt/trusthealth'
|
|
15
|
-
|
|
18
|
+
rules: [
|
|
16
19
|
{ path: '/*', static: '.', spa: true }
|
|
17
20
|
]
|
|
18
21
|
}, '/home/shreeve')
|
|
19
|
-
eq config.version,
|
|
20
|
-
eq config.kind, '
|
|
22
|
+
eq config.version, 1
|
|
23
|
+
eq config.kind, 'serve'
|
|
21
24
|
ok config.sites['*.trusthealth.com']
|
|
22
25
|
eq config.sites['*.trusthealth.com'].host, '*.trusthealth.com'
|
|
23
26
|
eq config.sites['*.trusthealth.com'].routes.length, 1
|
|
24
27
|
|
|
25
|
-
test "
|
|
26
|
-
config =
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
test "extracts cert map from host blocks and certs", ->
|
|
29
|
+
config = normalizeConfig({
|
|
30
|
+
certs:
|
|
31
|
+
zion: '/ssl/zionlabshare.com'
|
|
29
32
|
hosts:
|
|
30
33
|
'*.trusthealth.com':
|
|
31
34
|
cert: '/ssl/trusthealth.com.crt'
|
|
32
35
|
key: '/ssl/trusthealth.com.key'
|
|
33
|
-
|
|
36
|
+
rules: [
|
|
34
37
|
{ path: '/*', static: '/mnt/web' }
|
|
35
38
|
]
|
|
36
39
|
'*.zionlabshare.com':
|
|
37
|
-
cert: '
|
|
38
|
-
|
|
39
|
-
routes: [
|
|
40
|
+
cert: 'zion'
|
|
41
|
+
rules: [
|
|
40
42
|
{ path: '/*', static: '/mnt/zion' }
|
|
41
43
|
]
|
|
42
44
|
}, '/home/shreeve')
|
|
43
|
-
ok config.
|
|
44
|
-
ok config.
|
|
45
|
-
ok config.
|
|
46
|
-
ok config.
|
|
45
|
+
ok config.resolvedCerts['*.trusthealth.com']
|
|
46
|
+
ok config.resolvedCerts['*.zionlabshare.com']
|
|
47
|
+
ok config.resolvedCerts['*.trusthealth.com'].certPath.endsWith('trusthealth.com.crt')
|
|
48
|
+
ok config.resolvedCerts['*.zionlabshare.com'].certPath.endsWith('zionlabshare.com.crt')
|
|
47
49
|
|
|
48
|
-
test "
|
|
49
|
-
config =
|
|
50
|
-
version: 2
|
|
51
|
-
edge: {}
|
|
50
|
+
test "inherits host root into static routes", ->
|
|
51
|
+
config = normalizeConfig({
|
|
52
52
|
hosts:
|
|
53
53
|
'*.example.com':
|
|
54
54
|
root: '/mnt/site'
|
|
55
|
-
|
|
55
|
+
rules: [
|
|
56
56
|
{ path: '/*', static: '.' }
|
|
57
57
|
]
|
|
58
58
|
}, '/home/shreeve')
|
|
59
59
|
route = config.sites['*.example.com'].routes[0]
|
|
60
60
|
ok route.root.includes('mnt/site')
|
|
61
61
|
|
|
62
|
-
test "
|
|
63
|
-
config =
|
|
64
|
-
version: 2
|
|
65
|
-
edge: {}
|
|
62
|
+
test "route root overrides host root", ->
|
|
63
|
+
config = normalizeConfig({
|
|
66
64
|
hosts:
|
|
67
65
|
'*.example.com':
|
|
68
66
|
root: '/mnt/default'
|
|
69
|
-
|
|
67
|
+
rules: [
|
|
70
68
|
{ path: '/*', static: '.', root: '/mnt/override' }
|
|
71
69
|
]
|
|
72
70
|
}, '/home/shreeve')
|
|
73
71
|
route = config.sites['*.example.com'].routes[0]
|
|
74
72
|
ok route.root.includes('mnt/override')
|
|
75
73
|
|
|
76
|
-
test "
|
|
77
|
-
config =
|
|
78
|
-
version: 2
|
|
79
|
-
edge: {}
|
|
74
|
+
test "rules inherit host from the host block", ->
|
|
75
|
+
config = normalizeConfig({
|
|
80
76
|
hosts:
|
|
81
77
|
app.example.com:
|
|
82
|
-
|
|
78
|
+
rules: [
|
|
83
79
|
{ path: '/*', static: '/mnt/app' }
|
|
84
80
|
]
|
|
85
81
|
}, '/home/shreeve')
|
|
86
82
|
route = config.sites['app.example.com'].routes[0]
|
|
87
83
|
eq route.host, 'app.example.com'
|
|
88
84
|
|
|
89
|
-
test "
|
|
90
|
-
config =
|
|
91
|
-
version: 2
|
|
92
|
-
edge: {}
|
|
85
|
+
test "preserves spa flag on rules", ->
|
|
86
|
+
config = normalizeConfig({
|
|
93
87
|
hosts:
|
|
94
88
|
'*.example.com':
|
|
95
|
-
|
|
89
|
+
rules: [
|
|
96
90
|
{ path: '/*', static: '/mnt/app', spa: true }
|
|
97
91
|
]
|
|
98
92
|
}, '/home/shreeve')
|
|
99
93
|
route = config.sites['*.example.com'].routes[0]
|
|
100
94
|
eq route.spa, true
|
|
101
95
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
threw = false
|
|
106
|
-
try
|
|
107
|
-
normalizeEdgeConfig({
|
|
108
|
-
version: 1
|
|
109
|
-
edge: {}
|
|
96
|
+
test "rejects host block with no rules and no root", ->
|
|
97
|
+
errors = getErrors ->
|
|
98
|
+
normalizeConfig({
|
|
110
99
|
hosts:
|
|
111
100
|
'*.example.com':
|
|
112
101
|
cert: '/ssl/example.com.crt'
|
|
113
102
|
key: '/ssl/example.com.key'
|
|
114
103
|
}, '/home/shreeve')
|
|
115
|
-
|
|
116
|
-
threw = true
|
|
117
|
-
ok e.validationErrors.some((err) -> err.code is 'E_SERVER_ROUTES')
|
|
118
|
-
ok threw
|
|
104
|
+
ok errors.some((err) -> err.code is 'E_SERVER_RULES')
|
|
119
105
|
|
|
120
|
-
test "root-only
|
|
121
|
-
config =
|
|
122
|
-
version: 1
|
|
123
|
-
edge: {}
|
|
106
|
+
test "root-only host block generates implicit static route", ->
|
|
107
|
+
config = normalizeConfig({
|
|
124
108
|
hosts:
|
|
125
109
|
'*.example.com':
|
|
126
110
|
root: '/mnt/site'
|
|
@@ -130,10 +114,8 @@ test "root-only server block generates implicit static route", ->
|
|
|
130
114
|
eq routes[0].static, '.'
|
|
131
115
|
ok routes[0].root.includes('mnt/site')
|
|
132
116
|
|
|
133
|
-
test "root-only
|
|
134
|
-
config =
|
|
135
|
-
version: 1
|
|
136
|
-
edge: {}
|
|
117
|
+
test "root-only host block with spa", ->
|
|
118
|
+
config = normalizeConfig({
|
|
137
119
|
hosts:
|
|
138
120
|
'*.example.com':
|
|
139
121
|
root: '/mnt/site'
|
|
@@ -141,83 +123,72 @@ test "root-only server block with spa", ->
|
|
|
141
123
|
}, '/home/shreeve')
|
|
142
124
|
eq config.sites['*.example.com'].routes[0].spa, true
|
|
143
125
|
|
|
144
|
-
test "passthrough
|
|
145
|
-
config =
|
|
146
|
-
|
|
126
|
+
test "passthrough host block creates stream entries", ->
|
|
127
|
+
config = normalizeConfig({
|
|
128
|
+
proxies:
|
|
129
|
+
incus:
|
|
130
|
+
hosts: ['tcp://127.0.0.1:8443']
|
|
147
131
|
hosts:
|
|
148
132
|
incus.example.com:
|
|
149
|
-
|
|
133
|
+
proxy: 'incus'
|
|
150
134
|
}, '/home/shreeve')
|
|
151
|
-
ok config.streamUpstreams['
|
|
135
|
+
ok config.streamUpstreams['incus']
|
|
152
136
|
eq config.streams.length, 1
|
|
137
|
+
eq config.streams[0].proxy, 'incus'
|
|
153
138
|
eq config.streams[0].sni[0], 'incus.example.com'
|
|
154
139
|
|
|
155
|
-
test "passthrough
|
|
156
|
-
config =
|
|
157
|
-
|
|
140
|
+
test "passthrough host block has no sites entry", ->
|
|
141
|
+
config = normalizeConfig({
|
|
142
|
+
proxies:
|
|
143
|
+
incus:
|
|
144
|
+
hosts: ['tcp://127.0.0.1:8443']
|
|
158
145
|
hosts:
|
|
159
146
|
incus.example.com:
|
|
160
|
-
|
|
147
|
+
proxy: 'incus'
|
|
161
148
|
}, '/home/shreeve')
|
|
162
149
|
eq config.sites['incus.example.com'], undefined
|
|
163
150
|
|
|
164
|
-
test "
|
|
165
|
-
config =
|
|
151
|
+
test "server settings and version are optional", ->
|
|
152
|
+
config = normalizeConfig({
|
|
166
153
|
hosts:
|
|
167
154
|
'*.example.com':
|
|
168
155
|
root: '/mnt/site'
|
|
169
156
|
}, '/home/shreeve')
|
|
170
157
|
eq config.version, 1
|
|
171
|
-
ok config.
|
|
158
|
+
ok config.server
|
|
172
159
|
|
|
173
|
-
test "
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
normalizeEdgeConfig({
|
|
177
|
-
version: 2
|
|
178
|
-
edge: {}
|
|
160
|
+
test "rejects route host fields inside host blocks", ->
|
|
161
|
+
errors = getErrors ->
|
|
162
|
+
normalizeConfig({
|
|
179
163
|
hosts:
|
|
180
164
|
'*.example.com':
|
|
181
|
-
|
|
165
|
+
rules: [{ path: '/*', static: '.', host: 'other.com' }]
|
|
182
166
|
}, '/home/shreeve')
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
normalizeEdgeConfig({
|
|
192
|
-
version: 2
|
|
193
|
-
edge: {}
|
|
167
|
+
ok errors.some((err) -> err.code is 'E_SERVER_ROUTE_HOST')
|
|
168
|
+
|
|
169
|
+
test "validates proxy references in host rules", ->
|
|
170
|
+
errors = getErrors ->
|
|
171
|
+
normalizeConfig({
|
|
172
|
+
proxies:
|
|
173
|
+
api:
|
|
174
|
+
hosts: ['http://127.0.0.1:4000']
|
|
194
175
|
hosts:
|
|
195
176
|
'*.example.com':
|
|
196
|
-
|
|
177
|
+
rules: [{ path: '/api/*', proxy: 'nonexistent' }]
|
|
197
178
|
}, '/home/shreeve')
|
|
198
|
-
|
|
199
|
-
threw = true
|
|
200
|
-
ok e.validationErrors.some((err) -> err.code is 'E_ROUTE_UPSTREAM_REF')
|
|
201
|
-
ok threw
|
|
179
|
+
ok errors.some((err) -> err.code is 'E_ROUTE_PROXY_REF')
|
|
202
180
|
|
|
203
|
-
test "
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
normalizeEdgeConfig({
|
|
207
|
-
version: 2
|
|
208
|
-
edge: {}
|
|
181
|
+
test "validates app references in host rules", ->
|
|
182
|
+
errors = getErrors ->
|
|
183
|
+
normalizeConfig({
|
|
209
184
|
hosts:
|
|
210
185
|
'*.example.com':
|
|
211
|
-
|
|
186
|
+
rules: [{ path: '/*', app: 'nonexistent' }]
|
|
212
187
|
}, '/home/shreeve')
|
|
213
|
-
|
|
214
|
-
threw = true
|
|
215
|
-
ok e.validationErrors.some((err) -> err.code is 'E_ROUTE_APP_REF')
|
|
216
|
-
ok threw
|
|
188
|
+
ok errors.some((err) -> err.code is 'E_ROUTE_APP_REF')
|
|
217
189
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
config = normalizeEdgeConfig({
|
|
190
|
+
test "host block with app field creates app route", ->
|
|
191
|
+
config = normalizeConfig({
|
|
221
192
|
hosts:
|
|
222
193
|
'dev.example.com':
|
|
223
194
|
cert: '/ssl/example.com.crt'
|
|
@@ -225,38 +196,33 @@ test "server block with app field creates app route", ->
|
|
|
225
196
|
app: 'browser'
|
|
226
197
|
apps:
|
|
227
198
|
browser:
|
|
228
|
-
entry: '/srv/
|
|
199
|
+
entry: '/srv/browse.rip'
|
|
229
200
|
root: '/mnt/www'
|
|
230
201
|
}, '/home/shreeve')
|
|
231
202
|
route = config.sites['dev.example.com'].routes[0]
|
|
232
203
|
eq route.app, 'browser'
|
|
233
204
|
eq route.host, 'dev.example.com'
|
|
234
|
-
ok config.
|
|
205
|
+
ok config.resolvedCerts['dev.example.com']
|
|
235
206
|
|
|
236
|
-
test "
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
normalizeEdgeConfig({
|
|
207
|
+
test "host block rejects unknown app reference", ->
|
|
208
|
+
errors = getErrors ->
|
|
209
|
+
normalizeConfig({
|
|
240
210
|
hosts:
|
|
241
211
|
'dev.example.com':
|
|
242
212
|
app: 'nonexistent'
|
|
243
213
|
}, '/home/shreeve')
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
route = config.sites['api.example.com'].routes[0]
|
|
260
|
-
eq route.app, 'app-api.example.com'
|
|
261
|
-
ok config.apps['app-api.example.com']
|
|
262
|
-
ok config.apps['app-api.example.com'].entry.includes('myapi')
|
|
214
|
+
ok errors.some((err) -> err.code is 'E_SERVER_APP_REF')
|
|
215
|
+
|
|
216
|
+
test "rejects TCP proxy host blocks that also terminate TLS", ->
|
|
217
|
+
errors = getErrors ->
|
|
218
|
+
normalizeConfig({
|
|
219
|
+
proxies:
|
|
220
|
+
incus:
|
|
221
|
+
hosts: ['tcp://127.0.0.1:8443']
|
|
222
|
+
hosts:
|
|
223
|
+
'incus.example.com':
|
|
224
|
+
proxy: 'incus'
|
|
225
|
+
cert: '/ssl/example.com.crt'
|
|
226
|
+
key: '/ssl/example.com.key'
|
|
227
|
+
}, '/home/shreeve')
|
|
228
|
+
ok errors.some((err) -> err.code is 'E_SERVER_TCP_PROXY_ONLY')
|
package/tests/static.rip
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { test, eq, ok } from '
|
|
2
|
-
import { serveStaticRoute, buildRedirectResponse } from '../
|
|
1
|
+
import { test, eq, ok } from './runner.rip'
|
|
2
|
+
import { serveStaticRoute, buildRedirectResponse } from '../serving/static.rip'
|
|
3
3
|
|
|
4
4
|
# --- helpers ---
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { test, eq, ok } from '
|
|
2
|
-
import { parseSNI } from '../streams/
|
|
1
|
+
import { test, eq, ok } from './runner.rip'
|
|
2
|
+
import { parseSNI } from '../streams/clienthello.rip'
|
|
3
3
|
|
|
4
4
|
buildClientHello = (hostname = 'incus.example.com', opts = {}) ->
|
|
5
5
|
hostBytes = new TextEncoder().encode(hostname)
|
package/tests/streams_index.rip
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test, eq } from '
|
|
1
|
+
import { test, eq } from './runner.rip'
|
|
2
2
|
import { buildStreamRuntime, resolveHandshakeTarget } from '../streams/index.rip'
|
|
3
3
|
|
|
4
4
|
test "resolveHandshakeTarget falls through to http fallback when no route matches", ->
|
|
@@ -7,7 +7,7 @@ test "resolveHandshakeTarget falls through to http fallback when no route matche
|
|
|
7
7
|
incus:
|
|
8
8
|
targets: [{ host: '127.0.0.1', port: 8443 }]
|
|
9
9
|
streams: [
|
|
10
|
-
{ id: 'incus', order: 0, listen: 443, sni: ['incus.example.com'],
|
|
10
|
+
{ id: 'incus', order: 0, listen: 443, sni: ['incus.example.com'], proxy: 'incus' }
|
|
11
11
|
]
|
|
12
12
|
)
|
|
13
13
|
result = resolveHandshakeTarget(runtime, 443, 'app.example.com',
|
|
@@ -24,7 +24,7 @@ test "resolveHandshakeTarget prefers configured stream routes over fallback", ->
|
|
|
24
24
|
incus:
|
|
25
25
|
targets: [{ host: '127.0.0.1', port: 8443 }]
|
|
26
26
|
streams: [
|
|
27
|
-
{ id: 'incus', order: 0, listen: 443, sni: ['incus.example.com'],
|
|
27
|
+
{ id: 'incus', order: 0, listen: 443, sni: ['incus.example.com'], proxy: 'incus' }
|
|
28
28
|
]
|
|
29
29
|
)
|
|
30
30
|
result = resolveHandshakeTarget(runtime, 443, 'incus.example.com',
|
|
@@ -41,7 +41,7 @@ test "resolveHandshakeTarget rejects when stream route matches but upstream is u
|
|
|
41
41
|
incus:
|
|
42
42
|
targets: []
|
|
43
43
|
streams: [
|
|
44
|
-
{ id: 'incus', order: 0, listen: 443, sni: ['incus.example.com'],
|
|
44
|
+
{ id: 'incus', order: 0, listen: 443, sni: ['incus.example.com'], proxy: 'incus' }
|
|
45
45
|
]
|
|
46
46
|
)
|
|
47
47
|
result = resolveHandshakeTarget(runtime, 443, 'incus.example.com',
|
package/tests/streams_pipe.rip
CHANGED
package/tests/streams_router.rip
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { test, eq } from '
|
|
1
|
+
import { test, eq } from './runner.rip'
|
|
2
2
|
import { compileStreamTable, matchStreamRoute, describeStreamRoute } from '../streams/router.rip'
|
|
3
3
|
|
|
4
4
|
test "compileStreamTable expands sni patterns", ->
|
|
5
5
|
table = compileStreamTable([
|
|
6
|
-
{ id: 'incus', order: 0, listen: 8443, sni: ['incus.example.com', '*.example.com'],
|
|
6
|
+
{ id: 'incus', order: 0, listen: 8443, sni: ['incus.example.com', '*.example.com'], proxy: 'incus' }
|
|
7
7
|
])
|
|
8
8
|
eq table.routes.length, 2
|
|
9
9
|
|
|
10
10
|
test "matchStreamRoute prefers exact sni", ->
|
|
11
11
|
table = compileStreamTable([
|
|
12
|
-
{ id: 'wild', order: 0, listen: 8443, sni: ['*.example.com'],
|
|
13
|
-
{ id: 'exact', order: 1, listen: 8443, sni: ['incus.example.com'],
|
|
12
|
+
{ id: 'wild', order: 0, listen: 8443, sni: ['*.example.com'], proxy: 'wild' }
|
|
13
|
+
{ id: 'exact', order: 1, listen: 8443, sni: ['incus.example.com'], proxy: 'exact' }
|
|
14
14
|
])
|
|
15
|
-
eq matchStreamRoute(table, 8443, 'incus.example.com').
|
|
15
|
+
eq matchStreamRoute(table, 8443, 'incus.example.com').proxy, 'exact'
|
|
16
16
|
|
|
17
17
|
test "matchStreamRoute matches wildcard sni", ->
|
|
18
18
|
table = compileStreamTable([
|
|
19
|
-
{ id: 'wild', order: 0, listen: 8443, sni: ['*.example.com'],
|
|
19
|
+
{ id: 'wild', order: 0, listen: 8443, sni: ['*.example.com'], proxy: 'wild' }
|
|
20
20
|
])
|
|
21
|
-
eq matchStreamRoute(table, 8443, 'api.example.com').
|
|
21
|
+
eq matchStreamRoute(table, 8443, 'api.example.com').proxy, 'wild'
|
|
22
22
|
|
|
23
23
|
test "matchStreamRoute wildcard is single-label only", ->
|
|
24
24
|
table = compileStreamTable([
|
|
25
|
-
{ id: 'wild', order: 0, listen: 8443, sni: ['*.example.com'],
|
|
25
|
+
{ id: 'wild', order: 0, listen: 8443, sni: ['*.example.com'], proxy: 'wild' }
|
|
26
26
|
])
|
|
27
27
|
eq matchStreamRoute(table, 8443, 'a.b.example.com'), null
|
|
28
28
|
|
|
29
29
|
test "matchStreamRoute filters by listen port", ->
|
|
30
30
|
table = compileStreamTable([
|
|
31
|
-
{ id: 'incus', order: 0, listen: 8443, sni: ['incus.example.com'],
|
|
31
|
+
{ id: 'incus', order: 0, listen: 8443, sni: ['incus.example.com'], proxy: 'incus' }
|
|
32
32
|
])
|
|
33
33
|
eq matchStreamRoute(table, 443, 'incus.example.com'), null
|
|
34
34
|
|
|
35
35
|
test "describeStreamRoute summarizes route", ->
|
|
36
36
|
table = compileStreamTable([
|
|
37
|
-
{ id: 'incus', order: 0, listen: 8443, sni: ['incus.example.com'],
|
|
37
|
+
{ id: 'incus', order: 0, listen: 8443, sni: ['incus.example.com'], proxy: 'incus' }
|
|
38
38
|
])
|
|
39
39
|
eq describeStreamRoute(table.routes[0]), '8443 incus.example.com -> incus'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test, eq } from '
|
|
1
|
+
import { test, eq } from './runner.rip'
|
|
2
2
|
import { createStreamRuntime, describeStreamRuntime, buildStreamConfigInfo, streamUsesListenPort } from '../streams/runtime.rip'
|
|
3
3
|
|
|
4
4
|
test "createStreamRuntime starts empty", ->
|
|
@@ -20,7 +20,7 @@ test "buildStreamConfigInfo reports counts and descriptions", ->
|
|
|
20
20
|
streamUpstreams:
|
|
21
21
|
incus: {}
|
|
22
22
|
streams: [
|
|
23
|
-
{ id: 'incus', order: 0, listen: 8443, sni: ['incus.example.com'],
|
|
23
|
+
{ id: 'incus', order: 0, listen: 8443, sni: ['incus.example.com'], proxy: 'incus' }
|
|
24
24
|
]
|
|
25
25
|
)
|
|
26
26
|
eq info.streamCounts.streamUpstreams, 1
|
|
@@ -30,8 +30,8 @@ test "buildStreamConfigInfo reports counts and descriptions", ->
|
|
|
30
30
|
test "streamUsesListenPort detects shared listener ports", ->
|
|
31
31
|
runtime = createStreamRuntime(null, null,
|
|
32
32
|
routes: [
|
|
33
|
-
{ listen: 443, sni: 'incus.example.com',
|
|
34
|
-
{ listen: 8443, sni: 'db.example.com',
|
|
33
|
+
{ listen: 443, sni: 'incus.example.com', proxy: 'incus' }
|
|
34
|
+
{ listen: 8443, sni: 'db.example.com', proxy: 'db' }
|
|
35
35
|
]
|
|
36
36
|
)
|
|
37
37
|
eq streamUsesListenPort(runtime, 443), true
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test, eq } from '
|
|
1
|
+
import { test, eq } from './runner.rip'
|
|
2
2
|
import { createStreamUpstreamPool, addStreamUpstream, getStreamUpstream, selectStreamTarget, openStreamConnection, releaseStreamConnection } from '../streams/upstream.rip'
|
|
3
3
|
|
|
4
4
|
test "addStreamUpstream registers targets", ->
|
package/tests/upstream.rip
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test, eq, ok } from '
|
|
1
|
+
import { test, eq, ok } from './runner.rip'
|
|
2
2
|
import {
|
|
3
3
|
createUpstreamPool
|
|
4
4
|
addUpstream
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
shouldRetry
|
|
12
12
|
computeRetryDelayMs
|
|
13
13
|
updateTargetHealth
|
|
14
|
-
} from '../
|
|
14
|
+
} from '../serving/upstream.rip'
|
|
15
15
|
|
|
16
16
|
test "createUpstreamPool starts empty", ->
|
|
17
17
|
pool = createUpstreamPool()
|
package/tests/verify.rip
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { test, eq, ok } from '
|
|
2
|
-
import { compileRouteTable } from '../
|
|
3
|
-
import { collectRouteRequirements, verifyRouteRuntime } from '../
|
|
1
|
+
import { test, eq, ok } from './runner.rip'
|
|
2
|
+
import { compileRouteTable } from '../serving/router.rip'
|
|
3
|
+
import { collectRouteRequirements, verifyRouteRuntime } from '../serving/verify.rip'
|
|
4
4
|
|
|
5
|
-
test "collectRouteRequirements gathers
|
|
5
|
+
test "collectRouteRequirements gathers proxies and managed apps", ->
|
|
6
6
|
table = compileRouteTable([
|
|
7
|
-
{ path: '/api/*',
|
|
7
|
+
{ path: '/api/*', proxy: 'api' }
|
|
8
8
|
{ path: '/admin/*', app: 'admin' }
|
|
9
9
|
])
|
|
10
10
|
appRegistry =
|
|
@@ -13,17 +13,17 @@ test "collectRouteRequirements gathers upstreams and managed apps", ->
|
|
|
13
13
|
['reports', { config: { entry: '/srv/reports/index.rip' } }]
|
|
14
14
|
])
|
|
15
15
|
reqs = collectRouteRequirements(table, appRegistry, 'default')
|
|
16
|
-
eq reqs.
|
|
16
|
+
eq reqs.proxyIds, ['api']
|
|
17
17
|
ok reqs.appIds.includes('admin')
|
|
18
18
|
ok reqs.appIds.includes('reports')
|
|
19
19
|
|
|
20
|
-
test "verifyRouteRuntime returns
|
|
20
|
+
test "verifyRouteRuntime returns proxy reason code", ->
|
|
21
21
|
runtime =
|
|
22
22
|
upstreamPool:
|
|
23
23
|
upstreams: new Map([
|
|
24
24
|
['api', { id: 'api', targets: [{ targetId: 'api:0' }, { targetId: 'api:1' }] }]
|
|
25
25
|
])
|
|
26
|
-
routeTable: compileRouteTable([{ path: '/api/*',
|
|
26
|
+
routeTable: compileRouteTable([{ path: '/api/*', proxy: 'api' }])
|
|
27
27
|
appRegistry = { apps: new Map() }
|
|
28
28
|
result = verifyRouteRuntime(
|
|
29
29
|
runtime,
|
|
@@ -34,8 +34,8 @@ test "verifyRouteRuntime returns upstream reason code", ->
|
|
|
34
34
|
((registry, appId) -> registry.apps.get(appId))
|
|
35
35
|
)
|
|
36
36
|
eq result.ok, false
|
|
37
|
-
eq result.code, '
|
|
38
|
-
eq result.details.
|
|
37
|
+
eq result.code, 'proxy_no_healthy_targets'
|
|
38
|
+
eq result.details.proxyId, 'api'
|
|
39
39
|
eq result.details.requiredHealthyTargets, 1
|
|
40
40
|
|
|
41
41
|
test "verifyRouteRuntime returns app worker reason code", ->
|
|
@@ -59,14 +59,14 @@ test "verifyRouteRuntime returns app worker reason code", ->
|
|
|
59
59
|
eq result.code, 'app_no_ready_workers'
|
|
60
60
|
eq result.details.appId, 'admin'
|
|
61
61
|
|
|
62
|
-
test "verifyRouteRuntime succeeds when referenced
|
|
62
|
+
test "verifyRouteRuntime succeeds when referenced proxies and apps are healthy", ->
|
|
63
63
|
runtime =
|
|
64
64
|
upstreamPool:
|
|
65
65
|
upstreams: new Map([
|
|
66
66
|
['api', { id: 'api', targets: [{ targetId: 'api:0' }] }]
|
|
67
67
|
])
|
|
68
68
|
routeTable: compileRouteTable([
|
|
69
|
-
{ path: '/api/*',
|
|
69
|
+
{ path: '/api/*', proxy: 'api' }
|
|
70
70
|
{ path: '/admin/*', app: 'admin' }
|
|
71
71
|
])
|
|
72
72
|
appRegistry =
|
|
@@ -90,7 +90,7 @@ test "verifyRouteRuntime honors minimum healthy targets policy", ->
|
|
|
90
90
|
upstreams: new Map([
|
|
91
91
|
['api', { id: 'api', targets: [{ targetId: 'api:0' }, { targetId: 'api:1' }] }]
|
|
92
92
|
])
|
|
93
|
-
routeTable: compileRouteTable([{ path: '/api/*',
|
|
93
|
+
routeTable: compileRouteTable([{ path: '/api/*', proxy: 'api' }])
|
|
94
94
|
appRegistry = { apps: new Map() }
|
|
95
95
|
healthyCount = 0
|
|
96
96
|
result = verifyRouteRuntime(
|
|
@@ -103,18 +103,18 @@ test "verifyRouteRuntime honors minimum healthy targets policy", ->
|
|
|
103
103
|
healthyCount is 1
|
|
104
104
|
),
|
|
105
105
|
((registry, appId) -> registry.apps.get(appId)),
|
|
106
|
-
|
|
106
|
+
minHealthyTargetsPerProxy: 2
|
|
107
107
|
)
|
|
108
108
|
eq result.ok, false
|
|
109
|
-
eq result.code, '
|
|
109
|
+
eq result.code, 'proxy_no_healthy_targets'
|
|
110
110
|
eq result.details.healthyTargets, 1
|
|
111
111
|
eq result.details.requiredHealthyTargets, 2
|
|
112
112
|
|
|
113
|
-
test "verifyRouteRuntime can skip healthy
|
|
113
|
+
test "verifyRouteRuntime can skip healthy proxy checks", ->
|
|
114
114
|
runtime =
|
|
115
115
|
upstreamPool:
|
|
116
116
|
upstreams: new Map()
|
|
117
|
-
routeTable: compileRouteTable([{ path: '/api/*',
|
|
117
|
+
routeTable: compileRouteTable([{ path: '/api/*', proxy: 'api' }])
|
|
118
118
|
appRegistry = { apps: new Map() }
|
|
119
119
|
result = verifyRouteRuntime(
|
|
120
120
|
runtime,
|
|
@@ -123,7 +123,7 @@ test "verifyRouteRuntime can skip healthy upstream checks", ->
|
|
|
123
123
|
((pool, id) -> pool.upstreams.get(id)),
|
|
124
124
|
((pool, target) -> false),
|
|
125
125
|
((registry, appId) -> registry.apps.get(appId)),
|
|
126
|
-
|
|
126
|
+
requireHealthyProxies: false
|
|
127
127
|
)
|
|
128
128
|
eq result.ok, true
|
|
129
129
|
|