mirror of
https://github.com/lammertb/libhttp.git
synced 2025-07-31 08:24:23 +03:00
Handler for websocket connection (Step 2/?)
Add an interface to register handler for websocket connections. See enhancement #30 and question #101
This commit is contained in:
@ -96,10 +96,10 @@ int WebSocketStartHandler(struct mg_connection *conn, void *cbdata)
|
|||||||
mg_printf(conn,
|
mg_printf(conn,
|
||||||
"function load() {\n"
|
"function load() {\n"
|
||||||
" var wsproto = (location.protocol === 'https:') ? 'wss:' : 'ws:';\n"
|
" var wsproto = (location.protocol === 'https:') ? 'wss:' : 'ws:';\n"
|
||||||
" connection = new WebSocket(wsproto + '//' + window.location.host + '/websocket.lua');\n"
|
" connection = new WebSocket(wsproto + '//' + window.location.host + '/websocket');\n"
|
||||||
" websock_text_field = document.getElementById('websock_text_field');\n"
|
" websock_text_field = document.getElementById('websock_text_field');\n"
|
||||||
" connection.onmessage = function (e) {\n"
|
" connection.onmessage = function (e) {\n"
|
||||||
" websock_text_field.innerHtml=e.data;\n"
|
" websock_text_field.innerHTML=e.data;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" connection.onerror = function (error) {\n"
|
" connection.onerror = function (error) {\n"
|
||||||
" alert('WebSocket error');\n"
|
" alert('WebSocket error');\n"
|
||||||
@ -121,26 +121,59 @@ int WebSocketStartHandler(struct mg_connection *conn, void *cbdata)
|
|||||||
|
|
||||||
|
|
||||||
#ifdef USE_WEBSOCKET
|
#ifdef USE_WEBSOCKET
|
||||||
|
static struct mg_connection * ws_client;
|
||||||
|
static unsigned long cnt;
|
||||||
|
|
||||||
int WebSocketConnectHandler(const struct mg_connection * conn, void *cbdata)
|
int WebSocketConnectHandler(const struct mg_connection * conn, void *cbdata)
|
||||||
{
|
{
|
||||||
return 0;
|
int reject = 0;
|
||||||
|
fprintf(stdout, "Websocket client %s\r\n\r\n", reject ? "rejected" : "accepted");
|
||||||
|
return reject;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketReadyHandler(const struct mg_connection * conn, void *cbdata)
|
void WebSocketReadyHandler(const struct mg_connection * conn, void *cbdata)
|
||||||
{
|
{
|
||||||
|
struct mg_context *ctx = mg_get_context((struct mg_connection *) /* TODO: check const_casts */ conn);
|
||||||
|
|
||||||
const char * text = "Hello from the websocket ready handler";
|
const char * text = "Hello from the websocket ready handler";
|
||||||
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, text, strlen(text));
|
/* TODO: check "const struct mg_connection *" vs "struct mg_connection *" everywhere */
|
||||||
|
mg_websocket_write((struct mg_connection *)conn, WEBSOCKET_OPCODE_TEXT, text, strlen(text));
|
||||||
|
fprintf(stdout, "Client added to the set of webserver connections\r\n\r\n");
|
||||||
|
mg_lock_context(ctx);
|
||||||
|
ws_client = conn;
|
||||||
|
mg_unlock_context(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int WebsocketDataHandler(const struct mg_connection * conn, int bits, char * data, size_t len, void *cbdata)
|
int WebsocketDataHandler(const struct mg_connection * conn, int bits, char * data, size_t len, void *cbdata)
|
||||||
{
|
{
|
||||||
|
fprintf(stdout, "Websocket got data:\r\n");
|
||||||
|
fwrite(data, len, 1, stdout);
|
||||||
|
fprintf(stdout, "\r\n\r\n");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketCloseHandler(const struct mg_connection * conn, void *cbdata)
|
void WebSocketCloseHandler(const struct mg_connection * conn, void *cbdata)
|
||||||
{
|
{
|
||||||
|
struct mg_context *ctx = mg_get_context((struct mg_connection *) /* TODO: check const_casts */ conn);
|
||||||
|
|
||||||
|
mg_lock_context(ctx);
|
||||||
|
ws_client = NULL;
|
||||||
|
mg_unlock_context(ctx);
|
||||||
|
fprintf(stdout, "Client droped from the set of webserver connections\r\n\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InformWebsockets(struct mg_context *ctx)
|
||||||
|
{
|
||||||
|
char text[32];
|
||||||
|
|
||||||
|
mg_lock_context(ctx);
|
||||||
|
if (ws_client) {
|
||||||
|
sprintf(text, "%lu", ++cnt);
|
||||||
|
mg_websocket_write(ws_client, WEBSOCKET_OPCODE_TEXT, text, strlen(text));
|
||||||
|
}
|
||||||
|
mg_unlock_context(ctx);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -172,6 +205,9 @@ int main(int argc, char *argv[])
|
|||||||
Sleep(1000);
|
Sleep(1000);
|
||||||
#else
|
#else
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_WEBSOCKET
|
||||||
|
InformWebsockets(ctx);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +151,7 @@ int main(int argc, char *argv[])
|
|||||||
printf("Server init\n\n");
|
printf("Server init\n\n");
|
||||||
|
|
||||||
/* Then connect a first client */
|
/* Then connect a first client */
|
||||||
|
/* TODO: parameters changed -> fix them */ xxx
|
||||||
newconn1 = mg_connect_websocket_client("localhost", atoi(PORT), 0, ebuf, sizeof(ebuf),
|
newconn1 = mg_connect_websocket_client("localhost", atoi(PORT), 0, ebuf, sizeof(ebuf),
|
||||||
"/websocket", NULL, websocket_client_data_handler, websocket_client_close_handler,
|
"/websocket", NULL, websocket_client_data_handler, websocket_client_close_handler,
|
||||||
&client1_data);
|
&client1_data);
|
||||||
|
@ -118,11 +118,13 @@ struct mg_callbacks {
|
|||||||
/* Called when websocket request is received, before websocket handshake.
|
/* Called when websocket request is received, before websocket handshake.
|
||||||
Return value:
|
Return value:
|
||||||
0: civetweb proceeds with websocket handshake.
|
0: civetweb proceeds with websocket handshake.
|
||||||
1: connection is closed immediately. */
|
1: connection is closed immediately.
|
||||||
|
This callback is deprecated, use mg_set_websocket_handler instead. */
|
||||||
int (*websocket_connect)(const struct mg_connection *);
|
int (*websocket_connect)(const struct mg_connection *);
|
||||||
|
|
||||||
/* Called when websocket handshake is successfully completed, and
|
/* Called when websocket handshake is successfully completed, and
|
||||||
connection is ready for data exchange. */
|
connection is ready for data exchange.
|
||||||
|
This callback is deprecated, use mg_set_websocket_handler instead. */
|
||||||
void (*websocket_ready)(struct mg_connection *);
|
void (*websocket_ready)(struct mg_connection *);
|
||||||
|
|
||||||
/* Called when data frame has been received from the client.
|
/* Called when data frame has been received from the client.
|
||||||
@ -132,14 +134,17 @@ struct mg_callbacks {
|
|||||||
data, data_len: payload, with mask (if any) already applied.
|
data, data_len: payload, with mask (if any) already applied.
|
||||||
Return value:
|
Return value:
|
||||||
1: keep this websocket connection open.
|
1: keep this websocket connection open.
|
||||||
0: close this websocket connection. */
|
0: close this websocket connection.
|
||||||
|
This callback is deprecated, use mg_set_websocket_handler instead. */
|
||||||
int (*websocket_data)(struct mg_connection *, int bits,
|
int (*websocket_data)(struct mg_connection *, int bits,
|
||||||
char *data, size_t data_len);
|
char *data, size_t data_len);
|
||||||
|
|
||||||
/* Called when civetweb is closing a connection. The per-context mutex is
|
/* Called when civetweb is closing a connection. The per-context mutex is
|
||||||
locked when this is invoked. This is primarily useful for noting when
|
locked when this is invoked. This is primarily useful for noting when
|
||||||
a websocket is closing and removing it from any application-maintained
|
a websocket is closing and removing it from any application-maintained
|
||||||
list of clients. */
|
list of clients.
|
||||||
|
Using this callback for websocket connections is deprecated, use
|
||||||
|
mg_set_websocket_handler instead. */
|
||||||
void (*connection_close)(struct mg_connection *);
|
void (*connection_close)(struct mg_connection *);
|
||||||
|
|
||||||
/* Called when civetweb tries to open a file. Used to intercept file open
|
/* Called when civetweb tries to open a file. Used to intercept file open
|
||||||
@ -262,19 +267,47 @@ typedef int (* mg_request_handler)(struct mg_connection *conn, void *cbdata);
|
|||||||
cbdata: the callback data to give to the handler when it is called. */
|
cbdata: the callback data to give to the handler when it is called. */
|
||||||
CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata);
|
CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata);
|
||||||
|
|
||||||
|
/* Callback types for websocket handlers in C/C++.
|
||||||
|
|
||||||
|
mg_websocket_connect_handler
|
||||||
|
Is called when the client intends to establish a websocket connection,
|
||||||
|
before websocket handshake.
|
||||||
|
Return value:
|
||||||
|
0: civetweb proceeds with websocket handshake.
|
||||||
|
1: connection is closed immediately.
|
||||||
|
|
||||||
|
mg_websocket_ready_handler
|
||||||
|
Is called when websocket handshake is successfully completed, and
|
||||||
|
connection is ready for data exchange.
|
||||||
|
|
||||||
|
mg_websocket_data_handler
|
||||||
|
Is called when a data frame has been received from the client.
|
||||||
|
Parameters:
|
||||||
|
bits: first byte of the websocket frame, see websocket RFC at
|
||||||
|
http://tools.ietf.org/html/rfc6455, section 5.2
|
||||||
|
data, data_len: payload, with mask (if any) already applied.
|
||||||
|
Return value:
|
||||||
|
1: keep this websocket connection open.
|
||||||
|
0: close this websocket connection.
|
||||||
|
|
||||||
|
mg_connection_close_handler
|
||||||
|
Is called, when the connection is closed.*/
|
||||||
typedef int (*mg_websocket_connect_handler)(const struct mg_connection *, void *);
|
typedef int (*mg_websocket_connect_handler)(const struct mg_connection *, void *);
|
||||||
typedef void (*mg_websocket_ready_handler)(struct mg_connection *, void *);
|
typedef void (*mg_websocket_ready_handler)(const struct mg_connection *, void *);
|
||||||
typedef int (*mg_websocket_data_handler)(struct mg_connection *, int, char *, size_t, void *);
|
typedef int (*mg_websocket_data_handler)(const struct mg_connection *, int, char *, size_t, void *);
|
||||||
typedef void (*mg_connection_close_handler)(struct mg_connection *, void *);
|
typedef void (*mg_websocket_close_handler)(const struct mg_connection *, void *);
|
||||||
|
|
||||||
|
/* mg_set_websocket_handler
|
||||||
|
|
||||||
CIVETWEB_API void mg_set_websocket_handler(struct mg_context *ctx,
|
Set or remove handler functions for websocket connections.
|
||||||
const char *uri,
|
This function works similar to mg_set_request_handler - see there. */
|
||||||
mg_websocket_connect_handler connect_handler,
|
CIVETWEB_API void mg_set_websocket_handler(struct mg_context *ctx,
|
||||||
mg_websocket_ready_handler ready_handler,
|
const char *uri,
|
||||||
mg_websocket_data_handler data_handler,
|
mg_websocket_connect_handler connect_handler,
|
||||||
mg_connection_close_handler close_handler,
|
mg_websocket_ready_handler ready_handler,
|
||||||
void *cbdata
|
mg_websocket_data_handler data_handler,
|
||||||
|
mg_websocket_close_handler close_handler,
|
||||||
|
void *cbdata
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Get the value of particular configuration parameter.
|
/* Get the value of particular configuration parameter.
|
||||||
@ -627,15 +660,10 @@ CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
|
|||||||
On error, NULL. Se error_buffer for details.
|
On error, NULL. Se error_buffer for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef int (*websocket_data_func)(struct mg_connection *, int bits,
|
|
||||||
char *data, size_t data_len);
|
|
||||||
|
|
||||||
typedef void (*websocket_close_func)(struct mg_connection *);
|
|
||||||
|
|
||||||
CIVETWEB_API struct mg_connection *mg_connect_websocket_client(const char *host, int port, int use_ssl,
|
CIVETWEB_API struct mg_connection *mg_connect_websocket_client(const char *host, int port, int use_ssl,
|
||||||
char *error_buffer, size_t error_buffer_size,
|
char *error_buffer, size_t error_buffer_size,
|
||||||
const char *path, const char *origin,
|
const char *path, const char *origin,
|
||||||
websocket_data_func data_func, websocket_close_func close_func,
|
mg_websocket_data_handler data_func, mg_websocket_close_handler close_func,
|
||||||
void * user_data);
|
void * user_data);
|
||||||
|
|
||||||
|
|
||||||
|
195
src/civetweb.c
195
src/civetweb.c
@ -832,7 +832,7 @@ struct mg_request_handler_info {
|
|||||||
mg_websocket_connect_handler connect_handler;
|
mg_websocket_connect_handler connect_handler;
|
||||||
mg_websocket_ready_handler ready_handler;
|
mg_websocket_ready_handler ready_handler;
|
||||||
mg_websocket_data_handler data_handler;
|
mg_websocket_data_handler data_handler;
|
||||||
mg_connection_close_handler close_handler;
|
mg_websocket_close_handler close_handler;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -5748,7 +5748,7 @@ static void send_websocket_handshake(struct mg_connection *conn)
|
|||||||
"Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
|
"Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read_websocket(struct mg_connection *conn)
|
static void read_websocket(struct mg_connection *conn, mg_websocket_data_handler ws_data_handler, void *callback_data)
|
||||||
{
|
{
|
||||||
/* Pointer to the beginning of the portion of the incoming websocket
|
/* Pointer to the beginning of the portion of the incoming websocket
|
||||||
message queue.
|
message queue.
|
||||||
@ -5867,11 +5867,11 @@ static void read_websocket(struct mg_connection *conn)
|
|||||||
|
|
||||||
/* Exit the loop if callback signalled to exit,
|
/* Exit the loop if callback signalled to exit,
|
||||||
or "connection close" opcode received. */
|
or "connection close" opcode received. */
|
||||||
if ((conn->ctx->callbacks.websocket_data != NULL &&
|
if ((ws_data_handler != NULL &&
|
||||||
#ifdef USE_LUA
|
#ifdef USE_LUA
|
||||||
(conn->lua_websocket_state == NULL) &&
|
(conn->lua_websocket_state == NULL) &&
|
||||||
#endif
|
#endif
|
||||||
!conn->ctx->callbacks.websocket_data(conn, mop, data, data_len)) ||
|
!ws_data_handler(conn, mop, data, data_len, callback_data)) ||
|
||||||
#ifdef USE_LUA
|
#ifdef USE_LUA
|
||||||
(conn->lua_websocket_state &&
|
(conn->lua_websocket_state &&
|
||||||
!lua_websocket_data(conn, conn->lua_websocket_state, mop, data, data_len)) ||
|
!lua_websocket_data(conn, conn->lua_websocket_state, mop, data, data_len)) ||
|
||||||
@ -5939,7 +5939,15 @@ int mg_websocket_write(struct mg_connection* conn, int opcode, const char* data,
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_websocket_request(struct mg_connection *conn, const char *path, int is_script_resource)
|
static void handle_websocket_request(struct mg_connection *conn,
|
||||||
|
const char *path,
|
||||||
|
int is_script_resource,
|
||||||
|
mg_websocket_connect_handler ws_connect_handler,
|
||||||
|
mg_websocket_ready_handler ws_ready_handler,
|
||||||
|
mg_websocket_data_handler ws_data_handler,
|
||||||
|
mg_websocket_close_handler ws_close_handler,
|
||||||
|
void *cbData
|
||||||
|
)
|
||||||
{
|
{
|
||||||
const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
|
const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
|
||||||
#ifdef USE_LUA
|
#ifdef USE_LUA
|
||||||
@ -5953,8 +5961,8 @@ static void handle_websocket_request(struct mg_connection *conn, const char *pat
|
|||||||
|
|
||||||
if (version == NULL || strcmp(version, "13") != 0) {
|
if (version == NULL || strcmp(version, "13") != 0) {
|
||||||
send_http_error(conn, 426, "%s", "Protocol upgrade required");
|
send_http_error(conn, 426, "%s", "Protocol upgrade required");
|
||||||
} else if (conn->ctx->callbacks.websocket_connect != NULL &&
|
} else if (ws_connect_handler != NULL &&
|
||||||
conn->ctx->callbacks.websocket_connect(conn) != 0) {
|
ws_connect_handler(conn, cbData) != 0) {
|
||||||
/* C callback has returned non-zero, do not proceed with handshake. */
|
/* C callback has returned non-zero, do not proceed with handshake. */
|
||||||
/* The C callback is called before Lua and may prevent Lua from handling the websocket. */
|
/* The C callback is called before Lua and may prevent Lua from handling the websocket. */
|
||||||
} else {
|
} else {
|
||||||
@ -5978,10 +5986,14 @@ static void handle_websocket_request(struct mg_connection *conn, const char *pat
|
|||||||
{
|
{
|
||||||
/* No Lua websock script specified. */
|
/* No Lua websock script specified. */
|
||||||
send_websocket_handshake(conn);
|
send_websocket_handshake(conn);
|
||||||
if (conn->ctx->callbacks.websocket_ready != NULL) {
|
if (ws_ready_handler != NULL) {
|
||||||
conn->ctx->callbacks.websocket_ready(conn);
|
ws_ready_handler(conn, cbData);
|
||||||
}
|
}
|
||||||
read_websocket(conn);
|
read_websocket(conn, ws_data_handler, cbData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ws_close_handler) {
|
||||||
|
ws_close_handler(conn, cbData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6269,7 +6281,7 @@ static void mg_set_request_handler_type(struct mg_context *ctx,
|
|||||||
mg_websocket_connect_handler connect_handler,
|
mg_websocket_connect_handler connect_handler,
|
||||||
mg_websocket_ready_handler ready_handler,
|
mg_websocket_ready_handler ready_handler,
|
||||||
mg_websocket_data_handler data_handler,
|
mg_websocket_data_handler data_handler,
|
||||||
mg_connection_close_handler close_handler,
|
mg_websocket_close_handler close_handler,
|
||||||
void *cbdata)
|
void *cbdata)
|
||||||
{
|
{
|
||||||
struct mg_request_handler_info *tmp_rh, **lastref;
|
struct mg_request_handler_info *tmp_rh, **lastref;
|
||||||
@ -6360,15 +6372,23 @@ void mg_set_websocket_handler(struct mg_context *ctx,
|
|||||||
mg_websocket_connect_handler connect_handler,
|
mg_websocket_connect_handler connect_handler,
|
||||||
mg_websocket_ready_handler ready_handler,
|
mg_websocket_ready_handler ready_handler,
|
||||||
mg_websocket_data_handler data_handler,
|
mg_websocket_data_handler data_handler,
|
||||||
mg_connection_close_handler close_handler,
|
mg_websocket_close_handler close_handler,
|
||||||
void *cbdata
|
void *cbdata
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int is_delete_request = (connect_handler!=NULL) || (ready_handler!=NULL) || (data_handler!=NULL) || (close_handler!=NULL);
|
int is_delete_request = (connect_handler==NULL) && (ready_handler==NULL) && (data_handler==NULL) && (close_handler==NULL);
|
||||||
mg_set_request_handler_type(ctx, uri, 1, is_delete_request, NULL, connect_handler, ready_handler, data_handler, close_handler, cbdata);
|
mg_set_request_handler_type(ctx, uri, 1, is_delete_request, NULL, connect_handler, ready_handler, data_handler, close_handler, cbdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_request_handler(struct mg_connection *conn, int is_websocket_request, mg_request_handler *handler, void **cbdata)
|
static int get_request_handler(struct mg_connection *conn,
|
||||||
|
int is_websocket_request,
|
||||||
|
mg_request_handler *handler,
|
||||||
|
mg_websocket_connect_handler *connect_handler,
|
||||||
|
mg_websocket_ready_handler *ready_handler,
|
||||||
|
mg_websocket_data_handler *data_handler,
|
||||||
|
mg_websocket_close_handler *close_handler,
|
||||||
|
void **cbdata
|
||||||
|
)
|
||||||
{
|
{
|
||||||
struct mg_request_info *request_info = mg_get_request_info(conn);
|
struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||||
const char *uri = request_info->uri;
|
const char *uri = request_info->uri;
|
||||||
@ -6381,7 +6401,15 @@ static int get_request_handler(struct mg_connection *conn, int is_websocket_requ
|
|||||||
for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
|
for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
|
||||||
if (tmp_rh->is_websocket_handler == is_websocket_request) {
|
if (tmp_rh->is_websocket_handler == is_websocket_request) {
|
||||||
if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri,uri)) {
|
if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri,uri)) {
|
||||||
*handler = tmp_rh->handler;
|
|
||||||
|
if (is_websocket_request) {
|
||||||
|
*connect_handler = tmp_rh->connect_handler;
|
||||||
|
*ready_handler = tmp_rh->ready_handler;
|
||||||
|
*data_handler = tmp_rh->data_handler;
|
||||||
|
*close_handler = tmp_rh->close_handler;
|
||||||
|
} else {
|
||||||
|
*handler = tmp_rh->handler;
|
||||||
|
}
|
||||||
*cbdata = tmp_rh->cbdata;
|
*cbdata = tmp_rh->cbdata;
|
||||||
mg_unlock_context(conn->ctx);
|
mg_unlock_context(conn->ctx);
|
||||||
return 1;
|
return 1;
|
||||||
@ -6396,7 +6424,14 @@ static int get_request_handler(struct mg_connection *conn, int is_websocket_requ
|
|||||||
&& uri[tmp_rh->uri_len] == '/'
|
&& uri[tmp_rh->uri_len] == '/'
|
||||||
&& memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) {
|
&& memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) {
|
||||||
|
|
||||||
*handler = tmp_rh->handler;
|
if (is_websocket_request) {
|
||||||
|
*connect_handler = tmp_rh->connect_handler;
|
||||||
|
*ready_handler = tmp_rh->ready_handler;
|
||||||
|
*data_handler = tmp_rh->data_handler;
|
||||||
|
*close_handler = tmp_rh->close_handler;
|
||||||
|
} else {
|
||||||
|
*handler = tmp_rh->handler;
|
||||||
|
}
|
||||||
*cbdata = tmp_rh->cbdata;
|
*cbdata = tmp_rh->cbdata;
|
||||||
mg_unlock_context(conn->ctx);
|
mg_unlock_context(conn->ctx);
|
||||||
return 1;
|
return 1;
|
||||||
@ -6408,7 +6443,15 @@ static int get_request_handler(struct mg_connection *conn, int is_websocket_requ
|
|||||||
for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
|
for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
|
||||||
if (tmp_rh->is_websocket_handler == is_websocket_request) {
|
if (tmp_rh->is_websocket_handler == is_websocket_request) {
|
||||||
if (match_prefix(tmp_rh->uri, (int)tmp_rh->uri_len, uri) > 0) {
|
if (match_prefix(tmp_rh->uri, (int)tmp_rh->uri_len, uri) > 0) {
|
||||||
*handler = tmp_rh->handler;
|
|
||||||
|
if (is_websocket_request) {
|
||||||
|
*connect_handler = tmp_rh->connect_handler;
|
||||||
|
*ready_handler = tmp_rh->ready_handler;
|
||||||
|
*data_handler = tmp_rh->data_handler;
|
||||||
|
*close_handler = tmp_rh->close_handler;
|
||||||
|
} else {
|
||||||
|
*handler = tmp_rh->handler;
|
||||||
|
}
|
||||||
*cbdata = tmp_rh->cbdata;
|
*cbdata = tmp_rh->cbdata;
|
||||||
mg_unlock_context(conn->ctx);
|
mg_unlock_context(conn->ctx);
|
||||||
return 1;
|
return 1;
|
||||||
@ -6420,6 +6463,33 @@ static int get_request_handler(struct mg_connection *conn, int is_websocket_requ
|
|||||||
return 0; /* none found */
|
return 0; /* none found */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int deprecated_websocket_connect_wrapper(const struct mg_connection * conn, void *cbdata)
|
||||||
|
{
|
||||||
|
struct mg_callbacks *pcallbacks = (struct mg_callbacks*)cbdata;
|
||||||
|
if (pcallbacks->websocket_connect) {
|
||||||
|
return pcallbacks->websocket_connect(conn);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deprecated_websocket_ready_wrapper(const struct mg_connection * conn, void *cbdata)
|
||||||
|
{
|
||||||
|
struct mg_callbacks *pcallbacks = (struct mg_callbacks*)cbdata;
|
||||||
|
if (pcallbacks->websocket_ready) {
|
||||||
|
pcallbacks->websocket_ready((struct mg_connection *)conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int deprecated_websocket_data_wrapper(const struct mg_connection * conn, int bits, char * data, size_t len, void *cbdata)
|
||||||
|
{
|
||||||
|
struct mg_callbacks *pcallbacks = (struct mg_callbacks*)cbdata;
|
||||||
|
if (pcallbacks->websocket_data) {
|
||||||
|
return pcallbacks->websocket_data((struct mg_connection *)conn, bits, data, len);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* This is the heart of the Civetweb's logic.
|
/* This is the heart of the Civetweb's logic.
|
||||||
This function is called when the request is read, parsed and validated,
|
This function is called when the request is read, parsed and validated,
|
||||||
and Civetweb must decide what action to take: serve a file, or
|
and Civetweb must decide what action to take: serve a file, or
|
||||||
@ -6428,11 +6498,15 @@ static void handle_request(struct mg_connection *conn)
|
|||||||
{
|
{
|
||||||
struct mg_request_info *ri = &conn->request_info;
|
struct mg_request_info *ri = &conn->request_info;
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
int uri_len, ssl_index, is_script_resource, is_websocket_request, is_put_or_delete_request;
|
int uri_len, ssl_index, is_script_resource, is_websocket_request, is_put_or_delete_request, is_callback_resource;
|
||||||
int i;
|
int i;
|
||||||
struct file file = STRUCT_FILE_INITIALIZER;
|
struct file file = STRUCT_FILE_INITIALIZER;
|
||||||
time_t curtime = time(NULL);
|
time_t curtime = time(NULL);
|
||||||
mg_request_handler callback_handler = NULL;
|
mg_request_handler callback_handler = NULL;
|
||||||
|
mg_websocket_connect_handler ws_connect_handler = NULL;
|
||||||
|
mg_websocket_ready_handler ws_ready_handler = NULL;
|
||||||
|
mg_websocket_data_handler ws_data_handler = NULL;
|
||||||
|
mg_websocket_close_handler ws_close_handler = NULL;
|
||||||
void * callback_data = NULL;
|
void * callback_data = NULL;
|
||||||
#if !defined(NO_FILES)
|
#if !defined(NO_FILES)
|
||||||
char date[64];
|
char date[64];
|
||||||
@ -6505,14 +6579,19 @@ static void handle_request(struct mg_connection *conn)
|
|||||||
is_websocket_request = is_websocket_protocol(conn);
|
is_websocket_request = is_websocket_protocol(conn);
|
||||||
|
|
||||||
/* 5.2. check if the request will be handled by a callback */
|
/* 5.2. check if the request will be handled by a callback */
|
||||||
if (get_request_handler(conn, is_websocket_request, &callback_handler, &callback_data)) {
|
if (get_request_handler(conn, is_websocket_request,
|
||||||
|
&callback_handler,
|
||||||
|
&ws_connect_handler, &ws_ready_handler, &ws_data_handler, &ws_close_handler,
|
||||||
|
&callback_data)) {
|
||||||
/* 5.2.1. A callback will handle this request. All requests handled by a callback
|
/* 5.2.1. A callback will handle this request. All requests handled by a callback
|
||||||
have to be considered as requests to a script resource. */
|
have to be considered as requests to a script resource. */
|
||||||
|
is_callback_resource = 1;
|
||||||
is_script_resource = 1;
|
is_script_resource = 1;
|
||||||
is_put_or_delete_request = is_put_or_delete_method(conn);
|
is_put_or_delete_request = is_put_or_delete_method(conn);
|
||||||
} else {
|
} else {
|
||||||
/* 5.2.2. No callback is responsible for this request. The URI addresses a file
|
/* 5.2.2. No callback is responsible for this request. The URI addresses a file
|
||||||
based resource (static content or Lua/cgi scripts in the file system). */
|
based resource (static content or Lua/cgi scripts in the file system). */
|
||||||
|
is_callback_resource = 0;
|
||||||
interpret_uri(conn, path, sizeof(path), &file, &is_script_resource, &is_websocket_request, &is_put_or_delete_request);
|
interpret_uri(conn, path, sizeof(path), &file, &is_script_resource, &is_websocket_request, &is_put_or_delete_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6552,34 +6631,44 @@ static void handle_request(struct mg_connection *conn)
|
|||||||
/* request is authorized or does not need authorization */
|
/* request is authorized or does not need authorization */
|
||||||
|
|
||||||
/* 7. check if there are request handlers for this uri */
|
/* 7. check if there are request handlers for this uri */
|
||||||
if (callback_handler != NULL) {
|
if (is_callback_resource) {
|
||||||
if (callback_handler(conn, callback_data)) {
|
if (!is_websocket_request) {
|
||||||
/* Do nothing, callback has served the request */
|
if (callback_handler(conn, callback_data)) {
|
||||||
discard_unread_request_data(conn);
|
/* Do nothing, callback has served the request */
|
||||||
return;
|
discard_unread_request_data(conn);
|
||||||
} else {
|
} else {
|
||||||
/* TODO: what if the handler did NOT handle the request */
|
/* TODO: what if the handler did NOT handle the request */
|
||||||
/* The last version did handle this as a file request, but
|
/* The last version did handle this as a file request, but
|
||||||
since a file request is not always a script resource,
|
since a file request is not always a script resource,
|
||||||
the authorization check might be different */
|
the authorization check might be different */
|
||||||
interpret_uri(conn, path, sizeof(path), &file, &is_script_resource, &is_websocket_request, &is_put_or_delete_request);
|
interpret_uri(conn, path, sizeof(path), &file, &is_script_resource, &is_websocket_request, &is_put_or_delete_request);
|
||||||
callback_handler = NULL;
|
callback_handler = NULL;
|
||||||
|
|
||||||
/* TODO: for the moment, a goto is simpler than some curious loop. */
|
/* TODO: for the moment, a goto is simpler than some curious loop. */
|
||||||
/* The situation "callback does not handle the request" needs to be reconsidered anyway. */
|
/* The situation "callback does not handle the request" needs to be reconsidered anyway. */
|
||||||
goto auth_check;
|
goto auth_check;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#if defined(USE_WEBSOCKET)
|
||||||
|
handle_websocket_request(conn, path, is_script_resource, ws_connect_handler, ws_ready_handler, ws_data_handler, ws_close_handler, callback_data);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 8. handle websocket requests */
|
/* 8. handle websocket requests */
|
||||||
#if defined(USE_WEBSOCKET)
|
#if defined(USE_WEBSOCKET)
|
||||||
if (is_websocket_request) {
|
if (is_websocket_request) {
|
||||||
handle_websocket_request(conn, path, is_script_resource);
|
handle_websocket_request(conn, path, is_script_resource,
|
||||||
|
deprecated_websocket_connect_wrapper,
|
||||||
|
deprecated_websocket_ready_wrapper,
|
||||||
|
deprecated_websocket_data_wrapper,
|
||||||
|
NULL,
|
||||||
|
&conn->ctx->callbacks
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(NO_FILES)
|
#if defined(NO_FILES)
|
||||||
/* 9a. In case the server uses only callbacks, this uri is unknown.
|
/* 9a. In case the server uses only callbacks, this uri is unknown.
|
||||||
@ -7532,6 +7621,13 @@ struct mg_connection *mg_download(const char *host, int port, int use_ssl,
|
|||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct websocket_client_thread_data {
|
||||||
|
struct mg_connection* conn;
|
||||||
|
mg_websocket_data_handler data_handler;
|
||||||
|
mg_websocket_close_handler close_handler;
|
||||||
|
void *callback_data;
|
||||||
|
};
|
||||||
|
|
||||||
#if defined(USE_WEBSOCKET)
|
#if defined(USE_WEBSOCKET)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static unsigned __stdcall websocket_client_thread(void *data)
|
static unsigned __stdcall websocket_client_thread(void *data)
|
||||||
@ -7539,15 +7635,17 @@ static unsigned __stdcall websocket_client_thread(void *data)
|
|||||||
static void* websocket_client_thread(void *data)
|
static void* websocket_client_thread(void *data)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
struct mg_connection* conn = (struct mg_connection*)data;
|
struct websocket_client_thread_data* cdata = (struct websocket_client_thread_data*)data;
|
||||||
read_websocket(conn);
|
read_websocket(cdata->conn, cdata->data_handler, cdata->callback_data);
|
||||||
|
|
||||||
DEBUG_TRACE("%s", "Websocket client thread exited\n");
|
DEBUG_TRACE("%s", "Websocket client thread exited\n");
|
||||||
|
|
||||||
if (conn->ctx->callbacks.connection_close != NULL) {
|
if (cdata->close_handler != NULL) {
|
||||||
conn->ctx->callbacks.connection_close(conn);
|
cdata->close_handler(cdata->conn, cdata->callback_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mg_free((void*)cdata);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
@ -7559,13 +7657,14 @@ static void* websocket_client_thread(void *data)
|
|||||||
struct mg_connection *mg_connect_websocket_client(const char *host, int port, int use_ssl,
|
struct mg_connection *mg_connect_websocket_client(const char *host, int port, int use_ssl,
|
||||||
char *error_buffer, size_t error_buffer_size,
|
char *error_buffer, size_t error_buffer_size,
|
||||||
const char *path, const char *origin,
|
const char *path, const char *origin,
|
||||||
websocket_data_func data_func, websocket_close_func close_func,
|
mg_websocket_data_handler data_func, mg_websocket_close_handler close_func,
|
||||||
void * user_data)
|
void * user_data)
|
||||||
{
|
{
|
||||||
struct mg_connection* conn = NULL;
|
struct mg_connection* conn = NULL;
|
||||||
|
|
||||||
#if defined(USE_WEBSOCKET)
|
#if defined(USE_WEBSOCKET)
|
||||||
struct mg_context * newctx = NULL;
|
struct mg_context * newctx = NULL;
|
||||||
|
struct websocket_client_thread_data *thread_data;
|
||||||
static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
|
static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
|
||||||
static const char *handshake_req;
|
static const char *handshake_req;
|
||||||
|
|
||||||
@ -7597,7 +7696,7 @@ struct mg_connection *mg_connect_websocket_client(const char *host, int port, in
|
|||||||
handshake_req, path, host, magic, origin);
|
handshake_req, path, host, magic, origin);
|
||||||
|
|
||||||
/* Connection object will be null if something goes wrong */
|
/* Connection object will be null if something goes wrong */
|
||||||
if(conn == NULL || (strcmp(conn->request_info.uri, "101") != 0))
|
if (conn == NULL || (strcmp(conn->request_info.uri, "101") != 0))
|
||||||
{
|
{
|
||||||
DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer);
|
DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer);
|
||||||
if(conn != NULL) { mg_free(conn); conn = NULL; }
|
if(conn != NULL) { mg_free(conn); conn = NULL; }
|
||||||
@ -7608,19 +7707,23 @@ struct mg_connection *mg_connect_websocket_client(const char *host, int port, in
|
|||||||
function, we need to create a copy and modify it. */
|
function, we need to create a copy and modify it. */
|
||||||
newctx = (struct mg_context *) mg_malloc(sizeof(struct mg_context));
|
newctx = (struct mg_context *) mg_malloc(sizeof(struct mg_context));
|
||||||
memcpy(newctx, conn->ctx, sizeof(struct mg_context));
|
memcpy(newctx, conn->ctx, sizeof(struct mg_context));
|
||||||
newctx->callbacks.websocket_data = data_func; /* read_websocket will automatically call it */
|
|
||||||
newctx->callbacks.connection_close = close_func;
|
|
||||||
newctx->user_data = user_data;
|
newctx->user_data = user_data;
|
||||||
newctx->context_type = 2; /* client context type */
|
newctx->context_type = 2; /* client context type */
|
||||||
newctx->workerthreadcount = 1; /* one worker thread will be created */
|
newctx->workerthreadcount = 1; /* one worker thread will be created */
|
||||||
newctx->workerthreadids = (pthread_t*) mg_calloc(newctx->workerthreadcount, sizeof(pthread_t));
|
newctx->workerthreadids = (pthread_t*) mg_calloc(newctx->workerthreadcount, sizeof(pthread_t));
|
||||||
conn->ctx = newctx;
|
conn->ctx = newctx;
|
||||||
|
thread_data = (struct websocket_client_thread_data*) mg_calloc(sizeof(struct websocket_client_thread_data), 1);
|
||||||
|
thread_data->conn = conn;
|
||||||
|
thread_data->data_handler = data_func;
|
||||||
|
thread_data->close_handler = close_func;
|
||||||
|
thread_data->callback_data = NULL;
|
||||||
|
|
||||||
/* Start a thread to read the websocket client connection
|
/* Start a thread to read the websocket client connection
|
||||||
This thread will automatically stop when mg_disconnect is
|
This thread will automatically stop when mg_disconnect is
|
||||||
called on the client connection */
|
called on the client connection */
|
||||||
if (mg_start_thread_with_id(websocket_client_thread, (void*)conn, newctx->workerthreadids) != 0)
|
if (mg_start_thread_with_id(websocket_client_thread, (void*)thread_data, newctx->workerthreadids) != 0)
|
||||||
{
|
{
|
||||||
|
mg_free((void*)thread_data);
|
||||||
mg_free((void*)newctx->workerthreadids);
|
mg_free((void*)newctx->workerthreadids);
|
||||||
mg_free((void*)newctx);
|
mg_free((void*)newctx);
|
||||||
mg_free((void*)conn);
|
mg_free((void*)conn);
|
||||||
|
@ -568,12 +568,13 @@ static void test_mg_download(int use_ssl) {
|
|||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int websocket_data_handler(struct mg_connection *conn, int flags, char *data, size_t data_len)
|
static int websocket_data_handler(const struct mg_connection *conn, int flags, char *data, size_t data_len, void *cbdata)
|
||||||
{
|
{
|
||||||
(void)conn;
|
(void)conn;
|
||||||
(void)flags;
|
(void)flags;
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)data_len;
|
(void)data_len;
|
||||||
|
(void)cbdata;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user