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