Add test82 (HTTP)
This test connects to a remote HTTP server to retrieve files, using various chunk sizes and concurrency settings to exercise the network stack. The test is only performed is USENETWORK=yes. This test requires the following URLs to remain available: http://test82.minix3.org/test1.txt and http://test82.minix3.org/test2.bin. The former contains a 'Hello world' message followed by a newline, the latter all 16-bit values in increasing order, using big-endian notation. Change-Id: I696106482fb1658f9657be2b6845a1b37a3d6172
This commit is contained in:
parent
294d159017
commit
86e41e22cf
4 changed files with 690 additions and 3 deletions
|
@ -6331,6 +6331,7 @@
|
||||||
./usr/tests/minix-posix/test8 minix-sys
|
./usr/tests/minix-posix/test8 minix-sys
|
||||||
./usr/tests/minix-posix/test80 minix-sys
|
./usr/tests/minix-posix/test80 minix-sys
|
||||||
./usr/tests/minix-posix/test81 minix-sys
|
./usr/tests/minix-posix/test81 minix-sys
|
||||||
|
./usr/tests/minix-posix/test82 minix-sys
|
||||||
./usr/tests/minix-posix/test9 minix-sys
|
./usr/tests/minix-posix/test9 minix-sys
|
||||||
./usr/tests/minix-posix/testinterp minix-sys
|
./usr/tests/minix-posix/testinterp minix-sys
|
||||||
./usr/tests/minix-posix/testisofs minix-sys
|
./usr/tests/minix-posix/testisofs minix-sys
|
||||||
|
|
|
@ -59,7 +59,7 @@ MINIX_TESTS= \
|
||||||
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
|
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
|
||||||
41 42 43 44 45 46 48 49 50 52 53 54 55 56 58 59 60 \
|
41 42 43 44 45 46 48 49 50 52 53 54 55 56 58 59 60 \
|
||||||
61 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \
|
61 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \
|
||||||
81
|
81 82
|
||||||
|
|
||||||
.if ${MACHINE_ARCH} == "i386"
|
.if ${MACHINE_ARCH} == "i386"
|
||||||
MINIX_TESTS+= \
|
MINIX_TESTS+= \
|
||||||
|
|
|
@ -16,7 +16,7 @@ failed=`expr 0` # count number of tests that failed
|
||||||
skipped=`expr 0` # count number of tests that were skipped
|
skipped=`expr 0` # count number of tests that were skipped
|
||||||
total=`expr 0` # total number of tests tried
|
total=`expr 0` # total number of tests tried
|
||||||
badones= # list of tests that failed
|
badones= # list of tests that failed
|
||||||
export USENETWORK # set to "yes" for test48 to use the network
|
export USENETWORK # set to "yes" for test48+82 to use the network
|
||||||
|
|
||||||
# In the lists below, shell scripts should be listed without ".sh" suffix
|
# In the lists below, shell scripts should be listed without ".sh" suffix
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ alltests="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
|
||||||
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
|
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
|
||||||
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \
|
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \
|
||||||
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \
|
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \
|
||||||
81 sh1 sh2 interp mfs isofs vnd"
|
81 82 sh1 sh2 interp mfs isofs vnd"
|
||||||
tests_no=`expr 0`
|
tests_no=`expr 0`
|
||||||
|
|
||||||
# If root, make sure the setuid tests have the correct permissions
|
# If root, make sure the setuid tests have the correct permissions
|
||||||
|
|
686
minix/tests/test82.c
Normal file
686
minix/tests/test82.c
Normal file
|
@ -0,0 +1,686 @@
|
||||||
|
/*
|
||||||
|
* test82: test HTTP with a remote server (is $USENETWORK="yes")
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DEBUG 0
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
#define dbgprintf(...) do { \
|
||||||
|
fprintf(stderr, "[%s:%s:%d %d] ", \
|
||||||
|
__FILE__, __FUNCTION__, \
|
||||||
|
__LINE__, getpid()); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
fflush(stderr); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define dbgprintf(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define FAIL(...) fail(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
||||||
|
#define CLOSE(fd) do { assert(fd >= 0); if (close((fd)) != 0) FAIL("close failed"); } while (0);
|
||||||
|
#define REALLOC(p, size) do { p = realloc(p, size); if (!p) FAIL("realloc of %zu bytes failed", size); } while (0);
|
||||||
|
|
||||||
|
#define HOST "test82.minix3.org"
|
||||||
|
#define PORT 80
|
||||||
|
#define PATH1 "/test1.txt"
|
||||||
|
#define PATH1_DATA "Hello world\n"
|
||||||
|
#define PATH2 "/test2.bin"
|
||||||
|
|
||||||
|
static void callback_verify_path1(const void *data, size_t size);
|
||||||
|
static void callback_verify_path2(const void *data, size_t size);
|
||||||
|
|
||||||
|
#define URL_COUNT 2
|
||||||
|
|
||||||
|
struct url {
|
||||||
|
const char *host;
|
||||||
|
int port;
|
||||||
|
const char *path;
|
||||||
|
void (* callback_verify)(const void *data, size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct url urls[URL_COUNT] = {
|
||||||
|
{ HOST, PORT, PATH1, callback_verify_path1 },
|
||||||
|
{ HOST, PORT, PATH2, callback_verify_path2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void fail(const char *file, const char *func, int line,
|
||||||
|
const char *fmt, ...) __attribute__ ((format(printf, 4, 5)));
|
||||||
|
|
||||||
|
static void fail(const char *file, const char *func, int line,
|
||||||
|
const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
char buf[1024];
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
assert(file);
|
||||||
|
assert(func);
|
||||||
|
assert(fmt);
|
||||||
|
|
||||||
|
len = snprintf(buf, sizeof(buf), "[%s:%s:%d] ", file, func, line);
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
len += vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
snprintf(buf + len, sizeof(buf) - len, " errno=%d error=%s",
|
||||||
|
errno, strerror(errno));
|
||||||
|
|
||||||
|
em(line, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int http_connect(const char *host, int port) {
|
||||||
|
struct addrinfo *addr = NULL;
|
||||||
|
int fd = -1;
|
||||||
|
struct addrinfo hints = {
|
||||||
|
.ai_family = PF_INET,
|
||||||
|
.ai_socktype = SOCK_STREAM,
|
||||||
|
};
|
||||||
|
char serv[12];
|
||||||
|
|
||||||
|
assert(host);
|
||||||
|
|
||||||
|
snprintf(serv, sizeof(serv), "%d", port);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if (getaddrinfo(host, serv, &hints, &addr) != 0 || !addr) {
|
||||||
|
FAIL("host %s not found", host);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
FAIL("cannot create socket");
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(fd, addr->ai_addr, addr->ai_addrlen) != 0) {
|
||||||
|
FAIL("cannot connect to %s:%d", host, port);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(addr);
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
if (fd >= 0) CLOSE(fd);
|
||||||
|
if (addr) freeaddrinfo(addr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_chunked(
|
||||||
|
int fd,
|
||||||
|
const char *data,
|
||||||
|
size_t size,
|
||||||
|
size_t chunksize) {
|
||||||
|
ssize_t r;
|
||||||
|
size_t s;
|
||||||
|
|
||||||
|
assert(fd >= 0);
|
||||||
|
assert(data);
|
||||||
|
assert(chunksize > 0);
|
||||||
|
|
||||||
|
while (size > 0) {
|
||||||
|
s = chunksize;
|
||||||
|
if (s > size) s = size;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = write(fd, data, s);
|
||||||
|
if (r <= 0 || (size_t) r > s) {
|
||||||
|
errno = 0;
|
||||||
|
FAIL("write of %zu bytes failed with result %zd", s, r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data += r;
|
||||||
|
size -= r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void http_send_request(
|
||||||
|
int fd,
|
||||||
|
const char *host,
|
||||||
|
const char *path,
|
||||||
|
size_t chunksize,
|
||||||
|
int bigrequest) {
|
||||||
|
char buf[8192];
|
||||||
|
size_t len;
|
||||||
|
int lineno;
|
||||||
|
|
||||||
|
assert(fd >= 0);
|
||||||
|
assert(host);
|
||||||
|
assert(path);
|
||||||
|
assert(chunksize > 0);
|
||||||
|
|
||||||
|
/* http://tools.ietf.org/html/rfc2616#section-5 */
|
||||||
|
len = snprintf(buf, sizeof(buf),
|
||||||
|
"GET %s HTTP/1.1\r\n"
|
||||||
|
"Host: %s\r\n",
|
||||||
|
path, host);
|
||||||
|
if (bigrequest) {
|
||||||
|
lineno = 0;
|
||||||
|
while (len + 24 < sizeof(buf)) {
|
||||||
|
len += snprintf(buf + len, sizeof(buf) - len,
|
||||||
|
"X-Padding%d: %d\r\n",
|
||||||
|
lineno, lineno);
|
||||||
|
lineno++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len += snprintf(buf + len, sizeof(buf) - len, "\r\n");
|
||||||
|
|
||||||
|
dbgprintf("sending request:\n%.*s", (int) len, buf);
|
||||||
|
write_chunked(fd, buf, len, chunksize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_whitespace(char c) {
|
||||||
|
return c == ' ' || c == '\t';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_whitespace_or_linebreak(char c) {
|
||||||
|
return is_whitespace(c) || c == '\r' || c == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_numeric(char c) {
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int http_get_header_line(
|
||||||
|
const char *data,
|
||||||
|
size_t len,
|
||||||
|
size_t *index_p,
|
||||||
|
size_t *linelen_p) {
|
||||||
|
int has_cr;
|
||||||
|
size_t index;
|
||||||
|
size_t linelen;
|
||||||
|
|
||||||
|
assert(data);
|
||||||
|
assert(index_p);
|
||||||
|
assert(*index_p <= len);
|
||||||
|
assert(linelen_p);
|
||||||
|
|
||||||
|
/* starting the next line with whitespace means the line is continued */
|
||||||
|
index = *index_p;
|
||||||
|
do {
|
||||||
|
while (index < len && data[index] != '\n') index++;
|
||||||
|
if (index >= len) goto notfound;
|
||||||
|
index++;
|
||||||
|
} while (index < len && is_whitespace(data[index]));
|
||||||
|
|
||||||
|
/* exclude LF or CR+LF from line length */
|
||||||
|
assert(index - 1 >= *index_p && data[index - 1] == '\n');
|
||||||
|
has_cr = (index - 2 >= *index_p) && data[index - 2] == '\r';
|
||||||
|
linelen = index - *index_p - (has_cr ? 2 : 1);
|
||||||
|
|
||||||
|
/* if LF is the last character in the buffer, the line may be continued
|
||||||
|
* when more data is retrieved unless we reached the end of the headers
|
||||||
|
*/
|
||||||
|
if (index >= len && linelen > 0) goto notfound;
|
||||||
|
|
||||||
|
*linelen_p = linelen;
|
||||||
|
*index_p = index;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
notfound:
|
||||||
|
*linelen_p = 0;
|
||||||
|
*index_p = index;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int http_get_status_line(
|
||||||
|
const char *data,
|
||||||
|
size_t len,
|
||||||
|
size_t *index_p,
|
||||||
|
int *error_p,
|
||||||
|
int *code_p) {
|
||||||
|
int code, i;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
assert(data);
|
||||||
|
assert(index_p);
|
||||||
|
assert(*index_p <= len);
|
||||||
|
assert(error_p);
|
||||||
|
assert(*error_p == 0);
|
||||||
|
assert(code_p);
|
||||||
|
|
||||||
|
/* skip leading whitespace/blank lines */
|
||||||
|
index = *index_p;
|
||||||
|
while (index < len && is_whitespace_or_linebreak(data[index])) index++;
|
||||||
|
|
||||||
|
/* parse version */
|
||||||
|
while (index < len && !is_whitespace(data[index])) index++;
|
||||||
|
|
||||||
|
/* skip separator */
|
||||||
|
while (index < len && is_whitespace(data[index])) index++;
|
||||||
|
|
||||||
|
/* parse status code */
|
||||||
|
code = 0;
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
if (index >= len) goto notfound;
|
||||||
|
if (!is_numeric(data[index])) {
|
||||||
|
errno = 0;
|
||||||
|
FAIL("HTTP error: bad status line: \"%.*s\"",
|
||||||
|
(int) (index - *index_p), data + *index_p);
|
||||||
|
*error_p = 1;
|
||||||
|
goto notfound;
|
||||||
|
}
|
||||||
|
code = code * 10 + (data[index++] - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip separator */
|
||||||
|
while (index < len && is_whitespace(data[index])) index++;
|
||||||
|
|
||||||
|
/* parse reason phrase */
|
||||||
|
while (index < len && data[index] != '\n') index++;
|
||||||
|
if (index >= len) goto notfound;
|
||||||
|
index++;
|
||||||
|
|
||||||
|
*code_p = code;
|
||||||
|
*index_p = index;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
notfound:
|
||||||
|
*code_p = 0;
|
||||||
|
*index_p = index;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int http_header_is(
|
||||||
|
const char *data,
|
||||||
|
size_t len,
|
||||||
|
size_t index,
|
||||||
|
const char *name,
|
||||||
|
size_t *index_value_p) {
|
||||||
|
size_t namelen;
|
||||||
|
|
||||||
|
assert(data);
|
||||||
|
assert(index <= len);
|
||||||
|
assert(name);
|
||||||
|
assert(index_value_p);
|
||||||
|
|
||||||
|
namelen = strlen(name);
|
||||||
|
if (index + namelen > len) goto notfound;
|
||||||
|
if (strncasecmp(data + index, name, namelen) != 0) goto notfound;
|
||||||
|
index += namelen;
|
||||||
|
while (index < len && is_whitespace(data[index])) index++;
|
||||||
|
if (index >= len || data[index] != ':') goto notfound;
|
||||||
|
index++;
|
||||||
|
|
||||||
|
while (index < len && is_whitespace(data[index])) index++;
|
||||||
|
*index_value_p = index;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
notfound:
|
||||||
|
*index_value_p = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int http_parse_int_header(
|
||||||
|
const char *data,
|
||||||
|
size_t index,
|
||||||
|
size_t index_end,
|
||||||
|
int *value_p,
|
||||||
|
int *error_p) {
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
assert(data);
|
||||||
|
assert(index <= index_end);
|
||||||
|
assert(value_p);
|
||||||
|
assert(error_p);
|
||||||
|
assert(!*error_p);
|
||||||
|
|
||||||
|
while (index < index_end && is_numeric(data[index])) {
|
||||||
|
value = value * 10 + (data[index++] - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
while (index < index_end && is_whitespace_or_linebreak(data[index])) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < index_end) {
|
||||||
|
errno = 0;
|
||||||
|
FAIL("HTTP error: bad numeric header value: \"%.*s\"",
|
||||||
|
(int) (index_end - index), data + index);
|
||||||
|
*error_p = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value_p = value;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int http_response_complete(
|
||||||
|
const char *data,
|
||||||
|
size_t len,
|
||||||
|
int *error_p,
|
||||||
|
int *code_p,
|
||||||
|
size_t *index_body_p) {
|
||||||
|
int content_length = -1;
|
||||||
|
size_t index = 0, index_line;
|
||||||
|
size_t index_value;
|
||||||
|
size_t linelen;
|
||||||
|
|
||||||
|
assert(data);
|
||||||
|
assert(error_p);
|
||||||
|
assert(!*error_p);
|
||||||
|
assert(code_p);
|
||||||
|
assert(index_body_p);
|
||||||
|
|
||||||
|
/* parse status line */
|
||||||
|
if (!http_get_status_line(data, len, &index, error_p, code_p)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse headers */
|
||||||
|
for (;;) {
|
||||||
|
index_line = index;
|
||||||
|
if (!http_get_header_line(data, len, &index, &linelen)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (linelen == 0) break;
|
||||||
|
if (http_header_is(data, len, index_line,
|
||||||
|
"Content-Length", &index_value)) {
|
||||||
|
if (!http_parse_int_header(data, index_value,
|
||||||
|
index_line + linelen, &content_length,
|
||||||
|
error_p)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do we know how long the response will be? */
|
||||||
|
if (content_length < 0) {
|
||||||
|
errno = 0;
|
||||||
|
FAIL("HTTP error: missing Content-Length header "
|
||||||
|
"(maybe Transfer-Encoding is specified instead "
|
||||||
|
"but this is currently unsupported)");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check whether the amount of data is correct */
|
||||||
|
if (len > index + content_length) {
|
||||||
|
errno = 0;
|
||||||
|
FAIL("HTTP error: more data received than expected");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
*index_body_p = index;
|
||||||
|
return len == index + content_length;
|
||||||
|
|
||||||
|
error:
|
||||||
|
*error_p = 1;
|
||||||
|
*code_p = 0;
|
||||||
|
*index_body_p = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void http_recv_response(
|
||||||
|
int fd,
|
||||||
|
void (* callback_verify)(const void *data, size_t size),
|
||||||
|
size_t chunksize) {
|
||||||
|
int code;
|
||||||
|
char *data;
|
||||||
|
size_t datalen = 0, datasize = 0;
|
||||||
|
int error = 0;
|
||||||
|
size_t index_body;
|
||||||
|
ssize_t r;
|
||||||
|
|
||||||
|
assert(fd >= 0);
|
||||||
|
assert(callback_verify);
|
||||||
|
assert(chunksize > 0);
|
||||||
|
|
||||||
|
data = NULL;
|
||||||
|
for (;;) {
|
||||||
|
/* make room for another chunk in the buffer if needed */
|
||||||
|
if (datasize < datalen + chunksize) {
|
||||||
|
datasize = (datalen + chunksize) * 2;
|
||||||
|
REALLOC(data, datasize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read a chunk of data */
|
||||||
|
errno = 0;
|
||||||
|
r = read(fd, data + datalen, chunksize);
|
||||||
|
if (r < 0 || (size_t) r > chunksize) {
|
||||||
|
FAIL("read of %zu bytes failed with result %zd",
|
||||||
|
chunksize, r);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
datalen += r;
|
||||||
|
|
||||||
|
/* if we received all headers+data, we are done */
|
||||||
|
if (http_response_complete(data, datalen, &error, &code,
|
||||||
|
&index_body)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (error) goto cleanup;
|
||||||
|
|
||||||
|
/* check for premature disconnection */
|
||||||
|
if (r == 0) {
|
||||||
|
errno = 0;
|
||||||
|
FAIL("server disconnected even though the response "
|
||||||
|
"seems to be incomplete");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbgprintf("received response:\n%.*s", (int) datalen, data);
|
||||||
|
|
||||||
|
assert(index_body <= datalen);
|
||||||
|
if (code == 200) {
|
||||||
|
callback_verify(data + index_body, datalen - index_body);
|
||||||
|
} else {
|
||||||
|
errno = 0;
|
||||||
|
FAIL("unexpected HTTP status code %d", code);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (data) free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void http_test(
|
||||||
|
const struct url *url,
|
||||||
|
size_t chunksize,
|
||||||
|
int bigrequest,
|
||||||
|
int delay,
|
||||||
|
int withshutdown) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
assert(url);
|
||||||
|
assert(chunksize > 0);
|
||||||
|
|
||||||
|
dbgprintf("attempting download from http://%s:%d%s, "
|
||||||
|
"chunksize=%zu, bigrequest=%d, delay=%d, withshutdown=%d\n",
|
||||||
|
url->host, url->port, url->path, chunksize, bigrequest,
|
||||||
|
delay, withshutdown);
|
||||||
|
|
||||||
|
fd = http_connect(url->host, url->port);
|
||||||
|
if (fd < 0) return;
|
||||||
|
|
||||||
|
http_send_request(fd, url->host, url->path, chunksize, bigrequest);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if (withshutdown && shutdown(fd, SHUT_WR) != 0) {
|
||||||
|
FAIL("shutdown failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delay) sleep(1);
|
||||||
|
http_recv_response(fd, url->callback_verify, chunksize);
|
||||||
|
|
||||||
|
CLOSE(fd);
|
||||||
|
|
||||||
|
dbgprintf("download attempt completed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int child_count;
|
||||||
|
|
||||||
|
static void http_test_fork(
|
||||||
|
const struct url *url,
|
||||||
|
size_t chunksize,
|
||||||
|
int bigrequest,
|
||||||
|
int delay,
|
||||||
|
int withshutdown) {
|
||||||
|
int errctold;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
assert(url);
|
||||||
|
assert(chunksize > 0);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
FAIL("fork failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid > 0) {
|
||||||
|
child_count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
errctold = errct;
|
||||||
|
http_test(
|
||||||
|
url,
|
||||||
|
chunksize,
|
||||||
|
bigrequest,
|
||||||
|
delay,
|
||||||
|
withshutdown);
|
||||||
|
assert(errct >= errctold);
|
||||||
|
exit(errct - errctold);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wait_all(void) {
|
||||||
|
int exitcode, status;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
while (child_count > 0) {
|
||||||
|
errno = 0;
|
||||||
|
pid = waitpid(-1, &status, 0);
|
||||||
|
if (pid <= 0) {
|
||||||
|
FAIL("waitpid failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
exitcode = WEXITSTATUS(status);
|
||||||
|
dbgprintf("child %d completed with exit code %d\n",
|
||||||
|
(int) pid, exitcode);
|
||||||
|
if (exitcode >= 0) {
|
||||||
|
errct += exitcode;
|
||||||
|
} else {
|
||||||
|
FAIL("child has negative exit code %d",
|
||||||
|
exitcode);
|
||||||
|
}
|
||||||
|
} else if (WIFSIGNALED(status)) {
|
||||||
|
dbgprintf("child %d killed by signal %d\n",
|
||||||
|
(int) pid, WTERMSIG(status));
|
||||||
|
FAIL("child killed by signal %d", WTERMSIG(status));
|
||||||
|
} else {
|
||||||
|
dbgprintf("child %d gone with status 0x%x\n",
|
||||||
|
(int) pid, status);
|
||||||
|
FAIL("child gone, but neither exit nor signal");
|
||||||
|
}
|
||||||
|
child_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if (waitpid(-1, &status, 0) != -1 || errno != ECHILD) {
|
||||||
|
FAIL("waitpid should have returned ECHILD");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OPTION_BIGREQUEST (1 << 0)
|
||||||
|
#define OPTION_DELAY (1 << 1)
|
||||||
|
#define OPTION_SHUTDOWN (1 << 2)
|
||||||
|
|
||||||
|
static void http_test_all(int multiproc) {
|
||||||
|
static const size_t chunksizes[] = { 1, 1024, 65536 };
|
||||||
|
static const int optionsets[] = {
|
||||||
|
0,
|
||||||
|
OPTION_BIGREQUEST,
|
||||||
|
OPTION_DELAY,
|
||||||
|
OPTION_SHUTDOWN,
|
||||||
|
OPTION_BIGREQUEST | OPTION_DELAY | OPTION_SHUTDOWN,
|
||||||
|
};
|
||||||
|
int chunksizeindex;
|
||||||
|
int options;
|
||||||
|
int optionindex;
|
||||||
|
int urlindex;
|
||||||
|
|
||||||
|
for (urlindex = 0; urlindex < URL_COUNT; urlindex++) {
|
||||||
|
for (chunksizeindex = 0; chunksizeindex < 3; chunksizeindex++) {
|
||||||
|
for (optionindex = 0; optionindex < 3; optionindex++) {
|
||||||
|
options = optionsets[optionindex];
|
||||||
|
(multiproc ? http_test_fork : http_test)(
|
||||||
|
&urls[urlindex],
|
||||||
|
chunksizes[chunksizeindex],
|
||||||
|
options & OPTION_BIGREQUEST,
|
||||||
|
options & OPTION_DELAY,
|
||||||
|
options & OPTION_SHUTDOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void verify_data(
|
||||||
|
const void *httpdata, size_t httpsize,
|
||||||
|
const void *refdata, size_t refsize,
|
||||||
|
const char *path) {
|
||||||
|
|
||||||
|
assert(httpdata);
|
||||||
|
assert(refdata);
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
if (httpsize != refsize) {
|
||||||
|
errno = 0;
|
||||||
|
FAIL("download from http://%s:%d%s returned wrong number "
|
||||||
|
"of bytes: %zd (expected %zd)",
|
||||||
|
HOST, PORT, path, httpsize, refsize);
|
||||||
|
} else if (memcmp(httpdata, refdata, refsize) != 0) {
|
||||||
|
errno = 0;
|
||||||
|
FAIL("download from http://%s:%d%s returned wrong data",
|
||||||
|
HOST, PORT, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void callback_verify_path1(const void *data, size_t size) {
|
||||||
|
verify_data(data, size, PATH1_DATA, strlen(PATH1_DATA), PATH1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void callback_verify_path2(const void *data, size_t size) {
|
||||||
|
unsigned short buf[65536];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 65536; i++) buf[i] = htons(i);
|
||||||
|
|
||||||
|
verify_data(data, size, buf, sizeof(buf), PATH2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int use_network;
|
||||||
|
|
||||||
|
start(82);
|
||||||
|
|
||||||
|
use_network = get_setting_use_network();
|
||||||
|
if (use_network) {
|
||||||
|
http_test_all(0 /* multiproc */);
|
||||||
|
http_test_all(1 /* multiproc */);
|
||||||
|
} else {
|
||||||
|
dbgprintf("test disabled, set USENETWORK=yes to enable\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
quit();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue