| | varnish-cache/bin/varnishtest/vtc_http.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2008-2019 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 |
|
* are met: |
11 |
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
* documentation and/or other materials provided with the distribution. |
16 |
|
* |
17 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
21 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 |
|
* SUCH DAMAGE. |
28 |
|
*/ |
29 |
|
|
30 |
|
#include "config.h" |
31 |
|
|
32 |
|
#include <sys/socket.h> |
33 |
|
|
34 |
|
#include <math.h> |
35 |
|
#include <poll.h> |
36 |
|
#include <stdio.h> |
37 |
|
#include <stdlib.h> |
38 |
|
#include <unistd.h> |
39 |
|
#include <string.h> |
40 |
|
|
41 |
|
#include "vtc.h" |
42 |
|
#include "vtc_http.h" |
43 |
|
|
44 |
|
#include "vct.h" |
45 |
|
#include "vfil.h" |
46 |
|
#include "vnum.h" |
47 |
|
#include "vrnd.h" |
48 |
|
#include "vtcp.h" |
49 |
|
#include "vtim.h" |
50 |
|
#include "hpack.h" |
51 |
|
|
52 |
|
extern const struct cmds http_cmds[]; |
53 |
|
|
54 |
|
/* SECTION: client-server client/server |
55 |
|
* |
56 |
|
* Client and server threads are fake HTTP entities used to test your Varnish |
57 |
|
* and VCL. They take any number of arguments, and the one that are not |
58 |
|
* recognized, assuming they don't start with '-', are treated as |
59 |
|
* specifications, laying out the actions to undertake:: |
60 |
|
* |
61 |
|
* client cNAME [...] |
62 |
|
* server sNAME [...] |
63 |
|
* |
64 |
|
* Clients and server are identified by a string that's the first argument, |
65 |
|
* clients' names start with 'c' and servers' names start with 's'. |
66 |
|
* |
67 |
|
* As the client and server commands share a good deal of arguments and |
68 |
|
* specification actions, they are grouped in this single section, specific |
69 |
|
* items will be explicitly marked as such. |
70 |
|
* |
71 |
|
* SECTION: client-server.macros Macros and automatic behaviour |
72 |
|
* |
73 |
|
* To make things easier in the general case, clients will connect by default |
74 |
|
* to a Varnish server called v1. To connect to a different Varnish server, use |
75 |
|
* '-connect ${vNAME_sock}'. |
76 |
|
* |
77 |
|
* The -vcl+backend switch of the ``varnish`` command will add all the declared |
78 |
|
* servers as backends. Be careful though, servers will by default listen to |
79 |
|
* the 127.0.0.1 IP and will pick a random port, and publish 3 macros: |
80 |
|
* sNAME_addr, sNAME_port and sNAME_sock, but only once they are started. For |
81 |
|
* 'varnish -vcl+backend' to create the vcl with the correct values, the server |
82 |
|
* must be started first. |
83 |
|
* |
84 |
|
* SECTION: client-server.args Arguments |
85 |
|
* |
86 |
|
* \-start |
87 |
|
* Start the thread in background, processing the last given |
88 |
|
* specification. |
89 |
|
* |
90 |
|
* \-wait |
91 |
|
* Block until the thread finishes. |
92 |
|
* |
93 |
|
* \-run (client only) |
94 |
|
* Equivalent to "-start -wait". |
95 |
|
* |
96 |
|
* \-repeat NUMBER |
97 |
|
* Instead of processing the specification only once, do it NUMBER times. |
98 |
|
* |
99 |
|
* \-keepalive |
100 |
|
* For repeat, do not open new connections but rather run all |
101 |
|
* iterations in the same connection |
102 |
|
* |
103 |
|
* \-break (server only) |
104 |
|
* Stop the server. |
105 |
|
* |
106 |
|
* \-listen STRING (server only) |
107 |
|
* Dictate the listening socket for the server. STRING is of the form |
108 |
|
* "IP PORT", or "/PATH/TO/SOCKET" for a Unix domain socket. In the |
109 |
|
* latter case, the path must begin with '/', and the server must be |
110 |
|
* able to create it. |
111 |
|
* |
112 |
|
* \-connect STRING (client only) |
113 |
|
* Indicate the server to connect to. STRING is also of the form |
114 |
|
* "IP PORT", or "/PATH/TO/SOCKET". As with "server -listen", a |
115 |
|
* Unix domain socket is recognized when STRING begins with a '/'. |
116 |
|
* |
117 |
|
* \-dispatch (server only, s0 only) |
118 |
|
* Normally, to keep things simple, server threads only handle one |
119 |
|
* connection at a time, but the -dispatch switch allows to accept |
120 |
|
* any number of connection and handle them following the given spec. |
121 |
|
* |
122 |
|
* However, -dispatch is only allowed for the server name "s0". |
123 |
|
* |
124 |
|
* \-proxy1 STRING (client only) |
125 |
|
* Use the PROXY protocol version 1 for this connection. STRING |
126 |
|
* is of the form "CLIENTIP:PORT SERVERIP:PORT". |
127 |
|
* |
128 |
|
* \-proxy2 STRING (client only) |
129 |
|
* Use the PROXY protocol version 2 for this connection. STRING |
130 |
|
* is of the form "CLIENTIP:PORT SERVERIP:PORT [TLV [TLV ... ]]". |
131 |
|
* |
132 |
|
* TLV is in the form name=val |
133 |
|
* |
134 |
|
* name: 0xID or alpn, authority, crc32c, noop, unique_id, netns |
135 |
|
* val: 0x... or string |
136 |
|
* |
137 |
|
* ssl is currently not implemented (can be sent as hex) |
138 |
|
* |
139 |
|
* SECTION: client-server.spec Specification |
140 |
|
* |
141 |
|
* It's a string, either double-quoted "like this", but most of the time |
142 |
|
* enclosed in curly brackets, allowing multilining. Write a command per line in |
143 |
|
* it, empty line are ignored, and long line can be wrapped by using a |
144 |
|
* backslash. For example:: |
145 |
|
* |
146 |
|
* client c1 { |
147 |
|
* txreq -url /foo \ |
148 |
|
* -hdr "bar: baz" |
149 |
|
* |
150 |
|
* rxresp |
151 |
|
* } -run |
152 |
|
*/ |
153 |
|
|
154 |
|
#define ONLY_CLIENT(hp, av) \ |
155 |
|
do { \ |
156 |
|
if (hp->h2) \ |
157 |
|
vtc_fatal(hp->vl, \ |
158 |
|
"\"%s\" only possible before H/2 upgrade", \ |
159 |
|
av[0]); \ |
160 |
|
if (hp->sfd != NULL) \ |
161 |
|
vtc_fatal(hp->vl, \ |
162 |
|
"\"%s\" only possible in client", av[0]); \ |
163 |
|
} while (0) |
164 |
|
|
165 |
|
#define ONLY_SERVER(hp, av) \ |
166 |
|
do { \ |
167 |
|
if (hp->h2) \ |
168 |
|
vtc_fatal(hp->vl, \ |
169 |
|
"\"%s\" only possible before H/2 upgrade", \ |
170 |
|
av[0]); \ |
171 |
|
if (hp->sfd == NULL) \ |
172 |
|
vtc_fatal(hp->vl, \ |
173 |
|
"\"%s\" only possible in server", av[0]); \ |
174 |
|
} while (0) |
175 |
|
|
176 |
|
|
177 |
|
/* XXX: we may want to vary this */ |
178 |
|
static const char * const nl = "\r\n"; |
179 |
|
|
180 |
|
/********************************************************************** |
181 |
|
* Generate a synthetic body |
182 |
|
*/ |
183 |
|
|
184 |
|
char * |
185 |
21320 |
synth_body(const char *len, int rnd) |
186 |
|
{ |
187 |
|
int i, j, k, l; |
188 |
|
char *b; |
189 |
|
|
190 |
|
|
191 |
21320 |
AN(len); |
192 |
21320 |
i = strtoul(len, NULL, 0); |
193 |
21320 |
assert(i > 0); |
194 |
21320 |
b = malloc(i + 1L); |
195 |
21320 |
AN(b); |
196 |
21320 |
l = k = '!'; |
197 |
1442810840 |
for (j = 0; j < i; j++) { |
198 |
1442789520 |
if ((j % 64) == 63) { |
199 |
22530600 |
b[j] = '\n'; |
200 |
22530600 |
k++; |
201 |
22530600 |
if (k == '~') |
202 |
240200 |
k = '!'; |
203 |
22530600 |
l = k; |
204 |
1442789520 |
} else if (rnd) { |
205 |
165120 |
b[j] = (VRND_RandomTestable() % 95) + ' '; |
206 |
165120 |
} else { |
207 |
1420093800 |
b[j] = (char)l; |
208 |
1420093800 |
if (++l == '~') |
209 |
15210520 |
l = '!'; |
210 |
|
} |
211 |
1442789520 |
} |
212 |
21320 |
b[i - 1] = '\n'; |
213 |
21320 |
b[i] = '\0'; |
214 |
21320 |
return (b); |
215 |
|
} |
216 |
|
|
217 |
|
/********************************************************************** |
218 |
|
* Finish and write the vsb to the fd |
219 |
|
*/ |
220 |
|
|
221 |
|
static void |
222 |
225119 |
http_write(const struct http *hp, int lvl, const char *pfx) |
223 |
|
{ |
224 |
|
|
225 |
225119 |
AZ(VSB_finish(hp->vsb)); |
226 |
225119 |
vtc_dump(hp->vl, lvl, pfx, VSB_data(hp->vsb), VSB_len(hp->vsb)); |
227 |
225119 |
if (VSB_tofile(hp->vsb, hp->sess->fd)) |
228 |
532 |
vtc_log(hp->vl, hp->fatal, "Write failed: %s", |
229 |
266 |
strerror(errno)); |
230 |
225119 |
} |
231 |
|
|
232 |
|
/********************************************************************** |
233 |
|
* find header |
234 |
|
*/ |
235 |
|
|
236 |
|
static char * |
237 |
568043 |
http_find_header(char * const *hh, const char *hdr) |
238 |
|
{ |
239 |
|
int n, l; |
240 |
|
char *r; |
241 |
|
|
242 |
568043 |
l = strlen(hdr); |
243 |
|
|
244 |
4141876 |
for (n = 3; hh[n] != NULL; n++) { |
245 |
3820545 |
if (strncasecmp(hdr, hh[n], l) || hh[n][l] != ':') |
246 |
3573833 |
continue; |
247 |
493821 |
for (r = hh[n] + l + 1; vct_issp(*r); r++) |
248 |
247109 |
continue; |
249 |
246712 |
return (r); |
250 |
|
} |
251 |
321331 |
return (NULL); |
252 |
568043 |
} |
253 |
|
|
254 |
|
/********************************************************************** |
255 |
|
* count header |
256 |
|
*/ |
257 |
|
|
258 |
|
static int |
259 |
223108 |
http_count_header(char * const *hh, const char *hdr) |
260 |
|
{ |
261 |
223108 |
int n, l, r = 0; |
262 |
|
|
263 |
223108 |
l = strlen(hdr); |
264 |
|
|
265 |
1783224 |
for (n = 3; hh[n] != NULL; n++) { |
266 |
1560114 |
if (strncasecmp(hdr, hh[n], l) || hh[n][l] != ':') |
267 |
1449395 |
continue; |
268 |
110721 |
r++; |
269 |
110721 |
} |
270 |
223110 |
return (r); |
271 |
|
} |
272 |
|
|
273 |
|
/* SECTION: client-server.spec.expect |
274 |
|
* |
275 |
|
* expect STRING1 OP STRING2 |
276 |
|
* Test if "STRING1 OP STRING2" is true, and if not, fails the test. |
277 |
|
* OP can be ==, <, <=, >, >= when STRING1 and STRING2 represent numbers |
278 |
|
* in which case it's an order operator. If STRING1 and STRING2 are |
279 |
|
* meant as strings OP is a matching operator, either == (exact match) |
280 |
|
* or ~ (regex match). |
281 |
|
* |
282 |
|
* varnishtest will first try to resolve STRING1 and STRING2 by looking |
283 |
|
* if they have special meanings, in which case, the resolved value is |
284 |
|
* use for the test. Note that this value can be a string representing a |
285 |
|
* number, allowing for tests such as:: |
286 |
|
* |
287 |
|
* expect req.http.x-num > 2 |
288 |
|
* |
289 |
|
* Here's the list of recognized strings, most should be obvious as they |
290 |
|
* either match VCL logic, or the txreq/txresp options: |
291 |
|
* |
292 |
|
* - remote.ip |
293 |
|
* - remote.port |
294 |
|
* - remote.path |
295 |
|
* - req.method |
296 |
|
* - req.url |
297 |
|
* - req.proto |
298 |
|
* - resp.proto |
299 |
|
* - resp.status |
300 |
|
* - resp.reason |
301 |
|
* - resp.chunklen |
302 |
|
* - req.bodylen |
303 |
|
* - req.body |
304 |
|
* - resp.bodylen |
305 |
|
* - resp.body |
306 |
|
* - req.http.NAME |
307 |
|
* - resp.http.NAME |
308 |
|
*/ |
309 |
|
|
310 |
|
static const char * |
311 |
539359 |
cmd_var_resolve(struct http *hp, char *spec) |
312 |
|
{ |
313 |
|
char **hh, *hdr; |
314 |
539359 |
if (!strcmp(spec, "remote.ip")) |
315 |
160 |
return (hp->rem_ip); |
316 |
539199 |
if (!strcmp(spec, "remote.port")) |
317 |
160 |
return (hp->rem_port); |
318 |
539039 |
if (!strcmp(spec, "remote.path")) |
319 |
160 |
return (hp->rem_path); |
320 |
538879 |
if (!strcmp(spec, "req.method")) |
321 |
2160 |
return (hp->req[0]); |
322 |
536719 |
if (!strcmp(spec, "req.url")) |
323 |
25480 |
return (hp->req[1]); |
324 |
511239 |
if (!strcmp(spec, "req.proto")) |
325 |
1800 |
return (hp->req[2]); |
326 |
509439 |
if (!strcmp(spec, "resp.proto")) |
327 |
1360 |
return (hp->resp[0]); |
328 |
508079 |
if (!strcmp(spec, "resp.status")) |
329 |
64838 |
return (hp->resp[1]); |
330 |
443241 |
if (!strcmp(spec, "resp.reason")) |
331 |
9080 |
return (hp->resp[2]); |
332 |
434161 |
if (!strcmp(spec, "resp.chunklen")) |
333 |
0 |
return (hp->chunklen); |
334 |
434161 |
if (!strcmp(spec, "req.bodylen")) |
335 |
1160 |
return (hp->bodylen); |
336 |
433001 |
if (!strcmp(spec, "req.body")) |
337 |
800 |
return (hp->body != NULL ? hp->body : spec); |
338 |
432201 |
if (!strcmp(spec, "resp.bodylen")) |
339 |
26078 |
return (hp->bodylen); |
340 |
406123 |
if (!strcmp(spec, "resp.body")) |
341 |
8400 |
return (hp->body != NULL ? hp->body : spec); |
342 |
397723 |
if (!strncmp(spec, "req.http.", 9)) { |
343 |
15120 |
hh = hp->req; |
344 |
15120 |
hdr = spec + 9; |
345 |
397723 |
} else if (!strncmp(spec, "resp.http.", 10)) { |
346 |
120314 |
hh = hp->resp; |
347 |
120314 |
hdr = spec + 10; |
348 |
382603 |
} else if (!strcmp(spec, "h2.state")) { |
349 |
291 |
if (hp->h2) |
350 |
131 |
return ("true"); |
351 |
|
else |
352 |
160 |
return ("false"); |
353 |
|
} else |
354 |
261998 |
return (spec); |
355 |
135434 |
hdr = http_find_header(hh, hdr); |
356 |
135434 |
return (hdr); |
357 |
539359 |
} |
358 |
|
|
359 |
|
static void |
360 |
269676 |
cmd_http_expect(CMD_ARGS) |
361 |
|
{ |
362 |
|
struct http *hp; |
363 |
|
const char *lhs; |
364 |
|
char *cmp; |
365 |
|
const char *rhs; |
366 |
|
|
367 |
269676 |
(void)vl; |
368 |
269676 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
369 |
269676 |
AZ(strcmp(av[0], "expect")); |
370 |
269676 |
av++; |
371 |
|
|
372 |
269676 |
AN(av[0]); |
373 |
269676 |
AN(av[1]); |
374 |
269676 |
AN(av[2]); |
375 |
269676 |
AZ(av[3]); |
376 |
269676 |
lhs = cmd_var_resolve(hp, av[0]); |
377 |
269676 |
cmp = av[1]; |
378 |
269676 |
rhs = cmd_var_resolve(hp, av[2]); |
379 |
|
|
380 |
269676 |
vtc_expect(vl, av[0], lhs, cmp, av[2], rhs); |
381 |
269676 |
} |
382 |
|
|
383 |
|
/* SECTION: client-server.spec.expect_pattern |
384 |
|
* |
385 |
|
* expect_pattern |
386 |
|
* |
387 |
|
* Expect as the http body the test pattern generated by chunkedlen ('0'..'7' |
388 |
|
* repeating). |
389 |
|
*/ |
390 |
|
static void |
391 |
160 |
cmd_http_expect_pattern(CMD_ARGS) |
392 |
|
{ |
393 |
|
char *p; |
394 |
|
struct http *hp; |
395 |
160 |
char t = '0'; |
396 |
|
|
397 |
160 |
(void)vl; |
398 |
160 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
399 |
160 |
AZ(strcmp(av[0], "expect_pattern")); |
400 |
160 |
av++; |
401 |
160 |
AZ(av[0]); |
402 |
10485920 |
for (p = hp->body; *p != '\0'; p++) { |
403 |
10485760 |
if (*p != t) |
404 |
0 |
vtc_fatal(hp->vl, |
405 |
|
"EXPECT PATTERN FAIL @%zd should 0x%02x is 0x%02x", |
406 |
0 |
(ssize_t) (p - hp->body), t, *p); |
407 |
10485760 |
t += 1; |
408 |
10485760 |
t &= ~0x08; |
409 |
10485760 |
} |
410 |
160 |
vtc_log(hp->vl, 4, "EXPECT PATTERN SUCCESS"); |
411 |
160 |
} |
412 |
|
|
413 |
|
/********************************************************************** |
414 |
|
* Split a HTTP protocol header |
415 |
|
*/ |
416 |
|
|
417 |
|
static void |
418 |
223107 |
http_splitheader(struct http *hp, int req) |
419 |
|
{ |
420 |
|
char *p, *q, **hh; |
421 |
|
int n; |
422 |
|
char buf[20]; |
423 |
|
|
424 |
223107 |
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); |
425 |
223107 |
if (req) { |
426 |
93387 |
memset(hp->req, 0, sizeof hp->req); |
427 |
93387 |
hh = hp->req; |
428 |
93387 |
} else { |
429 |
129720 |
memset(hp->resp, 0, sizeof hp->resp); |
430 |
129720 |
hh = hp->resp; |
431 |
|
} |
432 |
|
|
433 |
223107 |
n = 0; |
434 |
223107 |
p = hp->rx_b; |
435 |
223107 |
if (*p == '\0') { |
436 |
3200 |
vtc_log(hp->vl, 4, "No headers"); |
437 |
3200 |
return; |
438 |
|
} |
439 |
|
|
440 |
|
/* REQ/PROTO */ |
441 |
219947 |
while (vct_islws(*p)) |
442 |
40 |
p++; |
443 |
219907 |
hh[n++] = p; |
444 |
1520257 |
while (!vct_islws(*p)) |
445 |
1300350 |
p++; |
446 |
219907 |
AZ(vct_iscrlf(p, hp->rx_e)); |
447 |
219907 |
*p++ = '\0'; |
448 |
|
|
449 |
|
/* URL/STATUS */ |
450 |
219907 |
while (vct_issp(*p)) /* XXX: H space only */ |
451 |
0 |
p++; |
452 |
219907 |
AZ(vct_iscrlf(p, hp->rx_e)); |
453 |
219907 |
hh[n++] = p; |
454 |
1080741 |
while (!vct_islws(*p)) |
455 |
860834 |
p++; |
456 |
219907 |
if (vct_iscrlf(p, hp->rx_e)) { |
457 |
40 |
hh[n++] = NULL; |
458 |
40 |
q = p; |
459 |
40 |
p = vct_skipcrlf(p, hp->rx_e); |
460 |
40 |
*q = '\0'; |
461 |
40 |
} else { |
462 |
219867 |
*p++ = '\0'; |
463 |
|
/* PROTO/MSG */ |
464 |
219867 |
while (vct_issp(*p)) /* XXX: H space only */ |
465 |
0 |
p++; |
466 |
219867 |
hh[n++] = p; |
467 |
1709589 |
while (!vct_iscrlf(p, hp->rx_e)) |
468 |
1489722 |
p++; |
469 |
219867 |
q = p; |
470 |
219867 |
p = vct_skipcrlf(p, hp->rx_e); |
471 |
219867 |
*q = '\0'; |
472 |
|
} |
473 |
219907 |
assert(n == 3); |
474 |
|
|
475 |
1779870 |
while (*p != '\0') { |
476 |
1780211 |
assert(n < MAX_HDR); |
477 |
1780211 |
if (vct_iscrlf(p, hp->rx_e)) |
478 |
219904 |
break; |
479 |
1560307 |
hh[n++] = p++; |
480 |
34122322 |
while (*p != '\0' && !vct_iscrlf(p, hp->rx_e)) |
481 |
32562015 |
p++; |
482 |
1559963 |
if (*p == '\0') { |
483 |
0 |
break; |
484 |
|
} |
485 |
1559963 |
q = p; |
486 |
1559963 |
p = vct_skipcrlf(p, hp->rx_e); |
487 |
1559963 |
*q = '\0'; |
488 |
|
} |
489 |
220253 |
p = vct_skipcrlf(p, hp->rx_e); |
490 |
220253 |
assert(*p == '\0'); |
491 |
|
|
492 |
2440106 |
for (n = 0; n < 3 || hh[n] != NULL; n++) { |
493 |
2219853 |
bprintf(buf, "http[%2d] ", n); |
494 |
2219853 |
vtc_dump(hp->vl, 4, buf, hh[n], -1); |
495 |
2219853 |
} |
496 |
223453 |
} |
497 |
|
|
498 |
|
|
499 |
|
/********************************************************************** |
500 |
|
* Receive another character |
501 |
|
*/ |
502 |
|
|
503 |
|
static int |
504 |
50568659 |
http_rxchar(struct http *hp, int n, int eof) |
505 |
|
{ |
506 |
|
int i; |
507 |
|
struct pollfd pfd[1]; |
508 |
|
|
509 |
101185510 |
while (n > 0) { |
510 |
50621011 |
pfd[0].fd = hp->sess->fd; |
511 |
50621011 |
pfd[0].events = POLLIN; |
512 |
50621011 |
pfd[0].revents = 0; |
513 |
50621011 |
i = poll(pfd, 1, (int)(hp->timeout * 1000)); |
514 |
50621011 |
if (i < 0 && errno == EINTR) |
515 |
0 |
continue; |
516 |
50621011 |
if (i == 0) { |
517 |
0 |
vtc_log(hp->vl, hp->fatal, |
518 |
|
"HTTP rx timeout (fd:%d %.3fs)", |
519 |
0 |
hp->sess->fd, hp->timeout); |
520 |
0 |
continue; |
521 |
|
} |
522 |
50621011 |
if (i < 0) { |
523 |
0 |
vtc_log(hp->vl, hp->fatal, |
524 |
|
"HTTP rx failed (fd:%d poll: %s)", |
525 |
0 |
hp->sess->fd, strerror(errno)); |
526 |
0 |
continue; |
527 |
|
} |
528 |
50621011 |
assert(i > 0); |
529 |
50621011 |
assert(hp->rx_p + n < hp->rx_e); |
530 |
50621011 |
i = read(hp->sess->fd, hp->rx_p, n); |
531 |
50621011 |
if (!(pfd[0].revents & POLLIN)) |
532 |
0 |
vtc_log(hp->vl, 4, |
533 |
|
"HTTP rx poll (fd:%d revents: %x n=%d, i=%d)", |
534 |
0 |
hp->sess->fd, pfd[0].revents, n, i); |
535 |
50621011 |
if (i == 0 && eof) |
536 |
3400 |
return (i); |
537 |
50617611 |
if (i == 0) { |
538 |
1520 |
vtc_log(hp->vl, hp->fatal, |
539 |
|
"HTTP rx EOF (fd:%d read: %s) %d", |
540 |
760 |
hp->sess->fd, strerror(errno), n); |
541 |
760 |
return (-1); |
542 |
|
} |
543 |
50616851 |
if (i < 0) { |
544 |
0 |
vtc_log(hp->vl, hp->fatal, |
545 |
|
"HTTP rx failed (fd:%d read: %s)", |
546 |
0 |
hp->sess->fd, strerror(errno)); |
547 |
0 |
return (-1); |
548 |
|
} |
549 |
50616851 |
hp->rx_p += i; |
550 |
50616851 |
*hp->rx_p = '\0'; |
551 |
50616851 |
n -= i; |
552 |
|
} |
553 |
50564499 |
return (1); |
554 |
50568659 |
} |
555 |
|
|
556 |
|
static int |
557 |
53814 |
http_rxchunk(struct http *hp) |
558 |
|
{ |
559 |
|
char *q, *old; |
560 |
|
int i; |
561 |
|
|
562 |
53814 |
old = hp->rx_p; |
563 |
53814 |
do { |
564 |
271553 |
if (http_rxchar(hp, 1, 0) < 0) |
565 |
410 |
return (-1); |
566 |
271143 |
} while (hp->rx_p[-1] != '\n'); |
567 |
53404 |
vtc_dump(hp->vl, 4, "len", old, -1); |
568 |
53404 |
i = strtoul(old, &q, 16); |
569 |
53404 |
bprintf(hp->chunklen, "%d", i); |
570 |
53404 |
if ((q == old) || (q == hp->rx_p) || (*q != '\0' && !vct_islws(*q))) { |
571 |
8 |
vtc_log(hp->vl, hp->fatal, "Chunklen fail (%02x @ %td)", |
572 |
4 |
(*q & 0xff), q - old); |
573 |
4 |
return (-1); |
574 |
|
} |
575 |
53404 |
assert(*q == '\0' || vct_islws(*q)); |
576 |
53404 |
hp->rx_p = old; |
577 |
53404 |
if (i > 0) { |
578 |
43005 |
if (http_rxchar(hp, i, 0) < 0) |
579 |
70 |
return (-1); |
580 |
42935 |
vtc_dump(hp->vl, 4, "chunk", old, i); |
581 |
42935 |
} |
582 |
53334 |
old = hp->rx_p; |
583 |
53334 |
if (http_rxchar(hp, 2, 0) < 0) |
584 |
0 |
return (-1); |
585 |
53334 |
if (!vct_iscrlf(old, hp->rx_e)) { |
586 |
0 |
vtc_log(hp->vl, hp->fatal, "Chunklen without CRLF"); |
587 |
0 |
return (-1); |
588 |
|
} |
589 |
53334 |
hp->rx_p = old; |
590 |
53334 |
*hp->rx_p = '\0'; |
591 |
53334 |
return (i); |
592 |
53814 |
} |
593 |
|
|
594 |
|
/********************************************************************** |
595 |
|
* Swallow a HTTP message body |
596 |
|
* |
597 |
|
* max: 0 is all |
598 |
|
*/ |
599 |
|
|
600 |
|
static void |
601 |
216179 |
http_swallow_body(struct http *hp, char * const *hh, int body, int max) |
602 |
|
{ |
603 |
|
const char *p, *q; |
604 |
|
int i, l, ll; |
605 |
|
|
606 |
216179 |
l = hp->rx_p - hp->body; |
607 |
|
|
608 |
216179 |
p = http_find_header(hh, "transfer-encoding"); |
609 |
216179 |
q = http_find_header(hh, "content-length"); |
610 |
216179 |
if (p != NULL && !strcasecmp(p, "chunked")) { |
611 |
10996 |
if (q != NULL) { |
612 |
0 |
vtc_log(hp->vl, hp->fatal, "Both C-E: Chunked and C-L"); |
613 |
0 |
return; |
614 |
|
} |
615 |
10996 |
ll = 0; |
616 |
53292 |
while (http_rxchunk(hp) > 0) { |
617 |
42536 |
ll = (hp->rx_p - hp->body) - l; |
618 |
42536 |
if (max && ll >= max) |
619 |
240 |
break; |
620 |
|
} |
621 |
10996 |
p = "chunked"; |
622 |
216179 |
} else if (q != NULL) { |
623 |
108676 |
ll = strtoul(q, NULL, 10); |
624 |
108676 |
if (max && ll > l + max) |
625 |
80 |
ll = max; |
626 |
|
else |
627 |
108596 |
ll -= l; |
628 |
108676 |
i = http_rxchar(hp, ll, 0); |
629 |
108676 |
if (i < 0) |
630 |
280 |
return; |
631 |
108396 |
p = "c-l"; |
632 |
204903 |
} else if (body) { |
633 |
280 |
ll = 0; |
634 |
280 |
do { |
635 |
7869000 |
i = http_rxchar(hp, 1, 1); |
636 |
7869000 |
if (i < 0) |
637 |
0 |
return; |
638 |
7869000 |
ll += i; |
639 |
7869000 |
if (max && ll >= max) |
640 |
80 |
break; |
641 |
7868920 |
} while (i > 0); |
642 |
280 |
p = "eof"; |
643 |
280 |
} else { |
644 |
96227 |
p = "none"; |
645 |
96227 |
ll = l = 0; |
646 |
|
} |
647 |
215899 |
vtc_dump(hp->vl, 4, p, hp->body + l, ll); |
648 |
215899 |
l += ll; |
649 |
215899 |
hp->bodyl = l; |
650 |
215899 |
bprintf(hp->bodylen, "%d", l); |
651 |
216179 |
} |
652 |
|
|
653 |
|
/********************************************************************** |
654 |
|
* Receive a HTTP protocol header |
655 |
|
*/ |
656 |
|
|
657 |
|
static void |
658 |
223108 |
http_rxhdr(struct http *hp) |
659 |
|
{ |
660 |
223108 |
int i, s = 0; |
661 |
|
char *p; |
662 |
|
ssize_t l; |
663 |
|
|
664 |
223108 |
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); |
665 |
223108 |
hp->rx_p = hp->rx_b; |
666 |
223108 |
*hp->rx_p = '\0'; |
667 |
223108 |
hp->body = NULL; |
668 |
223108 |
bprintf(hp->bodylen, "%s", "<undef>"); |
669 |
42214710 |
while (1) { |
670 |
42214710 |
p = hp->rx_p; |
671 |
42214710 |
i = http_rxchar(hp, 1, 1); |
672 |
42214710 |
if (i < 1) |
673 |
3200 |
break; |
674 |
42211510 |
if (s == 0 && *p == '\r') |
675 |
1779900 |
s = 1; |
676 |
40431610 |
else if ((s == 0 || s == 1) && *p == '\n') |
677 |
1780054 |
s = 2; |
678 |
38651556 |
else if (s == 2 && *p == '\r') |
679 |
219868 |
s = 3; |
680 |
38431688 |
else if ((s == 2 || s == 3) && *p == '\n') |
681 |
219908 |
break; |
682 |
|
else |
683 |
38211780 |
s = 0; |
684 |
|
} |
685 |
223108 |
l = hp->rx_p - hp->rx_b; |
686 |
223108 |
vtc_dump(hp->vl, 4, "rxhdr", hp->rx_b, l); |
687 |
223108 |
vtc_log(hp->vl, 4, "rxhdrlen = %zd", l); |
688 |
223108 |
if (i < 1) |
689 |
3200 |
vtc_log(hp->vl, hp->fatal, "HTTP header is incomplete"); |
690 |
223108 |
*hp->rx_p = '\0'; |
691 |
223108 |
hp->body = hp->rx_p; |
692 |
223108 |
} |
693 |
|
|
694 |
|
/* SECTION: client-server.spec.rxresp |
695 |
|
* |
696 |
|
* rxresp [-no_obj] (client only) |
697 |
|
* Receive and parse a response's headers and body. If -no_obj is |
698 |
|
* present, only get the headers. |
699 |
|
*/ |
700 |
|
|
701 |
|
static void |
702 |
127878 |
cmd_http_rxresp(CMD_ARGS) |
703 |
|
{ |
704 |
|
struct http *hp; |
705 |
127878 |
int has_obj = 1; |
706 |
|
|
707 |
127878 |
(void)vl; |
708 |
127878 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
709 |
127878 |
ONLY_CLIENT(hp, av); |
710 |
127878 |
AZ(strcmp(av[0], "rxresp")); |
711 |
127878 |
av++; |
712 |
|
|
713 |
128918 |
for (; *av != NULL; av++) |
714 |
2080 |
if (!strcmp(*av, "-no_obj")) |
715 |
1040 |
has_obj = 0; |
716 |
|
else |
717 |
0 |
vtc_fatal(hp->vl, |
718 |
0 |
"Unknown http rxresp spec: %s\n", *av); |
719 |
127878 |
http_rxhdr(hp); |
720 |
127878 |
http_splitheader(hp, 0); |
721 |
127878 |
if (http_count_header(hp->resp, "Content-Length") > 1) |
722 |
0 |
vtc_fatal(hp->vl, |
723 |
|
"Multiple Content-Length headers.\n"); |
724 |
127878 |
if (!has_obj) |
725 |
1040 |
return; |
726 |
126838 |
if (!hp->resp[0] || !hp->resp[1]) |
727 |
2520 |
return; |
728 |
124320 |
if (hp->head_method) |
729 |
2800 |
return; |
730 |
121520 |
if (!strcmp(hp->resp[1], "200")) |
731 |
91400 |
http_swallow_body(hp, hp->resp, 1, 0); |
732 |
|
else |
733 |
30120 |
http_swallow_body(hp, hp->resp, 0, 0); |
734 |
121520 |
vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); |
735 |
127880 |
} |
736 |
|
|
737 |
|
/* SECTION: client-server.spec.rxresphdrs |
738 |
|
* |
739 |
|
* rxresphdrs (client only) |
740 |
|
* Receive and parse a response's headers. |
741 |
|
*/ |
742 |
|
|
743 |
|
static void |
744 |
1840 |
cmd_http_rxresphdrs(CMD_ARGS) |
745 |
|
{ |
746 |
|
struct http *hp; |
747 |
|
|
748 |
1840 |
(void)vl; |
749 |
1840 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
750 |
1840 |
ONLY_CLIENT(hp, av); |
751 |
1840 |
AZ(strcmp(av[0], "rxresphdrs")); |
752 |
1840 |
av++; |
753 |
|
|
754 |
1840 |
for (; *av != NULL; av++) |
755 |
0 |
vtc_fatal(hp->vl, "Unknown http rxresp spec: %s\n", *av); |
756 |
1840 |
http_rxhdr(hp); |
757 |
1840 |
http_splitheader(hp, 0); |
758 |
1840 |
if (http_count_header(hp->resp, "Content-Length") > 1) |
759 |
0 |
vtc_fatal(hp->vl, |
760 |
|
"Multiple Content-Length headers.\n"); |
761 |
1840 |
} |
762 |
|
|
763 |
|
/* SECTION: client-server.spec.gunzip |
764 |
|
* |
765 |
|
* gunzip |
766 |
|
* Gunzip the body in place. |
767 |
|
*/ |
768 |
|
static void |
769 |
1480 |
cmd_http_gunzip(CMD_ARGS) |
770 |
|
{ |
771 |
|
struct http *hp; |
772 |
|
|
773 |
1480 |
(void)av; |
774 |
1480 |
(void)vl; |
775 |
|
|
776 |
1480 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
777 |
1480 |
vtc_gunzip(hp, hp->body, &hp->bodyl); |
778 |
1480 |
} |
779 |
|
|
780 |
|
/********************************************************************** |
781 |
|
* Handle common arguments of a transmitted request or response |
782 |
|
*/ |
783 |
|
|
784 |
|
static char* const * |
785 |
215307 |
http_tx_parse_args(char * const *av, struct vtclog *vl, struct http *hp, |
786 |
|
char *body, unsigned nohost, unsigned nodate, unsigned noserver, unsigned nouseragent) |
787 |
|
{ |
788 |
215307 |
long bodylen = 0; |
789 |
|
char *b, *c; |
790 |
|
char *nullbody; |
791 |
|
ssize_t len; |
792 |
215307 |
int nolen = 0; |
793 |
|
int l; |
794 |
|
|
795 |
215307 |
nullbody = body; |
796 |
|
|
797 |
304750 |
for (; *av != NULL; av++) { |
798 |
135161 |
if (!strcmp(*av, "-nolen")) { |
799 |
5320 |
nolen = 1; |
800 |
135161 |
} else if (!strcmp(*av, "-nohost")) { |
801 |
80 |
nohost = 1; |
802 |
129841 |
} else if (!strcmp(*av, "-nodate")) { |
803 |
320 |
nodate = 1; |
804 |
129761 |
} else if (!strcmp(*av, "-hdr")) { |
805 |
83563 |
if (!strncasecmp(av[1], "Content-Length:", 15) || |
806 |
81005 |
!strncasecmp(av[1], "Transfer-Encoding:", 18)) |
807 |
5959 |
nolen = 1; |
808 |
83563 |
if (!strncasecmp(av[1], "Host:", 5)) |
809 |
1440 |
nohost = 1; |
810 |
83563 |
if (!strncasecmp(av[1], "Date:", 5)) |
811 |
320 |
nodate = 1; |
812 |
83563 |
if (!strncasecmp(av[1], "Server:", 7)) |
813 |
600 |
noserver = 1; |
814 |
83563 |
if (!strncasecmp(av[1], "User-Agent:", 11)) |
815 |
80 |
nouseragent = 1; |
816 |
83563 |
VSB_printf(hp->vsb, "%s%s", av[1], nl); |
817 |
83563 |
av++; |
818 |
129441 |
} else if (!strcmp(*av, "-hdrlen")) { |
819 |
160 |
VSB_printf(hp->vsb, "%s: ", av[1]); |
820 |
160 |
l = atoi(av[2]); |
821 |
30160 |
while (l-- > 0) |
822 |
30000 |
VSB_putc(hp->vsb, '0' + (l % 10)); |
823 |
160 |
VSB_printf(hp->vsb, "%s", nl); |
824 |
160 |
av+=2; |
825 |
160 |
} else |
826 |
45718 |
break; |
827 |
89443 |
} |
828 |
261666 |
for (; *av != NULL; av++) { |
829 |
46359 |
if (!strcmp(*av, "-body")) { |
830 |
30239 |
assert(body == nullbody); |
831 |
30239 |
REPLACE(body, av[1]); |
832 |
|
|
833 |
30239 |
AN(body); |
834 |
30239 |
av++; |
835 |
30239 |
bodylen = strlen(body); |
836 |
928915 |
for (b = body; *b != '\0'; b++) { |
837 |
898676 |
if (*b == '\\' && b[1] == '0') { |
838 |
160 |
*b = '\0'; |
839 |
1640 |
for (c = b+1; *c != '\0'; c++) { |
840 |
1480 |
*c = c[1]; |
841 |
1480 |
} |
842 |
160 |
b++; |
843 |
160 |
bodylen--; |
844 |
160 |
} |
845 |
898676 |
} |
846 |
46359 |
} else if (!strcmp(*av, "-bodyfrom")) { |
847 |
80 |
assert(body == nullbody); |
848 |
80 |
free(body); |
849 |
80 |
body = VFIL_readfile(NULL, av[1], &len); |
850 |
80 |
AN(body); |
851 |
80 |
assert(len < INT_MAX); |
852 |
80 |
bodylen = len; |
853 |
80 |
av++; |
854 |
16120 |
} else if (!strcmp(*av, "-bodylen")) { |
855 |
8880 |
assert(body == nullbody); |
856 |
8880 |
free(body); |
857 |
8880 |
body = synth_body(av[1], 0); |
858 |
8880 |
bodylen = strlen(body); |
859 |
8880 |
av++; |
860 |
16040 |
} else if (!strncmp(*av, "-gzip", 5)) { |
861 |
7160 |
l = vtc_gzip_cmd(hp, av, &body, &bodylen); |
862 |
7160 |
if (l == 0) |
863 |
0 |
break; |
864 |
7160 |
av += l; |
865 |
7160 |
if (l > 1) |
866 |
6520 |
VSB_printf(hp->vsb, "Content-Encoding: gzip%s", nl); |
867 |
7160 |
} else |
868 |
0 |
break; |
869 |
46359 |
} |
870 |
215307 |
if (!nohost) { |
871 |
124911 |
VSB_cat(hp->vsb, "Host: "); |
872 |
124911 |
macro_cat(vl, hp->vsb, "localhost", NULL); |
873 |
124911 |
VSB_cat(hp->vsb, nl); |
874 |
124911 |
} |
875 |
215307 |
if (!nodate) { |
876 |
86528 |
VSB_cat(hp->vsb, "Date: "); |
877 |
86528 |
macro_cat(vl, hp->vsb, "date", NULL); |
878 |
86528 |
VSB_cat(hp->vsb, nl); |
879 |
86528 |
} |
880 |
215307 |
if (!noserver) |
881 |
86011 |
VSB_printf(hp->vsb, "Server: %s%s", hp->sess->name, nl); |
882 |
215307 |
if (!nouseragent) |
883 |
127916 |
VSB_printf(hp->vsb, "User-Agent: %s%s", hp->sess->name, nl); |
884 |
215307 |
if (body != NULL && !nolen) |
885 |
84571 |
VSB_printf(hp->vsb, "Content-Length: %ld%s", bodylen, nl); |
886 |
215307 |
VSB_cat(hp->vsb, nl); |
887 |
215307 |
if (body != NULL) { |
888 |
89771 |
VSB_bcat(hp->vsb, body, bodylen); |
889 |
89771 |
free(body); |
890 |
89771 |
} |
891 |
215307 |
return (av); |
892 |
|
} |
893 |
|
|
894 |
|
/* SECTION: client-server.spec.txreq |
895 |
|
* |
896 |
|
* txreq|txresp [...] |
897 |
|
* Send a minimal request or response, but overload it if necessary. |
898 |
|
* |
899 |
|
* txreq is client-specific and txresp is server-specific. |
900 |
|
* |
901 |
|
* The only thing different between a request and a response, apart |
902 |
|
* from who can send them is that the first line (request line vs |
903 |
|
* status line), so all the options are pretty much the same. |
904 |
|
* |
905 |
|
* \-method STRING (txreq only) |
906 |
|
* What method to use (default: "GET"). |
907 |
|
* |
908 |
|
* \-req STRING (txreq only) |
909 |
|
* Alias for -method. |
910 |
|
* |
911 |
|
* \-url STRING (txreq only) |
912 |
|
* What location to use (default "/"). |
913 |
|
* |
914 |
|
* \-proto STRING |
915 |
|
* What protocol use in the status line. |
916 |
|
* (default: "HTTP/1.1"). |
917 |
|
* |
918 |
|
* \-status NUMBER (txresp only) |
919 |
|
* What status code to return (default 200). |
920 |
|
* |
921 |
|
* \-reason STRING (txresp only) |
922 |
|
* What message to put in the status line (default: "OK"). |
923 |
|
* |
924 |
|
* \-noserver (txresp only) |
925 |
|
* Don't include a Server header with the id of the server. |
926 |
|
* |
927 |
|
* \-nouseragent (txreq only) |
928 |
|
* Don't include a User-Agent header with the id of the client. |
929 |
|
* |
930 |
|
* These three switches can appear in any order but must come before the |
931 |
|
* following ones. |
932 |
|
* |
933 |
|
* \-nohost |
934 |
|
* Don't include a Host header in the request. Also Implied |
935 |
|
* by the addition of a Host header with ``-hdr``. |
936 |
|
* |
937 |
|
* \-nolen |
938 |
|
* Don't include a Content-Length header. Also implied by the |
939 |
|
* addition of a Content-Length or Transfer-Encoding header |
940 |
|
* with ``-hdr``. |
941 |
|
* |
942 |
|
* \-nodate |
943 |
|
* Don't include a Date header in the response. Also implied |
944 |
|
* by the addition of a Date header with ``-hdr``. |
945 |
|
* |
946 |
|
* \-hdr STRING |
947 |
|
* Add STRING as a header, it must follow this format: |
948 |
|
* "name: value". It can be called multiple times. |
949 |
|
* |
950 |
|
* \-hdrlen STRING NUMBER |
951 |
|
* Add STRING as a header with NUMBER bytes of content. |
952 |
|
* |
953 |
|
* You can then use the arguments related to the body: |
954 |
|
* |
955 |
|
* \-body STRING |
956 |
|
* Input STRING as body. |
957 |
|
* |
958 |
|
* \-bodyfrom FILE |
959 |
|
* Same as -body but content is read from FILE. |
960 |
|
* |
961 |
|
* \-bodylen NUMBER |
962 |
|
* Generate and input a body that is NUMBER bytes-long. |
963 |
|
* |
964 |
|
* \-gziplevel NUMBER |
965 |
|
* Set the gzip level (call it before any of the other gzip |
966 |
|
* switches). |
967 |
|
* |
968 |
|
* \-gzipresidual NUMBER |
969 |
|
* Add extra gzip bits. You should never need it. |
970 |
|
* |
971 |
|
* \-gzipbody STRING |
972 |
|
* Gzip STRING and send it as body. |
973 |
|
* |
974 |
|
* \-gziplen NUMBER |
975 |
|
* Combine -bodylen and -gzipbody: generate a string of length |
976 |
|
* NUMBER, gzip it and send as body. |
977 |
|
*/ |
978 |
|
|
979 |
|
/********************************************************************** |
980 |
|
* Transmit a response |
981 |
|
*/ |
982 |
|
|
983 |
|
static void |
984 |
87165 |
cmd_http_txresp(CMD_ARGS) |
985 |
|
{ |
986 |
|
struct http *hp; |
987 |
87165 |
const char *proto = "HTTP/1.1"; |
988 |
87165 |
const char *status = "200"; |
989 |
87165 |
const char *reason = "OK"; |
990 |
87165 |
char* body = NULL; |
991 |
87165 |
unsigned noserver = 0; |
992 |
|
|
993 |
87165 |
(void)vl; |
994 |
87165 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
995 |
87165 |
ONLY_SERVER(hp, av); |
996 |
87165 |
AZ(strcmp(av[0], "txresp")); |
997 |
87165 |
av++; |
998 |
|
|
999 |
87165 |
VSB_clear(hp->vsb); |
1000 |
|
|
1001 |
96885 |
for (; *av != NULL; av++) { |
1002 |
66837 |
if (!strcmp(*av, "-proto")) { |
1003 |
1640 |
proto = av[1]; |
1004 |
1640 |
av++; |
1005 |
66837 |
} else if (!strcmp(*av, "-status")) { |
1006 |
6400 |
status = av[1]; |
1007 |
6400 |
av++; |
1008 |
65197 |
} else if (!strcmp(*av, "-reason")) { |
1009 |
1120 |
reason = av[1]; |
1010 |
1120 |
av++; |
1011 |
1120 |
continue; |
1012 |
57677 |
} else if (!strcmp(*av, "-noserver")) { |
1013 |
560 |
noserver = 1; |
1014 |
560 |
continue; |
1015 |
|
} else |
1016 |
57117 |
break; |
1017 |
8040 |
} |
1018 |
|
|
1019 |
87165 |
VSB_printf(hp->vsb, "%s %s %s%s", proto, status, reason, nl); |
1020 |
|
|
1021 |
|
/* send a "Content-Length: 0" header unless something else happens */ |
1022 |
87165 |
REPLACE(body, ""); |
1023 |
|
|
1024 |
87165 |
av = http_tx_parse_args(av, vl, hp, body, 1, 0, noserver, 1); |
1025 |
87165 |
if (*av != NULL) |
1026 |
0 |
vtc_fatal(hp->vl, "Unknown http txresp spec: %s\n", *av); |
1027 |
|
|
1028 |
87165 |
http_write(hp, 4, "txresp"); |
1029 |
87165 |
} |
1030 |
|
|
1031 |
|
static void |
1032 |
80 |
cmd_http_upgrade(CMD_ARGS) |
1033 |
|
{ |
1034 |
|
char *h; |
1035 |
|
struct http *hp; |
1036 |
|
|
1037 |
80 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1038 |
80 |
ONLY_SERVER(hp, av); |
1039 |
80 |
AN(hp->sfd); |
1040 |
|
|
1041 |
80 |
h = http_find_header(hp->req, "Upgrade"); |
1042 |
80 |
if (!h || strcmp(h, "h2c")) |
1043 |
0 |
vtc_fatal(vl, "Req misses \"Upgrade: h2c\" header"); |
1044 |
|
|
1045 |
80 |
h = http_find_header(hp->req, "Connection"); |
1046 |
80 |
if (!h || strcmp(h, "Upgrade, HTTP2-Settings")) |
1047 |
0 |
vtc_fatal(vl, "Req misses \"Connection: " |
1048 |
|
"Upgrade, HTTP2-Settings\" header"); |
1049 |
|
|
1050 |
80 |
h = http_find_header(hp->req, "HTTP2-Settings"); |
1051 |
80 |
if (!h) |
1052 |
0 |
vtc_fatal(vl, "Req misses \"HTTP2-Settings\" header"); |
1053 |
|
|
1054 |
80 |
parse_string(vl, hp, |
1055 |
|
"txresp -status 101" |
1056 |
|
" -hdr \"Connection: Upgrade\"" |
1057 |
|
" -hdr \"Upgrade: h2c\"\n" |
1058 |
|
); |
1059 |
|
|
1060 |
80 |
parse_string(vl, hp, |
1061 |
|
"rxpri\n" |
1062 |
|
"stream 0 {\n" |
1063 |
|
" txsettings\n" |
1064 |
|
" rxsettings\n" |
1065 |
|
" txsettings -ack\n" |
1066 |
|
" rxsettings\n" |
1067 |
|
" expect settings.ack == true\n" |
1068 |
|
"} -start\n" |
1069 |
|
); |
1070 |
|
|
1071 |
80 |
b64_settings(hp, h); |
1072 |
|
|
1073 |
80 |
} |
1074 |
|
|
1075 |
|
/********************************************************************** |
1076 |
|
* Receive a request |
1077 |
|
*/ |
1078 |
|
|
1079 |
|
/* SECTION: client-server.spec.rxreq |
1080 |
|
* |
1081 |
|
* rxreq (server only) |
1082 |
|
* Receive and parse a request's headers and body. |
1083 |
|
*/ |
1084 |
|
static void |
1085 |
93860 |
cmd_http_rxreq(CMD_ARGS) |
1086 |
|
{ |
1087 |
|
struct http *hp; |
1088 |
|
|
1089 |
93860 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1090 |
93860 |
ONLY_SERVER(hp, av); |
1091 |
93860 |
AZ(strcmp(av[0], "rxreq")); |
1092 |
93860 |
av++; |
1093 |
|
|
1094 |
93860 |
for (; *av != NULL; av++) |
1095 |
0 |
vtc_fatal(vl, "Unknown http rxreq spec: %s\n", *av); |
1096 |
93860 |
http_rxhdr(hp); |
1097 |
93860 |
http_splitheader(hp, 1); |
1098 |
93860 |
if (http_count_header(hp->req, "Content-Length") > 1) |
1099 |
0 |
vtc_fatal(vl, "Multiple Content-Length headers.\n"); |
1100 |
93860 |
http_swallow_body(hp, hp->req, 0, 0); |
1101 |
93860 |
vtc_log(vl, 4, "bodylen = %s", hp->bodylen); |
1102 |
93860 |
} |
1103 |
|
|
1104 |
|
/* SECTION: client-server.spec.rxreqhdrs |
1105 |
|
* |
1106 |
|
* rxreqhdrs (server only) |
1107 |
|
* Receive and parse a request's headers (but not the body). |
1108 |
|
*/ |
1109 |
|
|
1110 |
|
static void |
1111 |
200 |
cmd_http_rxreqhdrs(CMD_ARGS) |
1112 |
|
{ |
1113 |
|
struct http *hp; |
1114 |
|
|
1115 |
200 |
(void)vl; |
1116 |
200 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1117 |
200 |
AZ(strcmp(av[0], "rxreqhdrs")); |
1118 |
200 |
av++; |
1119 |
|
|
1120 |
200 |
for (; *av != NULL; av++) |
1121 |
0 |
vtc_fatal(hp->vl, "Unknown http rxreq spec: %s\n", *av); |
1122 |
200 |
http_rxhdr(hp); |
1123 |
200 |
http_splitheader(hp, 1); |
1124 |
200 |
if (http_count_header(hp->req, "Content-Length") > 1) |
1125 |
0 |
vtc_fatal(hp->vl, "Multiple Content-Length headers.\n"); |
1126 |
200 |
} |
1127 |
|
|
1128 |
|
/* SECTION: client-server.spec.rxreqbody |
1129 |
|
* |
1130 |
|
* rxreqbody (server only) |
1131 |
|
* Receive a request's body. |
1132 |
|
*/ |
1133 |
|
|
1134 |
|
static void |
1135 |
160 |
cmd_http_rxreqbody(CMD_ARGS) |
1136 |
|
{ |
1137 |
|
struct http *hp; |
1138 |
|
|
1139 |
160 |
(void)vl; |
1140 |
160 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1141 |
160 |
ONLY_SERVER(hp, av); |
1142 |
160 |
AZ(strcmp(av[0], "rxreqbody")); |
1143 |
160 |
av++; |
1144 |
|
|
1145 |
160 |
for (; *av != NULL; av++) |
1146 |
0 |
vtc_fatal(hp->vl, "Unknown http rxreq spec: %s\n", *av); |
1147 |
160 |
http_swallow_body(hp, hp->req, 0, 0); |
1148 |
160 |
vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); |
1149 |
160 |
} |
1150 |
|
|
1151 |
|
/* SECTION: client-server.spec.rxrespbody |
1152 |
|
* |
1153 |
|
* rxrespbody (client only) |
1154 |
|
* Receive (part of) a response's body. |
1155 |
|
* |
1156 |
|
* -max : max length of this receive, 0 for all |
1157 |
|
*/ |
1158 |
|
|
1159 |
|
static void |
1160 |
1318 |
cmd_http_rxrespbody(CMD_ARGS) |
1161 |
|
{ |
1162 |
|
struct http *hp; |
1163 |
1318 |
int max = 0; |
1164 |
|
|
1165 |
1318 |
(void)vl; |
1166 |
1318 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1167 |
1318 |
ONLY_CLIENT(hp, av); |
1168 |
1318 |
AZ(strcmp(av[0], "rxrespbody")); |
1169 |
1318 |
av++; |
1170 |
|
|
1171 |
1718 |
for (; *av != NULL; av++) |
1172 |
800 |
if (!strcmp(*av, "-max")) { |
1173 |
400 |
max = atoi(av[1]); |
1174 |
400 |
av++; |
1175 |
400 |
} else |
1176 |
0 |
vtc_fatal(hp->vl, |
1177 |
0 |
"Unknown http rxrespbody spec: %s\n", *av); |
1178 |
|
|
1179 |
1318 |
http_swallow_body(hp, hp->resp, 1, max); |
1180 |
1318 |
vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); |
1181 |
1318 |
} |
1182 |
|
|
1183 |
|
/* SECTION: client-server.spec.rxchunk |
1184 |
|
* |
1185 |
|
* rxchunk |
1186 |
|
* Receive an HTTP chunk. |
1187 |
|
*/ |
1188 |
|
|
1189 |
|
static void |
1190 |
520 |
cmd_http_rxchunk(CMD_ARGS) |
1191 |
|
{ |
1192 |
|
struct http *hp; |
1193 |
|
int ll, i; |
1194 |
|
|
1195 |
520 |
(void)vl; |
1196 |
520 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1197 |
520 |
ONLY_CLIENT(hp, av); |
1198 |
|
|
1199 |
520 |
i = http_rxchunk(hp); |
1200 |
520 |
if (i == 0) { |
1201 |
0 |
ll = hp->rx_p - hp->body; |
1202 |
0 |
hp->bodyl = ll; |
1203 |
0 |
bprintf(hp->bodylen, "%d", ll); |
1204 |
0 |
vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); |
1205 |
0 |
} |
1206 |
520 |
} |
1207 |
|
|
1208 |
|
/********************************************************************** |
1209 |
|
* Transmit a request |
1210 |
|
*/ |
1211 |
|
|
1212 |
|
static void |
1213 |
128155 |
cmd_http_txreq(CMD_ARGS) |
1214 |
|
{ |
1215 |
|
struct http *hp; |
1216 |
128155 |
const char *req = "GET"; |
1217 |
128155 |
const char *url = "/"; |
1218 |
128155 |
const char *proto = "HTTP/1.1"; |
1219 |
128155 |
const char *up = NULL; |
1220 |
|
unsigned nohost; |
1221 |
128155 |
unsigned nouseragent = 0; |
1222 |
|
|
1223 |
128155 |
(void)vl; |
1224 |
128155 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1225 |
128155 |
ONLY_CLIENT(hp, av); |
1226 |
128155 |
AZ(strcmp(av[0], "txreq")); |
1227 |
128155 |
av++; |
1228 |
|
|
1229 |
128155 |
VSB_clear(hp->vsb); |
1230 |
|
|
1231 |
128155 |
hp->head_method = 0; |
1232 |
208310 |
for (; *av != NULL; av++) { |
1233 |
116595 |
if (!strcmp(*av, "-url")) { |
1234 |
67275 |
url = av[1]; |
1235 |
67275 |
av++; |
1236 |
116595 |
} else if (!strcmp(*av, "-proto")) { |
1237 |
2000 |
proto = av[1]; |
1238 |
2000 |
av++; |
1239 |
49320 |
} else if (!strcmp(*av, "-method") || |
1240 |
42480 |
!strcmp(*av, "-req")) { |
1241 |
10680 |
req = av[1]; |
1242 |
10680 |
hp->head_method = !strcmp(av[1], "HEAD") ; |
1243 |
10680 |
av++; |
1244 |
47320 |
} else if (!hp->sfd && !strcmp(*av, "-up")) { |
1245 |
40 |
up = av[1]; |
1246 |
40 |
av++; |
1247 |
36640 |
} else if (!strcmp(*av, "-nouseragent")) { |
1248 |
160 |
nouseragent = 1; |
1249 |
160 |
} else |
1250 |
36440 |
break; |
1251 |
80155 |
} |
1252 |
128155 |
VSB_printf(hp->vsb, "%s %s %s%s", req, url, proto, nl); |
1253 |
|
|
1254 |
128155 |
if (up) |
1255 |
80 |
VSB_printf(hp->vsb, "Connection: Upgrade, HTTP2-Settings%s" |
1256 |
|
"Upgrade: h2c%s" |
1257 |
40 |
"HTTP2-Settings: %s%s", nl, nl, up, nl); |
1258 |
|
|
1259 |
128155 |
nohost = strcmp(proto, "HTTP/1.1") != 0; |
1260 |
128155 |
av = http_tx_parse_args(av, vl, hp, NULL, nohost, 1, 1, nouseragent); |
1261 |
128155 |
if (*av != NULL) |
1262 |
0 |
vtc_fatal(hp->vl, "Unknown http txreq spec: %s\n", *av); |
1263 |
128155 |
http_write(hp, 4, "txreq"); |
1264 |
|
|
1265 |
128155 |
if (up) { |
1266 |
40 |
parse_string(vl, hp, |
1267 |
|
"rxresp\n" |
1268 |
|
"expect resp.status == 101\n" |
1269 |
|
"expect resp.http.connection == Upgrade\n" |
1270 |
|
"expect resp.http.upgrade == h2c\n" |
1271 |
|
"txpri\n" |
1272 |
|
); |
1273 |
40 |
b64_settings(hp, up); |
1274 |
40 |
parse_string(vl, hp, |
1275 |
|
"stream 0 {\n" |
1276 |
|
" txsettings\n" |
1277 |
|
" rxsettings\n" |
1278 |
|
" txsettings -ack\n" |
1279 |
|
" rxsettings\n" |
1280 |
|
" expect settings.ack == true" |
1281 |
|
"} -start\n" |
1282 |
|
); |
1283 |
40 |
} |
1284 |
128155 |
} |
1285 |
|
|
1286 |
|
/* SECTION: client-server.spec.recv |
1287 |
|
* |
1288 |
|
* recv NUMBER |
1289 |
|
* Read NUMBER bytes from the connection. |
1290 |
|
*/ |
1291 |
|
|
1292 |
|
static void |
1293 |
360 |
cmd_http_recv(CMD_ARGS) |
1294 |
|
{ |
1295 |
|
struct http *hp; |
1296 |
|
int i, n; |
1297 |
|
char u[32]; |
1298 |
|
|
1299 |
360 |
(void)vl; |
1300 |
360 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1301 |
360 |
AN(av[1]); |
1302 |
360 |
AZ(av[2]); |
1303 |
360 |
n = strtoul(av[1], NULL, 0); |
1304 |
1160 |
while (n > 0) { |
1305 |
800 |
i = read(hp->sess->fd, u, n > 32 ? 32 : n); |
1306 |
800 |
if (i > 0) |
1307 |
800 |
vtc_dump(hp->vl, 4, "recv", u, i); |
1308 |
|
else |
1309 |
0 |
vtc_log(hp->vl, hp->fatal, "recv() got %d (%s)", i, |
1310 |
0 |
strerror(errno)); |
1311 |
800 |
n -= i; |
1312 |
|
} |
1313 |
360 |
} |
1314 |
|
|
1315 |
|
/* SECTION: client-server.spec.send |
1316 |
|
* |
1317 |
|
* send STRING |
1318 |
|
* Push STRING on the connection. |
1319 |
|
*/ |
1320 |
|
|
1321 |
|
static void |
1322 |
35288 |
cmd_http_send(CMD_ARGS) |
1323 |
|
{ |
1324 |
|
struct http *hp; |
1325 |
|
int i; |
1326 |
|
|
1327 |
35288 |
(void)vl; |
1328 |
35288 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1329 |
35288 |
AN(av[1]); |
1330 |
35288 |
AZ(av[2]); |
1331 |
35288 |
vtc_dump(hp->vl, 4, "send", av[1], -1); |
1332 |
35288 |
i = write(hp->sess->fd, av[1], strlen(av[1])); |
1333 |
35288 |
if (i != strlen(av[1])) |
1334 |
12 |
vtc_log(hp->vl, hp->fatal, "Write error in http_send(): %s", |
1335 |
6 |
strerror(errno)); |
1336 |
35288 |
} |
1337 |
|
|
1338 |
|
/* SECTION: client-server.spec.send_n |
1339 |
|
* |
1340 |
|
* send_n NUMBER STRING |
1341 |
|
* Write STRING on the socket NUMBER times. |
1342 |
|
*/ |
1343 |
|
|
1344 |
|
static void |
1345 |
80 |
cmd_http_send_n(CMD_ARGS) |
1346 |
|
{ |
1347 |
|
struct http *hp; |
1348 |
|
int i, n, l; |
1349 |
|
|
1350 |
80 |
(void)vl; |
1351 |
80 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1352 |
80 |
AN(av[1]); |
1353 |
80 |
AN(av[2]); |
1354 |
80 |
AZ(av[3]); |
1355 |
80 |
n = strtoul(av[1], NULL, 0); |
1356 |
80 |
vtc_dump(hp->vl, 4, "send_n", av[2], -1); |
1357 |
80 |
l = strlen(av[2]); |
1358 |
82400 |
while (n--) { |
1359 |
82320 |
i = write(hp->sess->fd, av[2], l); |
1360 |
82320 |
if (i != l) |
1361 |
10852 |
vtc_log(hp->vl, hp->fatal, |
1362 |
|
"Write error in http_send(): %s", |
1363 |
5426 |
strerror(errno)); |
1364 |
|
} |
1365 |
80 |
} |
1366 |
|
|
1367 |
|
/* SECTION: client-server.spec.send_urgent |
1368 |
|
* |
1369 |
|
* send_urgent STRING |
1370 |
|
* Send string as TCP OOB urgent data. You will never need this. |
1371 |
|
*/ |
1372 |
|
|
1373 |
|
static void |
1374 |
400 |
cmd_http_send_urgent(CMD_ARGS) |
1375 |
|
{ |
1376 |
|
struct http *hp; |
1377 |
|
int i; |
1378 |
|
|
1379 |
400 |
(void)vl; |
1380 |
400 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1381 |
400 |
AN(av[1]); |
1382 |
400 |
AZ(av[2]); |
1383 |
400 |
vtc_dump(hp->vl, 4, "send_urgent", av[1], -1); |
1384 |
400 |
i = send(hp->sess->fd, av[1], strlen(av[1]), MSG_OOB); |
1385 |
400 |
if (i != strlen(av[1])) |
1386 |
0 |
vtc_log(hp->vl, hp->fatal, |
1387 |
0 |
"Write error in http_send_urgent(): %s", strerror(errno)); |
1388 |
400 |
} |
1389 |
|
|
1390 |
|
/* SECTION: client-server.spec.sendhex |
1391 |
|
* |
1392 |
|
* sendhex STRING |
1393 |
|
* Send bytes as described by STRING. STRING should consist of hex pairs |
1394 |
|
* possibly separated by whitespace or newlines. For example: |
1395 |
|
* "0F EE a5 3df2". |
1396 |
|
*/ |
1397 |
|
|
1398 |
|
static void |
1399 |
7159 |
cmd_http_sendhex(CMD_ARGS) |
1400 |
|
{ |
1401 |
|
struct vsb *vsb; |
1402 |
|
struct http *hp; |
1403 |
|
|
1404 |
7159 |
(void)vl; |
1405 |
7159 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1406 |
7159 |
AN(av[1]); |
1407 |
7159 |
AZ(av[2]); |
1408 |
7159 |
vsb = vtc_hex_to_bin(hp->vl, av[1]); |
1409 |
7159 |
assert(VSB_len(vsb) >= 0); |
1410 |
7159 |
vtc_hexdump(hp->vl, 4, "sendhex", VSB_data(vsb), VSB_len(vsb)); |
1411 |
7159 |
if (VSB_tofile(vsb, hp->sess->fd)) |
1412 |
0 |
vtc_log(hp->vl, hp->fatal, "Write failed: %s", |
1413 |
0 |
strerror(errno)); |
1414 |
7159 |
VSB_destroy(&vsb); |
1415 |
7159 |
} |
1416 |
|
|
1417 |
|
/* SECTION: client-server.spec.chunked |
1418 |
|
* |
1419 |
|
* chunked STRING |
1420 |
|
* Send STRING as chunked encoding. |
1421 |
|
*/ |
1422 |
|
|
1423 |
|
static void |
1424 |
2400 |
cmd_http_chunked(CMD_ARGS) |
1425 |
|
{ |
1426 |
|
struct http *hp; |
1427 |
|
|
1428 |
2400 |
(void)vl; |
1429 |
2400 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1430 |
2400 |
AN(av[1]); |
1431 |
2400 |
AZ(av[2]); |
1432 |
2400 |
VSB_clear(hp->vsb); |
1433 |
4800 |
VSB_printf(hp->vsb, "%jx%s%s%s", |
1434 |
2400 |
(uintmax_t)strlen(av[1]), nl, av[1], nl); |
1435 |
2400 |
http_write(hp, 4, "chunked"); |
1436 |
2400 |
} |
1437 |
|
|
1438 |
|
/* SECTION: client-server.spec.chunkedlen |
1439 |
|
* |
1440 |
|
* chunkedlen NUMBER |
1441 |
|
* Do as ``chunked`` except that the string will be generated |
1442 |
|
* for you, with a length of NUMBER characters. |
1443 |
|
*/ |
1444 |
|
|
1445 |
|
static void |
1446 |
7399 |
cmd_http_chunkedlen(CMD_ARGS) |
1447 |
|
{ |
1448 |
|
unsigned len; |
1449 |
|
unsigned u, v; |
1450 |
|
char buf[16384]; |
1451 |
|
struct http *hp; |
1452 |
|
|
1453 |
7399 |
(void)vl; |
1454 |
7399 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1455 |
7399 |
AN(av[1]); |
1456 |
7399 |
AZ(av[2]); |
1457 |
7399 |
VSB_clear(hp->vsb); |
1458 |
|
|
1459 |
7399 |
len = atoi(av[1]); |
1460 |
|
|
1461 |
7399 |
if (len == 0) { |
1462 |
2760 |
VSB_printf(hp->vsb, "0%s%s", nl, nl); |
1463 |
2760 |
} else { |
1464 |
67954529 |
for (u = 0; u < sizeof buf; u++) |
1465 |
67949890 |
buf[u] = (u & 7) + '0'; |
1466 |
|
|
1467 |
4639 |
VSB_printf(hp->vsb, "%x%s", len, nl); |
1468 |
16238 |
for (u = 0; u < len; u += v) { |
1469 |
11599 |
v = vmin_t(unsigned, len - u, sizeof buf); |
1470 |
11599 |
VSB_bcat(hp->vsb, buf, v); |
1471 |
11599 |
} |
1472 |
4639 |
VSB_printf(hp->vsb, "%s", nl); |
1473 |
|
} |
1474 |
7399 |
http_write(hp, 4, "chunked"); |
1475 |
7399 |
} |
1476 |
|
|
1477 |
|
|
1478 |
|
/* SECTION: client-server.spec.timeout |
1479 |
|
* |
1480 |
|
* timeout NUMBER |
1481 |
|
* Set the TCP timeout for this entity. |
1482 |
|
*/ |
1483 |
|
|
1484 |
|
static void |
1485 |
1360 |
cmd_http_timeout(CMD_ARGS) |
1486 |
|
{ |
1487 |
|
struct http *hp; |
1488 |
|
double d; |
1489 |
|
|
1490 |
1360 |
(void)vl; |
1491 |
1360 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1492 |
1360 |
AN(av[1]); |
1493 |
1360 |
AZ(av[2]); |
1494 |
1360 |
d = VNUM(av[1]); |
1495 |
1360 |
if (isnan(d)) |
1496 |
0 |
vtc_fatal(vl, "timeout is not a number (%s)", av[1]); |
1497 |
1360 |
hp->timeout = d; |
1498 |
1360 |
} |
1499 |
|
|
1500 |
|
/* SECTION: client-server.spec.expect_close |
1501 |
|
* |
1502 |
|
* expect_close |
1503 |
|
* Reads from the connection, expecting nothing to read but an EOF. |
1504 |
|
*/ |
1505 |
|
static void |
1506 |
6653 |
cmd_http_expect_close(CMD_ARGS) |
1507 |
|
{ |
1508 |
|
struct http *hp; |
1509 |
|
struct pollfd fds[1]; |
1510 |
|
char c; |
1511 |
|
int i; |
1512 |
|
|
1513 |
6653 |
(void)vl; |
1514 |
6653 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1515 |
6653 |
AZ(av[1]); |
1516 |
|
|
1517 |
6653 |
vtc_log(vl, 4, "Expecting close (fd = %d)", hp->sess->fd); |
1518 |
6653 |
if (hp->h2) |
1519 |
560 |
stop_h2(hp); |
1520 |
6653 |
while (1) { |
1521 |
6653 |
fds[0].fd = hp->sess->fd; |
1522 |
6653 |
fds[0].events = POLLIN; |
1523 |
6653 |
fds[0].revents = 0; |
1524 |
6653 |
i = poll(fds, 1, (int)(hp->timeout * 1000)); |
1525 |
6653 |
if (i < 0 && errno == EINTR) |
1526 |
0 |
continue; |
1527 |
6653 |
if (i == 0) |
1528 |
0 |
vtc_log(vl, hp->fatal, "Expected close: timeout"); |
1529 |
6653 |
if (i != 1 || !(fds[0].revents & (POLLIN|POLLERR|POLLHUP))) |
1530 |
404 |
vtc_log(vl, hp->fatal, |
1531 |
|
"Expected close: poll = %d, revents = 0x%x", |
1532 |
202 |
i, fds[0].revents); |
1533 |
6451 |
i = read(hp->sess->fd, &c, 1); |
1534 |
6451 |
if (i <= 0 && VTCP_Check(i)) |
1535 |
6451 |
break; |
1536 |
202 |
if (i == 1 && vct_islws(c)) |
1537 |
0 |
continue; |
1538 |
0 |
vtc_log(vl, hp->fatal, |
1539 |
0 |
"Expecting close: read = %d, c = 0x%02x", i, c); |
1540 |
|
} |
1541 |
6451 |
vtc_log(vl, 4, "fd=%d EOF, as expected", hp->sess->fd); |
1542 |
6451 |
} |
1543 |
|
|
1544 |
|
/* SECTION: client-server.spec.close |
1545 |
|
* |
1546 |
|
* close (server only) |
1547 |
|
* Close the connection. Note that if operating in HTTP/2 mode no |
1548 |
|
* extra (GOAWAY) frame is sent, it's simply a TCP close. |
1549 |
|
*/ |
1550 |
|
static void |
1551 |
760 |
cmd_http_close(CMD_ARGS) |
1552 |
|
{ |
1553 |
|
struct http *hp; |
1554 |
|
|
1555 |
760 |
(void)vl; |
1556 |
760 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1557 |
760 |
ONLY_SERVER(hp, av); |
1558 |
760 |
AZ(av[1]); |
1559 |
760 |
assert(hp->sfd != NULL); |
1560 |
760 |
assert(*hp->sfd >= 0); |
1561 |
760 |
if (hp->h2) |
1562 |
0 |
stop_h2(hp); |
1563 |
760 |
VTCP_close(&hp->sess->fd); |
1564 |
760 |
vtc_log(vl, 4, "Closed"); |
1565 |
760 |
} |
1566 |
|
|
1567 |
|
/* SECTION: client-server.spec.accept |
1568 |
|
* |
1569 |
|
* accept (server only) |
1570 |
|
* Close the current connection, if any, and accept a new one. Note |
1571 |
|
* that this new connection is HTTP/1.x. |
1572 |
|
*/ |
1573 |
|
static void |
1574 |
6477 |
cmd_http_accept(CMD_ARGS) |
1575 |
|
{ |
1576 |
|
struct http *hp; |
1577 |
|
|
1578 |
6477 |
(void)vl; |
1579 |
6477 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1580 |
6477 |
ONLY_SERVER(hp, av); |
1581 |
6477 |
AZ(av[1]); |
1582 |
6477 |
assert(hp->sfd != NULL); |
1583 |
6477 |
assert(*hp->sfd >= 0); |
1584 |
6477 |
if (hp->h2) |
1585 |
0 |
stop_h2(hp); |
1586 |
6477 |
if (hp->sess->fd >= 0) |
1587 |
5757 |
VTCP_close(&hp->sess->fd); |
1588 |
6477 |
vtc_log(vl, 4, "Accepting"); |
1589 |
6477 |
hp->sess->fd = accept(*hp->sfd, NULL, NULL); |
1590 |
6477 |
if (hp->sess->fd < 0) |
1591 |
0 |
vtc_log(vl, hp->fatal, "Accepted failed: %s", strerror(errno)); |
1592 |
6477 |
vtc_log(vl, 3, "Accepted socket fd is %d", hp->sess->fd); |
1593 |
6477 |
} |
1594 |
|
|
1595 |
|
/* SECTION: client-server.spec.shutdown |
1596 |
|
* |
1597 |
|
* shutdown |
1598 |
|
* Initiate shutdown. |
1599 |
|
* |
1600 |
|
* \-read |
1601 |
|
* Shutdown the read direction. |
1602 |
|
* \-write |
1603 |
|
* Shutdown the write direction. |
1604 |
|
* |
1605 |
|
* The default is both direction. |
1606 |
|
*/ |
1607 |
|
static void |
1608 |
400 |
cmd_http_shutdown(CMD_ARGS) |
1609 |
|
{ |
1610 |
|
struct http *hp; |
1611 |
400 |
int how = SHUT_RDWR; |
1612 |
400 |
const char *str[] = { |
1613 |
|
[SHUT_RD] = "RD", |
1614 |
|
[SHUT_WR] = "WR", |
1615 |
|
[SHUT_RDWR] = "RDWR", |
1616 |
|
}; |
1617 |
|
|
1618 |
400 |
(void)vl; |
1619 |
400 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1620 |
400 |
AZ(strcmp(av[0], "shutdown")); |
1621 |
400 |
av++; |
1622 |
|
|
1623 |
400 |
if (*av != NULL) { |
1624 |
320 |
if (!strcmp(*av, "-read")) { |
1625 |
80 |
how = SHUT_RD; |
1626 |
80 |
av++; |
1627 |
320 |
} else if (!strcmp(*av, "-write")) { |
1628 |
240 |
how = SHUT_WR; |
1629 |
240 |
av++; |
1630 |
240 |
} |
1631 |
320 |
} |
1632 |
|
|
1633 |
400 |
if (*av != NULL) |
1634 |
0 |
vtc_fatal(hp->vl, "Unknown http shutdown spec: %s\n", *av); |
1635 |
|
|
1636 |
400 |
vtc_log(vl, 4, "Shutting down fd (%s): %d", str[how], hp->sess->fd); |
1637 |
400 |
if (shutdown(hp->sess->fd, how) < 0) |
1638 |
0 |
vtc_log(vl, hp->fatal, "Shutdown failed: %s", strerror(errno)); |
1639 |
400 |
vtc_log(vl, 3, "Shutdown socket fd (%d): %d", how, hp->sess->fd); |
1640 |
400 |
} |
1641 |
|
|
1642 |
|
/* SECTION: client-server.spec.fatal |
1643 |
|
* |
1644 |
|
* fatal|non_fatal |
1645 |
|
* Control whether a failure of this entity should stop the test. |
1646 |
|
*/ |
1647 |
|
|
1648 |
|
static void |
1649 |
16207 |
cmd_http_fatal(CMD_ARGS) |
1650 |
|
{ |
1651 |
|
struct http *hp; |
1652 |
16207 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1653 |
|
|
1654 |
16207 |
(void)vl; |
1655 |
16207 |
AZ(av[1]); |
1656 |
16207 |
if (!strcmp(av[0], "fatal")) { |
1657 |
640 |
hp->fatal = 0; |
1658 |
640 |
} else { |
1659 |
15567 |
assert(!strcmp(av[0], "non_fatal")); |
1660 |
15567 |
hp->fatal = -1; |
1661 |
|
} |
1662 |
16207 |
} |
1663 |
|
|
1664 |
|
#define cmd_http_non_fatal cmd_http_fatal |
1665 |
|
|
1666 |
|
static const char PREFACE[24] = { |
1667 |
|
0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, |
1668 |
|
0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, |
1669 |
|
0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a |
1670 |
|
}; |
1671 |
|
|
1672 |
|
/* SECTION: client-server.spec.txpri |
1673 |
|
* |
1674 |
|
* txpri (client only) |
1675 |
|
* Send an HTTP/2 preface ("PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n") |
1676 |
|
* and set client to HTTP/2. |
1677 |
|
*/ |
1678 |
|
static void |
1679 |
7240 |
cmd_http_txpri(CMD_ARGS) |
1680 |
|
{ |
1681 |
|
size_t l; |
1682 |
|
struct http *hp; |
1683 |
|
|
1684 |
7240 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1685 |
7240 |
ONLY_CLIENT(hp, av); |
1686 |
|
|
1687 |
7240 |
vtc_dump(hp->vl, 4, "txpri", PREFACE, sizeof(PREFACE)); |
1688 |
|
/* Dribble out the preface */ |
1689 |
7240 |
l = write(hp->sess->fd, PREFACE, 18); |
1690 |
7240 |
if (l != 18) |
1691 |
0 |
vtc_log(vl, hp->fatal, "Write failed: (%zd vs %zd) %s", |
1692 |
0 |
l, sizeof(PREFACE), strerror(errno)); |
1693 |
7240 |
VTIM_sleep(0.01); |
1694 |
7240 |
l = write(hp->sess->fd, PREFACE + 18, sizeof(PREFACE) - 18); |
1695 |
7240 |
if (l != sizeof(PREFACE) - 18) |
1696 |
0 |
vtc_log(vl, hp->fatal, "Write failed: (%zd vs %zd) %s", |
1697 |
0 |
l, sizeof(PREFACE), strerror(errno)); |
1698 |
|
|
1699 |
7240 |
start_h2(hp); |
1700 |
7240 |
AN(hp->h2); |
1701 |
7240 |
} |
1702 |
|
|
1703 |
|
/* SECTION: client-server.spec.rxpri |
1704 |
|
* |
1705 |
|
* rxpri (server only) |
1706 |
|
* Receive a preface. If valid set the server to HTTP/2, abort |
1707 |
|
* otherwise. |
1708 |
|
*/ |
1709 |
|
static void |
1710 |
1200 |
cmd_http_rxpri(CMD_ARGS) |
1711 |
|
{ |
1712 |
|
struct http *hp; |
1713 |
|
|
1714 |
1200 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1715 |
1200 |
ONLY_SERVER(hp, av); |
1716 |
|
|
1717 |
1200 |
hp->rx_p = hp->rx_b; |
1718 |
1200 |
if (!http_rxchar(hp, sizeof(PREFACE), 0)) |
1719 |
0 |
vtc_fatal(vl, "Couldn't retrieve connection preface"); |
1720 |
1200 |
if (memcmp(hp->rx_b, PREFACE, sizeof(PREFACE))) |
1721 |
0 |
vtc_fatal(vl, "Received invalid preface\n"); |
1722 |
1200 |
start_h2(hp); |
1723 |
1200 |
AN(hp->h2); |
1724 |
1200 |
} |
1725 |
|
|
1726 |
|
/* SECTION: client-server.spec.settings |
1727 |
|
* |
1728 |
|
* settings -dectbl INT |
1729 |
|
* Force internal HTTP/2 settings to certain values. Currently only |
1730 |
|
* support setting the decoding table size. |
1731 |
|
*/ |
1732 |
|
static void |
1733 |
0 |
cmd_http_settings(CMD_ARGS) |
1734 |
|
{ |
1735 |
|
uint32_t n; |
1736 |
|
char *p; |
1737 |
|
struct http *hp; |
1738 |
0 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1739 |
|
|
1740 |
0 |
if (!hp->h2) |
1741 |
0 |
vtc_fatal(hp->vl, "Only possible in H/2 mode"); |
1742 |
|
|
1743 |
0 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1744 |
|
|
1745 |
0 |
for (; *av != NULL; av++) { |
1746 |
0 |
if (!strcmp(*av, "-dectbl")) { |
1747 |
0 |
n = strtoul(av[1], &p, 0); |
1748 |
0 |
if (*p != '\0') |
1749 |
0 |
vtc_fatal(hp->vl, "-dectbl takes an integer as " |
1750 |
0 |
"argument (found %s)", av[1]); |
1751 |
0 |
assert(HPK_ResizeTbl(hp->decctx, n) != hpk_err); |
1752 |
0 |
av++; |
1753 |
0 |
} else |
1754 |
0 |
vtc_fatal(vl, "Unknown settings spec: %s\n", *av); |
1755 |
0 |
} |
1756 |
0 |
} |
1757 |
|
|
1758 |
|
static void |
1759 |
24000 |
cmd_http_stream(CMD_ARGS) |
1760 |
|
{ |
1761 |
|
struct http *hp; |
1762 |
24000 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1763 |
24000 |
if (!hp->h2) { |
1764 |
7480 |
vtc_log(hp->vl, 4, "Not in H/2 mode, do what's needed"); |
1765 |
7480 |
if (hp->sfd) |
1766 |
1040 |
parse_string(vl, hp, "rxpri"); |
1767 |
|
else |
1768 |
6440 |
parse_string(vl, hp, "txpri"); |
1769 |
7480 |
parse_string(vl, hp, |
1770 |
|
"stream 0 {\n" |
1771 |
|
" txsettings\n" |
1772 |
|
" rxsettings\n" |
1773 |
|
" txsettings -ack\n" |
1774 |
|
" rxsettings\n" |
1775 |
|
" expect settings.ack == true" |
1776 |
|
"} -run\n" |
1777 |
|
); |
1778 |
7480 |
} |
1779 |
24000 |
cmd_stream(av, hp, vl); |
1780 |
24000 |
} |
1781 |
|
|
1782 |
|
/* SECTION: client-server.spec.write_body |
1783 |
|
* |
1784 |
|
* write_body STRING |
1785 |
|
* Write the body of a request or a response to a file. By using the |
1786 |
|
* shell command, higher-level checks on the body can be performed |
1787 |
|
* (eg. XML, JSON, ...) provided that such checks can be delegated |
1788 |
|
* to an external program. |
1789 |
|
*/ |
1790 |
|
static void |
1791 |
80 |
cmd_http_write_body(CMD_ARGS) |
1792 |
|
{ |
1793 |
|
struct http *hp; |
1794 |
|
|
1795 |
80 |
(void)vl; |
1796 |
80 |
CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); |
1797 |
80 |
AN(av[0]); |
1798 |
80 |
AN(av[1]); |
1799 |
80 |
AZ(av[2]); |
1800 |
80 |
AZ(strcmp(av[0], "write_body")); |
1801 |
80 |
if (VFIL_writefile(NULL, av[1], hp->body, hp->bodyl) != 0) |
1802 |
0 |
vtc_fatal(hp->vl, "failed to write body: %s (%d)", |
1803 |
0 |
strerror(errno), errno); |
1804 |
80 |
} |
1805 |
|
|
1806 |
|
/********************************************************************** |
1807 |
|
* Execute HTTP specifications |
1808 |
|
*/ |
1809 |
|
|
1810 |
|
const struct cmds http_cmds[] = { |
1811 |
|
#define CMD_HTTP(n) { #n, cmd_http_##n }, |
1812 |
|
/* session */ |
1813 |
|
CMD_HTTP(accept) |
1814 |
|
CMD_HTTP(close) |
1815 |
|
CMD_HTTP(recv) |
1816 |
|
CMD_HTTP(send) |
1817 |
|
CMD_HTTP(send_n) |
1818 |
|
CMD_HTTP(send_urgent) |
1819 |
|
CMD_HTTP(sendhex) |
1820 |
|
CMD_HTTP(shutdown) |
1821 |
|
CMD_HTTP(timeout) |
1822 |
|
|
1823 |
|
/* spec */ |
1824 |
|
CMD_HTTP(fatal) |
1825 |
|
CMD_HTTP(non_fatal) |
1826 |
|
|
1827 |
|
/* body */ |
1828 |
|
CMD_HTTP(gunzip) |
1829 |
|
CMD_HTTP(write_body) |
1830 |
|
|
1831 |
|
/* HTTP/1.x */ |
1832 |
|
CMD_HTTP(chunked) |
1833 |
|
CMD_HTTP(chunkedlen) |
1834 |
|
CMD_HTTP(rxchunk) |
1835 |
|
|
1836 |
|
/* HTTP/2 */ |
1837 |
|
CMD_HTTP(stream) |
1838 |
|
CMD_HTTP(settings) |
1839 |
|
|
1840 |
|
/* client */ |
1841 |
|
CMD_HTTP(rxresp) |
1842 |
|
CMD_HTTP(rxrespbody) |
1843 |
|
CMD_HTTP(rxresphdrs) |
1844 |
|
CMD_HTTP(txpri) |
1845 |
|
CMD_HTTP(txreq) |
1846 |
|
|
1847 |
|
/* server */ |
1848 |
|
CMD_HTTP(rxpri) |
1849 |
|
CMD_HTTP(rxreq) |
1850 |
|
CMD_HTTP(rxreqbody) |
1851 |
|
CMD_HTTP(rxreqhdrs) |
1852 |
|
CMD_HTTP(txresp) |
1853 |
|
CMD_HTTP(upgrade) |
1854 |
|
|
1855 |
|
/* expect */ |
1856 |
|
CMD_HTTP(expect) |
1857 |
|
CMD_HTTP(expect_close) |
1858 |
|
CMD_HTTP(expect_pattern) |
1859 |
|
#undef CMD_HTTP |
1860 |
|
{ NULL, NULL } |
1861 |
|
}; |
1862 |
|
|
1863 |
|
static void |
1864 |
156812 |
http_process_cleanup(void *arg) |
1865 |
|
{ |
1866 |
|
struct http *hp; |
1867 |
|
|
1868 |
156812 |
CAST_OBJ_NOTNULL(hp, arg, HTTP_MAGIC); |
1869 |
|
|
1870 |
156812 |
if (hp->h2) |
1871 |
7879 |
stop_h2(hp); |
1872 |
156812 |
VSB_destroy(&hp->vsb); |
1873 |
156812 |
free(hp->rx_b); |
1874 |
156812 |
free(hp->rem_ip); |
1875 |
156812 |
free(hp->rem_port); |
1876 |
156812 |
free(hp->rem_path); |
1877 |
156812 |
FREE_OBJ(hp); |
1878 |
156812 |
} |
1879 |
|
|
1880 |
|
int |
1881 |
156827 |
http_process(struct vtclog *vl, struct vtc_sess *vsp, const char *spec, |
1882 |
|
int sock, int *sfd, const char *addr, int rcvbuf) |
1883 |
|
{ |
1884 |
|
struct http *hp; |
1885 |
|
int retval, oldbuf; |
1886 |
156827 |
socklen_t intlen = sizeof(int); |
1887 |
|
|
1888 |
156827 |
(void)sfd; |
1889 |
156827 |
ALLOC_OBJ(hp, HTTP_MAGIC); |
1890 |
156827 |
AN(hp); |
1891 |
156827 |
hp->sess = vsp; |
1892 |
156827 |
hp->sess->fd = sock; |
1893 |
156827 |
hp->timeout = vtc_maxdur * .5; |
1894 |
|
|
1895 |
156827 |
if (rcvbuf) { |
1896 |
|
// XXX setsockopt() too late on SunOS |
1897 |
|
// https://github.com/varnishcache/varnish-cache/pull/2980#issuecomment-486214661 |
1898 |
359 |
hp->rcvbuf = rcvbuf; |
1899 |
|
|
1900 |
359 |
oldbuf = 0; |
1901 |
359 |
AZ(getsockopt(hp->sess->fd, SOL_SOCKET, SO_RCVBUF, &oldbuf, &intlen)); |
1902 |
359 |
AZ(setsockopt(hp->sess->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, intlen)); |
1903 |
359 |
AZ(getsockopt(hp->sess->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &intlen)); |
1904 |
|
|
1905 |
718 |
vtc_log(vl, 3, "-rcvbuf fd=%d old=%d new=%d actual=%d", |
1906 |
359 |
hp->sess->fd, oldbuf, hp->rcvbuf, rcvbuf); |
1907 |
359 |
} |
1908 |
|
|
1909 |
156827 |
hp->nrxbuf = 2048*1024; |
1910 |
156827 |
hp->rx_b = malloc(hp->nrxbuf); |
1911 |
156827 |
AN(hp->rx_b); |
1912 |
156827 |
hp->rx_e = hp->rx_b + hp->nrxbuf; |
1913 |
156827 |
hp->rx_p = hp->rx_b; |
1914 |
156827 |
*hp->rx_p = '\0'; |
1915 |
|
|
1916 |
156827 |
hp->vsb = VSB_new_auto(); |
1917 |
156827 |
AN(hp->vsb); |
1918 |
|
|
1919 |
156827 |
hp->sfd = sfd; |
1920 |
|
|
1921 |
156827 |
hp->rem_ip = malloc(VTCP_ADDRBUFSIZE); |
1922 |
156827 |
AN(hp->rem_ip); |
1923 |
|
|
1924 |
156827 |
hp->rem_port = malloc(VTCP_PORTBUFSIZE); |
1925 |
156827 |
AN(hp->rem_port); |
1926 |
|
|
1927 |
156827 |
hp->vl = vl; |
1928 |
156827 |
vtc_log_set_cmd(hp->vl, http_cmds); |
1929 |
156827 |
hp->gziplevel = 0; |
1930 |
156827 |
hp->gzipresidual = -1; |
1931 |
|
|
1932 |
156827 |
if (*addr != '/') { |
1933 |
143791 |
VTCP_hisname(sock, hp->rem_ip, VTCP_ADDRBUFSIZE, hp->rem_port, |
1934 |
|
VTCP_PORTBUFSIZE); |
1935 |
143791 |
hp->rem_path = NULL; |
1936 |
143791 |
} else { |
1937 |
13036 |
strcpy(hp->rem_ip, "0.0.0.0"); |
1938 |
13036 |
strcpy(hp->rem_port, "0"); |
1939 |
13036 |
hp->rem_path = strdup(addr); |
1940 |
|
} |
1941 |
|
/* XXX: After an upgrade to HTTP/2 the cleanup of a server that is |
1942 |
|
* not -wait'ed before the test resets is subject to a race where the |
1943 |
|
* cleanup does not happen, so ASAN reports leaks despite the push |
1944 |
|
* of a cleanup handler. To easily reproduce, remove the server wait |
1945 |
|
* from a02022.vtc and run with ASAN enabled. |
1946 |
|
*/ |
1947 |
156827 |
pthread_cleanup_push(http_process_cleanup, hp); |
1948 |
156827 |
parse_string(vl, hp, spec); |
1949 |
156827 |
retval = hp->sess->fd; |
1950 |
156827 |
pthread_cleanup_pop(0); |
1951 |
156827 |
http_process_cleanup(hp); |
1952 |
156827 |
return (retval); |
1953 |
|
} |
1954 |
|
|
1955 |
|
/********************************************************************** |
1956 |
|
* Magic test routine |
1957 |
|
* |
1958 |
|
* This function brute-forces some short strings through gzip(9) to |
1959 |
|
* find candidates for all possible 8 bit positions of the stopbit. |
1960 |
|
* |
1961 |
|
* Here is some good short output strings: |
1962 |
|
* |
1963 |
|
* 0 184 <e04c8d0fd604c> |
1964 |
|
* 1 257 <1ea86e6cf31bf4ec3d7a86> |
1965 |
|
* 2 106 <10> |
1966 |
|
* 3 163 <a5e2e2e1c2e2> |
1967 |
|
* 4 180 <71c5d18ec5d5d1> |
1968 |
|
* 5 189 <39886d28a6d2988> |
1969 |
|
* 6 118 <80000> |
1970 |
|
* 7 151 <386811868> |
1971 |
|
* |
1972 |
|
*/ |
1973 |
|
|
1974 |
|
#if 0 |
1975 |
|
void xxx(void); |
1976 |
|
|
1977 |
|
void |
1978 |
|
xxx(void) |
1979 |
|
{ |
1980 |
|
z_stream vz; |
1981 |
|
int n; |
1982 |
|
char ibuf[200]; |
1983 |
|
char obuf[200]; |
1984 |
|
int fl[8]; |
1985 |
|
int i, j; |
1986 |
|
|
1987 |
|
for (n = 0; n < 8; n++) |
1988 |
|
fl[n] = 9999; |
1989 |
|
|
1990 |
|
memset(&vz, 0, sizeof vz); |
1991 |
|
|
1992 |
|
for (n = 0; n < 999999999; n++) { |
1993 |
|
*ibuf = 0; |
1994 |
|
for (j = 0; j < 7; j++) { |
1995 |
|
snprintf(strchr(ibuf, 0), 5, "%x", |
1996 |
|
(unsigned)VRND_RandomTestable() & 0xffff); |
1997 |
|
vz.next_in = TRUST_ME(ibuf); |
1998 |
|
vz.avail_in = strlen(ibuf); |
1999 |
|
vz.next_out = TRUST_ME(obuf); |
2000 |
|
vz.avail_out = sizeof obuf; |
2001 |
|
assert(Z_OK == deflateInit2(&vz, |
2002 |
|
9, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY)); |
2003 |
|
assert(Z_STREAM_END == deflate(&vz, Z_FINISH)); |
2004 |
|
i = vz.stop_bit & 7; |
2005 |
|
if (fl[i] > strlen(ibuf)) { |
2006 |
|
printf("%d %jd <%s>\n", i, vz.stop_bit, ibuf); |
2007 |
|
fl[i] = strlen(ibuf); |
2008 |
|
} |
2009 |
|
assert(Z_OK == deflateEnd(&vz)); |
2010 |
|
} |
2011 |
|
} |
2012 |
|
|
2013 |
|
printf("FOO\n"); |
2014 |
|
} |
2015 |
|
#endif |