From 4ca4c862a5ff9573c902aada02ff8b9d4c3357c8 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Thu, 9 Oct 2014 09:26:45 +0100 Subject: [PATCH] Updated net_skeleton, added extra_headers param to mg_send_file() --- examples/Makefile | 2 +- examples/file_upload/Makefile | 3 +- examples/hello_world/Makefile | 2 + examples/send_file/Makefile | 2 + examples/send_file/send_file.c | 2 +- examples/websocket_chat/websocket_chat.c | 2 +- .../websocket_echo_server.c | 2 +- examples/websocket_ssl_proxy/net_skeleton.c | 93 +++++---- examples/websocket_ssl_proxy/net_skeleton.h | 66 +++--- examples/websocket_ssl_proxy/ssl_wrapper.c | 18 +- examples/websocket_ssl_proxy/ssl_wrapper.h | 2 +- examples/websocket_ssl_proxy/ws_ssl.c | 2 +- mongoose.c | 190 ++++++++++-------- mongoose.h | 2 +- 14 files changed, 225 insertions(+), 163 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index bd14ceba..fc0939bd 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -6,7 +6,7 @@ CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA) RM = rm -rf OUT = -o $@ -ALL_PROGS = hello websocket server post multi_threaded upload auth pubsub +ALL_PROGS = server post auth NS = ../../net_skeleton SW = ../../ssl_wrapper diff --git a/examples/file_upload/Makefile b/examples/file_upload/Makefile index d4a074fb..6e43388a 100644 --- a/examples/file_upload/Makefile +++ b/examples/file_upload/Makefile @@ -6,6 +6,8 @@ CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA) SOURCES = $(PROG).c ../../mongoose.c all: $(PROG) + +run: $(PROG) ./$(PROG) $(PROG): $(SOURCES) Makefile @@ -13,7 +15,6 @@ $(PROG): $(SOURCES) Makefile win: wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe - wine $(PROG).exe clean: rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc* diff --git a/examples/hello_world/Makefile b/examples/hello_world/Makefile index c2c836bb..bab0dba7 100644 --- a/examples/hello_world/Makefile +++ b/examples/hello_world/Makefile @@ -6,6 +6,8 @@ CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA) SOURCES = $(PROG).c ../../mongoose.c all: $(PROG) + +run: $(PROG) ./$(PROG) $(PROG): $(SOURCES) Makefile diff --git a/examples/send_file/Makefile b/examples/send_file/Makefile index 3793867e..0badd358 100644 --- a/examples/send_file/Makefile +++ b/examples/send_file/Makefile @@ -6,6 +6,8 @@ CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA) SOURCES = $(PROG).c ../../mongoose.c all: $(PROG) + +run: $(PROG) ./$(PROG) $(PROG): $(SOURCES) Makefile diff --git a/examples/send_file/send_file.c b/examples/send_file/send_file.c index ddb9350a..345c3af6 100644 --- a/examples/send_file/send_file.c +++ b/examples/send_file/send_file.c @@ -8,7 +8,7 @@ static int ev_handler(struct mg_connection *conn, enum mg_event ev) { switch (ev) { case MG_REQUEST: - mg_send_file(conn, "send_file.c"); // Also could be a directory, or CGI file + mg_send_file(conn, "send_file.c", NULL); // Also could be a dir, or CGI return MG_MORE; // It is important to return MG_MORE after mg_send_file! case MG_AUTH: return MG_TRUE; default: return MG_FALSE; diff --git a/examples/websocket_chat/websocket_chat.c b/examples/websocket_chat/websocket_chat.c index a1de7234..5ba53801 100644 --- a/examples/websocket_chat/websocket_chat.c +++ b/examples/websocket_chat/websocket_chat.c @@ -49,7 +49,7 @@ static int ev_handler(struct mg_connection *conn, enum mg_event ev) { handle_websocket_message(conn); return MG_TRUE; } else { - mg_send_file(conn, "index.html"); // Return MG_MORE after mg_send_file() + mg_send_file(conn, "index.html", NULL); // Return MG_MORE after! return MG_MORE; } case MG_WS_CONNECT: diff --git a/examples/websocket_echo_server/websocket_echo_server.c b/examples/websocket_echo_server/websocket_echo_server.c index ceb40917..9001a8a4 100644 --- a/examples/websocket_echo_server/websocket_echo_server.c +++ b/examples/websocket_echo_server/websocket_echo_server.c @@ -27,7 +27,7 @@ static int send_reply(struct mg_connection *conn) { return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ? MG_FALSE : MG_TRUE; } else { - mg_send_file(conn, "index.html"); + mg_send_file(conn, "index.html", NULL); return MG_MORE; } } diff --git a/examples/websocket_ssl_proxy/net_skeleton.c b/examples/websocket_ssl_proxy/net_skeleton.c index 98b37f65..5b9904e0 100644 --- a/examples/websocket_ssl_proxy/net_skeleton.c +++ b/examples/websocket_ssl_proxy/net_skeleton.c @@ -14,7 +14,7 @@ // Alternatively, you can license this software under a commercial // license, as set out in . // -// $Date: 2014-09-09 17:09:33 UTC $ +// $Date: 2014-09-28 05:04:41 UTC $ #include "net_skeleton.h" @@ -38,13 +38,19 @@ struct ctl_msg { char message[1024 * 8]; }; -void iobuf_init(struct iobuf *iobuf, size_t size) { +void iobuf_resize(struct iobuf *io, size_t new_size) { + char *p; + if ((new_size > io->size || (new_size < io->size && new_size >= io->len)) && + (p = (char *) NS_REALLOC(io->buf, new_size)) != NULL) { + io->size = new_size; + io->buf = p; + } +} + +void iobuf_init(struct iobuf *iobuf, size_t initial_size) { iobuf->len = iobuf->size = 0; iobuf->buf = NULL; - - if (size > 0 && (iobuf->buf = (char *) NS_MALLOC(size)) != NULL) { - iobuf->size = size; - } + iobuf_resize(iobuf, initial_size); } void iobuf_free(struct iobuf *iobuf) { @@ -85,8 +91,8 @@ void iobuf_remove(struct iobuf *io, size_t n) { static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) { if (nc->flags & NSF_UDP) { - long n = send(nc->sock, buf, len, 0); - DBG(("%p %d send %ld (%d)", nc, nc->sock, n, errno)); + long n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno))); return n < 0 ? 0 : n; } else { return iobuf_append(&nc->send_iobuf, buf, len); @@ -191,7 +197,7 @@ int ns_printf(struct ns_connection *conn, const char *fmt, ...) { } static void hexdump(struct ns_connection *nc, const char *path, - int num_bytes, enum ns_event ev) { + int num_bytes, int ev) { const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf; FILE *fp; char *buf, src[60], dst[60]; @@ -201,7 +207,7 @@ static void hexdump(struct ns_connection *nc, const char *path, ns_sock_to_str(nc->sock, src, sizeof(src), 3); ns_sock_to_str(nc->sock, dst, sizeof(dst), 7); fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL), - nc->connection_data, src, + nc->user_data, src, ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" : ev == NS_ACCEPT ? "" : "XX", dst, num_bytes); @@ -215,12 +221,13 @@ static void hexdump(struct ns_connection *nc, const char *path, } } -static void ns_call(struct ns_connection *conn, enum ns_event ev, void *p) { - if (conn->mgr->hexdump_file != NULL && ev != NS_POLL) { +static void ns_call(struct ns_connection *nc, int ev, void *p) { + if (nc->mgr->hexdump_file != NULL && ev != NS_POLL) { int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0; - hexdump(conn, conn->mgr->hexdump_file, len, ev); + hexdump(nc, nc->mgr->hexdump_file, len, ev); } - if (conn->mgr->callback) conn->mgr->callback(conn, ev, p); + + nc->callback(nc, ev, p); } static void ns_destroy_conn(struct ns_connection *conn) { @@ -443,7 +450,8 @@ static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) { } #endif // NS_ENABLE_SSL -struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, void *data) { +struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, + ns_callback_t callback, void *user_data) { union socket_address sa; struct ns_connection *nc = NULL; int use_ssl, proto; @@ -454,12 +462,13 @@ struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, void *data) { if (use_ssl && cert[0] == '\0') return NULL; if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) { - } else if ((nc = ns_add_sock(srv, sock, NULL)) == NULL) { + } else if ((nc = ns_add_sock(srv, sock, callback, NULL)) == NULL) { closesocket(sock); } else { nc->sa = sa; nc->flags |= NSF_LISTENING; - nc->connection_data = data; + nc->user_data = user_data; + nc->callback = callback; if (proto == SOCK_DGRAM) { nc->flags |= NSF_UDP; @@ -490,7 +499,8 @@ static struct ns_connection *accept_conn(struct ns_connection *ls) { // NOTE(lsm): on Windows, sock is always > FD_SETSIZE if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) { - } else if ((c = ns_add_sock(ls->mgr, sock, NULL)) == NULL) { + } else if ((c = ns_add_sock(ls->mgr, sock, ls->callback, + ls->user_data)) == NULL) { closesocket(sock); #ifdef NS_ENABLE_SSL } else if (ls->ssl_ctx != NULL && @@ -502,6 +512,7 @@ static struct ns_connection *accept_conn(struct ns_connection *ls) { #endif } else { c->listener = ls; + c->proto_data = ls->proto_data; ns_call(c, NS_ACCEPT, &sa); DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl)); } @@ -698,10 +709,16 @@ static void ns_handle_udp(struct ns_connection *ls) { if (n <= 0) { DBG(("%p recvfrom: %s", ls, strerror(errno))); } else { + nc.mgr = ls->mgr; nc.recv_iobuf.buf = buf; nc.recv_iobuf.len = nc.recv_iobuf.size = n; nc.sock = ls->sock; + nc.callback = ls->callback; + nc.user_data = ls->user_data; + nc.proto_data = ls->proto_data; nc.mgr = ls->mgr; + nc.listener = ls; + nc.flags = NSF_UDP; DBG(("%p %d bytes received", ls, n)); ns_call(&nc, NS_RECV, &n); } @@ -716,11 +733,10 @@ static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { } } -int ns_mgr_poll(struct ns_mgr *mgr, int milli) { +time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) { struct ns_connection *conn, *tmp_conn; struct timeval tv; fd_set read_set, write_set; - int num_active_connections = 0; sock_t max_fd = INVALID_SOCKET; time_t current_time = time(NULL); @@ -730,7 +746,9 @@ int ns_mgr_poll(struct ns_mgr *mgr, int milli) { for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { tmp_conn = conn->next; - ns_call(conn, NS_POLL, ¤t_time); + if (!(conn->flags & (NSF_LISTENING | NSF_CONNECTING))) { + ns_call(conn, NS_POLL, ¤t_time); + } if (!(conn->flags & NSF_WANT_WRITE)) { //DBG(("%p read_set", conn)); ns_add_to_set(conn->sock, &read_set, &max_fd); @@ -799,47 +817,41 @@ int ns_mgr_poll(struct ns_mgr *mgr, int milli) { for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { tmp_conn = conn->next; - num_active_connections++; if ((conn->flags & NSF_CLOSE_IMMEDIATELY) || (conn->send_iobuf.len == 0 && (conn->flags & NSF_FINISHED_SENDING_DATA))) { ns_close_conn(conn); } } - //DBG(("%d active connections", num_active_connections)); - return num_active_connections; + return current_time; } -struct ns_connection *ns_connect(struct ns_mgr *mgr, - const char *address, void *param) { +struct ns_connection *ns_connect(struct ns_mgr *mgr, const char *address, + ns_callback_t callback, void *user_data) { sock_t sock = INVALID_SOCKET; struct ns_connection *nc = NULL; union socket_address sa; char cert[100], ca_cert[100]; - int connect_ret_val, use_ssl, proto; + int rc, use_ssl, proto; ns_parse_address(address, &sa, &proto, &use_ssl, cert, ca_cert); if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) { return NULL; } ns_set_non_blocking_mode(sock); - connect_ret_val = connect(sock, &sa.sa, sizeof(sa.sin)); + rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin)); - if (connect_ret_val != 0 && ns_is_error(connect_ret_val)) { + if (rc != 0 && ns_is_error(rc)) { closesocket(sock); return NULL; - } else if ((nc = ns_add_sock(mgr, sock, param)) == NULL) { + } else if ((nc = ns_add_sock(mgr, sock, callback, user_data)) == NULL) { closesocket(sock); return NULL; } - nc->sa = sa; // Essential, cause UDP conns will use sendto() - if (proto == SOCK_DGRAM) { - nc->flags = NSF_UDP; - } else { - nc->flags = NSF_CONNECTING; - } + nc->sa = sa; // Important, cause UDP conns will use sendto() + nc->flags = (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING; #ifdef NS_ENABLE_SSL if (use_ssl) { @@ -858,14 +870,16 @@ struct ns_connection *ns_connect(struct ns_mgr *mgr, return nc; } -struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock, void *p) { +struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock, + ns_callback_t callback, void *user_data) { struct ns_connection *conn; if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) { memset(conn, 0, sizeof(*conn)); ns_set_non_blocking_mode(sock); ns_set_close_on_exec(sock); conn->sock = sock; - conn->connection_data = p; + conn->user_data = user_data; + conn->callback = callback; conn->mgr = s; conn->last_io_time = time(NULL); ns_add_conn(s, conn); @@ -890,11 +904,10 @@ void ns_broadcast(struct ns_mgr *mgr, ns_callback_t cb,void *data, size_t len) { } } -void ns_mgr_init(struct ns_mgr *s, void *user_data, ns_callback_t cb) { +void ns_mgr_init(struct ns_mgr *s, void *user_data) { memset(s, 0, sizeof(*s)); s->ctl[0] = s->ctl[1] = INVALID_SOCKET; s->user_data = user_data; - s->callback = cb; #ifdef _WIN32 { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } diff --git a/examples/websocket_ssl_proxy/net_skeleton.h b/examples/websocket_ssl_proxy/net_skeleton.h index a2453f72..4d1c33fc 100644 --- a/examples/websocket_ssl_proxy/net_skeleton.h +++ b/examples/websocket_ssl_proxy/net_skeleton.h @@ -14,12 +14,12 @@ // Alternatively, you can license this software under a commercial // license, as set out in . // -// $Date: 2014-09-09 17:09:33 UTC $ +// $Date: 2014-09-28 05:04:41 UTC $ #ifndef NS_SKELETON_HEADER_INCLUDED #define NS_SKELETON_HEADER_INCLUDED -#define NS_SKELETON_VERSION "2.0.0" +#define NS_SKELETON_VERSION "2.1.0" #undef UNICODE // Use ANSI WinAPI functions #undef _UNICODE // Use multibyte encoding on Windows @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -84,6 +85,7 @@ typedef unsigned short uint16_t; typedef unsigned __int64 uint64_t; typedef __int64 int64_t; typedef SOCKET sock_t; +typedef struct _stati64 ns_stat_t; #ifndef S_ISDIR #define S_ISDIR(x) ((x) & _S_IFDIR) #endif @@ -103,6 +105,7 @@ typedef SOCKET sock_t; #define INVALID_SOCKET (-1) #define to64(x) strtoll(x, NULL, 10) typedef int sock_t; +typedef struct stat ns_stat_t; #endif #ifdef NS_ENABLE_DEBUG @@ -140,6 +143,12 @@ union socket_address { #endif }; +// Describes chunk of memory +struct ns_str { + const char *p; + size_t len; +}; + // IO buffers interface struct iobuf { char *buf; @@ -151,43 +160,45 @@ void iobuf_init(struct iobuf *, size_t initial_size); void iobuf_free(struct iobuf *); size_t iobuf_append(struct iobuf *, const void *data, size_t data_size); void iobuf_remove(struct iobuf *, size_t data_size); - -// Net skeleton interface -// Events. Meaning of event parameter (evp) is given in the comment. -enum ns_event { - NS_POLL, // Sent to each connection on each call to ns_mgr_poll() - NS_ACCEPT, // New connection accept()-ed. union socket_address *remote_addr - NS_CONNECT, // connect() succeeded or failed. int *success_status - NS_RECV, // Data has benn received. int *num_bytes - NS_SEND, // Data has been written to a socket. int *num_bytes - NS_CLOSE // Connection is closed. NULL -}; +void iobuf_resize(struct iobuf *, size_t new_size); // Callback function (event handler) prototype, must be defined by user. // Net skeleton will call event handler, passing events defined above. struct ns_connection; -typedef void (*ns_callback_t)(struct ns_connection *, enum ns_event, void *evp); +typedef void (*ns_callback_t)(struct ns_connection *, int event_num, void *evp); + +// Events. Meaning of event parameter (evp) is given in the comment. +#define NS_POLL 0 // Sent to each connection on each call to ns_mgr_poll() +#define NS_ACCEPT 1 // New connection accept()-ed. union socket_address *addr +#define NS_CONNECT 2 // connect() succeeded or failed. int *success_status +#define NS_RECV 3 // Data has benn received. int *num_bytes +#define NS_SEND 4 // Data has been written to a socket. int *num_bytes +#define NS_CLOSE 5 // Connection is closed. NULL + struct ns_mgr { struct ns_connection *active_connections; - ns_callback_t callback; // Event handler function const char *hexdump_file; // Debug hexdump file path sock_t ctl[2]; // Socketpair for mg_wakeup() void *user_data; // User data }; + struct ns_connection { struct ns_connection *next, *prev; // ns_mgr::active_connections linkage struct ns_connection *listener; // Set only for accept()-ed connections struct ns_mgr *mgr; - sock_t sock; - union socket_address sa; - struct iobuf recv_iobuf; - struct iobuf send_iobuf; + + sock_t sock; // Socket + union socket_address sa; // Peer address + struct iobuf recv_iobuf; // Received data + struct iobuf send_iobuf; // Data scheduled for sending SSL *ssl; SSL_CTX *ssl_ctx; - void *connection_data; - time_t last_io_time; + void *user_data; // User-specific data + void *proto_data; // Application protocol-specific data + time_t last_io_time; // Timestamp of the last socket IO + ns_callback_t callback; // Event handler function unsigned int flags; #define NSF_FINISHED_SENDING_DATA (1 << 0) @@ -208,15 +219,18 @@ struct ns_connection { #define NSF_USER_6 (1 << 25) }; -void ns_mgr_init(struct ns_mgr *, void *data, ns_callback_t); +void ns_mgr_init(struct ns_mgr *, void *user_data); void ns_mgr_free(struct ns_mgr *); -int ns_mgr_poll(struct ns_mgr *, int milli); +time_t ns_mgr_poll(struct ns_mgr *, int milli); void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t); struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *); -struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t sock, void *p); -struct ns_connection *ns_bind(struct ns_mgr *, const char *addr, void *p); -struct ns_connection *ns_connect(struct ns_mgr *, const char *addr, void *p); +struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t, + ns_callback_t, void *); +struct ns_connection *ns_bind(struct ns_mgr *, const char *, + ns_callback_t, void *); +struct ns_connection *ns_connect(struct ns_mgr *, const char *, + ns_callback_t, void *); int ns_send(struct ns_connection *, const void *buf, int len); int ns_printf(struct ns_connection *, const char *fmt, ...); diff --git a/examples/websocket_ssl_proxy/ssl_wrapper.c b/examples/websocket_ssl_proxy/ssl_wrapper.c index a2c49066..969aa979 100644 --- a/examples/websocket_ssl_proxy/ssl_wrapper.c +++ b/examples/websocket_ssl_proxy/ssl_wrapper.c @@ -14,22 +14,22 @@ // Alternatively, you can license this software under a commercial // license, as set out in . // -// $Date: 2014-09-09 17:09:33 UTC $ +// $Date$ #include "net_skeleton.h" #include "ssl_wrapper.h" -static void ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) { +static void ev_handler(struct ns_connection *nc, int ev, void *p) { const char *target_addr = (const char *) nc->mgr->user_data; - struct ns_connection *pc = (struct ns_connection *) nc->connection_data; + struct ns_connection *pc = (struct ns_connection *) nc->user_data; struct iobuf *io = &nc->recv_iobuf; (void) p; switch (ev) { case NS_ACCEPT: // Create a connection to the target, and interlink both connections - nc->connection_data = ns_connect(nc->mgr, target_addr, nc); - if (nc->connection_data == NULL) { + nc->user_data = ns_connect(nc->mgr, target_addr, ev_handler, nc); + if (nc->user_data == NULL) { nc->flags |= NSF_CLOSE_IMMEDIATELY; } break; @@ -38,9 +38,9 @@ static void ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) { // If either connection closes, unlink them and shedule closing if (pc != NULL) { pc->flags |= NSF_FINISHED_SENDING_DATA; - pc->connection_data = NULL; + pc->user_data = NULL; } - nc->connection_data = NULL; + nc->user_data = NULL; break; case NS_RECV: @@ -64,8 +64,8 @@ void *ssl_wrapper_init(const char *local_addr, const char *target_addr, if (mgr == NULL) { *err_msg = "malloc failed"; } else { - ns_mgr_init(mgr, (void *) target_addr, ev_handler); - if (ns_bind(mgr, local_addr, NULL) == NULL) { + ns_mgr_init(mgr, (void *) target_addr); + if (ns_bind(mgr, local_addr, ev_handler, NULL) == NULL) { *err_msg = "ns_bind() failed: bad listening_port"; ns_mgr_free(mgr); free(mgr); diff --git a/examples/websocket_ssl_proxy/ssl_wrapper.h b/examples/websocket_ssl_proxy/ssl_wrapper.h index 8d6e576f..ccdf7805 100644 --- a/examples/websocket_ssl_proxy/ssl_wrapper.h +++ b/examples/websocket_ssl_proxy/ssl_wrapper.h @@ -14,7 +14,7 @@ // Alternatively, you can license this software under a commercial // license, as set out in . // -// $Date: 2014-09-09 17:09:33 UTC $ +// $Date$ #ifndef SSL_WRAPPER_HEADER_INCLUDED #define SSL_WRAPPER_HEADER_INCLUDED diff --git a/examples/websocket_ssl_proxy/ws_ssl.c b/examples/websocket_ssl_proxy/ws_ssl.c index d39d04db..d2fceee6 100644 --- a/examples/websocket_ssl_proxy/ws_ssl.c +++ b/examples/websocket_ssl_proxy/ws_ssl.c @@ -88,7 +88,7 @@ static int ev_handler(struct mg_connection *conn, enum mg_event ev) { } // Not a CONNECT request, serve HTML file. - mg_send_file(conn, "ws_ssl.html"); + mg_send_file(conn, "ws_ssl.html", NULL); return MG_MORE; default: diff --git a/mongoose.c b/mongoose.c index 12a79f04..76048b28 100644 --- a/mongoose.c +++ b/mongoose.c @@ -37,12 +37,12 @@ // Alternatively, you can license this software under a commercial // license, as set out in . // -// $Date: 2014-09-16 06:47:40 UTC $ +// $Date: 2014-09-28 05:04:41 UTC $ #ifndef NS_SKELETON_HEADER_INCLUDED #define NS_SKELETON_HEADER_INCLUDED -#define NS_SKELETON_VERSION "2.0.0" +#define NS_SKELETON_VERSION "2.1.0" #undef UNICODE // Use ANSI WinAPI functions #undef _UNICODE // Use multibyte encoding on Windows @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,7 @@ typedef unsigned short uint16_t; typedef unsigned __int64 uint64_t; typedef __int64 int64_t; typedef SOCKET sock_t; +typedef struct _stati64 ns_stat_t; #ifndef S_ISDIR #define S_ISDIR(x) ((x) & _S_IFDIR) #endif @@ -126,6 +128,7 @@ typedef SOCKET sock_t; #define INVALID_SOCKET (-1) #define to64(x) strtoll(x, NULL, 10) typedef int sock_t; +typedef struct stat ns_stat_t; #endif #ifdef NS_ENABLE_DEBUG @@ -163,6 +166,12 @@ union socket_address { #endif }; +// Describes chunk of memory +struct ns_str { + const char *p; + size_t len; +}; + // IO buffers interface struct iobuf { char *buf; @@ -174,43 +183,45 @@ void iobuf_init(struct iobuf *, size_t initial_size); void iobuf_free(struct iobuf *); size_t iobuf_append(struct iobuf *, const void *data, size_t data_size); void iobuf_remove(struct iobuf *, size_t data_size); - -// Net skeleton interface -// Events. Meaning of event parameter (evp) is given in the comment. -enum ns_event { - NS_POLL, // Sent to each connection on each call to ns_mgr_poll() - NS_ACCEPT, // New connection accept()-ed. union socket_address *remote_addr - NS_CONNECT, // connect() succeeded or failed. int *success_status - NS_RECV, // Data has benn received. int *num_bytes - NS_SEND, // Data has been written to a socket. int *num_bytes - NS_CLOSE // Connection is closed. NULL -}; +void iobuf_resize(struct iobuf *, size_t new_size); // Callback function (event handler) prototype, must be defined by user. // Net skeleton will call event handler, passing events defined above. struct ns_connection; -typedef void (*ns_callback_t)(struct ns_connection *, enum ns_event, void *evp); +typedef void (*ns_callback_t)(struct ns_connection *, int event_num, void *evp); + +// Events. Meaning of event parameter (evp) is given in the comment. +#define NS_POLL 0 // Sent to each connection on each call to ns_mgr_poll() +#define NS_ACCEPT 1 // New connection accept()-ed. union socket_address *addr +#define NS_CONNECT 2 // connect() succeeded or failed. int *success_status +#define NS_RECV 3 // Data has benn received. int *num_bytes +#define NS_SEND 4 // Data has been written to a socket. int *num_bytes +#define NS_CLOSE 5 // Connection is closed. NULL + struct ns_mgr { struct ns_connection *active_connections; - ns_callback_t callback; // Event handler function const char *hexdump_file; // Debug hexdump file path sock_t ctl[2]; // Socketpair for mg_wakeup() void *user_data; // User data }; + struct ns_connection { struct ns_connection *next, *prev; // ns_mgr::active_connections linkage struct ns_connection *listener; // Set only for accept()-ed connections struct ns_mgr *mgr; - sock_t sock; - union socket_address sa; - struct iobuf recv_iobuf; - struct iobuf send_iobuf; + + sock_t sock; // Socket + union socket_address sa; // Peer address + struct iobuf recv_iobuf; // Received data + struct iobuf send_iobuf; // Data scheduled for sending SSL *ssl; SSL_CTX *ssl_ctx; - void *user_data; - time_t last_io_time; + void *user_data; // User-specific data + void *proto_data; // Application protocol-specific data + time_t last_io_time; // Timestamp of the last socket IO + ns_callback_t callback; // Event handler function unsigned int flags; #define NSF_FINISHED_SENDING_DATA (1 << 0) @@ -231,15 +242,18 @@ struct ns_connection { #define NSF_USER_6 (1 << 25) }; -void ns_mgr_init(struct ns_mgr *, void *data, ns_callback_t); +void ns_mgr_init(struct ns_mgr *, void *user_data); void ns_mgr_free(struct ns_mgr *); -int ns_mgr_poll(struct ns_mgr *, int milli); +time_t ns_mgr_poll(struct ns_mgr *, int milli); void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t); struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *); -struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t sock, void *p); -struct ns_connection *ns_bind(struct ns_mgr *, const char *addr, void *p); -struct ns_connection *ns_connect(struct ns_mgr *, const char *addr, void *p); +struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t, + ns_callback_t, void *); +struct ns_connection *ns_bind(struct ns_mgr *, const char *, + ns_callback_t, void *); +struct ns_connection *ns_connect(struct ns_mgr *, const char *, + ns_callback_t, void *); int ns_send(struct ns_connection *, const void *buf, int len); int ns_printf(struct ns_connection *, const char *fmt, ...); @@ -276,7 +290,7 @@ int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); // Alternatively, you can license this software under a commercial // license, as set out in . // -// $Date: 2014-09-16 06:47:40 UTC $ +// $Date: 2014-09-28 05:04:41 UTC $ #ifndef NS_MALLOC @@ -299,13 +313,19 @@ struct ctl_msg { char message[1024 * 8]; }; -void iobuf_init(struct iobuf *iobuf, size_t size) { +void iobuf_resize(struct iobuf *io, size_t new_size) { + char *p; + if ((new_size > io->size || (new_size < io->size && new_size >= io->len)) && + (p = (char *) NS_REALLOC(io->buf, new_size)) != NULL) { + io->size = new_size; + io->buf = p; + } +} + +void iobuf_init(struct iobuf *iobuf, size_t initial_size) { iobuf->len = iobuf->size = 0; iobuf->buf = NULL; - - if (size > 0 && (iobuf->buf = (char *) NS_MALLOC(size)) != NULL) { - iobuf->size = size; - } + iobuf_resize(iobuf, initial_size); } void iobuf_free(struct iobuf *iobuf) { @@ -347,7 +367,7 @@ void iobuf_remove(struct iobuf *io, size_t n) { static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) { if (nc->flags & NSF_UDP) { long n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); - DBG(("%p %d send %ld (%d)", nc, nc->sock, n, errno)); + DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno))); return n < 0 ? 0 : n; } else { return iobuf_append(&nc->send_iobuf, buf, len); @@ -452,7 +472,7 @@ int ns_printf(struct ns_connection *conn, const char *fmt, ...) { } static void hexdump(struct ns_connection *nc, const char *path, - int num_bytes, enum ns_event ev) { + int num_bytes, int ev) { const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf; FILE *fp; char *buf, src[60], dst[60]; @@ -476,12 +496,13 @@ static void hexdump(struct ns_connection *nc, const char *path, } } -static void ns_call(struct ns_connection *conn, enum ns_event ev, void *p) { - if (conn->mgr->hexdump_file != NULL && ev != NS_POLL) { +static void ns_call(struct ns_connection *nc, int ev, void *p) { + if (nc->mgr->hexdump_file != NULL && ev != NS_POLL) { int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0; - hexdump(conn, conn->mgr->hexdump_file, len, ev); + hexdump(nc, nc->mgr->hexdump_file, len, ev); } - if (conn->mgr->callback) conn->mgr->callback(conn, ev, p); + + nc->callback(nc, ev, p); } static void ns_destroy_conn(struct ns_connection *conn) { @@ -704,7 +725,8 @@ static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) { } #endif // NS_ENABLE_SSL -struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, void *data) { +struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, + ns_callback_t callback, void *user_data) { union socket_address sa; struct ns_connection *nc = NULL; int use_ssl, proto; @@ -715,12 +737,13 @@ struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, void *data) { if (use_ssl && cert[0] == '\0') return NULL; if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) { - } else if ((nc = ns_add_sock(srv, sock, NULL)) == NULL) { + } else if ((nc = ns_add_sock(srv, sock, callback, NULL)) == NULL) { closesocket(sock); } else { nc->sa = sa; nc->flags |= NSF_LISTENING; - nc->user_data = data; + nc->user_data = user_data; + nc->callback = callback; if (proto == SOCK_DGRAM) { nc->flags |= NSF_UDP; @@ -751,7 +774,8 @@ static struct ns_connection *accept_conn(struct ns_connection *ls) { // NOTE(lsm): on Windows, sock is always > FD_SETSIZE if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) { - } else if ((c = ns_add_sock(ls->mgr, sock, NULL)) == NULL) { + } else if ((c = ns_add_sock(ls->mgr, sock, ls->callback, + ls->user_data)) == NULL) { closesocket(sock); #ifdef NS_ENABLE_SSL } else if (ls->ssl_ctx != NULL && @@ -763,6 +787,7 @@ static struct ns_connection *accept_conn(struct ns_connection *ls) { #endif } else { c->listener = ls; + c->proto_data = ls->proto_data; ns_call(c, NS_ACCEPT, &sa); DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl)); } @@ -963,6 +988,9 @@ static void ns_handle_udp(struct ns_connection *ls) { nc.recv_iobuf.buf = buf; nc.recv_iobuf.len = nc.recv_iobuf.size = n; nc.sock = ls->sock; + nc.callback = ls->callback; + nc.user_data = ls->user_data; + nc.proto_data = ls->proto_data; nc.mgr = ls->mgr; nc.listener = ls; nc.flags = NSF_UDP; @@ -980,11 +1008,10 @@ static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { } } -int ns_mgr_poll(struct ns_mgr *mgr, int milli) { +time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) { struct ns_connection *conn, *tmp_conn; struct timeval tv; fd_set read_set, write_set; - int num_active_connections = 0; sock_t max_fd = INVALID_SOCKET; time_t current_time = time(NULL); @@ -994,7 +1021,9 @@ int ns_mgr_poll(struct ns_mgr *mgr, int milli) { for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { tmp_conn = conn->next; - ns_call(conn, NS_POLL, ¤t_time); + if (!(conn->flags & (NSF_LISTENING | NSF_CONNECTING))) { + ns_call(conn, NS_POLL, ¤t_time); + } if (!(conn->flags & NSF_WANT_WRITE)) { //DBG(("%p read_set", conn)); ns_add_to_set(conn->sock, &read_set, &max_fd); @@ -1063,47 +1092,41 @@ int ns_mgr_poll(struct ns_mgr *mgr, int milli) { for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { tmp_conn = conn->next; - num_active_connections++; if ((conn->flags & NSF_CLOSE_IMMEDIATELY) || (conn->send_iobuf.len == 0 && (conn->flags & NSF_FINISHED_SENDING_DATA))) { ns_close_conn(conn); } } - //DBG(("%d active connections", num_active_connections)); - return num_active_connections; + return current_time; } -struct ns_connection *ns_connect(struct ns_mgr *mgr, - const char *address, void *param) { +struct ns_connection *ns_connect(struct ns_mgr *mgr, const char *address, + ns_callback_t callback, void *user_data) { sock_t sock = INVALID_SOCKET; struct ns_connection *nc = NULL; union socket_address sa; char cert[100], ca_cert[100]; - int connect_ret_val, use_ssl, proto; + int rc, use_ssl, proto; ns_parse_address(address, &sa, &proto, &use_ssl, cert, ca_cert); if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) { return NULL; } ns_set_non_blocking_mode(sock); - connect_ret_val = connect(sock, &sa.sa, sizeof(sa.sin)); + rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin)); - if (connect_ret_val != 0 && ns_is_error(connect_ret_val)) { + if (rc != 0 && ns_is_error(rc)) { closesocket(sock); return NULL; - } else if ((nc = ns_add_sock(mgr, sock, param)) == NULL) { + } else if ((nc = ns_add_sock(mgr, sock, callback, user_data)) == NULL) { closesocket(sock); return NULL; } - nc->sa = sa; // Essential, cause UDP conns will use sendto() - if (proto == SOCK_DGRAM) { - nc->flags = NSF_UDP; - } else { - nc->flags = NSF_CONNECTING; - } + nc->sa = sa; // Important, cause UDP conns will use sendto() + nc->flags = (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING; #ifdef NS_ENABLE_SSL if (use_ssl) { @@ -1122,14 +1145,16 @@ struct ns_connection *ns_connect(struct ns_mgr *mgr, return nc; } -struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock, void *p) { +struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock, + ns_callback_t callback, void *user_data) { struct ns_connection *conn; if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) { memset(conn, 0, sizeof(*conn)); ns_set_non_blocking_mode(sock); ns_set_close_on_exec(sock); conn->sock = sock; - conn->user_data = p; + conn->user_data = user_data; + conn->callback = callback; conn->mgr = s; conn->last_io_time = time(NULL); ns_add_conn(s, conn); @@ -1154,11 +1179,10 @@ void ns_broadcast(struct ns_mgr *mgr, ns_callback_t cb,void *data, size_t len) { } } -void ns_mgr_init(struct ns_mgr *s, void *user_data, ns_callback_t cb) { +void ns_mgr_init(struct ns_mgr *s, void *user_data) { memset(s, 0, sizeof(*s)); s->ctl[0] = s->ctl[1] = INVALID_SOCKET; s->user_data = user_data; - s->callback = cb; #ifdef _WIN32 { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } @@ -1427,6 +1451,7 @@ struct connection { static void open_local_endpoint(struct connection *conn, int skip_user); static void close_local_endpoint(struct connection *conn); +static void mg_ev_handler(struct ns_connection *nc, int ev, void *p); static const struct { const char *extension; @@ -2190,7 +2215,8 @@ static void open_cgi_endpoint(struct connection *conn, const char *prog) { if (start_process(conn->server->config_options[CGI_INTERPRETER], prog, blk.buf, blk.vars, dir, fds[1]) != 0) { conn->endpoint_type = EP_CGI; - conn->endpoint.nc = ns_add_sock(&conn->server->ns_mgr, fds[0], conn); + conn->endpoint.nc = ns_add_sock(&conn->server->ns_mgr, fds[0], + mg_ev_handler, conn); conn->endpoint.nc->flags |= MG_CGI_CONN; ns_send(conn->ns_conn, cgi_status, sizeof(cgi_status) - 1); conn->mg_conn.status_code = 200; @@ -3155,8 +3181,8 @@ static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { } static void open_file_endpoint(struct connection *conn, const char *path, - file_stat_t *st) { - char date[64], lm[64], etag[64], range[64], headers[500]; + file_stat_t *st, const char *extra_headers) { + char date[64], lm[64], etag[64], range[64], headers[1000]; const char *msg = "OK", *hdr; time_t curtime = time(NULL); int64_t r1, r2; @@ -3200,11 +3226,12 @@ static void open_file_endpoint(struct connection *conn, const char *path, "Content-Length: %" INT64_FMT "\r\n" "Connection: %s\r\n" "Accept-Ranges: bytes\r\n" - "%s%s\r\n", + "%s%s%s\r\n", conn->mg_conn.status_code, msg, date, lm, etag, (int) mime_vec.len, mime_vec.ptr, conn->cl, suggest_connection_header(&conn->mg_conn), - range, MONGOOSE_USE_EXTRA_HTTP_HEADERS); + range, extra_headers == NULL ? "" : extra_headers, + MONGOOSE_USE_EXTRA_HTTP_HEADERS); ns_send(conn->ns_conn, headers, n); if (!strcmp(conn->mg_conn.request_method, "HEAD")) { @@ -4283,7 +4310,8 @@ int mg_forward(struct mg_connection *c, const char *addr) { struct connection *conn = MG_CONN_2_CONN(c); struct ns_connection *pc; - if ((pc = ns_connect(&conn->server->ns_mgr, addr, conn)) == NULL) { + if ((pc = ns_connect(&conn->server->ns_mgr, addr, + mg_ev_handler, conn)) == NULL) { conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY; return 0; } @@ -4330,7 +4358,8 @@ static void proxify_connection(struct connection *conn) { #ifndef MONGOOSE_NO_FILESYSTEM void mg_send_file_internal(struct mg_connection *c, const char *file_name, - file_stat_t *st, int exists) { + file_stat_t *st, int exists, + const char *extra_headers) { struct connection *conn = MG_CONN_2_CONN(c); char path[MAX_PATH_SIZE]; const int is_directory = S_ISDIR(st->st_mode); @@ -4382,15 +4411,16 @@ void mg_send_file_internal(struct mg_connection *c, const char *file_name, } else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY)) != -1) { // O_BINARY is required for Windows, otherwise in default text mode // two bytes \r\n will be read as one. - open_file_endpoint(conn, path, st); + open_file_endpoint(conn, path, st, extra_headers); } else { send_http_error(conn, 404, NULL); } } -void mg_send_file(struct mg_connection *c, const char *file_name) { +void mg_send_file(struct mg_connection *c, const char *file_name, + const char *extra_headers) { file_stat_t st; const int exists = stat(file_name, &st) == 0; - mg_send_file_internal(c, file_name, &st, exists); + mg_send_file_internal(c, file_name, &st, exists, extra_headers); } #endif // !MONGOOSE_NO_FILESYSTEM @@ -4473,7 +4503,7 @@ static void open_local_endpoint(struct connection *conn, int skip_user) { handle_put(conn, path); #endif } else { - mg_send_file_internal(&conn->mg_conn, path, &st, exists); + mg_send_file_internal(&conn->mg_conn, path, &st, exists, NULL); } #endif // MONGOOSE_NO_FILESYSTEM } @@ -4619,7 +4649,7 @@ struct mg_connection *mg_connect(struct mg_server *server, const char *addr) { struct ns_connection *nsconn; struct connection *conn; - nsconn = ns_connect(&server->ns_mgr, addr, NULL); + nsconn = ns_connect(&server->ns_mgr, addr, mg_ev_handler, NULL); if (nsconn == NULL) return 0; if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) { @@ -4946,7 +4976,7 @@ const char *mg_set_option(struct mg_server *server, const char *name, DBG(("%s [%s]", name, *v)); if (ind == LISTENING_PORT) { - struct ns_connection *c = ns_bind(&server->ns_mgr, value, NULL); + struct ns_connection *c = ns_bind(&server->ns_mgr, value, mg_ev_handler, NULL); if (c == NULL) { error_msg = "Cannot bind to port"; } else { @@ -5020,7 +5050,7 @@ static void process_udp(struct ns_connection *nc) { //ns_printf(nc, "%s", "HTTP/1.0 200 OK\r\n\r\n"); } -static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) { +static void mg_ev_handler(struct ns_connection *nc, int ev, void *p) { struct connection *conn = (struct connection *) nc->user_data; // Send NS event to the handler. Note that call_user won't send an event @@ -5144,7 +5174,7 @@ static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) { } } -static void iter2(struct ns_connection *nc, enum ns_event ev, void *param) { +static void iter2(struct ns_connection *nc, int ev, void *param) { mg_handler_t func = NULL; struct connection *conn = (struct connection *) nc->user_data; const char *msg = (const char *) param; @@ -5199,7 +5229,7 @@ const char *mg_get_option(const struct mg_server *server, const char *name) { struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) { struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server)); - ns_mgr_init(&server->ns_mgr, server_data, mg_ev_handler); + ns_mgr_init(&server->ns_mgr, server_data); set_default_option_values(server->config_options); server->event_handler = handler; return server; diff --git a/mongoose.h b/mongoose.h index 572b863e..55009fac 100644 --- a/mongoose.h +++ b/mongoose.h @@ -113,7 +113,7 @@ size_t mg_websocket_write(struct mg_connection *, int opcode, size_t mg_websocket_printf(struct mg_connection* conn, int opcode, const char *fmt, ...); -void mg_send_file(struct mg_connection *, const char *path); +void mg_send_file(struct mg_connection *, const char *path, const char *); void mg_send_file_data(struct mg_connection *, int fd); const char *mg_get_header(const struct mg_connection *, const char *name);