| | varnish-cache/bin/varnishd/http2/cache_http2_deliver.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2016 Varnish Software AS |
2 |
|
* All rights reserved. |
3 |
|
* |
4 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
5 |
|
* |
6 |
|
* SPDX-License-Identifier: BSD-2-Clause |
7 |
|
* |
8 |
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
* modification, are permitted provided that the following conditions |
10 |
4611 |
* are met: |
11 |
0 |
* 1. Redistributions of source code must retain the above copyright |
12 |
0 |
* notice, this list of conditions and the following disclaimer. |
13 |
0 |
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
0 |
* notice, this list of conditions and the following disclaimer in the |
15 |
0 |
* documentation and/or other materials provided with the distribution. |
16 |
0 |
* |
17 |
0 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 |
0 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 |
0 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 |
0 |
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
21 |
0 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 |
0 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 |
0 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 |
4611 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 |
0 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 |
0 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 |
0 |
* SUCH DAMAGE. |
28 |
0 |
* |
29 |
0 |
*/ |
30 |
0 |
|
31 |
0 |
#include "config.h" |
32 |
0 |
|
33 |
4611 |
#include "cache/cache_varnishd.h" |
34 |
0 |
|
35 |
0 |
#include <ctype.h> |
36 |
0 |
#include <stdio.h> |
37 |
0 |
|
38 |
0 |
#include "cache/cache_filter.h" |
39 |
0 |
#include "cache/cache_transport.h" |
40 |
0 |
|
41 |
0 |
#include "http2/cache_http2.h" |
42 |
4611 |
|
43 |
4611 |
#include "vct.h" |
44 |
0 |
|
45 |
0 |
/**********************************************************************/ |
46 |
4611 |
|
47 |
4611 |
struct hpack_static { |
48 |
4611 |
uint8_t idx; |
49 |
0 |
const char * name; |
50 |
0 |
const char * val; |
51 |
0 |
}; |
52 |
0 |
|
53 |
4611 |
static const struct hpack_static hp_static[] = { |
54 |
0 |
#define HPS(I,N,V) [I] = { I, N ":", V }, |
55 |
0 |
#include "tbl/vhp_static.h" |
56 |
4611 |
{ 0, "\377:", ""} // Terminator |
57 |
4611 |
}; |
58 |
0 |
|
59 |
4611 |
static const struct hpack_static *hp_idx[256]; |
60 |
0 |
|
61 |
0 |
void |
62 |
4611 |
V2D_Init(void) |
63 |
4611 |
{ |
64 |
0 |
int i; |
65 |
0 |
#define HPS(I,N,V) \ |
66 |
4611 |
i = hp_static[I].name[0]; \ |
67 |
4611 |
if (hp_idx[i] == NULL) hp_idx[i] = &hp_static[I]; |
68 |
4611 |
#include "tbl/vhp_static.h" |
69 |
0 |
#undef HPS |
70 |
4611 |
} |
71 |
|
|
72 |
|
/**********************************************************************/ |
73 |
|
|
74 |
|
static int v_matchproto_(vdp_init_f) |
75 |
190 |
h2_init(VRT_CTX, struct vdp_ctx *vdc, void **priv) |
76 |
|
{ |
77 |
|
struct h2_req *r2; |
78 |
|
|
79 |
190 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
80 |
190 |
CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC); |
81 |
190 |
AN(priv); |
82 |
190 |
CAST_OBJ_NOTNULL(r2, *priv, H2_REQ_MAGIC); |
83 |
190 |
(void)r2; |
84 |
190 |
return (0); |
85 |
|
} |
86 |
|
|
87 |
|
static int v_matchproto_(vdp_fini_f) |
88 |
190 |
h2_fini(struct vdp_ctx *vdc, void **priv) |
89 |
|
{ |
90 |
|
struct h2_req *r2; |
91 |
|
|
92 |
190 |
CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC); |
93 |
190 |
CHECK_OBJ_NOTNULL(vdc->wrk, WORKER_MAGIC); |
94 |
190 |
TAKE_OBJ_NOTNULL(r2, priv, H2_REQ_MAGIC); |
95 |
|
|
96 |
190 |
if (r2->error) |
97 |
40 |
return (0); |
98 |
|
|
99 |
150 |
if (vdc->retval < 0) { |
100 |
8 |
r2->error = H2SE_INTERNAL_ERROR; /* XXX: proper error? */ |
101 |
8 |
H2_Send_Get(vdc->wrk, r2->h2sess, r2); |
102 |
8 |
H2_Send_RST(vdc->wrk, r2->h2sess, r2, r2->stream, r2->error); |
103 |
8 |
H2_Send_Rel(r2->h2sess, r2); |
104 |
8 |
return (0); |
105 |
|
} |
106 |
|
|
107 |
142 |
H2_Send_Get(vdc->wrk, r2->h2sess, r2); |
108 |
142 |
H2_Send(vdc->wrk, r2, H2_F_DATA, H2FF_DATA_END_STREAM, 0, "", NULL); |
109 |
142 |
H2_Send_Rel(r2->h2sess, r2); |
110 |
142 |
return (0); |
111 |
190 |
} |
112 |
|
|
113 |
|
static int v_matchproto_(vdp_bytes_f) |
114 |
310 |
h2_bytes(struct vdp_ctx *vdc, enum vdp_action act, void **priv, |
115 |
|
const void *ptr, ssize_t len) |
116 |
|
{ |
117 |
|
struct h2_req *r2; |
118 |
|
|
119 |
310 |
CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC); |
120 |
310 |
CAST_OBJ_NOTNULL(r2, *priv, H2_REQ_MAGIC); |
121 |
310 |
(void)act; |
122 |
|
|
123 |
310 |
if ((r2->h2sess->error || r2->error)) |
124 |
33 |
return (-1); |
125 |
277 |
if (len == 0) |
126 |
95 |
return (0); |
127 |
182 |
H2_Send_Get(vdc->wrk, r2->h2sess, r2); |
128 |
182 |
vdc->bytes_done = 0; |
129 |
182 |
H2_Send(vdc->wrk, r2, H2_F_DATA, H2FF_NONE, len, ptr, &vdc->bytes_done); |
130 |
182 |
H2_Send_Rel(r2->h2sess, r2); |
131 |
182 |
return (0); |
132 |
310 |
} |
133 |
|
|
134 |
|
static const struct vdp h2_vdp = { |
135 |
|
.name = "H2B", |
136 |
|
.init = h2_init, |
137 |
|
.bytes = h2_bytes, |
138 |
|
.fini = h2_fini, |
139 |
|
}; |
140 |
|
|
141 |
|
static inline size_t |
142 |
569 |
h2_status(uint8_t *p, uint16_t status) { |
143 |
569 |
size_t l = 1; |
144 |
|
|
145 |
569 |
switch (status) { |
146 |
375 |
case 200: *p = 0x80 | 8; break; |
147 |
0 |
case 204: *p = 0x80 | 9; break; |
148 |
10 |
case 206: *p = 0x80 | 10; break; |
149 |
15 |
case 304: *p = 0x80 | 11; break; |
150 |
5 |
case 400: *p = 0x80 | 12; break; |
151 |
5 |
case 404: *p = 0x80 | 13; break; |
152 |
5 |
case 500: *p = 0x80 | 14; break; |
153 |
|
default: |
154 |
154 |
*p++ = 0x18; |
155 |
154 |
*p++ = 0x03; |
156 |
154 |
l = 2; |
157 |
|
|
158 |
154 |
l += snprintf((char*)p, 4, "%03d", status); |
159 |
154 |
assert(l == 5); |
160 |
154 |
break; |
161 |
|
} |
162 |
|
|
163 |
569 |
return (l); |
164 |
|
} |
165 |
|
|
166 |
|
int v_matchproto_(vtr_minimal_response_f) |
167 |
125 |
h2_minimal_response(struct req *req, uint16_t status) |
168 |
|
{ |
169 |
|
struct h2_req *r2; |
170 |
|
size_t l; |
171 |
|
uint8_t buf[6]; |
172 |
|
|
173 |
125 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
174 |
125 |
CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC); |
175 |
|
|
176 |
125 |
assert(status >= 100); |
177 |
125 |
assert(status < 1000); |
178 |
|
|
179 |
125 |
l = h2_status(buf, status); |
180 |
125 |
assert(l < sizeof(buf)); |
181 |
|
|
182 |
125 |
VSLb(req->vsl, SLT_RespProtocol, "HTTP/2.0"); |
183 |
125 |
VSLb(req->vsl, SLT_RespStatus, "%03d", status); |
184 |
250 |
VSLbs(req->vsl, SLT_RespReason, |
185 |
125 |
TOSTRAND(http_Status2Reason(status, NULL))); |
186 |
|
|
187 |
125 |
if (status >= 400) |
188 |
115 |
req->err_code = status; |
189 |
|
|
190 |
|
/* XXX return code checking once H2_Send returns anything but 0 */ |
191 |
125 |
H2_Send_Get(req->wrk, r2->h2sess, r2); |
192 |
250 |
H2_Send(req->wrk, r2, |
193 |
|
H2_F_HEADERS, |
194 |
250 |
H2FF_HEADERS_END_HEADERS | |
195 |
125 |
(status < 200 ? 0 : H2FF_HEADERS_END_STREAM), |
196 |
125 |
l, buf, NULL); |
197 |
125 |
H2_Send_Rel(r2->h2sess, r2); |
198 |
125 |
return (0); |
199 |
|
} |
200 |
|
|
201 |
|
static void |
202 |
6128 |
h2_enc_len(struct vsb *vsb, unsigned bits, unsigned val, uint8_t b0) |
203 |
|
{ |
204 |
6128 |
assert(bits < 8); |
205 |
6128 |
unsigned mask = (1U << bits) - 1U; |
206 |
|
|
207 |
6128 |
if (val >= mask) { |
208 |
2545 |
VSB_putc(vsb, b0 | (uint8_t)mask); |
209 |
2545 |
val -= mask; |
210 |
2570 |
while (val >= 128) { |
211 |
25 |
VSB_putc(vsb, 0x80 | ((uint8_t)val & 0x7f)); |
212 |
25 |
val >>= 7; |
213 |
|
} |
214 |
2545 |
} |
215 |
6128 |
VSB_putc(vsb, (uint8_t)val); |
216 |
6128 |
} |
217 |
|
|
218 |
|
/* |
219 |
|
* Hand-crafted-H2-HEADERS-R-Us: |
220 |
|
* |
221 |
|
* This is a handbuilt HEADERS frame for when we run out of workspace |
222 |
|
* during delivery. |
223 |
|
*/ |
224 |
|
|
225 |
|
static const uint8_t h2_500_resp[] = { |
226 |
|
// :status 500 |
227 |
|
0x8e, |
228 |
|
|
229 |
|
// content-length 0 |
230 |
|
0x1f, 0x0d, 0x01, 0x30, |
231 |
|
|
232 |
|
// server Varnish |
233 |
|
0x1f, 0x27, 0x07, 'V', 'a', 'r', 'n', 'i', 's', 'h', |
234 |
|
}; |
235 |
|
|
236 |
|
static void |
237 |
445 |
h2_build_headers(struct vsb *resp, struct req *req) |
238 |
|
{ |
239 |
|
unsigned u, l; |
240 |
|
int i; |
241 |
|
struct http *hp; |
242 |
|
const char *r; |
243 |
|
const struct hpack_static *hps; |
244 |
|
uint8_t buf[6]; |
245 |
|
ssize_t sz, sz1; |
246 |
|
|
247 |
445 |
assert(req->resp->status % 1000 >= 100); |
248 |
445 |
l = h2_status(buf, req->resp->status % 1000); |
249 |
445 |
VSB_bcat(resp, buf, l); |
250 |
|
|
251 |
445 |
hp = req->resp; |
252 |
3524 |
for (u = HTTP_HDR_FIRST; u < hp->nhd && !VSB_error(resp); u++) { |
253 |
3079 |
r = strchr(hp->hd[u].b, ':'); |
254 |
3079 |
AN(r); |
255 |
|
|
256 |
3079 |
if (http_IsFiltered(hp, u, HTTPH_C_SPECIFIC)) |
257 |
15 |
continue; //rfc7540,l,2999,3006 |
258 |
|
|
259 |
3064 |
hps = hp_idx[tolower(*hp->hd[u].b)]; |
260 |
3064 |
sz = 1 + r - hp->hd[u].b; |
261 |
3064 |
assert(sz > 0); |
262 |
9137 |
while (hps != NULL && hps->idx > 0) { |
263 |
8678 |
i = strncasecmp(hps->name, hp->hd[u].b, sz); |
264 |
8678 |
if (i < 0) { |
265 |
6073 |
hps++; |
266 |
6073 |
continue; |
267 |
|
} |
268 |
2605 |
if (i > 0) |
269 |
85 |
hps = NULL; |
270 |
2605 |
break; |
271 |
|
} |
272 |
3064 |
if (hps != NULL) { |
273 |
5038 |
VSLb(req->vsl, SLT_Debug, |
274 |
|
"HP {%d, \"%s\", \"%s\"} <%s>", |
275 |
2519 |
hps->idx, hps->name, hps->val, hp->hd[u].b); |
276 |
2519 |
h2_enc_len(resp, 4, hps->idx, 0x10); |
277 |
2519 |
} else { |
278 |
545 |
VSB_putc(resp, 0x10); |
279 |
545 |
sz--; |
280 |
545 |
h2_enc_len(resp, 7, sz, 0); |
281 |
8080 |
for (sz1 = 0; sz1 < sz; sz1++) |
282 |
7535 |
VSB_putc(resp, tolower(hp->hd[u].b[sz1])); |
283 |
|
|
284 |
|
} |
285 |
|
|
286 |
6129 |
while (vct_islws(*++r)) |
287 |
3065 |
continue; |
288 |
3064 |
sz = hp->hd[u].e - r; |
289 |
3064 |
h2_enc_len(resp, 7, sz, 0); |
290 |
3064 |
VSB_bcat(resp, r, sz); |
291 |
3064 |
} |
292 |
445 |
} |
293 |
|
|
294 |
|
enum vtr_deliver_e v_matchproto_(vtr_deliver_f) |
295 |
445 |
h2_deliver(struct req *req, int sendbody) |
296 |
|
{ |
297 |
|
size_t sz; |
298 |
|
const char *r; |
299 |
|
struct sess *sp; |
300 |
|
struct h2_req *r2; |
301 |
|
struct vsb resp[1]; |
302 |
|
struct vrt_ctx ctx[1]; |
303 |
|
uintptr_t ss; |
304 |
|
|
305 |
445 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
306 |
445 |
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); |
307 |
445 |
CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC); |
308 |
445 |
sp = req->sp; |
309 |
445 |
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); |
310 |
|
|
311 |
445 |
VSLb(req->vsl, SLT_RespProtocol, "HTTP/2.0"); |
312 |
|
|
313 |
445 |
(void)http_DoConnection(req->resp, SC_RESP_CLOSE); |
314 |
|
|
315 |
445 |
ss = WS_Snapshot(req->ws); |
316 |
|
|
317 |
445 |
WS_VSB_new(resp, req->ws); |
318 |
445 |
h2_build_headers(resp, req); |
319 |
445 |
r = WS_VSB_finish(resp, req->ws, &sz); |
320 |
|
|
321 |
445 |
if (r == NULL) { |
322 |
5 |
VSLb(req->vsl, SLT_Error, "workspace_client overflow"); |
323 |
5 |
VSLb(req->vsl, SLT_RespStatus, "500"); |
324 |
5 |
VSLb(req->vsl, SLT_RespReason, "Internal Server Error"); |
325 |
5 |
req->wrk->stats->client_resp_500++; |
326 |
|
|
327 |
5 |
r = (const char*)h2_500_resp; |
328 |
5 |
sz = sizeof h2_500_resp; |
329 |
5 |
sendbody = 0; |
330 |
5 |
} |
331 |
|
|
332 |
445 |
r2->t_send = req->t_prev; |
333 |
|
|
334 |
445 |
H2_Send_Get(req->wrk, r2->h2sess, r2); |
335 |
890 |
H2_Send(req->wrk, r2, H2_F_HEADERS, |
336 |
445 |
(sendbody ? 0 : H2FF_HEADERS_END_STREAM) | H2FF_HEADERS_END_HEADERS, |
337 |
445 |
sz, r, &req->acct.resp_hdrbytes); |
338 |
445 |
H2_Send_Rel(r2->h2sess, r2); |
339 |
|
|
340 |
445 |
WS_Reset(req->ws, ss); |
341 |
|
|
342 |
|
/* XXX someone into H2 please add appropriate error handling */ |
343 |
445 |
if (sendbody) { |
344 |
190 |
INIT_OBJ(ctx, VRT_CTX_MAGIC); |
345 |
190 |
VCL_Req2Ctx(ctx, req); |
346 |
190 |
if (! VDP_Push(ctx, req->vdc, req->ws, &h2_vdp, r2)) |
347 |
190 |
(void)VDP_DeliverObj(req->vdc, req->objcore); |
348 |
190 |
} |
349 |
|
|
350 |
445 |
req->acct.resp_bodybytes += VDP_Close(req->vdc, req->objcore, req->boc); |
351 |
445 |
return (VTR_D_DONE); |
352 |
|
} |