@@ -779,6 +779,7 @@ struct mg_context {
779
779
char * config [NUM_OPTIONS ]; /* Civetweb configuration parameters */
780
780
struct mg_callbacks callbacks ; /* User-defined callback function */
781
781
void * user_data ; /* User-defined data */
782
+ int context_type ; /* 1 = server context, 2 = client context */
782
783
783
784
struct socket * listening_sockets ;
784
785
in_port_t * listening_ports ;
@@ -1187,6 +1188,9 @@ static char *skip_quoted(char **buf, const char *delimiters,
1187
1188
if (end_word > begin_word ) {
1188
1189
p = end_word - 1 ;
1189
1190
while (* p == quotechar ) {
1191
+ /* TODO (bel): it seems this code is never reached, so quotechar is actually
1192
+ not needed - check if this code may be droped */
1193
+
1190
1194
/* If there is anything beyond end_word, copy it */
1191
1195
if (* end_word == '\0' ) {
1192
1196
* p = '\0' ;
@@ -2267,6 +2271,7 @@ static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
2267
2271
int mg_read (struct mg_connection * conn , void * buf , size_t len )
2268
2272
{
2269
2273
int64_t n , buffered_len , nread ;
2274
+ int64_t len64 = (int64_t )(len > INT_MAX ? INT_MAX : len ); /* since the return value is int, we may not read more bytes */
2270
2275
const char * body ;
2271
2276
2272
2277
/* If Content-Length is not set for a PUT or POST request, read until socket is closed */
@@ -2279,33 +2284,33 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len)
2279
2284
if (conn -> consumed_content < conn -> content_len ) {
2280
2285
/* Adjust number of bytes to read. */
2281
2286
int64_t to_read = conn -> content_len - conn -> consumed_content ;
2282
- if (to_read < ( int64_t ) len ) {
2287
+ if (to_read < len64 ) {
2283
2288
len = (size_t ) to_read ;
2284
2289
}
2285
2290
2286
2291
/* Return buffered data */
2287
2292
body = conn -> buf + conn -> request_len + conn -> consumed_content ;
2288
2293
buffered_len = (int64_t )(& conn -> buf [conn -> data_len ] - body );
2289
2294
if (buffered_len > 0 ) {
2290
- if (len < (size_t ) buffered_len ) {
2291
- buffered_len = ( int64_t ) len ;
2295
+ if (len64 < (size_t ) buffered_len ) {
2296
+ buffered_len = len64 ;
2292
2297
}
2293
2298
memcpy (buf , body , (size_t ) buffered_len );
2294
- len -= buffered_len ;
2299
+ len64 -= buffered_len ;
2295
2300
conn -> consumed_content += buffered_len ;
2296
2301
nread += buffered_len ;
2297
2302
buf = (char * ) buf + buffered_len ;
2298
2303
}
2299
2304
2300
2305
/* We have returned all buffered data. Read new data from the remote
2301
2306
socket. */
2302
- if ((n = pull_all (NULL , conn , (char * ) buf , (int64_t ) len )) >= 0 ) {
2307
+ if ((n = pull_all (NULL , conn , (char * ) buf , (int ) len64 )) >= 0 ) {
2303
2308
nread += n ;
2304
2309
} else {
2305
2310
nread = (nread > 0 ? nread : n );
2306
2311
}
2307
2312
}
2308
- return nread ;
2313
+ return ( int ) nread ;
2309
2314
}
2310
2315
2311
2316
int mg_write (struct mg_connection * conn , const void * buf , size_t len )
@@ -5140,8 +5145,9 @@ static void read_websocket(struct mg_connection *conn)
5140
5145
5141
5146
/* Loop continuously, reading messages from the socket, invoking the
5142
5147
callback, and waiting repeatedly until an error occurs. */
5143
- assert (conn -> content_len == 0 );
5144
- for (;;) {
5148
+ /* TODO: Investigate if this next line is needed
5149
+ assert(conn->content_len == 0); */
5150
+ while (!conn -> ctx -> stop_flag ) {
5145
5151
header_len = 0 ;
5146
5152
assert (conn -> data_len >= conn -> request_len );
5147
5153
if ((body_len = conn -> data_len - conn -> request_len ) >= 2 ) {
@@ -6286,8 +6292,9 @@ static void close_connection(struct mg_connection *conn)
6286
6292
#endif
6287
6293
6288
6294
/* call the connection_close callback if assigned */
6289
- if (conn -> ctx -> callbacks .connection_close != NULL )
6295
+ if (( conn -> ctx -> callbacks .connection_close != NULL ) && ( conn -> ctx -> context_type == 1 )) {
6290
6296
conn -> ctx -> callbacks .connection_close (conn );
6297
+ }
6291
6298
6292
6299
mg_lock_connection (conn );
6293
6300
@@ -6311,12 +6318,29 @@ static void close_connection(struct mg_connection *conn)
6311
6318
6312
6319
void mg_close_connection (struct mg_connection * conn )
6313
6320
{
6321
+ struct mg_context * client_ctx = NULL ;
6322
+ int i ;
6323
+
6324
+ if (conn -> ctx -> context_type == 2 ) {
6325
+ client_ctx = conn -> ctx ;
6326
+ /* client context: loops must end */
6327
+ conn -> ctx -> stop_flag = 1 ;
6328
+ }
6329
+
6314
6330
#ifndef NO_SSL
6315
6331
if (conn -> client_ssl_ctx != NULL ) {
6316
6332
SSL_CTX_free ((SSL_CTX * ) conn -> client_ssl_ctx );
6317
6333
}
6318
6334
#endif
6319
6335
close_connection (conn );
6336
+ if (client_ctx != NULL ) {
6337
+ /* join worker thread and free context */
6338
+ for (i = 0 ; i < client_ctx -> workerthreadcount ; i ++ ) {
6339
+ mg_join_thread (client_ctx -> workerthreadids [i ]);
6340
+ }
6341
+ mg_free (client_ctx -> workerthreadids );
6342
+ mg_free (client_ctx );
6343
+ }
6320
6344
(void ) pthread_mutex_destroy (& conn -> mutex );
6321
6345
mg_free (conn );
6322
6346
}
@@ -6440,6 +6464,106 @@ struct mg_connection *mg_download(const char *host, int port, int use_ssl,
6440
6464
return conn ;
6441
6465
}
6442
6466
6467
+ #if defined(USE_WEBSOCKET )
6468
+ #ifdef _WIN32
6469
+ static unsigned __stdcall websocket_client_thread (void * data )
6470
+ #else
6471
+ static void * websocket_client_thread (void * data )
6472
+ #endif
6473
+ {
6474
+ struct mg_connection * conn = (struct mg_connection * )data ;
6475
+ read_websocket (conn );
6476
+
6477
+ DEBUG_TRACE ("Websocket client thread exited\n" );
6478
+
6479
+ if (conn -> ctx -> callbacks .connection_close != NULL ) {
6480
+ conn -> ctx -> callbacks .connection_close (conn );
6481
+ }
6482
+
6483
+ #ifdef _WIN32
6484
+ return 0 ;
6485
+ #else
6486
+ return NULL ;
6487
+ #endif
6488
+ }
6489
+ #endif
6490
+
6491
+ struct mg_connection * mg_connect_websocket_client (const char * host , int port , int use_ssl ,
6492
+ char * error_buffer , size_t error_buffer_size ,
6493
+ const char * path , const char * origin ,
6494
+ websocket_data_func data_func , websocket_close_func close_func ,
6495
+ void * user_data )
6496
+ {
6497
+ struct mg_connection * conn = NULL ;
6498
+ struct mg_context * newctx = NULL ;
6499
+
6500
+ #if defined(USE_WEBSOCKET )
6501
+ static const char * magic = "x3JJHMbDL1EzLkh9GBhXDw==" ;
6502
+ static const char * handshake_req ;
6503
+
6504
+ if (origin != NULL )
6505
+ {
6506
+ handshake_req = "GET %s HTTP/1.1\r\n"
6507
+ "Host: %s\r\n"
6508
+ "Upgrade: websocket\r\n"
6509
+ "Connection: Upgrade\r\n"
6510
+ "Sec-WebSocket-Key: %s\r\n"
6511
+ "Sec-WebSocket-Version: 13\r\n"
6512
+ "Origin: %s\r\n"
6513
+ "\r\n" ;
6514
+ }
6515
+ else
6516
+ {
6517
+ handshake_req = "GET %s HTTP/1.1\r\n"
6518
+ "Host: %s\r\n"
6519
+ "Upgrade: websocket\r\n"
6520
+ "Connection: Upgrade\r\n"
6521
+ "Sec-WebSocket-Key: %s\r\n"
6522
+ "Sec-WebSocket-Version: 13\r\n"
6523
+ "\r\n" ;
6524
+ }
6525
+
6526
+ /* Establish the client connection and request upgrade */
6527
+ conn = mg_download (host , port , use_ssl ,
6528
+ error_buffer , error_buffer_size ,
6529
+ handshake_req , path , host , magic , origin );
6530
+
6531
+ /* Connection object will be null if something goes wrong */
6532
+ if (conn == NULL || (strcmp (conn -> request_info .uri , "101" ) != 0 ))
6533
+ {
6534
+ DEBUG_TRACE ("Websocket client connect error: %s\r\n" , error_buffer );
6535
+ if (conn != NULL ) { mg_free (conn ); conn = NULL ; }
6536
+ return conn ;
6537
+ }
6538
+
6539
+ /* For client connections, mg_context is fake. Since we need to set a callback
6540
+ function, we need to create a copy and modify it. */
6541
+ newctx = (struct mg_context * ) mg_malloc (sizeof (struct mg_context ));
6542
+ memcpy (newctx , conn -> ctx , sizeof (struct mg_context ));
6543
+ newctx -> callbacks .websocket_data = data_func ; /* read_websocket will automatically call it */
6544
+ newctx -> callbacks .connection_close = close_func ;
6545
+ newctx -> user_data = user_data ;
6546
+ newctx -> context_type = 2 ; /* client context type */
6547
+ newctx -> workerthreadcount = 1 ; /* one worker thread will be created */
6548
+ newctx -> workerthreadids = (pthread_t * ) mg_calloc (newctx -> workerthreadcount , sizeof (pthread_t ));
6549
+ conn -> ctx = newctx ;
6550
+
6551
+ /* Start a thread to read the websocket client connection
6552
+ This thread will automatically stop when mg_disconnect is
6553
+ called on the client connection */
6554
+ if (mg_start_thread_with_id (websocket_client_thread , (void * )conn , newctx -> workerthreadids ) != 0 )
6555
+ {
6556
+ mg_free ((void * )newctx -> workerthreadids );
6557
+ mg_free ((void * )newctx );
6558
+ mg_free ((void * )conn );
6559
+ conn = NULL ;
6560
+ DEBUG_TRACE ("Websocket client connect thread could not be started\r\n" );
6561
+ }
6562
+ #endif
6563
+
6564
+ return conn ;
6565
+ }
6566
+
6443
6567
static void process_new_connection (struct mg_connection * conn )
6444
6568
{
6445
6569
struct mg_request_info * ri = & conn -> request_info ;
@@ -7054,6 +7178,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks,
7054
7178
ctx -> callbacks .init_context (ctx );
7055
7179
}
7056
7180
ctx -> callbacks .exit_context = exit_callback ;
7181
+ ctx -> context_type = 1 ; /* server context */
7057
7182
7058
7183
/* Start master (listening) thread */
7059
7184
mg_start_thread_with_id (master_thread , ctx , & ctx -> masterthreadid );
0 commit comments