mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-25 12:04:47 +00:00
Added new response creation interface
This commit is contained in:
1 parent
3a5e2fba88
commit
d0e2741832
5 files changed
+127
-48
No files matched your search
File renamed without changes.
+26
-8
@@ -112,16 +112,34 @@ is returned.
|
||||
|
||||
This is an interface primarily designed to push arbitrary data to websocket
|
||||
connections at any time. This function could be called from any thread. When
|
||||
it returns, an IO thread called `func()` on each active websocket connection,
|
||||
passing `param` as an extra parameter. It is allowed to call `mg_write()` or
|
||||
`mg_websocket_write()` within a callback, cause these write functions are
|
||||
thread-safe.
|
||||
it returns, an IO thread called `func()` on each active connection,
|
||||
passing `param` as an extra parameter. It is allowed to call `mg_send_data()` or
|
||||
`mg_websocket_write()` within a callback, cause `func` is executed in the
|
||||
context of the IO thread.
|
||||
|
||||
int mg_write(struct mg_connection *, const void *buf, int len);
|
||||
void mg_send_status(struct mg_connection *, int status_code);
|
||||
void mg_send_header(struct mg_connection *, const char *name,
|
||||
const char *value);
|
||||
void mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||
void mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||
|
||||
Send data to the client. This function is thread-safe. This function appends
|
||||
given buffer to a send queue of a given connection. It may return 0 if
|
||||
there is not enough memory, in which case the data will not be sent.
|
||||
These functions are used to construct a response to the client. HTTP response
|
||||
consists of three parts:
|
||||
|
||||
* a status line
|
||||
* zero or more HTTP headers
|
||||
* response body
|
||||
|
||||
Mongoose provides functions for all three parts:
|
||||
* `mg_send_status()` is used to create status line. This function can be
|
||||
called zero or once. If `mg_send_status()` is not called, then Mongoose
|
||||
will send status 200 (success) implicitly.
|
||||
* `mg_send_header()` adds HTTP header to the response. This function could
|
||||
be called zero or more times.
|
||||
* `mg_send_data()` and `mg_printf_data()` are used to send data to the
|
||||
client. Note that Mongoose adds `Transfer-Encoding: chunked` header
|
||||
implicitly, and sends data in chunks. Therefore, it is not necessary to
|
||||
set `Content-Length` header.
|
||||
|
||||
int mg_websocket_write(struct mg_connection* conn, int opcode,
|
||||
const char *data, size_t data_len);
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@
|
||||
|
||||
// This function will be called by mongoose on every new request
|
||||
static int index_html(struct mg_connection *conn) {
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n"
|
||||
"Hello! Requested URI is [%s], query string is [%s]", conn->uri,
|
||||
conn->query_string == NULL ? "(none)" : conn->query_string);
|
||||
mg_printf_data(conn, "Hello! Requested URI is [%s], query string is [%s]",
|
||||
conn->uri,
|
||||
conn->query_string == NULL ? "(none)" : conn->query_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+89
-36
@@ -227,7 +227,8 @@ union endpoint {
|
||||
|
||||
enum endpoint_type { EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT };
|
||||
enum connection_flags {
|
||||
CONN_CLOSE = 1, CONN_SPOOL_DONE = 2, CONN_SSL_HANDS_SHAKEN = 4
|
||||
CONN_CLOSE = 1, CONN_SPOOL_DONE = 2, CONN_SSL_HANDS_SHAKEN = 4,
|
||||
CONN_HEADERS_SENT = 8
|
||||
};
|
||||
|
||||
struct connection {
|
||||
@@ -445,28 +446,32 @@ static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) {
|
||||
return n;
|
||||
}
|
||||
|
||||
static const char *status_code_to_str(int status_code) {
|
||||
switch (status_code) {
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 204: return "No Content";
|
||||
case 304: return "Not Modified";
|
||||
case 400: return "Bad Request";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 409: return "Conflict";
|
||||
case 411: return "Length Required";
|
||||
case 413: return "Request Entity Too Large";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 423: return "Locked";
|
||||
case 500: return "Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
default: return "Server Error";
|
||||
}
|
||||
}
|
||||
|
||||
static void send_http_error(struct connection *conn, int code) {
|
||||
const char *message;
|
||||
const char *message = status_code_to_str(code);
|
||||
char headers[200], body[200];
|
||||
int body_len, headers_len;
|
||||
|
||||
switch (code) {
|
||||
case 201: message = "Created"; break;
|
||||
case 204: message = "No Content"; break;
|
||||
case 304: message = "Not Modified"; break;
|
||||
case 400: message = "Bad Request"; break;
|
||||
case 403: message = "Forbidden"; break;
|
||||
case 404: message = "Not Found"; break;
|
||||
case 405: message = "Method Not Allowed"; break;
|
||||
case 409: message = "Conflict"; break;
|
||||
case 411: message = "Length Required"; break;
|
||||
case 413: message = "Request Entity Too Large"; break;
|
||||
case 415: message = "Unsupported Media Type"; break;
|
||||
case 423: message = "Locked"; break;
|
||||
case 501: message = "Not Implemented"; break;
|
||||
default: message = "Server Error"; break;
|
||||
}
|
||||
|
||||
conn->mg_conn.status_code = code;
|
||||
body_len = mg_snprintf(body, sizeof(body), "%d %s\n", code, message);
|
||||
if (code >= 300 && code <= 399) {
|
||||
@@ -522,12 +527,25 @@ static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) {
|
||||
return len;
|
||||
}
|
||||
|
||||
int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) {
|
||||
static void write_chunk(struct connection *conn, const char *buf, int len) {
|
||||
char chunk_size[50];
|
||||
int n = mg_snprintf(chunk_size, sizeof(chunk_size), "%X\r\n", len);
|
||||
spool(&conn->remote_iobuf, chunk_size, n);
|
||||
spool(&conn->remote_iobuf, buf, len);
|
||||
spool(&conn->remote_iobuf, "\r\n", 2);
|
||||
}
|
||||
|
||||
int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap,
|
||||
int chunked) {
|
||||
char mem[IOBUF_SIZE], *buf = mem;
|
||||
int len;
|
||||
|
||||
if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
|
||||
len = mg_write(conn, buf, (size_t) len);
|
||||
if (chunked) {
|
||||
write_chunk((struct connection *) conn, buf, len);
|
||||
} else {
|
||||
len = mg_write(conn, buf, (size_t) len);
|
||||
}
|
||||
}
|
||||
if (buf != mem && buf != NULL) {
|
||||
free(buf);
|
||||
@@ -537,9 +555,12 @@ int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) {
|
||||
}
|
||||
|
||||
int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
|
||||
int len;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
return mg_vprintf(conn, fmt, ap);
|
||||
len = mg_vprintf(conn, fmt, ap, 0);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
// A helper function for traversing a comma separated list of values.
|
||||
@@ -1292,6 +1313,45 @@ int mg_write(struct mg_connection *c, const void *buf, int len) {
|
||||
return spool(&((struct connection *) c)->remote_iobuf, buf, len);
|
||||
}
|
||||
|
||||
void mg_send_status(struct mg_connection *c, int status) {
|
||||
if (c->status_code == 0) {
|
||||
c->status_code = status;
|
||||
mg_printf(c, "HTTP/1.1 %d %s\r\n", status, status_code_to_str(status));
|
||||
}
|
||||
}
|
||||
|
||||
void mg_send_header(struct mg_connection *c, const char *name, const char *v) {
|
||||
if (c->status_code == 0) {
|
||||
c->status_code = 200;
|
||||
mg_printf(c, "HTTP/1.1 %d %s\r\n", 200, status_code_to_str(200));
|
||||
}
|
||||
mg_printf(c, "%s: %s\r\n", name, v);
|
||||
}
|
||||
|
||||
static void terminate_headers(struct mg_connection *c) {
|
||||
struct connection *conn = (struct connection *) c;
|
||||
if (!(conn->flags & CONN_HEADERS_SENT)) {
|
||||
mg_send_header(c, "Transfer-Encoding", "chunked");
|
||||
mg_write(c, "\r\n", 2);
|
||||
conn->flags |= CONN_HEADERS_SENT;
|
||||
}
|
||||
}
|
||||
|
||||
void mg_send_data(struct mg_connection *c, const void *data, int data_len) {
|
||||
terminate_headers(c);
|
||||
write_chunk((struct connection *) c, data, data_len);
|
||||
}
|
||||
|
||||
void mg_printf_data(struct mg_connection *c, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
terminate_headers(c);
|
||||
|
||||
va_start(ap, fmt);
|
||||
mg_vprintf(c, fmt, ap, 1);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#if !defined(NO_WEBSOCKET) || !defined(NO_AUTH)
|
||||
static int is_big_endian(void) {
|
||||
static const int n = 1;
|
||||
@@ -1852,6 +1912,9 @@ static void call_uri_handler_if_data_is_buffered(struct connection *conn) {
|
||||
#endif
|
||||
if (loc->len >= c->content_len) {
|
||||
conn->endpoint.uh->handler(c);
|
||||
if (conn->flags & CONN_HEADERS_SENT) {
|
||||
write_chunk(conn, "", 0); // Write final zero-length chunk
|
||||
}
|
||||
close_local_endpoint(conn);
|
||||
}
|
||||
}
|
||||
@@ -2032,16 +2095,6 @@ static void mg_url_encode(const char *src, char *dst, size_t dst_len) {
|
||||
#endif // !NO_DIRECTORY_LISTING || !NO_DAV
|
||||
|
||||
#ifndef NO_DIRECTORY_LISTING
|
||||
static int mg_write_chunked(struct connection *conn, const char *buf, int len) {
|
||||
char chunk_size[50];
|
||||
int n = mg_snprintf(chunk_size, sizeof(chunk_size), "%X\r\n", len);
|
||||
|
||||
n = spool(&conn->remote_iobuf, chunk_size, n);
|
||||
n += spool(&conn->remote_iobuf, buf, len);
|
||||
n += spool(&conn->remote_iobuf, "\r\n", 2);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void print_dir_entry(const struct dir_entry *de) {
|
||||
char size[64], mod[64], href[MAX_PATH_SIZE * 3], chunk[MAX_PATH_SIZE * 4];
|
||||
@@ -2071,7 +2124,7 @@ static void print_dir_entry(const struct dir_entry *de) {
|
||||
"<td> %s</td><td> %s</td></tr>\n",
|
||||
de->conn->mg_conn.uri, href, slash, de->file_name, slash,
|
||||
mod, size);
|
||||
mg_write_chunked((struct connection *) de->conn, chunk, n);
|
||||
write_chunk((struct connection *) de->conn, chunk, n);
|
||||
}
|
||||
|
||||
// Sort directory entries by size, or name, or modification time.
|
||||
@@ -2124,7 +2177,7 @@ static void send_directory_listing(struct connection *conn, const char *dir) {
|
||||
"<tr><td colspan=\"3\"><hr></td></tr>",
|
||||
conn->mg_conn.uri, conn->mg_conn.uri,
|
||||
sort_direction, sort_direction, sort_direction);
|
||||
mg_write_chunked(conn, buf, strlen(buf));
|
||||
write_chunk(conn, buf, strlen(buf));
|
||||
|
||||
num_entries = scan_directory(conn, dir, &arr);
|
||||
qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries);
|
||||
@@ -2134,7 +2187,7 @@ static void send_directory_listing(struct connection *conn, const char *dir) {
|
||||
}
|
||||
free(arr);
|
||||
|
||||
mg_write_chunked(conn, "", 0); // Write final zero-length chunk
|
||||
write_chunk(conn, "", 0); // Write final zero-length chunk
|
||||
close_local_endpoint(conn);
|
||||
}
|
||||
#endif // NO_DIRECTORY_LISTING
|
||||
@@ -3226,7 +3279,7 @@ static void log_access(const struct connection *conn, const char *path) {
|
||||
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_FILE || conn->endpoint_type == EP_USER);
|
||||
|
||||
switch (conn->endpoint_type) {
|
||||
case EP_FILE: close(conn->endpoint.fd); break;
|
||||
|
||||
+9
-1
@@ -74,11 +74,19 @@ void mg_iterate_over_connections(struct mg_server *,
|
||||
void *param);
|
||||
|
||||
// Connection management functions
|
||||
int mg_write(struct mg_connection *, const void *buf, int len);
|
||||
void mg_send_status(struct mg_connection *, int status_code);
|
||||
void mg_send_header(struct mg_connection *, const char *name, const char *val);
|
||||
void mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||
void mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||
|
||||
int mg_websocket_write(struct mg_connection *, int opcode,
|
||||
const char *data, size_t data_len);
|
||||
|
||||
// Deprecated in favor of mg_send_* interface
|
||||
int mg_write(struct mg_connection *, const void *buf, int len);
|
||||
int mg_printf(struct mg_connection *conn, const char *fmt, ...);
|
||||
|
||||
|
||||
const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||
const char *mg_get_mime_type(const char *file_name);
|
||||
int mg_get_var(const struct mg_connection *conn, const char *var_name,
|
||||
|
||||
Reference in new issue
Block a user