| | 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 |
36670 |
* 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 |
36670 |
* 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 |
36670 |
#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 |
36670 |
|
43 |
36670 |
#include "vct.h" |
44 |
0 |
|
45 |
0 |
/**********************************************************************/ |
46 |
36670 |
|
47 |
36670 |
struct hpack_static { |
48 |
36670 |
uint8_t idx; |
49 |
0 |
const char * name; |
50 |
0 |
const char * val; |
51 |
0 |
}; |
52 |
0 |
|
53 |
36670 |
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 |
36670 |
{ 0, "\377:", ""} // Terminator |
57 |
36670 |
}; |
58 |
0 |
|
59 |
36670 |
static const struct hpack_static *hp_idx[256]; |
60 |
0 |
|
61 |
0 |
void |
62 |
36670 |
V2D_Init(void) |
63 |
36670 |
{ |
64 |
0 |
int i; |
65 |
0 |
#define HPS(I,N,V) \ |
66 |
36670 |
i = hp_static[I].name[0]; \ |
67 |
36670 |
if (hp_idx[i] == NULL) hp_idx[i] = &hp_static[I]; |
68 |
36670 |
#include "tbl/vhp_static.h" |
69 |
0 |
#undef HPS |
70 |
36670 |
} |
71 |
|
|
72 |
|
/**********************************************************************/ |
73 |
|
|
74 |
|
static int v_matchproto_(vdp_init_f) |
75 |
1520 |
h2_init(VRT_CTX, struct vdp_ctx *vdc, void **priv) |
76 |
|
{ |
77 |
|
struct h2_req *r2; |
78 |
|
|
79 |
1520 |
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); |
80 |
1520 |
CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC); |
81 |
1520 |
AN(priv); |
82 |
1520 |
CAST_OBJ_NOTNULL(r2, *priv, H2_REQ_MAGIC); |
83 |
1520 |
(void)r2; |
84 |
1520 |
return (0); |
85 |
|
} |
86 |
|
|
87 |
|
static int v_matchproto_(vdp_fini_f) |
88 |
1520 |
h2_fini(struct vdp_ctx *vdc, void **priv) |
89 |
|
{ |
90 |
|
struct h2_req *r2; |
91 |
|
|
92 |
1520 |
CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC); |
93 |
1520 |
CHECK_OBJ_NOTNULL(vdc->wrk, WORKER_MAGIC); |
94 |
1520 |
TAKE_OBJ_NOTNULL(r2, priv, H2_REQ_MAGIC); |
95 |
|
|
96 |
1520 |
if (r2->error) |
97 |
330 |
return (0); |
98 |
|
|
99 |
1190 |
if (vdc->retval < 0) { |
100 |
52 |
r2->error = H2SE_INTERNAL_ERROR; /* XXX: proper error? */ |
101 |
52 |
H2_Send_Get(vdc->wrk, r2->h2sess, r2); |
102 |
52 |
H2_Send_RST(vdc->wrk, r2->h2sess, r2, r2->stream, r2->error); |
103 |
52 |
H2_Send_Rel(r2->h2sess, r2); |
104 |
52 |
return (0); |
105 |
|
} |
106 |
|
|
107 |
1138 |
H2_Send_Get(vdc->wrk, r2->h2sess, r2); |
108 |
1138 |
H2_Send(vdc->wrk, r2, H2_F_DATA, H2FF_DATA_END_STREAM, 0, "", NULL); |
109 |
1138 |
H2_Send_Rel(r2->h2sess, r2); |
110 |
1138 |
return (0); |
111 |
1520 |
} |
112 |
|
|
113 |
|
static int v_matchproto_(vdp_bytes_f) |
114 |
2481 |
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 |
2481 |
CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC); |
120 |
2481 |
CAST_OBJ_NOTNULL(r2, *priv, H2_REQ_MAGIC); |
121 |
2481 |
(void)act; |
122 |
|
|
123 |
2481 |
if ((r2->h2sess->error || r2->error)) |
124 |
262 |
return (-1); |
125 |
2219 |
if (len == 0) |
126 |
761 |
return (0); |
127 |
1458 |
H2_Send_Get(vdc->wrk, r2->h2sess, r2); |
128 |
1458 |
vdc->bytes_done = 0; |
129 |
1458 |
H2_Send(vdc->wrk, r2, H2_F_DATA, H2FF_NONE, len, ptr, &vdc->bytes_done); |
130 |
1458 |
H2_Send_Rel(r2->h2sess, r2); |
131 |
1458 |
return (0); |
132 |
2481 |
} |
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 |
4558 |
h2_status(uint8_t *p, uint16_t status) { |
143 |
4558 |
size_t l = 1; |
144 |
|
|
145 |
4558 |
switch (status) { |
146 |
3000 |
case 200: *p = 0x80 | 8; break; |
147 |
0 |
case 204: *p = 0x80 | 9; break; |
148 |
80 |
case 206: *p = 0x80 | 10; break; |
149 |
120 |
case 304: *p = 0x80 | 11; break; |
150 |
40 |
case 400: *p = 0x80 | 12; break; |
151 |
40 |
case 404: *p = 0x80 | 13; break; |
152 |
40 |
case 500: *p = 0x80 | 14; break; |
153 |
|
default: |
154 |
1238 |
*p++ = 0x18; |
155 |
1238 |
*p++ = 0x03; |
156 |
1238 |
l = 2; |
157 |
|
|
158 |
1238 |
l += snprintf((char*)p, 4, "%03d", status); |
159 |
1238 |
assert(l == 5); |
160 |
1238 |
break; |
161 |
|
} |
162 |
|
|
163 |
4558 |
return (l); |
164 |
|
} |
165 |
|
|
166 |
|
int v_matchproto_(vtr_minimal_response_f) |
167 |
1000 |
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 |
1000 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
174 |
1000 |
CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC); |
175 |
|
|
176 |
1000 |
assert(status >= 100); |
177 |
1000 |
assert(status < 1000); |
178 |
|
|
179 |
1000 |
l = h2_status(buf, status); |
180 |
1000 |
assert(l < sizeof(buf)); |
181 |
|
|
182 |
1000 |
VSLb(req->vsl, SLT_RespProtocol, "HTTP/2.0"); |
183 |
1000 |
VSLb(req->vsl, SLT_RespStatus, "%03d", status); |
184 |
2000 |
VSLbs(req->vsl, SLT_RespReason, |
185 |
1000 |
TOSTRAND(http_Status2Reason(status, NULL))); |
186 |
|
|
187 |
1000 |
if (status >= 400) |
188 |
919 |
req->err_code = status; |
189 |
|
|
190 |
|
/* XXX return code checking once H2_Send returns anything but 0 */ |
191 |
1000 |
H2_Send_Get(req->wrk, r2->h2sess, r2); |
192 |
2000 |
H2_Send(req->wrk, r2, |
193 |
|
H2_F_HEADERS, |
194 |
2000 |
H2FF_HEADERS_END_HEADERS | |
195 |
1000 |
(status < 200 ? 0 : H2FF_HEADERS_END_STREAM), |
196 |
1000 |
l, buf, NULL); |
197 |
1000 |
H2_Send_Rel(r2->h2sess, r2); |
198 |
1000 |
return (0); |
199 |
|
} |
200 |
|
|
201 |
|
static void |
202 |
49014 |
h2_enc_len(struct vsb *vsb, unsigned bits, unsigned val, uint8_t b0) |
203 |
|
{ |
204 |
49014 |
assert(bits < 8); |
205 |
49014 |
unsigned mask = (1U << bits) - 1U; |
206 |
|
|
207 |
49014 |
if (val >= mask) { |
208 |
20359 |
VSB_putc(vsb, b0 | (uint8_t)mask); |
209 |
20359 |
val -= mask; |
210 |
20559 |
while (val >= 128) { |
211 |
200 |
VSB_putc(vsb, 0x80 | ((uint8_t)val & 0x7f)); |
212 |
200 |
val >>= 7; |
213 |
|
} |
214 |
20359 |
} |
215 |
49014 |
VSB_putc(vsb, (uint8_t)val); |
216 |
49014 |
} |
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 |
3560 |
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 |
3560 |
assert(req->resp->status % 1000 >= 100); |
248 |
3560 |
l = h2_status(buf, req->resp->status % 1000); |
249 |
3560 |
VSB_bcat(resp, buf, l); |
250 |
|
|
251 |
3560 |
hp = req->resp; |
252 |
28193 |
for (u = HTTP_HDR_FIRST; u < hp->nhd && !VSB_error(resp); u++) { |
253 |
24633 |
r = strchr(hp->hd[u].b, ':'); |
254 |
24633 |
AN(r); |
255 |
|
|
256 |
24633 |
if (http_IsFiltered(hp, u, HTTPH_C_SPECIFIC)) |
257 |
120 |
continue; //rfc7540,l,2999,3006 |
258 |
|
|
259 |
24513 |
hps = hp_idx[tolower(*hp->hd[u].b)]; |
260 |
24513 |
sz = 1 + r - hp->hd[u].b; |
261 |
24513 |
assert(sz > 0); |
262 |
73047 |
while (hps != NULL && hps->idx > 0) { |
263 |
69369 |
i = strncasecmp(hps->name, hp->hd[u].b, sz); |
264 |
69369 |
if (i < 0) { |
265 |
48534 |
hps++; |
266 |
48534 |
continue; |
267 |
|
} |
268 |
20835 |
if (i > 0) |
269 |
680 |
hps = NULL; |
270 |
20835 |
break; |
271 |
|
} |
272 |
24513 |
if (hps != NULL) { |
273 |
40308 |
VSLb(req->vsl, SLT_Debug, |
274 |
|
"HP {%d, \"%s\", \"%s\"} <%s>", |
275 |
20154 |
hps->idx, hps->name, hps->val, hp->hd[u].b); |
276 |
20154 |
h2_enc_len(resp, 4, hps->idx, 0x10); |
277 |
20154 |
} else { |
278 |
4359 |
VSB_putc(resp, 0x10); |
279 |
4359 |
sz--; |
280 |
4359 |
h2_enc_len(resp, 7, sz, 0); |
281 |
64568 |
for (sz1 = 0; sz1 < sz; sz1++) |
282 |
60209 |
VSB_putc(resp, tolower(hp->hd[u].b[sz1])); |
283 |
|
|
284 |
|
} |
285 |
|
|
286 |
49025 |
while (vct_islws(*++r)) |
287 |
24512 |
continue; |
288 |
24513 |
sz = hp->hd[u].e - r; |
289 |
24513 |
h2_enc_len(resp, 7, sz, 0); |
290 |
24513 |
VSB_bcat(resp, r, sz); |
291 |
24513 |
} |
292 |
3560 |
} |
293 |
|
|
294 |
|
enum vtr_deliver_e v_matchproto_(vtr_deliver_f) |
295 |
3560 |
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 |
3560 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
306 |
3560 |
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); |
307 |
3560 |
CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC); |
308 |
3560 |
sp = req->sp; |
309 |
3560 |
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); |
310 |
|
|
311 |
3560 |
VSLb(req->vsl, SLT_RespProtocol, "HTTP/2.0"); |
312 |
|
|
313 |
3560 |
(void)http_DoConnection(req->resp, SC_RESP_CLOSE); |
314 |
|
|
315 |
3560 |
ss = WS_Snapshot(req->ws); |
316 |
|
|
317 |
3560 |
WS_VSB_new(resp, req->ws); |
318 |
3560 |
h2_build_headers(resp, req); |
319 |
3560 |
r = WS_VSB_finish(resp, req->ws, &sz); |
320 |
|
|
321 |
3560 |
if (r == NULL) { |
322 |
40 |
VSLb(req->vsl, SLT_Error, "workspace_client overflow"); |
323 |
40 |
VSLb(req->vsl, SLT_RespStatus, "500"); |
324 |
40 |
VSLb(req->vsl, SLT_RespReason, "Internal Server Error"); |
325 |
40 |
req->wrk->stats->client_resp_500++; |
326 |
|
|
327 |
40 |
r = (const char*)h2_500_resp; |
328 |
40 |
sz = sizeof h2_500_resp; |
329 |
40 |
sendbody = 0; |
330 |
40 |
} |
331 |
|
|
332 |
3560 |
r2->t_send = req->t_prev; |
333 |
|
|
334 |
3560 |
H2_Send_Get(req->wrk, r2->h2sess, r2); |
335 |
7120 |
H2_Send(req->wrk, r2, H2_F_HEADERS, |
336 |
3560 |
(sendbody ? 0 : H2FF_HEADERS_END_STREAM) | H2FF_HEADERS_END_HEADERS, |
337 |
3560 |
sz, r, &req->acct.resp_hdrbytes); |
338 |
3560 |
H2_Send_Rel(r2->h2sess, r2); |
339 |
|
|
340 |
3560 |
WS_Reset(req->ws, ss); |
341 |
|
|
342 |
|
/* XXX someone into H2 please add appropriate error handling */ |
343 |
3560 |
if (sendbody) { |
344 |
1520 |
INIT_OBJ(ctx, VRT_CTX_MAGIC); |
345 |
1520 |
VCL_Req2Ctx(ctx, req); |
346 |
1520 |
if (! VDP_Push(ctx, req->vdc, req->ws, &h2_vdp, r2)) |
347 |
1520 |
(void)VDP_DeliverObj(req->vdc, req->objcore); |
348 |
1520 |
} |
349 |
|
|
350 |
3560 |
req->acct.resp_bodybytes += VDP_Close(req->vdc, req->objcore, req->boc); |
351 |
3560 |
return (VTR_D_DONE); |
352 |
|
} |