mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-18 10:59:45 +00:00
Added http client with unit tests
This commit is contained in:
1 parent
400084ea8a
commit
7a129c17ba
3 files changed
+233
-150
No files matched your search
+142
-77
@@ -318,7 +318,7 @@ enum connection_flags {
|
||||
CONN_HEADERS_SENT = 8, // User callback has sent HTTP headers
|
||||
CONN_BUFFER = 16, // CGI only. Holds data send until CGI prints
|
||||
// all HTTP headers
|
||||
CONN_CONNECTED = 32, // HTTP client has connected
|
||||
CONN_CONNECTING = 32, // HTTP client is doing non-blocking connect()
|
||||
CONN_LONG_RUNNING = 64 // Long-running URI handlers
|
||||
};
|
||||
|
||||
@@ -1154,6 +1154,7 @@ static void open_cgi_endpoint(struct connection *conn, const char *prog) {
|
||||
conn->endpoint_type = EP_CGI;
|
||||
conn->endpoint.cgi_sock = fds[0];
|
||||
spool(&conn->remote_iobuf, cgi_status, sizeof(cgi_status) - 1);
|
||||
conn->mg_conn.status_code = 200;
|
||||
conn->flags |= CONN_BUFFER;
|
||||
} else {
|
||||
closesocket(fds[0]);
|
||||
@@ -1193,18 +1194,23 @@ static void read_from_cgi(struct connection *conn) {
|
||||
}
|
||||
}
|
||||
memcpy(io->buf + 9, status, 3);
|
||||
conn->mg_conn.status_code = atoi(status);
|
||||
conn->flags &= ~CONN_BUFFER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void discard_leading_iobuf_bytes(struct iobuf *io, int n) {
|
||||
if (n >= 0 && n <= io->len) {
|
||||
memmove(io->buf, io->buf + n, io->len - n);
|
||||
io->len -= n;
|
||||
}
|
||||
}
|
||||
|
||||
static void forward_post_data(struct connection *conn) {
|
||||
struct iobuf *io = &conn->local_iobuf;
|
||||
int n = send(conn->endpoint.cgi_sock, io->buf, io->len, 0);
|
||||
if (n > 0) {
|
||||
memmove(io->buf, io->buf + n, io->len - n);
|
||||
io->len -= n;
|
||||
}
|
||||
discard_leading_iobuf_bytes(io, n);
|
||||
}
|
||||
#endif // !NO_CGI
|
||||
|
||||
@@ -1331,9 +1337,10 @@ static struct connection *accept_new_connection(struct mg_server *server) {
|
||||
}
|
||||
|
||||
static void close_conn(struct connection *conn) {
|
||||
DBG(("%p %d %d", conn, conn->flags, conn->endpoint_type));
|
||||
LINKED_LIST_REMOVE(&conn->link);
|
||||
closesocket(conn->client_sock);
|
||||
close_local_endpoint(conn);
|
||||
DBG(("%p %d %d", conn, conn->flags, conn->endpoint_type));
|
||||
free(conn->request); // It's OK to free(NULL), ditto below
|
||||
free(conn->path_info);
|
||||
free(conn->remote_iobuf.buf);
|
||||
@@ -1840,8 +1847,7 @@ static int deliver_websocket_frame(struct connection *conn) {
|
||||
if (conn->endpoint.uh->handler(&conn->mg_conn)) {
|
||||
conn->flags |= CONN_SPOOL_DONE;
|
||||
}
|
||||
memmove(buf, buf + frame_len, buf_len - frame_len);
|
||||
conn->local_iobuf.len -= frame_len;
|
||||
discard_leading_iobuf_bytes(&conn->local_iobuf, frame_len);
|
||||
}
|
||||
|
||||
return buffered;
|
||||
@@ -1938,8 +1944,7 @@ static void write_to_socket(struct connection *conn) {
|
||||
if (is_error(n)) {
|
||||
conn->flags |= CONN_CLOSE;
|
||||
} else if (n > 0) {
|
||||
memmove(io->buf, io->buf + n, io->len - n);
|
||||
io->len -= n;
|
||||
discard_leading_iobuf_bytes(io, n);
|
||||
conn->num_bytes_sent += n;
|
||||
}
|
||||
|
||||
@@ -2641,8 +2646,7 @@ static void forward_put_data(struct connection *conn) {
|
||||
struct iobuf *io = &conn->local_iobuf;
|
||||
int n = write(conn->endpoint.fd, io->buf, io->len);
|
||||
if (n > 0) {
|
||||
memmove(io->buf, io->buf + n, io->len - n);
|
||||
io->len -= n;
|
||||
discard_leading_iobuf_bytes(io, n);
|
||||
conn->cl -= n;
|
||||
if (conn->cl <= 0) {
|
||||
close_local_endpoint(conn);
|
||||
@@ -3312,7 +3316,6 @@ static void handle_lsp_request(struct connection *conn, const char *path,
|
||||
#endif // USE_LUA
|
||||
|
||||
static void open_local_endpoint(struct connection *conn) {
|
||||
const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
|
||||
#ifndef NO_FILESYSTEM
|
||||
static const char lua_pat[] = LUA_SCRIPT_PATTERN;
|
||||
file_stat_t st;
|
||||
@@ -3322,8 +3325,6 @@ static void open_local_endpoint(struct connection *conn) {
|
||||
const char *dir_lst = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
|
||||
#endif
|
||||
|
||||
conn->mg_conn.content_len = cl_hdr == NULL ? 0 : (int) to64(cl_hdr);
|
||||
|
||||
// Call URI handler if one is registered for this URI
|
||||
conn->endpoint.uh = find_uri_handler(conn->server, conn->mg_conn.uri);
|
||||
if (conn->endpoint.uh != NULL) {
|
||||
@@ -3425,7 +3426,7 @@ static int is_valid_uri(const char *uri) {
|
||||
return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
|
||||
}
|
||||
|
||||
static void process_request(struct connection *conn) {
|
||||
static void try_http_parse_and_set_content_length(struct connection *conn) {
|
||||
struct iobuf *io = &conn->local_iobuf;
|
||||
|
||||
if (conn->request_len == 0 &&
|
||||
@@ -3435,14 +3436,24 @@ static void process_request(struct connection *conn) {
|
||||
// become invalid.
|
||||
conn->request = (char *) malloc(conn->request_len);
|
||||
memcpy(conn->request, io->buf, conn->request_len);
|
||||
DBG(("%p ==> [%.*s]", conn, conn->request_len, conn->request));
|
||||
memmove(io->buf, io->buf + conn->request_len, io->len - conn->request_len);
|
||||
io->len -= conn->request_len;
|
||||
DBG(("%p [%.*s]", conn, conn->request_len, conn->request));
|
||||
discard_leading_iobuf_bytes(io, conn->request_len);
|
||||
conn->request_len = parse_http_message(conn->request, conn->request_len,
|
||||
&conn->mg_conn);
|
||||
if (conn->request_len > 0) {
|
||||
const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
|
||||
conn->cl = cl_hdr == NULL ? 0 : to64(cl_hdr);
|
||||
conn->mg_conn.content_len = (long int) conn->cl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DBG(("%p %d %d [%.*s]", conn, conn->request_len, io->len, io->len, io->buf));
|
||||
static void process_request(struct connection *conn) {
|
||||
struct iobuf *io = &conn->local_iobuf;
|
||||
|
||||
try_http_parse_and_set_content_length(conn);
|
||||
DBG(("%p %d %d %d [%.*s]", conn, conn->request_len, io->len, conn->flags,
|
||||
io->len, io->buf));
|
||||
if (conn->request_len < 0 ||
|
||||
(conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) {
|
||||
send_http_error(conn, 400, NULL);
|
||||
@@ -3475,48 +3486,96 @@ static void process_request(struct connection *conn) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void read_from_socket(struct connection *conn) {
|
||||
char buf[IOBUF_SIZE];
|
||||
int ok, n = 0;
|
||||
socklen_t len = sizeof(ok);
|
||||
static void call_http_client_handler(struct connection *conn, int code) {
|
||||
conn->mg_conn.status_code = code;
|
||||
// For responses without Content-Lengh, use the whole buffer
|
||||
if (conn->cl == 0 && code == MG_DOWNLOAD_SUCCESS) {
|
||||
conn->mg_conn.content_len = conn->local_iobuf.len;
|
||||
}
|
||||
conn->mg_conn.content = conn->local_iobuf.buf;
|
||||
if (conn->handler(&conn->mg_conn) || code == MG_CONNECT_FAILURE ||
|
||||
code == MG_DOWNLOAD_FAILURE) {
|
||||
conn->flags |= CONN_CLOSE;
|
||||
}
|
||||
discard_leading_iobuf_bytes(&conn->local_iobuf, conn->mg_conn.content_len);
|
||||
conn->flags = conn->mg_conn.status_code = 0;
|
||||
conn->cl = conn->num_bytes_sent = conn->request_len = 0;
|
||||
free(conn->request);
|
||||
conn->request = NULL;
|
||||
}
|
||||
|
||||
if (conn->endpoint_type == EP_CLIENT) {
|
||||
conn->mg_conn.wsbits = 1;
|
||||
if (!(conn->flags & CONN_CONNECTED) &&
|
||||
getsockopt(conn->client_sock, SOL_SOCKET, SO_ERROR,
|
||||
(char *) &ok, &len) < 0) {
|
||||
conn->mg_conn.wsbits = 0;
|
||||
}
|
||||
conn->handler(&conn->mg_conn);
|
||||
conn->flags |= CONN_CLOSE | CONN_CONNECTED;
|
||||
} else {
|
||||
if (conn->ssl != NULL) {
|
||||
#ifdef USE_SSL
|
||||
if (conn->flags & CONN_SSL_HANDS_SHAKEN) {
|
||||
n = SSL_read(conn->ssl, buf, sizeof(buf));
|
||||
} else {
|
||||
if (SSL_accept(conn->ssl) == 1) {
|
||||
conn->flags |= CONN_SSL_HANDS_SHAKEN;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
n = recv(conn->client_sock, buf, sizeof(buf), 0);
|
||||
}
|
||||
static void process_response(struct connection *conn) {
|
||||
struct iobuf *io = &conn->local_iobuf;
|
||||
|
||||
DBG(("%p %d", conn, n));
|
||||
if (is_error(n)) {
|
||||
conn->flags |= CONN_CLOSE;
|
||||
} else if (n > 0) {
|
||||
spool(&conn->local_iobuf, buf, n);
|
||||
process_request(conn);
|
||||
}
|
||||
try_http_parse_and_set_content_length(conn);
|
||||
DBG(("%p %d %d [%.*s]", conn, conn->request_len, io->len,
|
||||
io->len > 40 ? 40 : io->len, io->buf));
|
||||
if (conn->request_len < 0 ||
|
||||
(conn->request_len == 0 && io->len > MAX_REQUEST_SIZE)) {
|
||||
call_http_client_handler(conn, MG_DOWNLOAD_FAILURE);
|
||||
}
|
||||
if (io->len >= conn->cl) {
|
||||
call_http_client_handler(conn, MG_DOWNLOAD_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
int mg_connect(struct mg_server *server, const char *host,
|
||||
int port, mg_handler_t handler, void *param) {
|
||||
static void callback_http_client_on_connect(struct connection *conn) {
|
||||
int ok;
|
||||
socklen_t len = sizeof(ok);
|
||||
|
||||
if (getsockopt(conn->client_sock, SOL_SOCKET, SO_ERROR, (char *) &ok,
|
||||
&len) == 0 && ok == 0) {
|
||||
conn->mg_conn.status_code = MG_CONNECT_SUCCESS;
|
||||
}
|
||||
conn->flags &= ~CONN_CONNECTING;
|
||||
if (conn->handler(&conn->mg_conn) || ok != 0) {
|
||||
conn->flags |= CONN_CLOSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void read_from_socket(struct connection *conn) {
|
||||
char buf[IOBUF_SIZE];
|
||||
int n = 0;
|
||||
|
||||
if (conn->endpoint_type == EP_CLIENT && conn->flags & CONN_CONNECTING) {
|
||||
callback_http_client_on_connect(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (conn->ssl != NULL) {
|
||||
#ifdef USE_SSL
|
||||
if (conn->flags & CONN_SSL_HANDS_SHAKEN) {
|
||||
n = SSL_read(conn->ssl, buf, sizeof(buf));
|
||||
} else {
|
||||
if (SSL_accept(conn->ssl) == 1) {
|
||||
conn->flags |= CONN_SSL_HANDS_SHAKEN;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
n = recv(conn->client_sock, buf, sizeof(buf), 0);
|
||||
}
|
||||
|
||||
DBG(("%p %d %d (1)", conn, n, conn->flags));
|
||||
if (is_error(n)) {
|
||||
if (conn->endpoint_type == EP_CLIENT && conn->local_iobuf.len > 0) {
|
||||
call_http_client_handler(conn, MG_DOWNLOAD_SUCCESS);
|
||||
}
|
||||
conn->flags |= CONN_CLOSE;
|
||||
} else if (n > 0) {
|
||||
spool(&conn->local_iobuf, buf, n);
|
||||
if (conn->endpoint_type == EP_CLIENT) {
|
||||
process_response(conn);
|
||||
} else {
|
||||
process_request(conn);
|
||||
}
|
||||
}
|
||||
DBG(("%p %d %d (2)", conn, n, conn->flags));
|
||||
}
|
||||
|
||||
int mg_connect(struct mg_server *server, const char *host, int port,
|
||||
mg_handler_t handler, void *param) {
|
||||
sock_t sock = INVALID_SOCKET;
|
||||
struct sockaddr_in sin;
|
||||
struct hostent *he = NULL;
|
||||
@@ -3544,10 +3603,16 @@ int mg_connect(struct mg_server *server, const char *host,
|
||||
conn->handler = handler;
|
||||
conn->mg_conn.server_param = server->server_data;
|
||||
conn->mg_conn.connection_param = param;
|
||||
conn->birth_time = conn->last_activity_time = time(NULL);
|
||||
conn->flags = CONN_CONNECTING;
|
||||
conn->mg_conn.status_code = MG_CONNECT_FAILURE;
|
||||
LINKED_LIST_ADD_TO_FRONT(&server->active_connections, &conn->link);
|
||||
DBG(("%p %s:%d", conn, host, port));
|
||||
|
||||
if (connect_ret_val == 0) {
|
||||
conn->flags = CONN_CONNECTED;
|
||||
conn->mg_conn.status_code = MG_CONNECT_SUCCESS;
|
||||
conn->flags &= ~CONN_CONNECTING;
|
||||
conn->mg_conn.content = conn->local_iobuf.buf;
|
||||
handler(&conn->mg_conn);
|
||||
}
|
||||
|
||||
@@ -3593,17 +3658,11 @@ static void log_access(const struct connection *conn, const char *path) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static void gobble_prior_post_data(struct iobuf *io, int len) {
|
||||
if (len > 0 && len <= io->len) {
|
||||
memmove(io->buf, io->buf + len, io->len - len);
|
||||
io->len -= len;
|
||||
}
|
||||
}
|
||||
|
||||
static void close_local_endpoint(struct connection *conn) {
|
||||
// Must be done before free()
|
||||
int keep_alive = should_keep_alive(&conn->mg_conn) &&
|
||||
(conn->endpoint_type == EP_FILE || conn->endpoint_type == EP_USER);
|
||||
DBG(("%p %d %d %d", conn, conn->endpoint_type, keep_alive, conn->flags));
|
||||
|
||||
switch (conn->endpoint_type) {
|
||||
case EP_PUT: close(conn->endpoint.fd); break;
|
||||
@@ -3613,17 +3672,16 @@ static void close_local_endpoint(struct connection *conn) {
|
||||
}
|
||||
|
||||
#ifndef NO_LOGGING
|
||||
if (conn->mg_conn.status_code != 400) {
|
||||
if (conn->mg_conn.status_code > 0 && conn->endpoint_type != EP_CLIENT &&
|
||||
conn->mg_conn.status_code != 400) {
|
||||
log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (conn->endpoint_type == EP_USER) {
|
||||
gobble_prior_post_data(&conn->local_iobuf, conn->mg_conn.content_len);
|
||||
}
|
||||
|
||||
// Gobble possible POST data sent to the URI handler
|
||||
discard_leading_iobuf_bytes(&conn->local_iobuf, conn->mg_conn.content_len);
|
||||
conn->endpoint_type = EP_NONE;
|
||||
conn->flags = 0;
|
||||
conn->flags = conn->mg_conn.status_code = 0;
|
||||
conn->cl = conn->num_bytes_sent = conn->request_len = 0;
|
||||
free(conn->request);
|
||||
conn->request = NULL;
|
||||
@@ -3690,6 +3748,9 @@ unsigned int mg_poll_server(struct mg_server *server, int milliseconds) {
|
||||
LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
|
||||
conn = LINKED_LIST_ENTRY(lp, struct connection, link);
|
||||
add_to_set(conn->client_sock, &read_set, &max_fd);
|
||||
if (conn->endpoint_type == EP_CLIENT && (conn->flags & CONN_CONNECTING)) {
|
||||
add_to_set(conn->client_sock, &write_set, &max_fd);
|
||||
}
|
||||
if (conn->endpoint_type == EP_FILE) {
|
||||
transfer_file_data(conn);
|
||||
} else if (conn->endpoint_type == EP_CGI) {
|
||||
@@ -3730,10 +3791,14 @@ unsigned int mg_poll_server(struct mg_server *server, int milliseconds) {
|
||||
read_from_cgi(conn);
|
||||
}
|
||||
#endif
|
||||
if (FD_ISSET(conn->client_sock, &write_set) &&
|
||||
!(conn->flags & CONN_BUFFER)) {
|
||||
conn->last_activity_time = current_time;
|
||||
write_to_socket(conn);
|
||||
if (FD_ISSET(conn->client_sock, &write_set)) {
|
||||
if (conn->endpoint_type == EP_CLIENT &&
|
||||
(conn->flags & CONN_CONNECTING)) {
|
||||
read_from_socket(conn);
|
||||
} else if (!(conn->flags & CONN_BUFFER)) {
|
||||
conn->last_activity_time = current_time;
|
||||
write_to_socket(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3767,7 +3832,7 @@ void mg_destroy_server(struct mg_server **server) {
|
||||
closesocket((*server)->ctl[0]);
|
||||
closesocket((*server)->ctl[1]);
|
||||
LINKED_LIST_FOREACH(&(*server)->active_connections, lp, tmp) {
|
||||
free(LINKED_LIST_ENTRY(lp, struct connection, link));
|
||||
close_conn(LINKED_LIST_ENTRY(lp, struct connection, link));
|
||||
}
|
||||
LINKED_LIST_FOREACH(&(*server)->uri_handlers, lp, tmp) {
|
||||
free(LINKED_LIST_ENTRY(lp, struct uri_handler, link)->uri);
|
||||
@@ -3980,7 +4045,7 @@ const char *mg_set_option(struct mg_server *server, const char *name,
|
||||
free(server->config_options[ind]);
|
||||
}
|
||||
server->config_options[ind] = mg_strdup(value);
|
||||
DBG(("%s => %s", name, value));
|
||||
DBG(("%s [%s]", name, value));
|
||||
|
||||
if (ind == LISTENING_PORT) {
|
||||
if (server->listening_sock != INVALID_SOCKET) {
|
||||
|
||||
+10
-2
@@ -46,11 +46,11 @@ struct mg_connection {
|
||||
} http_headers[30];
|
||||
|
||||
char *content; // POST (or websocket message) data, or NULL
|
||||
int content_len; // content length
|
||||
long int content_len; // content length
|
||||
|
||||
int is_websocket; // Connection is a websocket connection
|
||||
int status_code; // HTTP status code for HTTP error handler
|
||||
unsigned char wsbits; // First byte of the websocket frame
|
||||
int wsbits; // First byte of the websocket frame
|
||||
void *server_param; // Parameter passed to mg_add_uri_handler()
|
||||
void *connection_param; // Placeholder for connection-specific data
|
||||
};
|
||||
@@ -101,6 +101,14 @@ char *mg_md5(char buf[33], ...);
|
||||
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
|
||||
void mg_send_digest_auth_request(struct mg_connection *conn);
|
||||
|
||||
// HTTP client interface
|
||||
enum {
|
||||
MG_CONNECT_SUCCESS, MG_CONNECT_FAILURE,
|
||||
MG_DOWNLOAD_SUCCESS, MG_DOWNLOAD_FAILURE
|
||||
};
|
||||
int mg_connect(struct mg_server *server, const char *host, int port,
|
||||
mg_handler_t handler, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
+81
-71
@@ -1,5 +1,5 @@
|
||||
// Unit test for the mongoose web server.
|
||||
// g++ -W -Wall -pedantic unit_test.c -o t && ./t
|
||||
// g++ -W -Wall -pedantic -g unit_test.c && ./a.out
|
||||
|
||||
#define USE_WEBSOCKET
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
static int static_num_tests = 0;
|
||||
|
||||
#if 0
|
||||
// Connects to host:port, and sends formatted request to it. Returns
|
||||
// malloc-ed reply and reply length, or NULL on error. Reply contains
|
||||
// everything including headers, not just the message body.
|
||||
@@ -73,6 +74,7 @@ static char *wget(const char *host, int port, int *len, const char *fmt, ...) {
|
||||
|
||||
return reply;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *read_file(const char *path, int *size) {
|
||||
FILE *fp;
|
||||
@@ -361,78 +363,42 @@ static const char *test_next_option(void) {
|
||||
|
||||
static int cb1(struct mg_connection *conn) {
|
||||
// We're not sending HTTP headers here, to make testing easier
|
||||
//mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n%s %s %s",
|
||||
mg_printf(conn, "%s %s %s",
|
||||
conn->server_param == NULL ? "?" : (char *) conn->server_param,
|
||||
conn->connection_param == NULL ? "?" : "!", conn->remote_ip);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *test_regular_file(void) {
|
||||
static const char *fname = "mongoose.c";
|
||||
int reply_len, file_len;
|
||||
char *reply, *file_data;
|
||||
file_stat_t st;
|
||||
|
||||
ASSERT(stat(fname, &st) == 0);
|
||||
ASSERT(st.st_size > 0);
|
||||
|
||||
ASSERT((file_data = read_file(fname, &file_len)) != NULL);
|
||||
ASSERT(file_len == st.st_size);
|
||||
|
||||
reply = wget("127.0.0.1", atoi(HTTP_PORT), &reply_len,
|
||||
"GET /%s.c HTTP/1.0\r\n\r\n", fname);
|
||||
ASSERT(reply != NULL);
|
||||
ASSERT(reply_len > 0);
|
||||
// TODO(lsm): test headers and content
|
||||
|
||||
free(reply);
|
||||
free(file_data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *test_server_param(void) {
|
||||
int reply_len;
|
||||
char *reply;
|
||||
|
||||
reply = wget("127.0.0.1", atoi(HTTP_PORT), &reply_len, "%s",
|
||||
"GET /cb1 HTTP/1.0\r\n\r\n");
|
||||
ASSERT(reply != NULL);
|
||||
ASSERT(reply_len == 15);
|
||||
// cb1() does not send HTTP headers
|
||||
ASSERT(memcmp(reply, "foo ? 127.0.0.1", 5) == 0);
|
||||
//printf("%d [%.*s]\n", reply_len, reply_len, reply);
|
||||
free(reply);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int error_handler(struct mg_connection *conn) {
|
||||
mg_printf(conn, "error: %d", conn->status_code);
|
||||
mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *test_error_handler(void) {
|
||||
int reply_len;
|
||||
char *reply;
|
||||
|
||||
reply = wget("127.0.0.1", atoi(HTTP_PORT), &reply_len, "%s",
|
||||
"GET /non_exist HTTP/1.0\r\n\r\n");
|
||||
ASSERT(reply != NULL);
|
||||
ASSERT(reply_len == 10);
|
||||
ASSERT(memcmp(reply, "error: 404", 10) == 0);
|
||||
free(reply);
|
||||
|
||||
return NULL;
|
||||
static int ts1(struct mg_connection *conn) {
|
||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
||||
mg_printf(conn, "%s", "GET /cb1 HTTP/1.0\r\n\r\n");
|
||||
return 0;
|
||||
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
|
||||
sprintf((char *) conn->connection_param, "%.*s",
|
||||
(int) conn->content_len, conn->content);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *server_thread(void *param) {
|
||||
int i;
|
||||
for (i = 0; i < 10; i++) mg_poll_server((struct mg_server *) param, 1);
|
||||
return NULL;
|
||||
static int ts2(struct mg_connection *conn) {
|
||||
if (conn->status_code == MG_CONNECT_SUCCESS) {
|
||||
mg_printf(conn, "%s", "GET /non_exist HTTP/1.0\r\n\r\n");
|
||||
return 0;
|
||||
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
|
||||
sprintf((char *) conn->connection_param, "%s %.*s",
|
||||
conn->uri, (int) conn->content_len, conn->content);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *test_server(void) {
|
||||
char buf1[100] = "", buf2[100] = "";
|
||||
struct mg_server *server = mg_create_server((void *) "foo");
|
||||
|
||||
ASSERT(server != NULL);
|
||||
@@ -440,34 +406,78 @@ static const char *test_server(void) {
|
||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
||||
mg_add_uri_handler(server, "/cb1", cb1);
|
||||
mg_set_http_error_handler(server, error_handler);
|
||||
mg_start_thread(server_thread, server);
|
||||
RUN_TEST(test_regular_file);
|
||||
RUN_TEST(test_server_param);
|
||||
RUN_TEST(test_error_handler);
|
||||
|
||||
// TODO(lsm): come up with a better way of thread sync
|
||||
sleep(1);
|
||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), ts1, buf1) == 1);
|
||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), ts2, buf2) == 1);
|
||||
|
||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 0); }
|
||||
ASSERT(strcmp(buf1, "foo ? 127.0.0.1") == 0);
|
||||
ASSERT(strcmp(buf2, "404 ERR: 404") == 0);
|
||||
|
||||
ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0);
|
||||
|
||||
mg_destroy_server(&server);
|
||||
ASSERT(server == NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This http client sends two requests using one connection
|
||||
static int cb2(struct mg_connection *conn) {
|
||||
(void) conn;
|
||||
return 0;
|
||||
const char *file1 = "mongoose.h", *file2 = "mongoose.c", *file_name = file2;
|
||||
char *buf = (char *) conn->connection_param, *file_data;
|
||||
int file_len, ret_val = 1;
|
||||
|
||||
switch (conn->status_code) {
|
||||
case MG_CONNECT_SUCCESS:
|
||||
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file1);
|
||||
strcat(buf, "a");
|
||||
ret_val = 0;
|
||||
break;
|
||||
case MG_CONNECT_FAILURE: strcat(buf, "b"); break;
|
||||
case MG_DOWNLOAD_FAILURE: strcat(buf, "c"); break;
|
||||
case MG_DOWNLOAD_SUCCESS:
|
||||
if (!strcmp(buf, "a")) {
|
||||
// Send second request
|
||||
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file2);
|
||||
file_name = file1;
|
||||
}
|
||||
if ((file_data = read_file(file_name, &file_len)) != NULL) {
|
||||
if (file_len == conn->content_len &&
|
||||
memcmp(file_data, conn->content, file_len) == 0) {
|
||||
strcat(buf, "d");
|
||||
} else {
|
||||
strcat(buf, "f");
|
||||
}
|
||||
free(file_data);
|
||||
}
|
||||
ret_val = !strcmp(buf, "ad") ? 0 : 1;
|
||||
break;
|
||||
default: strcat(buf, "e"); break;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int cb3(struct mg_connection *conn) {
|
||||
sprintf((char *) conn->connection_param, "%d", conn->status_code);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *test_mg_connect(void) {
|
||||
char buf[1000];
|
||||
struct mg_server *server = mg_create_server(buf);
|
||||
//mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), cb2);
|
||||
char buf2[40] = "", buf3[40] = "";
|
||||
struct mg_server *server = mg_create_server(NULL);
|
||||
|
||||
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
|
||||
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
|
||||
mg_add_uri_handler(server, "/cb1", cb2);
|
||||
ASSERT(mg_connect(server, "", 0, NULL, NULL) == 0);
|
||||
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), cb2, buf2) == 1);
|
||||
ASSERT(mg_connect(server, "127.0.0.1", 1, cb3, buf3) == 1);
|
||||
|
||||
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 0); }
|
||||
|
||||
ASSERT(strcmp(buf2, "add") == 0);
|
||||
ASSERT(strcmp(buf3, "1") == 0);
|
||||
mg_destroy_server(&server);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user