Commit 31f0409b authored by Sergey Lyubka's avatar Sergey Lyubka

Added CGI endpoint

parent ebf9ee08
...@@ -115,6 +115,10 @@ struct linked_list_link { struct linked_list_link *prev, *next; }; ...@@ -115,6 +115,10 @@ struct linked_list_link { struct linked_list_link *prev, *next; };
#define IOBUF_SIZE 8192 #define IOBUF_SIZE 8192
#define MAX_PATH_SIZE 8192 #define MAX_PATH_SIZE 8192
#define LUA_SCRIPT_PATTERN "mg_*.lua$" #define LUA_SCRIPT_PATTERN "mg_*.lua$"
#define CGI_ENVIRONMENT_SIZE 4096
#define MAX_CGI_ENVIR_VARS 64
#define ENV_EXPORT_TO_CGI "MONGOOSE_CGI"
#define MONGOOSE_VERSION "5.0"
// Extra HTTP headers to send in every static file reply // Extra HTTP headers to send in every static file reply
#if !defined(EXTRA_HTTP_HEADERS) #if !defined(EXTRA_HTTP_HEADERS)
...@@ -198,12 +202,13 @@ struct iobuf { ...@@ -198,12 +202,13 @@ struct iobuf {
union endpoint { union endpoint {
int fd; // Opened regular local file int fd; // Opened regular local file
sock_t cgi_sock; // CGI socket
void *ssl; // SSL descriptor void *ssl; // SSL descriptor
struct uri_handler *uh; // URI handler user function struct uri_handler *uh; // URI handler user function
}; };
enum endpoint_type { EP_NONE, EP_FILE, EP_USER }; enum endpoint_type { EP_NONE, EP_FILE, EP_CGI, EP_USER };
enum connection_flags { CONN_CLOSE = 1, CONN_SPOOL_DONE = 2 }; enum connection_flags { CONN_CLOSE = 1, CONN_SPOOL_DONE = 2, CONN_SSL = 4 };
struct connection { struct connection {
struct mg_connection mg_conn; // XXX: Must be first struct mg_connection mg_conn; // XXX: Must be first
...@@ -417,8 +422,10 @@ static int mg_socketpair(sock_t sp[2]) { ...@@ -417,8 +422,10 @@ static int mg_socketpair(sock_t sp[2]) {
return ret; return ret;
} }
#ifndef NO_CGI
#ifdef _WIN32 #ifdef _WIN32
static pid_t start_process(char *cmd, char *env[], char *dir, sock_t sock) { static pid_t start_process(const char *interp, const char *cmd, const char *env,
const char *envp[], const char *dir, sock_t sock) {
STARTUPINFOA si = {0}; STARTUPINFOA si = {0};
PROCESS_INFORMATION pi = {0}; PROCESS_INFORMATION pi = {0};
HANDLE hs = (HANDLE) sock, me = GetCurrentProcess(); HANDLE hs = (HANDLE) sock, me = GetCurrentProcess();
...@@ -433,7 +440,7 @@ static pid_t start_process(char *cmd, char *env[], char *dir, sock_t sock) { ...@@ -433,7 +440,7 @@ static pid_t start_process(char *cmd, char *env[], char *dir, sock_t sock) {
closesocket(sock); closesocket(sock);
DBG(("Starting commad: [%s]", cmd)); DBG(("Starting commad: [%s]", cmd));
CreateProcess(NULL, cmd, NULL, NULL, TRUE, CreateProcess(NULL, (char *) cmd, NULL, NULL, TRUE,
CREATE_NEW_PROCESS_GROUP, (void *) env, dir, &si, &pi); CREATE_NEW_PROCESS_GROUP, (void *) env, dir, &si, &pi);
CloseHandle(si.hStdOutput); CloseHandle(si.hStdOutput);
...@@ -443,8 +450,222 @@ static pid_t start_process(char *cmd, char *env[], char *dir, sock_t sock) { ...@@ -443,8 +450,222 @@ static pid_t start_process(char *cmd, char *env[], char *dir, sock_t sock) {
return (pid_t) pi.hProcess; return (pid_t) pi.hProcess;
} }
#else #else
static pid_t start_process(const char *interp, const char *cmd, const char *env,
const char *envp[], const char *dir, sock_t sock) {
pid_t pid = fork();
(void) env;
if (pid == 0) {
chdir(dir);
dup2(sock, 0);
dup2(sock, 1);
closesocket(sock);
// After exec, all signal handlers are restored to their default values,
// with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
// implementation, SIGCHLD's handler will leave unchanged after exec
// if it was set to be ignored. Restore it to default action.
signal(SIGCHLD, SIG_DFL);
if (interp == NULL) {
execle(cmd, cmd, NULL, envp);
} else {
execle(interp, interp, cmd, NULL, envp);
}
exit(EXIT_FAILURE); // exec call failed
}
return pid;
}
#endif #endif
// This structure helps to create an environment for the spawned CGI program.
// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
// last element must be NULL.
// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
// strings must reside in a contiguous buffer. The end of the buffer is
// marked by two '\0' characters.
// We satisfy both worlds: we create an envp array (which is vars), all
// entries are actually pointers inside buf.
struct cgi_env_block {
struct mg_connection *conn;
char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer
const char *vars[MAX_CGI_ENVIR_VARS]; // char *envp[]
int len; // Space taken
int nvars; // Number of variables in envp[]
};
// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
// pointer into the vars array.
static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
int n, space;
char *added;
va_list ap;
// Calculate how much space is left in the buffer
space = sizeof(block->buf) - block->len - 2;
assert(space >= 0);
// Make a pointer to the free space int the buffer
added = block->buf + block->len;
// Copy VARIABLE=VALUE\0 string into the free space
va_start(ap, fmt);
n = vsnprintf(added, (size_t) space, fmt, ap);
va_end(ap);
// Make sure we do not overflow buffer and the envp array
if (n > 0 && n + 1 < space &&
block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
// Append a pointer to the added string into the envp array
block->vars[block->nvars++] = added;
// Bump up used length counter. Include \0 terminator
block->len += n + 1;
}
return added;
}
static void prepare_cgi_environment(struct connection *conn,
const char *prog,
struct cgi_env_block *blk) {
struct mg_connection *ri = &conn->mg_conn;
const char *s, *slash;
char *p, **opts = conn->server->config_options;
int i;
blk->len = blk->nvars = 0;
blk->conn = ri;
addenv(blk, "SERVER_NAME=%s", opts[AUTH_DOMAIN]);
addenv(blk, "SERVER_ROOT=%s", opts[DOCUMENT_ROOT]);
addenv(blk, "DOCUMENT_ROOT=%s", opts[DOCUMENT_ROOT]);
addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MONGOOSE_VERSION);
// Prepare the environment block
addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
// TODO(lsm): fix this for IPv6 case
addenv(blk, "SERVER_PORT=%d", ri->remote_port);
addenv(blk, "REQUEST_METHOD=%s", ri->request_method);
addenv(blk, "REMOTE_ADDR=%s", ri->remote_ip);
addenv(blk, "REMOTE_PORT=%d", ri->remote_port);
addenv(blk, "REQUEST_URI=%s%s%s", ri->uri,
ri->query_string == NULL ? "" : "?",
ri->query_string == NULL ? "" : ri->query_string);
// SCRIPT_NAME
if (conn->path_info != NULL) {
addenv(blk, "SCRIPT_NAME=%.*s",
(int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri);
addenv(blk, "PATH_INFO=%s", conn->path_info);
} else {
s = strrchr(prog, '/');
slash = strrchr(ri->uri, '/');
addenv(blk, "SCRIPT_NAME=%.*s%s",
slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri,
s == NULL ? prog : s);
}
addenv(blk, "SCRIPT_FILENAME=%s", prog);
addenv(blk, "PATH_TRANSLATED=%s", prog);
addenv(blk, "HTTPS=%s", conn->flags & CONN_SSL ? "on" : "off");
if ((s = mg_get_header(ri, "Content-Type")) != NULL)
addenv(blk, "CONTENT_TYPE=%s", s);
if (ri->query_string != NULL) {
addenv(blk, "QUERY_STRING=%s", ri->query_string);
}
if ((s = mg_get_header(ri, "Content-Length")) != NULL)
addenv(blk, "CONTENT_LENGTH=%s", s);
if ((s = getenv("PATH")) != NULL)
addenv(blk, "PATH=%s", s);
#if defined(_WIN32)
if ((s = getenv("COMSPEC")) != NULL) {
addenv(blk, "COMSPEC=%s", s);
}
if ((s = getenv("SYSTEMROOT")) != NULL) {
addenv(blk, "SYSTEMROOT=%s", s);
}
if ((s = getenv("SystemDrive")) != NULL) {
addenv(blk, "SystemDrive=%s", s);
}
if ((s = getenv("ProgramFiles")) != NULL) {
addenv(blk, "ProgramFiles=%s", s);
}
if ((s = getenv("ProgramFiles(x86)")) != NULL) {
addenv(blk, "ProgramFiles(x86)=%s", s);
}
if ((s = getenv("CommonProgramFiles(x86)")) != NULL) {
addenv(blk, "CommonProgramFiles(x86)=%s", s);
}
#else
if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
addenv(blk, "LD_LIBRARY_PATH=%s", s);
#endif // _WIN32
if ((s = getenv("PERLLIB")) != NULL)
addenv(blk, "PERLLIB=%s", s);
if ((s = getenv(ENV_EXPORT_TO_CGI)) != NULL)
addenv(blk, "%s=%s", ENV_EXPORT_TO_CGI, s);
// Add all headers as HTTP_* variables
for (i = 0; i < ri->num_headers; i++) {
p = addenv(blk, "HTTP_%s=%s",
ri->http_headers[i].name, ri->http_headers[i].value);
// Convert variable name into uppercase, and change - to _
for (; *p != '=' && *p != '\0'; p++) {
if (*p == '-')
*p = '_';
*p = (char) toupper(* (unsigned char *) p);
}
}
blk->vars[blk->nvars++] = NULL;
blk->buf[blk->len++] = '\0';
assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
assert(blk->len > 0);
assert(blk->len < (int) sizeof(blk->buf));
}
static void open_cgi_endpoint(struct connection *conn, const char *prog) {
struct cgi_env_block blk;
char dir[MAX_PATH_SIZE], *p = NULL;
sock_t fds[2];
prepare_cgi_environment(conn, prog, &blk);
// CGI must be executed in its own directory. 'dir' must point to the
// directory containing executable program, 'p' must point to the
// executable program name relative to 'dir'.
snprintf(dir, sizeof(dir), "%s", prog);
if ((p = strrchr(dir, '/')) != NULL) {
*p++ = '\0';
} else {
dir[0] = '.', dir[1] = '\0';
p = (char *) prog;
}
mg_socketpair(fds);
start_process(conn->server->config_options[CGI_INTERPRETER],
prog, blk.buf, blk.vars, dir, fds[1]);
conn->endpoint_type = EP_CGI;
conn->endpoint.cgi_sock = fds[0];
closesocket(fds[1]);
}
#endif // !NO_CGI
// 'sa' must be an initialized address to bind to // 'sa' must be an initialized address to bind to
static sock_t open_listening_socket(union socket_address *sa) { static sock_t open_listening_socket(union socket_address *sa) {
sock_t on = 1, sock = INVALID_SOCKET; sock_t on = 1, sock = INVALID_SOCKET;
...@@ -1095,6 +1316,7 @@ static int deliver_websocket_frame(struct connection *conn) { ...@@ -1095,6 +1316,7 @@ static int deliver_websocket_frame(struct connection *conn) {
if (buffered) { if (buffered) {
conn->mg_conn.content_len = data_len; conn->mg_conn.content_len = data_len;
conn->mg_conn.content = buf + header_len; conn->mg_conn.content = buf + header_len;
conn->mg_conn.wsbits = buf[0];
// Apply mask if necessary // Apply mask if necessary
if (mask_len > 0) { if (mask_len > 0) {
...@@ -1114,100 +1336,6 @@ static int deliver_websocket_frame(struct connection *conn) { ...@@ -1114,100 +1336,6 @@ static int deliver_websocket_frame(struct connection *conn) {
return buffered; return buffered;
} }
#if 0
int mg_websocket_read(struct mg_connection *conn, int *bits, char **data) {
// Pointer to the beginning of the portion of the incoming websocket message
// queue. The original websocket upgrade request is never removed,
// so the queue begins after it.
unsigned char *buf = (unsigned char *) conn->buf + conn->request_len;
int n, stop = 0;
size_t i, len, mask_len, data_len, header_len, body_len;
char mask[4];
assert(conn->content_len == 0);
// Loop continuously, reading messages from the socket, invoking the callback,
// and waiting repeatedly until an error occurs.
while (!stop) {
header_len = 0;
// body_len is the length of the entire queue in bytes
// len is the length of the current message
// data_len is the length of the current message's data payload
// header_len is the length of the current message's header
if ((body_len = conn->data_len - conn->request_len) >= 2) {
len = buf[1] & 127;
mask_len = buf[1] & 128 ? 4 : 0;
if (len < 126 && body_len >= mask_len) {
data_len = len;
header_len = 2 + mask_len;
} else if (len == 126 && body_len >= 4 + mask_len) {
header_len = 4 + mask_len;
data_len = ((((int) buf[2]) << 8) + buf[3]);
} else if (body_len >= 10 + mask_len) {
header_len = 10 + mask_len;
data_len = (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
htonl(* (uint32_t *) &buf[6]);
}
}
// Data layout is as follows:
// conn->buf buf
// v v frame1 | frame2
// |---------------------|----------------|--------------|-------
// | |<--header_len-->|<--data_len-->|
// |<-conn->request_len->|<-----body_len----------->|
// |<-------------------conn->data_len------------->|
if (header_len > 0) {
// Allocate space to hold websocket payload
if ((*data = malloc(data_len)) == NULL) {
// Allocation failed, exit the loop and then close the connection
// TODO: notify user about the failure
data_len = 0;
break;
}
// Save mask and bits, otherwise it may be clobbered by memmove below
*bits = buf[0];
memcpy(mask, buf + header_len - mask_len, mask_len);
// Read frame payload into the allocated buffer.
assert(body_len >= header_len);
if (data_len + header_len > body_len) {
len = body_len - header_len;
memcpy(*data, buf + header_len, len);
// TODO: handle pull error
pull_all(NULL, conn, *data + len, data_len - len);
conn->data_len = conn->request_len;
} else {
len = data_len + header_len;
memcpy(*data, buf + header_len, data_len);
memmove(buf, buf + len, body_len - len);
conn->data_len -= len;
}
// Apply mask if necessary
if (mask_len > 0) {
for (i = 0; i < data_len; i++) {
(*data)[i] ^= mask[i % 4];
}
}
return data_len;
} else {
// Buffering websocket request
if ((n = pull(NULL, conn, conn->buf + conn->data_len,
conn->buf_size - conn->data_len)) <= 0) {
break;
}
conn->data_len += n;
}
}
return 0;
}
#endif
int mg_websocket_write(struct mg_connection* conn, int opcode, int mg_websocket_write(struct mg_connection* conn, int opcode,
const char *data, size_t data_len) { const char *data, size_t data_len) {
unsigned char *copy; unsigned char *copy;
...@@ -1229,14 +1357,14 @@ int mg_websocket_write(struct mg_connection* conn, int opcode, ...@@ -1229,14 +1357,14 @@ int mg_websocket_write(struct mg_connection* conn, int opcode,
} else if (data_len <= 0xFFFF) { } else if (data_len <= 0xFFFF) {
// 16-bit length field // 16-bit length field
copy[1] = 126; copy[1] = 126;
* (uint16_t *) (copy + 2) = htons(data_len); * (uint16_t *) (copy + 2) = (uint16_t) htons((uint16_t) data_len);
memcpy(copy + 4, data, data_len); memcpy(copy + 4, data, data_len);
copy_len = 4 + data_len; copy_len = 4 + data_len;
} else { } else {
// 64-bit length field // 64-bit length field
copy[1] = 127; copy[1] = 127;
* (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32); * (uint32_t *) (copy + 2) = (uint32_t) htonl(data_len >> 32);
* (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff); * (uint32_t *) (copy + 6) = (uint32_t) htonl(data_len & 0xffffffff);
memcpy(copy + 10, data, data_len); memcpy(copy + 10, data, data_len);
copy_len = 10 + data_len; copy_len = 10 + data_len;
} }
...@@ -1248,11 +1376,14 @@ int mg_websocket_write(struct mg_connection* conn, int opcode, ...@@ -1248,11 +1376,14 @@ int mg_websocket_write(struct mg_connection* conn, int opcode,
return retval; return retval;
} }
#else
static void send_websocket_handshake(struct mg_connection *conn, static void send_websocket_handshake_if_requested(struct mg_connection *conn) {
const char *key) { const char *ver = mg_get_header(conn, "Sec-WebSocket-Version"),
(void) key; *key = mg_get_header(conn, "Sec-WebSocket-Key");
send_http_error(conn, "%s", "HTTP/1.1 501 Not Implemented\r\n\r\n"); if (ver != NULL && key != NULL) {
conn->is_websocket = 1;
send_websocket_handshake(conn, key);
}
} }
#endif // !USE_WEBSOCKET #endif // !USE_WEBSOCKET
...@@ -1521,9 +1652,12 @@ static void call_uri_handler_if_data_is_buffered(struct connection *conn) { ...@@ -1521,9 +1652,12 @@ static void call_uri_handler_if_data_is_buffered(struct connection *conn) {
struct mg_connection *c = &conn->mg_conn; struct mg_connection *c = &conn->mg_conn;
c->content = loc->buf; c->content = loc->buf;
#ifdef USE_WEBSOCKET
if (conn->mg_conn.is_websocket) { if (conn->mg_conn.is_websocket) {
do { } while (deliver_websocket_frame(conn)); do { } while (deliver_websocket_frame(conn));
} else if (loc->len >= c->content_len) { } else
#endif
if (loc->len >= c->content_len) {
conn->endpoint.uh->handler(c); conn->endpoint.uh->handler(c);
close_local_endpoint(conn); close_local_endpoint(conn);
} }
...@@ -1534,6 +1668,7 @@ static void open_local_endpoint(struct connection *conn) { ...@@ -1534,6 +1668,7 @@ static void open_local_endpoint(struct connection *conn) {
file_stat_t st; file_stat_t st;
int exists = 0, is_directory = 0; int exists = 0, is_directory = 0;
const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length"); const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
conn->mg_conn.content_len = cl_hdr == NULL ? 0 : to64(cl_hdr); conn->mg_conn.content_len = cl_hdr == NULL ? 0 : to64(cl_hdr);
...@@ -1556,6 +1691,18 @@ static void open_local_endpoint(struct connection *conn) { ...@@ -1556,6 +1691,18 @@ static void open_local_endpoint(struct connection *conn) {
} else if (match_prefix(LUA_SCRIPT_PATTERN, 6, path) > 0) { } else if (match_prefix(LUA_SCRIPT_PATTERN, 6, path) > 0) {
send_http_error(conn, "%s", "HTTP/1.1 501 Not Implemented\r\n\r\n"); send_http_error(conn, "%s", "HTTP/1.1 501 Not Implemented\r\n\r\n");
conn->flags |= CONN_SPOOL_DONE; conn->flags |= CONN_SPOOL_DONE;
} else if (match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
if (strcmp(conn->mg_conn.request_method, "POST") &&
strcmp(conn->mg_conn.request_method, "HEAD") &&
strcmp(conn->mg_conn.request_method, "GET")) {
send_http_error(conn, "%s", "HTTP/1.1 501 Not Implemented\r\n\r\n");
} else {
#if !defined(NO_CGI)
open_cgi_endpoint(conn, path);
#else
send_http_error(conn, "%s", "HTTP/1.1 501 Not Implemented\r\n\r\n");
#endif // !NO_CGI
}
} else if (is_not_modified(conn, &st)) { } else if (is_not_modified(conn, &st)) {
send_http_error(conn, "%s", "HTTP/1.1 304 Not Modified\r\n\r\n"); send_http_error(conn, "%s", "HTTP/1.1 304 Not Modified\r\n\r\n");
} else if ((conn->endpoint.fd = open(path, O_RDONLY)) != -1) { } else if ((conn->endpoint.fd = open(path, O_RDONLY)) != -1) {
...@@ -1574,15 +1721,6 @@ static void send_continue_if_expected(struct connection *conn) { ...@@ -1574,15 +1721,6 @@ static void send_continue_if_expected(struct connection *conn) {
} }
} }
static void send_websocket_handshake_if_requested(struct mg_connection *conn) {
const char *ver = mg_get_header(conn, "Sec-WebSocket-Version"),
*key = mg_get_header(conn, "Sec-WebSocket-Key");
if (ver != NULL && key != NULL) {
conn->is_websocket = 1;
send_websocket_handshake(conn, key);
}
}
static void process_request(struct connection *conn) { static void process_request(struct connection *conn) {
struct iobuf *io = &conn->local_iobuf; struct iobuf *io = &conn->local_iobuf;
...@@ -1606,7 +1744,9 @@ static void process_request(struct connection *conn) { ...@@ -1606,7 +1744,9 @@ static void process_request(struct connection *conn) {
// Invalid request, or request is too big: close the connection // Invalid request, or request is too big: close the connection
conn->flags |= CONN_CLOSE; conn->flags |= CONN_CLOSE;
} else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) { } else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
#ifdef USE_WEBSOCKET
send_websocket_handshake_if_requested(&conn->mg_conn); send_websocket_handshake_if_requested(&conn->mg_conn);
#endif
send_continue_if_expected(conn); send_continue_if_expected(conn);
open_local_endpoint(conn); open_local_endpoint(conn);
} else if (conn->endpoint_type == EP_USER) { } else if (conn->endpoint_type == EP_USER) {
...@@ -1626,6 +1766,18 @@ static void read_from_client(struct connection *conn) { ...@@ -1626,6 +1766,18 @@ static void read_from_client(struct connection *conn) {
} }
} }
static void read_from_cgi(struct connection *conn) {
char buf[IOBUF_SIZE];
int n = recv(conn->endpoint.cgi_sock, buf, sizeof(buf), 0);
DBG(("-> %d", n));
if (is_error(n)) {
close_local_endpoint(conn);
} else if (n > 0) {
spool(&conn->remote_iobuf, buf, n);
}
}
static int should_keep_alive(const struct connection *conn) { static int should_keep_alive(const struct connection *conn) {
const char *method = conn->mg_conn.request_method; const char *method = conn->mg_conn.request_method;
const char *http_version = conn->mg_conn.http_version; const char *http_version = conn->mg_conn.http_version;
...@@ -1636,16 +1788,13 @@ static int should_keep_alive(const struct connection *conn) { ...@@ -1636,16 +1788,13 @@ static int should_keep_alive(const struct connection *conn) {
} }
static void close_local_endpoint(struct connection *conn) { static void close_local_endpoint(struct connection *conn) {
int keep_alive = should_keep_alive(conn); // Must be done before free() // Must be done before free()
int keep_alive = should_keep_alive(conn) && conn->endpoint_type == EP_FILE;
//DBG(("Closing for conn %p", conn));
// Close file descriptor
switch (conn->endpoint_type) { switch (conn->endpoint_type) {
case EP_FILE: close(conn->endpoint.fd); break; case EP_FILE: close(conn->endpoint.fd); break;
case EP_USER: break; case EP_CGI: closesocket(conn->endpoint.cgi_sock); break;
case EP_NONE: break; default: break;
default: assert(0); break;
} }
conn->endpoint_type = EP_NONE; conn->endpoint_type = EP_NONE;
...@@ -1699,6 +1848,8 @@ void mg_poll_server(struct mg_server *server, int milliseconds) { ...@@ -1699,6 +1848,8 @@ void mg_poll_server(struct mg_server *server, int milliseconds) {
add_to_set(conn->client_sock, &read_set, &max_fd); add_to_set(conn->client_sock, &read_set, &max_fd);
if (conn->endpoint_type == EP_FILE) { if (conn->endpoint_type == EP_FILE) {
transfer_file_data(conn); transfer_file_data(conn);
} else if (conn->endpoint_type == EP_CGI) {
add_to_set(conn->endpoint.cgi_sock, &read_set, &max_fd);
} }
if (conn->remote_iobuf.len > 0) { if (conn->remote_iobuf.len > 0) {
add_to_set(conn->client_sock, &write_set, &max_fd); add_to_set(conn->client_sock, &write_set, &max_fd);
...@@ -1732,6 +1883,10 @@ void mg_poll_server(struct mg_server *server, int milliseconds) { ...@@ -1732,6 +1883,10 @@ void mg_poll_server(struct mg_server *server, int milliseconds) {
conn->expire_time = expire_time; conn->expire_time = expire_time;
read_from_client(conn); read_from_client(conn);
} }
if (conn->endpoint_type == EP_CGI &&
FD_ISSET(conn->endpoint.cgi_sock, &read_set)) {
read_from_cgi(conn);
}
if (FD_ISSET(conn->client_sock, &write_set)) { if (FD_ISSET(conn->client_sock, &write_set)) {
conn->expire_time = expire_time; conn->expire_time = expire_time;
write_to_client(conn); write_to_client(conn);
...@@ -1893,11 +2048,11 @@ static int handler(struct mg_connection *conn) { ...@@ -1893,11 +2048,11 @@ static int handler(struct mg_connection *conn) {
return conn->content_len == 4 && !memcmp(conn->content, "exit", 4); return conn->content_len == 4 && !memcmp(conn->content, "exit", 4);
} }
int main(void) { int main(int argc, char *argv[]) {
struct mg_server *server = mg_create_server(NULL); struct mg_server *server = mg_create_server(NULL);
mg_set_option(server, "listening_port", "8080"); mg_set_option(server, "listening_port", "8080");
mg_set_option(server, "document_root", "."); mg_set_option(server, "document_root", argc > 1 ? argv[1] : ".");
mg_add_uri_handler(server, "/ws", handler); mg_add_uri_handler(server, "/ws", handler);
mg_start_thread(timer_thread, server); mg_start_thread(timer_thread, server);
......
...@@ -34,19 +34,20 @@ struct mg_connection { ...@@ -34,19 +34,20 @@ struct mg_connection {
const char *http_version; // E.g. "1.0", "1.1" const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL const char *query_string; // URL part after '?', not including '?', or NULL
long remote_ip; // Client's IP address char remote_ip[48]; // Max IPv6 string length is 45 characters
int remote_port; // Client's port int remote_port; // Client's port
int num_headers; // Number of HTTP headers int num_headers; // Number of HTTP headers
struct mg_header { struct mg_header {
const char *name; // HTTP header name const char *name; // HTTP header name
const char *value; // HTTP header value const char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers } http_headers[30];
char *content; // POST (or websocket message) data, or NULL char *content; // POST (or websocket message) data, or NULL
int content_len; // content length int content_len; // content length
int is_websocket; // Connection is a websocket connection int is_websocket; // Connection is a websocket connection
unsigned char wsbits; // First byte of the websocket frame
void *server_param; // Parameter passed to mg_add_uri_handler() void *server_param; // Parameter passed to mg_add_uri_handler()
void *connection_param; // Placeholder for connection-specific data void *connection_param; // Placeholder for connection-specific data
}; };
...@@ -68,29 +69,23 @@ void mg_set_log_handler(struct mg_server*, int (*)(struct mg_connection*, int)); ...@@ -68,29 +69,23 @@ void mg_set_log_handler(struct mg_server*, int (*)(struct mg_connection*, int));
const char **mg_get_valid_option_names(void); const char **mg_get_valid_option_names(void);
const char *mg_get_option(const struct mg_server *server, const char *name); const char *mg_get_option(const struct mg_server *server, const char *name);
// Websocket functions
//void mg_websocket_handshake(struct mg_connection *);
//int mg_websocket_read(struct mg_connection *, int *bits, char **data);
//int mg_websocket_write(struct mg_connection* conn, int opcode,
// const char *data, size_t data_len);
// Connection management functions // Connection management functions
int mg_write(struct mg_connection *, const void *buf, int len); int mg_write(struct mg_connection *, const void *buf, int len);
const char *mg_get_header(const struct mg_connection *, const char *name);
const char *mg_get_mime_type(const char *file_name);
#if 0 #if 0
int mg_printf(struct mg_connection *, const char *fmt, ...); int mg_printf(struct mg_connection *, const char *fmt, ...);
void mg_send_file(struct mg_connection *, const char *path); void mg_send_file(struct mg_connection *, const char *path);
int mg_read(struct mg_connection *, void *buf, int len); int mg_read(struct mg_connection *, void *buf, int len);
const char *mg_get_header(const struct mg_connection *, const char *name);
int mg_get_var(const char *data, size_t data_len, int mg_get_var(const char *data, size_t data_len,
const char *var_name, char *dst, size_t dst_len); const char *var_name, char *dst, size_t dst_len);
int mg_get_cookie(const char *cookie, const char *var_name, int mg_get_cookie(const char *cookie, const char *var_name,
char *buf, size_t buf_len); char *buf, size_t buf_len);
const char *mg_get_mime_type(const char *file_name); #endif
// Utility functions // Utility functions
int mg_start_thread(void *(*func)(void *), void *param); int mg_start_thread(void *(*func)(void *), void *param);
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment