xref: /minix3/minix/tests/test82.c (revision 17fbdaf51438dec1991af7b09c2f54ba874d7a03)
186e41e22SErik van der Kouwe /*
286e41e22SErik van der Kouwe  * test82: test HTTP with a remote server (is $USENETWORK="yes")
386e41e22SErik van der Kouwe  */
486e41e22SErik van der Kouwe 
586e41e22SErik van der Kouwe #define DEBUG 0
686e41e22SErik van der Kouwe 
786e41e22SErik van der Kouwe #if DEBUG
886e41e22SErik van der Kouwe #define dbgprintf(...)	do { 						\
986e41e22SErik van der Kouwe 				fprintf(stderr, "[%s:%s:%d %d] ",	\
1086e41e22SErik van der Kouwe 					__FILE__, __FUNCTION__,		\
1186e41e22SErik van der Kouwe 					__LINE__, getpid());		\
1286e41e22SErik van der Kouwe 				fprintf(stderr, __VA_ARGS__);		\
1386e41e22SErik van der Kouwe 				fflush(stderr);				\
1486e41e22SErik van der Kouwe 			} while (0)
1586e41e22SErik van der Kouwe #else
1686e41e22SErik van der Kouwe #define	dbgprintf(...)
1786e41e22SErik van der Kouwe #endif
1886e41e22SErik van der Kouwe 
1986e41e22SErik van der Kouwe #include <arpa/inet.h>
2086e41e22SErik van der Kouwe #include <assert.h>
2186e41e22SErik van der Kouwe #include <netdb.h>
2286e41e22SErik van der Kouwe #include <netinet/in.h>
2386e41e22SErik van der Kouwe #include <stdarg.h>
2486e41e22SErik van der Kouwe #include <stdio.h>
2586e41e22SErik van der Kouwe #include <stdlib.h>
2686e41e22SErik van der Kouwe #include <string.h>
2786e41e22SErik van der Kouwe #include <sys/socket.h>
2886e41e22SErik van der Kouwe #include <sys/wait.h>
2986e41e22SErik van der Kouwe 
3086e41e22SErik van der Kouwe #include "common.h"
3186e41e22SErik van der Kouwe 
32*17fbdaf5SErik van der Kouwe #define CLOSE(fd) do { assert(fd >= 0); if (close((fd)) != 0) efmt("close failed"); } while (0);
33*17fbdaf5SErik van der Kouwe #define REALLOC(p, size) do { p = realloc(p, size); if (!p) efmt("realloc of %zu bytes failed", size); } while (0);
3486e41e22SErik van der Kouwe 
3586e41e22SErik van der Kouwe #define HOST "test82.minix3.org"
3686e41e22SErik van der Kouwe #define PORT 80
3786e41e22SErik van der Kouwe #define PATH1 "/test1.txt"
3886e41e22SErik van der Kouwe #define PATH1_DATA "Hello world\n"
3986e41e22SErik van der Kouwe #define PATH2 "/test2.bin"
4086e41e22SErik van der Kouwe 
4186e41e22SErik van der Kouwe static void callback_verify_path1(const void *data, size_t size);
4286e41e22SErik van der Kouwe static void callback_verify_path2(const void *data, size_t size);
4386e41e22SErik van der Kouwe 
4486e41e22SErik van der Kouwe #define URL_COUNT 2
4586e41e22SErik van der Kouwe 
4686e41e22SErik van der Kouwe struct url {
4786e41e22SErik van der Kouwe 	const char *host;
4886e41e22SErik van der Kouwe 	int port;
4986e41e22SErik van der Kouwe 	const char *path;
5086e41e22SErik van der Kouwe 	void (* callback_verify)(const void *data, size_t size);
5186e41e22SErik van der Kouwe };
5286e41e22SErik van der Kouwe 
5386e41e22SErik van der Kouwe static const struct url urls[URL_COUNT] = {
5486e41e22SErik van der Kouwe 	{ HOST, PORT, PATH1, callback_verify_path1 },
5586e41e22SErik van der Kouwe 	{ HOST, PORT, PATH2, callback_verify_path2 },
5686e41e22SErik van der Kouwe };
5786e41e22SErik van der Kouwe 
http_connect(const char * host,int port)5886e41e22SErik van der Kouwe static int http_connect(const char *host, int port) {
5986e41e22SErik van der Kouwe 	struct addrinfo *addr = NULL;
6086e41e22SErik van der Kouwe 	int fd = -1;
6186e41e22SErik van der Kouwe 	struct addrinfo hints = {
6286e41e22SErik van der Kouwe 		.ai_family = PF_INET,
6386e41e22SErik van der Kouwe 		.ai_socktype = SOCK_STREAM,
6486e41e22SErik van der Kouwe 	};
6586e41e22SErik van der Kouwe 	char serv[12];
6686e41e22SErik van der Kouwe 
6786e41e22SErik van der Kouwe 	assert(host);
6886e41e22SErik van der Kouwe 
6986e41e22SErik van der Kouwe 	snprintf(serv, sizeof(serv), "%d", port);
7086e41e22SErik van der Kouwe 
7186e41e22SErik van der Kouwe 	errno = 0;
7286e41e22SErik van der Kouwe 	if (getaddrinfo(host, serv, &hints, &addr) != 0 || !addr) {
73*17fbdaf5SErik van der Kouwe 		efmt("host %s not found", host);
7486e41e22SErik van der Kouwe 		goto failure;
7586e41e22SErik van der Kouwe 	}
7686e41e22SErik van der Kouwe 
7786e41e22SErik van der Kouwe 	fd = socket(AF_INET, SOCK_STREAM, 0);
7886e41e22SErik van der Kouwe 	if (fd < 0) {
79*17fbdaf5SErik van der Kouwe 		efmt("cannot create socket");
8086e41e22SErik van der Kouwe 		goto failure;
8186e41e22SErik van der Kouwe 	}
8286e41e22SErik van der Kouwe 
8386e41e22SErik van der Kouwe 	if (connect(fd, addr->ai_addr, addr->ai_addrlen) != 0) {
84*17fbdaf5SErik van der Kouwe 		efmt("cannot connect to %s:%d", host, port);
8586e41e22SErik van der Kouwe 		goto failure;
8686e41e22SErik van der Kouwe 	}
8786e41e22SErik van der Kouwe 
8886e41e22SErik van der Kouwe 	freeaddrinfo(addr);
8986e41e22SErik van der Kouwe 	return fd;
9086e41e22SErik van der Kouwe 
9186e41e22SErik van der Kouwe failure:
9286e41e22SErik van der Kouwe 	if (fd >= 0) CLOSE(fd);
9386e41e22SErik van der Kouwe 	if (addr) freeaddrinfo(addr);
9486e41e22SErik van der Kouwe 	return -1;
9586e41e22SErik van der Kouwe }
9686e41e22SErik van der Kouwe 
write_chunked(int fd,const char * data,size_t size,size_t chunksize)9786e41e22SErik van der Kouwe static void write_chunked(
9886e41e22SErik van der Kouwe 	int fd,
9986e41e22SErik van der Kouwe 	const char *data,
10086e41e22SErik van der Kouwe 	size_t size,
10186e41e22SErik van der Kouwe 	size_t chunksize) {
10286e41e22SErik van der Kouwe 	ssize_t r;
10386e41e22SErik van der Kouwe 	size_t s;
10486e41e22SErik van der Kouwe 
10586e41e22SErik van der Kouwe 	assert(fd >= 0);
10686e41e22SErik van der Kouwe 	assert(data);
10786e41e22SErik van der Kouwe 	assert(chunksize > 0);
10886e41e22SErik van der Kouwe 
10986e41e22SErik van der Kouwe 	while (size > 0) {
11086e41e22SErik van der Kouwe 		s = chunksize;
11186e41e22SErik van der Kouwe 		if (s > size) s = size;
11286e41e22SErik van der Kouwe 
11386e41e22SErik van der Kouwe 		errno = 0;
11486e41e22SErik van der Kouwe 		r = write(fd, data, s);
11586e41e22SErik van der Kouwe 		if (r <= 0 || (size_t) r > s) {
11686e41e22SErik van der Kouwe 			errno = 0;
117*17fbdaf5SErik van der Kouwe 			efmt("write of %zu bytes failed with result %zd", s, r);
11886e41e22SErik van der Kouwe 			break;
11986e41e22SErik van der Kouwe 		}
12086e41e22SErik van der Kouwe 
12186e41e22SErik van der Kouwe 		data += r;
12286e41e22SErik van der Kouwe 		size -= r;
12386e41e22SErik van der Kouwe 	}
12486e41e22SErik van der Kouwe }
12586e41e22SErik van der Kouwe 
http_send_request(int fd,const char * host,const char * path,size_t chunksize,int bigrequest)12686e41e22SErik van der Kouwe static void http_send_request(
12786e41e22SErik van der Kouwe 	int fd,
12886e41e22SErik van der Kouwe 	const char *host,
12986e41e22SErik van der Kouwe 	const char *path,
13086e41e22SErik van der Kouwe 	size_t chunksize,
13186e41e22SErik van der Kouwe 	int bigrequest) {
13286e41e22SErik van der Kouwe 	char buf[8192];
13386e41e22SErik van der Kouwe 	size_t len;
13486e41e22SErik van der Kouwe 	int lineno;
13586e41e22SErik van der Kouwe 
13686e41e22SErik van der Kouwe 	assert(fd >= 0);
13786e41e22SErik van der Kouwe 	assert(host);
13886e41e22SErik van der Kouwe 	assert(path);
13986e41e22SErik van der Kouwe 	assert(chunksize > 0);
14086e41e22SErik van der Kouwe 
14186e41e22SErik van der Kouwe 	/* http://tools.ietf.org/html/rfc2616#section-5 */
14286e41e22SErik van der Kouwe 	len = snprintf(buf, sizeof(buf),
14386e41e22SErik van der Kouwe 		"GET %s HTTP/1.1\r\n"
14486e41e22SErik van der Kouwe 		"Host: %s\r\n",
14586e41e22SErik van der Kouwe 		path, host);
14686e41e22SErik van der Kouwe 	if (bigrequest) {
14786e41e22SErik van der Kouwe 		lineno = 0;
14886e41e22SErik van der Kouwe 		while (len + 24 < sizeof(buf)) {
14986e41e22SErik van der Kouwe 			len += snprintf(buf + len, sizeof(buf) - len,
15086e41e22SErik van der Kouwe 				"X-Padding%d: %d\r\n",
15186e41e22SErik van der Kouwe 				lineno, lineno);
15286e41e22SErik van der Kouwe 			lineno++;
15386e41e22SErik van der Kouwe 		}
15486e41e22SErik van der Kouwe 	}
15586e41e22SErik van der Kouwe 	len += snprintf(buf + len, sizeof(buf) - len, "\r\n");
15686e41e22SErik van der Kouwe 
15786e41e22SErik van der Kouwe 	dbgprintf("sending request:\n%.*s", (int) len, buf);
15886e41e22SErik van der Kouwe 	write_chunked(fd, buf, len, chunksize);
15986e41e22SErik van der Kouwe }
16086e41e22SErik van der Kouwe 
is_whitespace(char c)16186e41e22SErik van der Kouwe static int is_whitespace(char c) {
16286e41e22SErik van der Kouwe 	return c == ' ' || c == '\t';
16386e41e22SErik van der Kouwe }
16486e41e22SErik van der Kouwe 
is_whitespace_or_linebreak(char c)16586e41e22SErik van der Kouwe static int is_whitespace_or_linebreak(char c) {
16686e41e22SErik van der Kouwe 	return is_whitespace(c) || c == '\r' || c == '\n';
16786e41e22SErik van der Kouwe }
16886e41e22SErik van der Kouwe 
is_numeric(char c)16986e41e22SErik van der Kouwe static int is_numeric(char c) {
17086e41e22SErik van der Kouwe 	return c >= '0' && c <= '9';
17186e41e22SErik van der Kouwe }
17286e41e22SErik van der Kouwe 
http_get_header_line(const char * data,size_t len,size_t * index_p,size_t * linelen_p)17386e41e22SErik van der Kouwe static int http_get_header_line(
17486e41e22SErik van der Kouwe 	const char *data,
17586e41e22SErik van der Kouwe 	size_t len,
17686e41e22SErik van der Kouwe 	size_t *index_p,
17786e41e22SErik van der Kouwe 	size_t *linelen_p) {
17886e41e22SErik van der Kouwe 	int has_cr;
17986e41e22SErik van der Kouwe 	size_t index;
18086e41e22SErik van der Kouwe 	size_t linelen;
18186e41e22SErik van der Kouwe 
18286e41e22SErik van der Kouwe 	assert(data);
18386e41e22SErik van der Kouwe 	assert(index_p);
18486e41e22SErik van der Kouwe 	assert(*index_p <= len);
18586e41e22SErik van der Kouwe 	assert(linelen_p);
18686e41e22SErik van der Kouwe 
18786e41e22SErik van der Kouwe 	/* starting the next line with whitespace means the line is continued */
18886e41e22SErik van der Kouwe 	index = *index_p;
18986e41e22SErik van der Kouwe 	do {
19086e41e22SErik van der Kouwe 		while (index < len && data[index] != '\n') index++;
19186e41e22SErik van der Kouwe 		if (index >= len) goto notfound;
19286e41e22SErik van der Kouwe 		index++;
19386e41e22SErik van der Kouwe 	} while (index < len && is_whitespace(data[index]));
19486e41e22SErik van der Kouwe 
19586e41e22SErik van der Kouwe 	/* exclude LF or CR+LF from line length */
19686e41e22SErik van der Kouwe 	assert(index - 1 >= *index_p && data[index - 1] == '\n');
19786e41e22SErik van der Kouwe 	has_cr = (index - 2 >= *index_p) && data[index - 2] == '\r';
19886e41e22SErik van der Kouwe 	linelen = index - *index_p - (has_cr ? 2 : 1);
19986e41e22SErik van der Kouwe 
20086e41e22SErik van der Kouwe 	/* if LF is the last character in the buffer, the line may be continued
20186e41e22SErik van der Kouwe 	 * when more data is retrieved unless we reached the end of the headers
20286e41e22SErik van der Kouwe 	 */
20386e41e22SErik van der Kouwe 	if (index >= len && linelen > 0) goto notfound;
20486e41e22SErik van der Kouwe 
20586e41e22SErik van der Kouwe 	*linelen_p = linelen;
20686e41e22SErik van der Kouwe 	*index_p = index;
20786e41e22SErik van der Kouwe 	return 1;
20886e41e22SErik van der Kouwe 
20986e41e22SErik van der Kouwe notfound:
21086e41e22SErik van der Kouwe 	*linelen_p = 0;
21186e41e22SErik van der Kouwe 	*index_p = index;
21286e41e22SErik van der Kouwe 	return 0;
21386e41e22SErik van der Kouwe }
21486e41e22SErik van der Kouwe 
http_get_status_line(const char * data,size_t len,size_t * index_p,int * error_p,int * code_p)21586e41e22SErik van der Kouwe static int http_get_status_line(
21686e41e22SErik van der Kouwe 	const char *data,
21786e41e22SErik van der Kouwe 	size_t len,
21886e41e22SErik van der Kouwe 	size_t *index_p,
21986e41e22SErik van der Kouwe 	int *error_p,
22086e41e22SErik van der Kouwe 	int *code_p) {
22186e41e22SErik van der Kouwe 	int code, i;
22286e41e22SErik van der Kouwe 	size_t index;
22386e41e22SErik van der Kouwe 
22486e41e22SErik van der Kouwe 	assert(data);
22586e41e22SErik van der Kouwe 	assert(index_p);
22686e41e22SErik van der Kouwe 	assert(*index_p <= len);
22786e41e22SErik van der Kouwe 	assert(error_p);
22886e41e22SErik van der Kouwe 	assert(*error_p == 0);
22986e41e22SErik van der Kouwe 	assert(code_p);
23086e41e22SErik van der Kouwe 
23186e41e22SErik van der Kouwe 	/* skip leading whitespace/blank lines */
23286e41e22SErik van der Kouwe 	index = *index_p;
23386e41e22SErik van der Kouwe 	while (index < len && is_whitespace_or_linebreak(data[index])) index++;
23486e41e22SErik van der Kouwe 
23586e41e22SErik van der Kouwe 	/* parse version */
23686e41e22SErik van der Kouwe 	while (index < len && !is_whitespace(data[index])) index++;
23786e41e22SErik van der Kouwe 
23886e41e22SErik van der Kouwe 	/* skip separator */
23986e41e22SErik van der Kouwe 	while (index < len && is_whitespace(data[index])) index++;
24086e41e22SErik van der Kouwe 
24186e41e22SErik van der Kouwe 	/* parse status code */
24286e41e22SErik van der Kouwe 	code = 0;
24386e41e22SErik van der Kouwe 	for (i = 0; i < 3; i++) {
24486e41e22SErik van der Kouwe 		if (index >= len) goto notfound;
24586e41e22SErik van der Kouwe 		if (!is_numeric(data[index])) {
24686e41e22SErik van der Kouwe 			errno = 0;
247*17fbdaf5SErik van der Kouwe 			efmt("HTTP error: bad status line: \"%.*s\"",
24886e41e22SErik van der Kouwe 				(int) (index - *index_p), data + *index_p);
24986e41e22SErik van der Kouwe 			*error_p = 1;
25086e41e22SErik van der Kouwe 			goto notfound;
25186e41e22SErik van der Kouwe 		}
25286e41e22SErik van der Kouwe 		code = code * 10 + (data[index++] - '0');
25386e41e22SErik van der Kouwe 	}
25486e41e22SErik van der Kouwe 
25586e41e22SErik van der Kouwe 	/* skip separator */
25686e41e22SErik van der Kouwe 	while (index < len && is_whitespace(data[index])) index++;
25786e41e22SErik van der Kouwe 
25886e41e22SErik van der Kouwe 	/* parse reason phrase */
25986e41e22SErik van der Kouwe 	while (index < len && data[index] != '\n') index++;
26086e41e22SErik van der Kouwe 	if (index >= len) goto notfound;
26186e41e22SErik van der Kouwe 	index++;
26286e41e22SErik van der Kouwe 
26386e41e22SErik van der Kouwe 	*code_p = code;
26486e41e22SErik van der Kouwe 	*index_p = index;
26586e41e22SErik van der Kouwe 	return 1;
26686e41e22SErik van der Kouwe 
26786e41e22SErik van der Kouwe notfound:
26886e41e22SErik van der Kouwe 	*code_p = 0;
26986e41e22SErik van der Kouwe 	*index_p = index;
27086e41e22SErik van der Kouwe 	return 0;
27186e41e22SErik van der Kouwe }
27286e41e22SErik van der Kouwe 
http_header_is(const char * data,size_t len,size_t index,const char * name,size_t * index_value_p)27386e41e22SErik van der Kouwe static int http_header_is(
27486e41e22SErik van der Kouwe 	const char *data,
27586e41e22SErik van der Kouwe 	size_t len,
27686e41e22SErik van der Kouwe 	size_t index,
27786e41e22SErik van der Kouwe 	const char *name,
27886e41e22SErik van der Kouwe 	size_t *index_value_p) {
27986e41e22SErik van der Kouwe 	size_t namelen;
28086e41e22SErik van der Kouwe 
28186e41e22SErik van der Kouwe 	assert(data);
28286e41e22SErik van der Kouwe 	assert(index <= len);
28386e41e22SErik van der Kouwe 	assert(name);
28486e41e22SErik van der Kouwe 	assert(index_value_p);
28586e41e22SErik van der Kouwe 
28686e41e22SErik van der Kouwe 	namelen = strlen(name);
28786e41e22SErik van der Kouwe 	if (index + namelen > len) goto notfound;
28886e41e22SErik van der Kouwe 	if (strncasecmp(data + index, name, namelen) != 0) goto notfound;
28986e41e22SErik van der Kouwe 	index += namelen;
29086e41e22SErik van der Kouwe 	while (index < len && is_whitespace(data[index])) index++;
29186e41e22SErik van der Kouwe 	if (index >= len || data[index] != ':') goto notfound;
29286e41e22SErik van der Kouwe 	index++;
29386e41e22SErik van der Kouwe 
29486e41e22SErik van der Kouwe 	while (index < len && is_whitespace(data[index])) index++;
29586e41e22SErik van der Kouwe 	*index_value_p = index;
29686e41e22SErik van der Kouwe 	return 1;
29786e41e22SErik van der Kouwe 
29886e41e22SErik van der Kouwe notfound:
29986e41e22SErik van der Kouwe 	*index_value_p = 0;
30086e41e22SErik van der Kouwe 	return 0;
30186e41e22SErik van der Kouwe }
30286e41e22SErik van der Kouwe 
http_parse_int_header(const char * data,size_t index,size_t index_end,int * value_p,int * error_p)30386e41e22SErik van der Kouwe static int http_parse_int_header(
30486e41e22SErik van der Kouwe 	const char *data,
30586e41e22SErik van der Kouwe 	size_t index,
30686e41e22SErik van der Kouwe 	size_t index_end,
30786e41e22SErik van der Kouwe 	int *value_p,
30886e41e22SErik van der Kouwe 	int *error_p) {
30986e41e22SErik van der Kouwe 	int value = 0;
31086e41e22SErik van der Kouwe 
31186e41e22SErik van der Kouwe 	assert(data);
31286e41e22SErik van der Kouwe 	assert(index <= index_end);
31386e41e22SErik van der Kouwe 	assert(value_p);
31486e41e22SErik van der Kouwe 	assert(error_p);
31586e41e22SErik van der Kouwe 	assert(!*error_p);
31686e41e22SErik van der Kouwe 
31786e41e22SErik van der Kouwe 	while (index < index_end && is_numeric(data[index])) {
31886e41e22SErik van der Kouwe 		value = value * 10 + (data[index++] - '0');
31986e41e22SErik van der Kouwe 	}
32086e41e22SErik van der Kouwe 
32186e41e22SErik van der Kouwe 	while (index < index_end && is_whitespace_or_linebreak(data[index])) {
32286e41e22SErik van der Kouwe 		index++;
32386e41e22SErik van der Kouwe 	}
32486e41e22SErik van der Kouwe 
32586e41e22SErik van der Kouwe 	if (index < index_end) {
32686e41e22SErik van der Kouwe 		errno = 0;
327*17fbdaf5SErik van der Kouwe 		efmt("HTTP error: bad numeric header value: \"%.*s\"",
32886e41e22SErik van der Kouwe 			(int) (index_end - index), data + index);
32986e41e22SErik van der Kouwe 		*error_p = 1;
33086e41e22SErik van der Kouwe 		return 0;
33186e41e22SErik van der Kouwe 	}
33286e41e22SErik van der Kouwe 
33386e41e22SErik van der Kouwe 	*value_p = value;
33486e41e22SErik van der Kouwe 	return 1;
33586e41e22SErik van der Kouwe }
33686e41e22SErik van der Kouwe 
http_response_complete(const char * data,size_t len,int * error_p,int * code_p,size_t * index_body_p)33786e41e22SErik van der Kouwe static int http_response_complete(
33886e41e22SErik van der Kouwe 	const char *data,
33986e41e22SErik van der Kouwe 	size_t len,
34086e41e22SErik van der Kouwe 	int *error_p,
34186e41e22SErik van der Kouwe 	int *code_p,
34286e41e22SErik van der Kouwe 	size_t *index_body_p) {
34386e41e22SErik van der Kouwe 	int content_length = -1;
34486e41e22SErik van der Kouwe 	size_t index = 0, index_line;
34586e41e22SErik van der Kouwe 	size_t index_value;
34686e41e22SErik van der Kouwe 	size_t linelen;
34786e41e22SErik van der Kouwe 
34886e41e22SErik van der Kouwe 	assert(data);
34986e41e22SErik van der Kouwe 	assert(error_p);
35086e41e22SErik van der Kouwe 	assert(!*error_p);
35186e41e22SErik van der Kouwe 	assert(code_p);
35286e41e22SErik van der Kouwe 	assert(index_body_p);
35386e41e22SErik van der Kouwe 
35486e41e22SErik van der Kouwe 	/* parse status line */
35586e41e22SErik van der Kouwe 	if (!http_get_status_line(data, len, &index, error_p, code_p)) {
35686e41e22SErik van der Kouwe 		return 0;
35786e41e22SErik van der Kouwe 	}
35886e41e22SErik van der Kouwe 
35986e41e22SErik van der Kouwe 	/* parse headers */
36086e41e22SErik van der Kouwe 	for (;;) {
36186e41e22SErik van der Kouwe 		index_line = index;
36286e41e22SErik van der Kouwe 		if (!http_get_header_line(data, len, &index, &linelen)) {
36386e41e22SErik van der Kouwe 			return 0;
36486e41e22SErik van der Kouwe 		}
36586e41e22SErik van der Kouwe 		if (linelen == 0) break;
36686e41e22SErik van der Kouwe 		if (http_header_is(data, len, index_line,
36786e41e22SErik van der Kouwe 			"Content-Length", &index_value)) {
36886e41e22SErik van der Kouwe 			if (!http_parse_int_header(data, index_value,
36986e41e22SErik van der Kouwe 				index_line + linelen, &content_length,
37086e41e22SErik van der Kouwe 				error_p)) {
37186e41e22SErik van der Kouwe 				return 0;
37286e41e22SErik van der Kouwe 			}
37386e41e22SErik van der Kouwe 		}
37486e41e22SErik van der Kouwe 	}
37586e41e22SErik van der Kouwe 
37686e41e22SErik van der Kouwe 	/* do we know how long the response will be? */
37786e41e22SErik van der Kouwe 	if (content_length < 0) {
37886e41e22SErik van der Kouwe 		errno = 0;
379*17fbdaf5SErik van der Kouwe 		efmt("HTTP error: missing Content-Length header "
38086e41e22SErik van der Kouwe 			"(maybe Transfer-Encoding is specified instead "
38186e41e22SErik van der Kouwe 			"but this is currently unsupported)");
38286e41e22SErik van der Kouwe 		goto error;
38386e41e22SErik van der Kouwe 	}
38486e41e22SErik van der Kouwe 
38586e41e22SErik van der Kouwe 	/* check whether the amount of data is correct */
38686e41e22SErik van der Kouwe 	if (len > index + content_length) {
38786e41e22SErik van der Kouwe 		errno = 0;
388*17fbdaf5SErik van der Kouwe 		efmt("HTTP error: more data received than expected");
38986e41e22SErik van der Kouwe 		goto error;
39086e41e22SErik van der Kouwe 	}
39186e41e22SErik van der Kouwe 
39286e41e22SErik van der Kouwe 	*index_body_p = index;
39386e41e22SErik van der Kouwe 	return len == index + content_length;
39486e41e22SErik van der Kouwe 
39586e41e22SErik van der Kouwe error:
39686e41e22SErik van der Kouwe 	*error_p = 1;
39786e41e22SErik van der Kouwe 	*code_p = 0;
39886e41e22SErik van der Kouwe 	*index_body_p = 0;
39986e41e22SErik van der Kouwe 	return 0;
40086e41e22SErik van der Kouwe }
40186e41e22SErik van der Kouwe 
http_recv_response(int fd,void (* callback_verify)(const void * data,size_t size),size_t chunksize)40286e41e22SErik van der Kouwe static void http_recv_response(
40386e41e22SErik van der Kouwe 	int fd,
40486e41e22SErik van der Kouwe 	void (* callback_verify)(const void *data, size_t size),
40586e41e22SErik van der Kouwe 	size_t chunksize) {
40686e41e22SErik van der Kouwe 	int code;
40786e41e22SErik van der Kouwe 	char *data;
40886e41e22SErik van der Kouwe 	size_t datalen = 0, datasize = 0;
40986e41e22SErik van der Kouwe 	int error = 0;
41086e41e22SErik van der Kouwe 	size_t index_body;
41186e41e22SErik van der Kouwe 	ssize_t r;
41286e41e22SErik van der Kouwe 
41386e41e22SErik van der Kouwe 	assert(fd >= 0);
41486e41e22SErik van der Kouwe 	assert(callback_verify);
41586e41e22SErik van der Kouwe 	assert(chunksize > 0);
41686e41e22SErik van der Kouwe 
41786e41e22SErik van der Kouwe 	data = NULL;
41886e41e22SErik van der Kouwe 	for (;;) {
41986e41e22SErik van der Kouwe 		/* make room for another chunk in the buffer if needed */
42086e41e22SErik van der Kouwe 		if (datasize < datalen + chunksize) {
42186e41e22SErik van der Kouwe 			datasize = (datalen + chunksize) * 2;
42286e41e22SErik van der Kouwe 			REALLOC(data, datasize);
42386e41e22SErik van der Kouwe 		}
42486e41e22SErik van der Kouwe 
42586e41e22SErik van der Kouwe 		/* read a chunk of data */
42686e41e22SErik van der Kouwe 		errno = 0;
42786e41e22SErik van der Kouwe 		r = read(fd, data + datalen, chunksize);
42886e41e22SErik van der Kouwe 		if (r < 0 || (size_t) r > chunksize) {
429*17fbdaf5SErik van der Kouwe 			efmt("read of %zu bytes failed with result %zd",
43086e41e22SErik van der Kouwe 				chunksize, r);
43186e41e22SErik van der Kouwe 			goto cleanup;
43286e41e22SErik van der Kouwe 		}
43386e41e22SErik van der Kouwe 		datalen += r;
43486e41e22SErik van der Kouwe 
43586e41e22SErik van der Kouwe 		/* if we received all headers+data, we are done */
43686e41e22SErik van der Kouwe 		if (http_response_complete(data, datalen, &error, &code,
43786e41e22SErik van der Kouwe 			&index_body)) {
43886e41e22SErik van der Kouwe 			break;
43986e41e22SErik van der Kouwe 		}
44086e41e22SErik van der Kouwe 		if (error) goto cleanup;
44186e41e22SErik van der Kouwe 
44286e41e22SErik van der Kouwe 		/* check for premature disconnection */
44386e41e22SErik van der Kouwe 		if (r == 0) {
44486e41e22SErik van der Kouwe 			errno = 0;
445*17fbdaf5SErik van der Kouwe 			efmt("server disconnected even though the response "
44686e41e22SErik van der Kouwe 				"seems to be incomplete");
44786e41e22SErik van der Kouwe 			goto cleanup;
44886e41e22SErik van der Kouwe 		}
44986e41e22SErik van der Kouwe 	}
45086e41e22SErik van der Kouwe 
45186e41e22SErik van der Kouwe 	dbgprintf("received response:\n%.*s", (int) datalen, data);
45286e41e22SErik van der Kouwe 
45386e41e22SErik van der Kouwe 	assert(index_body <= datalen);
45486e41e22SErik van der Kouwe 	if (code == 200) {
45586e41e22SErik van der Kouwe 		callback_verify(data + index_body, datalen - index_body);
45686e41e22SErik van der Kouwe 	} else {
45786e41e22SErik van der Kouwe 		errno = 0;
458*17fbdaf5SErik van der Kouwe 		efmt("unexpected HTTP status code %d", code);
45986e41e22SErik van der Kouwe 	}
46086e41e22SErik van der Kouwe 
46186e41e22SErik van der Kouwe cleanup:
46286e41e22SErik van der Kouwe 	if (data) free(data);
46386e41e22SErik van der Kouwe }
46486e41e22SErik van der Kouwe 
http_test(const struct url * url,size_t chunksize,int bigrequest,int delay,int withshutdown)46586e41e22SErik van der Kouwe static void http_test(
46686e41e22SErik van der Kouwe 	const struct url *url,
46786e41e22SErik van der Kouwe 	size_t chunksize,
46886e41e22SErik van der Kouwe 	int bigrequest,
46986e41e22SErik van der Kouwe 	int delay,
47086e41e22SErik van der Kouwe 	int withshutdown) {
47186e41e22SErik van der Kouwe 	int fd;
47286e41e22SErik van der Kouwe 
47386e41e22SErik van der Kouwe 	assert(url);
47486e41e22SErik van der Kouwe 	assert(chunksize > 0);
47586e41e22SErik van der Kouwe 
47686e41e22SErik van der Kouwe 	dbgprintf("attempting download from http://%s:%d%s, "
47786e41e22SErik van der Kouwe 		"chunksize=%zu, bigrequest=%d, delay=%d, withshutdown=%d\n",
47886e41e22SErik van der Kouwe 		url->host, url->port, url->path, chunksize, bigrequest,
47986e41e22SErik van der Kouwe 		delay, withshutdown);
48086e41e22SErik van der Kouwe 
48186e41e22SErik van der Kouwe 	fd = http_connect(url->host, url->port);
48286e41e22SErik van der Kouwe 	if (fd < 0) return;
48386e41e22SErik van der Kouwe 
48486e41e22SErik van der Kouwe 	http_send_request(fd, url->host, url->path, chunksize, bigrequest);
48586e41e22SErik van der Kouwe 
48686e41e22SErik van der Kouwe 	errno = 0;
48786e41e22SErik van der Kouwe 	if (withshutdown && shutdown(fd, SHUT_WR) != 0) {
488*17fbdaf5SErik van der Kouwe 		efmt("shutdown failed");
48986e41e22SErik van der Kouwe 	}
49086e41e22SErik van der Kouwe 
49186e41e22SErik van der Kouwe 	if (delay) sleep(1);
49286e41e22SErik van der Kouwe 	http_recv_response(fd, url->callback_verify, chunksize);
49386e41e22SErik van der Kouwe 
49486e41e22SErik van der Kouwe 	CLOSE(fd);
49586e41e22SErik van der Kouwe 
49686e41e22SErik van der Kouwe 	dbgprintf("download attempt completed\n");
49786e41e22SErik van der Kouwe }
49886e41e22SErik van der Kouwe 
49986e41e22SErik van der Kouwe static int child_count;
50086e41e22SErik van der Kouwe 
http_test_fork(const struct url * url,size_t chunksize,int bigrequest,int delay,int withshutdown)50186e41e22SErik van der Kouwe static void http_test_fork(
50286e41e22SErik van der Kouwe 	const struct url *url,
50386e41e22SErik van der Kouwe 	size_t chunksize,
50486e41e22SErik van der Kouwe 	int bigrequest,
50586e41e22SErik van der Kouwe 	int delay,
50686e41e22SErik van der Kouwe 	int withshutdown) {
50786e41e22SErik van der Kouwe 	int errctold;
50886e41e22SErik van der Kouwe 	pid_t pid;
50986e41e22SErik van der Kouwe 
51086e41e22SErik van der Kouwe 	assert(url);
51186e41e22SErik van der Kouwe 	assert(chunksize > 0);
51286e41e22SErik van der Kouwe 
51386e41e22SErik van der Kouwe 	errno = 0;
51486e41e22SErik van der Kouwe 	pid = fork();
51586e41e22SErik van der Kouwe 	if (pid < 0) {
516*17fbdaf5SErik van der Kouwe 		efmt("fork failed");
51786e41e22SErik van der Kouwe 		return;
51886e41e22SErik van der Kouwe 	}
51986e41e22SErik van der Kouwe 
52086e41e22SErik van der Kouwe 	if (pid > 0) {
52186e41e22SErik van der Kouwe 		child_count++;
52286e41e22SErik van der Kouwe 		return;
52386e41e22SErik van der Kouwe 	}
52486e41e22SErik van der Kouwe 
52586e41e22SErik van der Kouwe 	errctold = errct;
52686e41e22SErik van der Kouwe 	http_test(
52786e41e22SErik van der Kouwe 		url,
52886e41e22SErik van der Kouwe 		chunksize,
52986e41e22SErik van der Kouwe 		bigrequest,
53086e41e22SErik van der Kouwe 		delay,
53186e41e22SErik van der Kouwe 		withshutdown);
53286e41e22SErik van der Kouwe 	assert(errct >= errctold);
53386e41e22SErik van der Kouwe 	exit(errct - errctold);
53486e41e22SErik van der Kouwe }
53586e41e22SErik van der Kouwe 
wait_all(void)53686e41e22SErik van der Kouwe static void wait_all(void) {
53786e41e22SErik van der Kouwe 	int exitcode, status;
53886e41e22SErik van der Kouwe 	pid_t pid;
53986e41e22SErik van der Kouwe 
54086e41e22SErik van der Kouwe 	while (child_count > 0) {
54186e41e22SErik van der Kouwe 		errno = 0;
54286e41e22SErik van der Kouwe 		pid = waitpid(-1, &status, 0);
54386e41e22SErik van der Kouwe 		if (pid <= 0) {
544*17fbdaf5SErik van der Kouwe 			efmt("waitpid failed");
54586e41e22SErik van der Kouwe 			return;
54686e41e22SErik van der Kouwe 		}
54786e41e22SErik van der Kouwe 		if (WIFEXITED(status)) {
54886e41e22SErik van der Kouwe 			exitcode = WEXITSTATUS(status);
54986e41e22SErik van der Kouwe 			dbgprintf("child %d completed with exit code %d\n",
55086e41e22SErik van der Kouwe 				(int) pid, exitcode);
55186e41e22SErik van der Kouwe 			if (exitcode >= 0) {
55286e41e22SErik van der Kouwe 				errct += exitcode;
55386e41e22SErik van der Kouwe 			} else {
554*17fbdaf5SErik van der Kouwe 				efmt("child has negative exit code %d",
55586e41e22SErik van der Kouwe 					exitcode);
55686e41e22SErik van der Kouwe 			}
55786e41e22SErik van der Kouwe 		} else if (WIFSIGNALED(status)) {
55886e41e22SErik van der Kouwe 			dbgprintf("child %d killed by signal %d\n",
55986e41e22SErik van der Kouwe 				(int) pid, WTERMSIG(status));
560*17fbdaf5SErik van der Kouwe 			efmt("child killed by signal %d", WTERMSIG(status));
56186e41e22SErik van der Kouwe 		} else {
56286e41e22SErik van der Kouwe 			dbgprintf("child %d gone with status 0x%x\n",
56386e41e22SErik van der Kouwe 				(int) pid, status);
564*17fbdaf5SErik van der Kouwe 			efmt("child gone, but neither exit nor signal");
56586e41e22SErik van der Kouwe 		}
56686e41e22SErik van der Kouwe 		child_count--;
56786e41e22SErik van der Kouwe 	}
56886e41e22SErik van der Kouwe 
56986e41e22SErik van der Kouwe 	errno = 0;
57086e41e22SErik van der Kouwe 	if (waitpid(-1, &status, 0) != -1 || errno != ECHILD) {
571*17fbdaf5SErik van der Kouwe 		efmt("waitpid should have returned ECHILD");
57286e41e22SErik van der Kouwe 	}
57386e41e22SErik van der Kouwe }
57486e41e22SErik van der Kouwe 
57586e41e22SErik van der Kouwe #define OPTION_BIGREQUEST	(1 << 0)
57686e41e22SErik van der Kouwe #define OPTION_DELAY		(1 << 1)
57786e41e22SErik van der Kouwe #define OPTION_SHUTDOWN		(1 << 2)
57886e41e22SErik van der Kouwe 
http_test_all(int multiproc)57986e41e22SErik van der Kouwe static void http_test_all(int multiproc) {
58086e41e22SErik van der Kouwe 	static const size_t chunksizes[] = { 1, 1024, 65536 };
58186e41e22SErik van der Kouwe 	static const int optionsets[] = {
58286e41e22SErik van der Kouwe 		0,
58386e41e22SErik van der Kouwe 		OPTION_BIGREQUEST,
58486e41e22SErik van der Kouwe 		OPTION_DELAY,
58586e41e22SErik van der Kouwe 		OPTION_SHUTDOWN,
58686e41e22SErik van der Kouwe 		OPTION_BIGREQUEST | OPTION_DELAY | OPTION_SHUTDOWN,
58786e41e22SErik van der Kouwe 	};
58886e41e22SErik van der Kouwe 	int chunksizeindex;
58986e41e22SErik van der Kouwe 	int options;
59086e41e22SErik van der Kouwe 	int optionindex;
59186e41e22SErik van der Kouwe 	int urlindex;
59286e41e22SErik van der Kouwe 
59386e41e22SErik van der Kouwe 	for (urlindex = 0; urlindex < URL_COUNT; urlindex++) {
59486e41e22SErik van der Kouwe 	for (chunksizeindex = 0; chunksizeindex < 3; chunksizeindex++) {
59586e41e22SErik van der Kouwe 	for (optionindex = 0; optionindex < 3; optionindex++) {
59686e41e22SErik van der Kouwe 		options = optionsets[optionindex];
59786e41e22SErik van der Kouwe 		(multiproc ? http_test_fork : http_test)(
59886e41e22SErik van der Kouwe 			&urls[urlindex],
59986e41e22SErik van der Kouwe 			chunksizes[chunksizeindex],
60086e41e22SErik van der Kouwe 			options & OPTION_BIGREQUEST,
60186e41e22SErik van der Kouwe 			options & OPTION_DELAY,
60286e41e22SErik van der Kouwe 			options & OPTION_SHUTDOWN);
60386e41e22SErik van der Kouwe 	}
60486e41e22SErik van der Kouwe 	}
60586e41e22SErik van der Kouwe 	}
60686e41e22SErik van der Kouwe 
60786e41e22SErik van der Kouwe 	wait_all();
60886e41e22SErik van der Kouwe }
60986e41e22SErik van der Kouwe 
verify_data(const void * httpdata,size_t httpsize,const void * refdata,size_t refsize,const char * path)61086e41e22SErik van der Kouwe static void verify_data(
61186e41e22SErik van der Kouwe 	const void *httpdata, size_t httpsize,
61286e41e22SErik van der Kouwe 	const void *refdata, size_t refsize,
61386e41e22SErik van der Kouwe 	const char *path) {
61486e41e22SErik van der Kouwe 
61586e41e22SErik van der Kouwe 	assert(httpdata);
61686e41e22SErik van der Kouwe 	assert(refdata);
61786e41e22SErik van der Kouwe 	assert(path);
61886e41e22SErik van der Kouwe 
61986e41e22SErik van der Kouwe 	if (httpsize != refsize) {
62086e41e22SErik van der Kouwe 		errno = 0;
621*17fbdaf5SErik van der Kouwe 		efmt("download from http://%s:%d%s returned wrong number "
62286e41e22SErik van der Kouwe 			"of bytes: %zd (expected %zd)",
62386e41e22SErik van der Kouwe 			HOST, PORT, path, httpsize, refsize);
62486e41e22SErik van der Kouwe 	} else if (memcmp(httpdata, refdata, refsize) != 0) {
62586e41e22SErik van der Kouwe 		errno = 0;
626*17fbdaf5SErik van der Kouwe 		efmt("download from http://%s:%d%s returned wrong data",
62786e41e22SErik van der Kouwe 			HOST, PORT, path);
62886e41e22SErik van der Kouwe 	}
62986e41e22SErik van der Kouwe }
63086e41e22SErik van der Kouwe 
callback_verify_path1(const void * data,size_t size)63186e41e22SErik van der Kouwe static void callback_verify_path1(const void *data, size_t size) {
63286e41e22SErik van der Kouwe 	verify_data(data, size, PATH1_DATA, strlen(PATH1_DATA), PATH1);
63386e41e22SErik van der Kouwe }
63486e41e22SErik van der Kouwe 
callback_verify_path2(const void * data,size_t size)63586e41e22SErik van der Kouwe static void callback_verify_path2(const void *data, size_t size) {
63686e41e22SErik van der Kouwe 	unsigned short buf[65536];
63786e41e22SErik van der Kouwe 	int i;
63886e41e22SErik van der Kouwe 
63986e41e22SErik van der Kouwe 	for (i = 0; i < 65536; i++) buf[i] = htons(i);
64086e41e22SErik van der Kouwe 
64186e41e22SErik van der Kouwe 	verify_data(data, size, buf, sizeof(buf), PATH2);
64286e41e22SErik van der Kouwe }
64386e41e22SErik van der Kouwe 
main(int argc,char ** argv)64486e41e22SErik van der Kouwe int main(int argc, char **argv)
64586e41e22SErik van der Kouwe {
64686e41e22SErik van der Kouwe 	int use_network;
64786e41e22SErik van der Kouwe 
64886e41e22SErik van der Kouwe 	start(82);
64986e41e22SErik van der Kouwe 
65086e41e22SErik van der Kouwe 	use_network = get_setting_use_network();
65186e41e22SErik van der Kouwe 	if (use_network) {
65286e41e22SErik van der Kouwe 		http_test_all(0 /* multiproc */);
65386e41e22SErik van der Kouwe 		http_test_all(1 /* multiproc */);
65486e41e22SErik van der Kouwe 	} else {
65586e41e22SErik van der Kouwe 		dbgprintf("test disabled, set USENETWORK=yes to enable\n");
65686e41e22SErik van der Kouwe 	}
65786e41e22SErik van der Kouwe 
65886e41e22SErik van der Kouwe 	quit();
65986e41e22SErik van der Kouwe 	return 0;
66086e41e22SErik van der Kouwe }
661