mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-10 16:54:16 +07:00
Added CGI endpoint
This commit is contained in:
+280
-125
@@ -115,6 +115,10 @@ struct linked_list_link { struct linked_list_link *prev, *next; };
|
||||
#define IOBUF_SIZE 8192
|
||||
#define MAX_PATH_SIZE 8192
|
||||
#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
|
||||
#if !defined(EXTRA_HTTP_HEADERS)
|
||||
@@ -198,12 +202,13 @@ struct iobuf {
|
||||
|
||||
union endpoint {
|
||||
int fd; // Opened regular local file
|
||||
sock_t cgi_sock; // CGI socket
|
||||
void *ssl; // SSL descriptor
|
||||
struct uri_handler *uh; // URI handler user function
|
||||
};
|
||||
|
||||
enum endpoint_type { EP_NONE, EP_FILE, EP_USER };
|
||||
enum connection_flags { CONN_CLOSE = 1, CONN_SPOOL_DONE = 2 };
|
||||
enum endpoint_type { EP_NONE, EP_FILE, EP_CGI, EP_USER };
|
||||
enum connection_flags { CONN_CLOSE = 1, CONN_SPOOL_DONE = 2, CONN_SSL = 4 };
|
||||
|
||||
struct connection {
|
||||
struct mg_connection mg_conn; // XXX: Must be first
|
||||
@@ -417,8 +422,10 @@ static int mg_socketpair(sock_t sp[2]) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef NO_CGI
|
||||
#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};
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
HANDLE hs = (HANDLE) sock, me = GetCurrentProcess();
|
||||
@@ -433,7 +440,7 @@ static pid_t start_process(char *cmd, char *env[], char *dir, sock_t sock) {
|
||||
closesocket(sock);
|
||||
|
||||
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);
|
||||
|
||||
CloseHandle(si.hStdOutput);
|
||||
@@ -443,8 +450,222 @@ static pid_t start_process(char *cmd, char *env[], char *dir, sock_t sock) {
|
||||
return (pid_t) pi.hProcess;
|
||||
}
|
||||
#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
|
||||
|
||||
// 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
|
||||
static sock_t open_listening_socket(union socket_address *sa) {
|
||||
sock_t on = 1, sock = INVALID_SOCKET;
|
||||
@@ -1095,6 +1316,7 @@ static int deliver_websocket_frame(struct connection *conn) {
|
||||
if (buffered) {
|
||||
conn->mg_conn.content_len = data_len;
|
||||
conn->mg_conn.content = buf + header_len;
|
||||
conn->mg_conn.wsbits = buf[0];
|
||||
|
||||
// Apply mask if necessary
|
||||
if (mask_len > 0) {
|
||||
@@ -1114,100 +1336,6 @@ static int deliver_websocket_frame(struct connection *conn) {
|
||||
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,
|
||||
const char *data, size_t data_len) {
|
||||
unsigned char *copy;
|
||||
@@ -1229,14 +1357,14 @@ int mg_websocket_write(struct mg_connection* conn, int opcode,
|
||||
} else if (data_len <= 0xFFFF) {
|
||||
// 16-bit length field
|
||||
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);
|
||||
copy_len = 4 + data_len;
|
||||
} else {
|
||||
// 64-bit length field
|
||||
copy[1] = 127;
|
||||
* (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32);
|
||||
* (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff);
|
||||
* (uint32_t *) (copy + 2) = (uint32_t) htonl(data_len >> 32);
|
||||
* (uint32_t *) (copy + 6) = (uint32_t) htonl(data_len & 0xffffffff);
|
||||
memcpy(copy + 10, data, data_len);
|
||||
copy_len = 10 + data_len;
|
||||
}
|
||||
@@ -1248,11 +1376,14 @@ int mg_websocket_write(struct mg_connection* conn, int opcode,
|
||||
|
||||
return retval;
|
||||
}
|
||||
#else
|
||||
static void send_websocket_handshake(struct mg_connection *conn,
|
||||
const char *key) {
|
||||
(void) key;
|
||||
send_http_error(conn, "%s", "HTTP/1.1 501 Not Implemented\r\n\r\n");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif // !USE_WEBSOCKET
|
||||
|
||||
@@ -1521,9 +1652,12 @@ static void call_uri_handler_if_data_is_buffered(struct connection *conn) {
|
||||
struct mg_connection *c = &conn->mg_conn;
|
||||
|
||||
c->content = loc->buf;
|
||||
#ifdef USE_WEBSOCKET
|
||||
if (conn->mg_conn.is_websocket) {
|
||||
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);
|
||||
close_local_endpoint(conn);
|
||||
}
|
||||
@@ -1534,6 +1668,7 @@ static void open_local_endpoint(struct connection *conn) {
|
||||
file_stat_t st;
|
||||
int exists = 0, is_directory = 0;
|
||||
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);
|
||||
|
||||
@@ -1556,6 +1691,18 @@ static void open_local_endpoint(struct connection *conn) {
|
||||
} 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");
|
||||
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)) {
|
||||
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) {
|
||||
@@ -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) {
|
||||
struct iobuf *io = &conn->local_iobuf;
|
||||
|
||||
@@ -1606,7 +1744,9 @@ static void process_request(struct connection *conn) {
|
||||
// Invalid request, or request is too big: close the connection
|
||||
conn->flags |= CONN_CLOSE;
|
||||
} else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
|
||||
#ifdef USE_WEBSOCKET
|
||||
send_websocket_handshake_if_requested(&conn->mg_conn);
|
||||
#endif
|
||||
send_continue_if_expected(conn);
|
||||
open_local_endpoint(conn);
|
||||
} else if (conn->endpoint_type == EP_USER) {
|
||||
@@ -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) {
|
||||
const char *method = conn->mg_conn.request_method;
|
||||
const char *http_version = conn->mg_conn.http_version;
|
||||
@@ -1636,16 +1788,13 @@ static int should_keep_alive(const 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) {
|
||||
case EP_FILE: close(conn->endpoint.fd); break;
|
||||
case EP_USER: break;
|
||||
case EP_NONE: break;
|
||||
default: assert(0); break;
|
||||
case EP_CGI: closesocket(conn->endpoint.cgi_sock); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
conn->endpoint_type = EP_NONE;
|
||||
@@ -1699,6 +1848,8 @@ void mg_poll_server(struct mg_server *server, int milliseconds) {
|
||||
add_to_set(conn->client_sock, &read_set, &max_fd);
|
||||
if (conn->endpoint_type == EP_FILE) {
|
||||
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) {
|
||||
add_to_set(conn->client_sock, &write_set, &max_fd);
|
||||
@@ -1732,6 +1883,10 @@ void mg_poll_server(struct mg_server *server, int milliseconds) {
|
||||
conn->expire_time = expire_time;
|
||||
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)) {
|
||||
conn->expire_time = expire_time;
|
||||
write_to_client(conn);
|
||||
@@ -1893,11 +2048,11 @@ static int handler(struct mg_connection *conn) {
|
||||
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);
|
||||
|
||||
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_start_thread(timer_thread, server);
|
||||
|
||||
|
||||
+6
-11
@@ -34,19 +34,20 @@ struct mg_connection {
|
||||
const char *http_version; // E.g. "1.0", "1.1"
|
||||
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 num_headers; // Number of HTTP headers
|
||||
struct mg_header {
|
||||
const char *name; // HTTP header name
|
||||
const char *value; // HTTP header value
|
||||
} http_headers[64]; // Maximum 64 headers
|
||||
} http_headers[30];
|
||||
|
||||
char *content; // POST (or websocket message) data, or NULL
|
||||
int content_len; // content length
|
||||
|
||||
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 *connection_param; // Placeholder for connection-specific data
|
||||
};
|
||||
@@ -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_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
|
||||
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
|
||||
int mg_printf(struct mg_connection *, const char *fmt, ...);
|
||||
void mg_send_file(struct mg_connection *, const char *path);
|
||||
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,
|
||||
const char *var_name, char *dst, size_t dst_len);
|
||||
int mg_get_cookie(const char *cookie, const char *var_name,
|
||||
char *buf, size_t buf_len);
|
||||
const char *mg_get_mime_type(const char *file_name);
|
||||
#endif
|
||||
|
||||
// Utility functions
|
||||
int mg_start_thread(void *(*func)(void *), void *param);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user