GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/server.c Lines: 0 344 0.0 %
Date: 2020-12-10 21:44:00 Branches: 0 155 0.0 %

Line Branch Exec Source
1
#include <errno.h> 				/* errno */
2
#include <unistd.h>             /* close */
3
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE */
4
#include <signal.h>             /* signal */
5
#include <stdio.h>
6
#include <poll.h>
7
#include <sys/time.h>
8
#include <sys/resource.h>
9
10
#include "server.h"
11
#include "frame.h"
12
#include "event.h"
13
#include "log.h"
14
#include "session.h"
15
#include "alloc.h"
16
#include "socket.h"
17
#include "pool.h"               /* threadpool_add, threadpool_strerror */
18
#include "worker.h"
19
#include "http.h"
20
#include "error.h"
21
#include "config.h"
22
#include "subprotocols.h"
23
#include "extensions.h"
24
#include "predict.h"
25
#include "ssl.h"
26
27
/**
28
 * Global state of server
29
 */
30
wss_server_state_t state;
31
32
/**
33
 * Global servers available
34
 */
35
wss_servers_t servers;
36
37
/**
38
 * Global variable holding current timestamp
39
 */
40
struct timespec now;
41
42
static inline void write_control_frame(wss_frame_t *frame, wss_session_t *session) {
43
    size_t j;
44
    char *message;
45
    size_t message_length;
46
    int n = 0;
47
    int fd = session->fd;
48
49
    // Use extensions
50
    for (j = 0; likely(j < session->header->ws_extensions_count); j++) {
51
        session->header->ws_extensions[j]->ext->outframes(
52
                fd,
53
                &frame,
54
                1);
55
56
        session->header->ws_extensions[j]->ext->outframe(
57
                fd,
58
                frame);
59
    }
60
61
    if (0 == (message_length = WSS_stringify_frame(frame, &message))) {
62
        WSS_log_fatal("Unable to represent frame as bytes");
63
        WSS_free_frame(frame);
64
        return;
65
    }
66
67
    WSS_free_frame(frame);
68
69
    if (session->ssl_connected) {
70
        WSS_ssl_write(session, message, message_length);
71
    } else {
72
        size_t written = 0;
73
        do {
74
            n = write(fd, message+written, message_length-written);
75
            if ( unlikely(n < 0) ) {
76
                if ( likely(errno == EINTR) ) {
77
                    errno = 0;
78
                    n = 0;
79
                    continue;
80
                }
81
                break;
82
            }
83
            written += n;
84
        } while ( written < message_length );
85
    }
86
87
    WSS_free((void **) &message);
88
}
89
90
static void cleanup_session(wss_session_t *session) {
91
    wss_error_t err;
92
    wss_frame_t *frame;
93
    long unsigned int ms;
94
    int fd = session->fd;
95
    wss_server_t *server = servers.http;
96
    bool dc;
97
98
    if (! session->handshaked) {
99
        return;
100
    }
101
102
    if (NULL != session->ssl && session->ssl_connected) {
103
        server = servers.https;
104
    }
105
106
    ms = (((now.tv_sec - session->alive.tv_sec)*1000)+(now.tv_nsec/1000000)) - (session->alive.tv_nsec/1000000);
107
108
    WSS_log_info("Check timeout %d ms", ms);
109
110
    // Session timed out
111
    if ( unlikely(server->config->timeout_client >= 0 && ms >= (long unsigned int)server->config->timeout_client) ) {
112
        WSS_session_is_disconnecting(session, &dc);
113
        if (dc) {
114
            return;
115
        }
116
117
        WSS_session_jobs_wait(session);
118
119
        WSS_log_info("Session %d has timedout last active %d ms ago", fd, ms);
120
121
        session->closing = true;
122
        session->state = CLOSING;
123
124
        if (! session->ssl_connected) {
125
            WSS_log_trace("Session %d disconnected from ip: %s:%d using HTTP request, due to timing out", session->fd, session->ip, session->port);
126
        } else {
127
            WSS_log_trace("Session %d disconnected from ip: %s:%d using HTTPS request, due to timing out", session->fd, session->ip, session->port);
128
        }
129
130
        WSS_log_trace("Informing subprotocol of client with file descriptor %d disconnecting", session->fd);
131
132
        if ( NULL != session->header && session->header->ws_protocol != NULL ) {
133
            WSS_log_trace("Informing subprotocol about session close");
134
            session->header->ws_protocol->close(session->fd);
135
        }
136
137
        WSS_log_trace("Removing poll filedescriptor from eventlist");
138
139
        WSS_poll_remove(server, fd);
140
141
        WSS_log_trace("Sending close frame", fd);
142
143
        frame = WSS_closing_frame(CLOSE_TRY_AGAIN, NULL);
144
145
        write_control_frame(frame, session);
146
147
        WSS_log_trace("Deleting client session");
148
149
        if ( unlikely(WSS_SUCCESS != (err = WSS_session_delete_no_lock(session))) ) {
150
            switch (err) {
151
                case WSS_SSL_SHUTDOWN_READ_ERROR:
152
                        WSS_poll_set_read(server, fd);
153
                    return;
154
                case WSS_SSL_SHUTDOWN_WRITE_ERROR:
155
                        WSS_poll_set_write(server, fd);
156
                    return;
157
                default:
158
                    break;
159
            }
160
            WSS_log_error("Unable to delete client session, received error code: %d", err);
161
            return;
162
        }
163
164
        WSS_log_info("Client with session %d disconnected", fd);
165
166
    // Ping session to keep it alive
167
    } else if (server->config->timeout_pings > 0 ) {
168
        WSS_log_info("Pinging session %d", fd);
169
170
        frame = WSS_ping_frame();
171
        if (session->pong != NULL) {
172
            WSS_free((void **) &session->pong);
173
            session->pong_length = 0;
174
        }
175
176
        if (NULL == (session->pong = WSS_malloc(frame->applicationDataLength))) {
177
            WSS_free_frame(frame);
178
            return;
179
        }
180
        session->pong_length = frame->applicationDataLength;
181
182
        memcpy(session->pong, frame->payload+frame->extensionDataLength, frame->applicationDataLength);
183
184
        write_control_frame(frame, session);
185
    }
186
}
187
188
/**
189
 * Function that updates the state of the server.
190
 *
191
 * @param 	s	[wss_state_t *]     "A state_t value describing the servers state"
192
 * @return 	    [void]
193
 */
194
void WSS_server_set_state(wss_state_t s) {
195
    pthread_mutex_lock(&state.lock);
196
    state.state = s;
197
    pthread_mutex_unlock(&state.lock);
198
}
199
200
/**
201
 * Sets the maximal file descriptor seen by the server
202
 *
203
 * @param 	server	[wss_server_t *]    "The server object"
204
 * @param 	fd	    [int]               "The file descriptor seen"
205
 * @return 	        [void]
206
 */
207
void WSS_server_set_max_fd(wss_server_t *server, int fd) {
208
    pthread_mutex_lock(&server->lock);
209
    if (server->max_fd < fd) {
210
        server->max_fd = fd;
211
    }
212
    pthread_mutex_unlock(&server->lock);
213
}
214
215
/**
216
 * Cleanup client sessions, that is, closes connections to clients which is
217
 * timedout based on the servers timeout_client setting.
218
 *
219
 * @return 	pthread_exit 	[void *] 	"0 if successfull and otherwise <0"
220
 */
221
void *WSS_cleanup() {
222
    int n;
223
    wss_error_t err;
224
    struct pollfd fds[1];
225
    wss_server_t *server = servers.http;
226
    long int timeout = server->config->timeout_client;
227
228
#ifdef USE_RPMALLOC
229
    rpmalloc_thread_initialize();
230
#endif
231
232
    fds[0].fd = close_pipefd[0];
233
    fds[0].events = POLLIN | POLLPRI;
234
235
    WSS_log_info("Cleanup thread started with %ld", timeout);
236
237
    if (timeout >= 0 && server->config->timeout_pings > 0) {
238
        timeout = timeout/server->config->timeout_pings;
239
        WSS_log_info("Cleanup thread will run every %ld milliseconds", timeout);
240
    }
241
242
    while ( likely(state.state == RUNNING) ) {
243
        n = poll(fds, 1, timeout);
244
        if ( unlikely(n < 0 && errno != EINTR) ) {
245
#ifdef USE_RPMALLOC
246
            rpmalloc_thread_finalize();
247
#endif
248
            pthread_exit( ((void *) ((uintptr_t)WSS_CLEANUP_ERROR)) );
249
        } else if ( likely(n == 0) ) {
250
            WSS_log_info("Pings clients and cleanup timedout ones");
251
252
            clock_gettime(CLOCK_MONOTONIC, &now);
253
            if ( unlikely(WSS_SUCCESS != (err = WSS_session_all(cleanup_session))) ) {
254
#ifdef USE_RPMALLOC
255
                rpmalloc_thread_finalize();
256
#endif
257
                pthread_exit( ((void *) &err) );
258
            }
259
        }
260
    }
261
262
    WSS_log_info("Cleanup thread shutting down");
263
264
#ifdef USE_RPMALLOC
265
    rpmalloc_thread_finalize();
266
#endif
267
    pthread_exit( ((void *) ((uintptr_t)WSS_SUCCESS)) );
268
}
269
270
/**
271
 * Function that loops over poll events and distribute the events to different
272
 * threadpools.
273
 *
274
 * @param 	arg				[void *] 	"Is in fact a wss_server_t instance"
275
 * @return 	pthread_exit 	[void *] 	"0 if successfull and otherwise <0"
276
 */
277
void *WSS_server_run(void *arg) {
278
    wss_error_t err;
279
    wss_server_t *server = (wss_server_t *) arg;
280
281
#ifdef USE_RPMALLOC
282
    rpmalloc_thread_initialize();
283
#endif
284
285
    if (server->ssl_ctx != NULL) {
286
        WSS_log_trace("Running HTTPS server");
287
    } else {
288
        WSS_log_trace("Running HTTP server");
289
    }
290
291
    // Listen for poll events
292
    while ( likely(state.state == RUNNING) ) {
293
        err = WSS_poll_delegate(server);
294
        if ( unlikely(err != WSS_SUCCESS) ) {
295
#ifdef USE_RPMALLOC
296
            rpmalloc_thread_finalize();
297
#endif
298
            pthread_exit( ((void *) err) );
299
        }
300
    }
301
302
    if (server->ssl_ctx != NULL) {
303
        WSS_log_trace("Stopping HTTPS server");
304
    } else {
305
        WSS_log_trace("Stopping HTTP server");
306
    }
307
308
#ifdef USE_RPMALLOC
309
    rpmalloc_thread_finalize();
310
#endif
311
312
    pthread_exit( ((void *) ((uintptr_t)WSS_SUCCESS)) );
313
}
314
315
/**
316
 * Handler to call when some specific interrupts are happening. This function
317
 * shuts down the server in a safe way or ignore certain interrupts.
318
 *
319
 * @param   sig     [int]   "The integer value of the interrupt"
320
 * @return 	        [void]
321
 */
322
static void WSS_server_interrupt(int sig) {
323
    int n;
324
    switch (sig) {
325
        case SIGINT:
326
        case SIGSEGV:
327
        case SIGILL:
328
        case SIGFPE:
329
            if (state.state != HALTING && state.state != HALT_ERROR) {
330
                WSS_log_trace("Server is shutting down gracefully");
331
                WSS_server_set_state(HALTING);
332
                if (close_pipefd[0] != -1 && close_pipefd[1] != -1) {
333
                    do {
334
                        errno = 0;
335
                        n = write(close_pipefd[1], "HALT", 5);
336
                        if ( unlikely(n < 0) ) {
337
                            if ( unlikely(errno != EINTR) ) {
338
                                WSS_log_fatal("Unable to write halting message through pipe");
339
                            }
340
                        }
341
                    } while ( unlikely(errno == EINTR) );
342
                }
343
            }
344
            break;
345
        case SIGPIPE:
346
            break;
347
        default:
348
            return;
349
    }
350
}
351
352
/**
353
 * Function that frees all memory that the server has allocated
354
 *
355
 * @return 	        [int]        "EXIT_SUCCESS if successfull or EXIT_FAILURE on error"
356
 */
357
static int WSS_server_free(wss_server_t *server) {
358
    int result = EXIT_SUCCESS;
359
360
    if ( unlikely(WSS_SUCCESS != WSS_http_server_free(server)) ) {
361
        result = EXIT_FAILURE;
362
    }
363
364
    if ( unlikely(WSS_SUCCESS != pthread_mutex_destroy(&server->lock)) ) {
365
        result = EXIT_FAILURE;
366
    }
367
368
    /**
369
     * Freeing memory from server instance
370
     */
371
    if ( likely(NULL != server) ) {
372
        WSS_free((void **) &server);
373
    }
374
375
    return result;
376
}
377
378
/**
379
 * Starts the websocket server.
380
 *
381
 * @param   config  [wss_config_t *] "The configuration of the server"
382
 * @return 	        [int]            "EXIT_SUCCESS if successfull or EXIT_FAILURE on error"
383
 */
384
int WSS_server_start(wss_config_t *config) {
385
    int err;
386
    struct rlimit limits;
387
    struct sigaction sa;
388
    int ret = EXIT_SUCCESS;
389
    wss_server_t *http = NULL;
390
    pthread_t cleanup_thread_id;
391
    wss_server_t *https = NULL;
392
    bool ssl = NULL != config->ssl_cert && NULL != config->ssl_key && (NULL != config->ssl_ca_file || NULL != config->ssl_ca_path);
393
394
    if ( unlikely(0 != (err = pthread_mutex_init(&state.lock, NULL))) ) {
395
        WSS_log_fatal("Failed initializing state lock: %s", strerror(err));
396
397
        return EXIT_FAILURE;
398
    }
399
400
    if ( getrlimit(RLIMIT_NOFILE, &limits) < 0 ) {
401
        WSS_log_fatal("Failed to get kernel file descriptor limits: %s", strerror(err));
402
403
        return EXIT_FAILURE;
404
    }
405
406
    WSS_log_info("Setting max amount of filedescriptors available for server to: %d", limits.rlim_max);
407
    limits.rlim_cur = limits.rlim_max;
408
409
    if ( setrlimit(RLIMIT_NOFILE, &limits) < 0 ) {
410
        WSS_log_fatal("Failed to set kernel file descriptor limits: %s", strerror(err));
411
412
        return EXIT_FAILURE;
413
    }
414
415
    // Load extensions available
416
    WSS_load_extensions(config);
417
418
    // Load subprotocols available
419
    WSS_load_subprotocols(config);
420
421
    // Setting starting state
422
    WSS_log_trace("Starting server");
423
424
    WSS_server_set_state(STARTING);
425
426
    // Listening for signals that could terminate program
427
    WSS_log_trace("Listening for signals");
428
429
    // Set up the structure to specify the new action
430
    sigemptyset(&sa.sa_mask);
431
    sa.sa_handler = WSS_server_interrupt;
432
    sa.sa_flags = 0;
433
434
    if (sigaction (SIGINT, &sa, NULL) == -1) {
435
        WSS_log_fatal("Unable to listen for SIGINT signal: %s", strerror(errno));
436
437
        WSS_destroy_subprotocols();
438
        WSS_destroy_extensions();
439
        pthread_mutex_destroy(&state.lock);
440
441
        return EXIT_FAILURE;
442
    }
443
444
    if (sigaction (SIGSEGV, &sa, NULL) == -1) {
445
        WSS_log_fatal("Unable to listen for SIGSEGV signal: %s", strerror(errno));
446
447
        WSS_destroy_subprotocols();
448
        WSS_destroy_extensions();
449
        pthread_mutex_destroy(&state.lock);
450
451
        return EXIT_FAILURE;
452
    }
453
454
    if (sigaction (SIGILL, &sa, NULL) == -1) {
455
        WSS_log_fatal("Unable to listen for SIGILL signal: %s", strerror(errno));
456
457
        WSS_destroy_subprotocols();
458
        WSS_destroy_extensions();
459
        pthread_mutex_destroy(&state.lock);
460
461
        return EXIT_FAILURE;
462
    }
463
464
    if (sigaction (SIGHUP, &sa, NULL) == -1) {
465
        WSS_log_fatal("Unable to listen for SIGHUB signal: %s", strerror(errno));
466
467
        WSS_destroy_subprotocols();
468
        WSS_destroy_extensions();
469
        pthread_mutex_destroy(&state.lock);
470
471
        return EXIT_FAILURE;
472
    }
473
474
    if (sigaction (SIGFPE, &sa, NULL) == -1) {
475
        WSS_log_fatal("Unable to listen for SIGFPE signal: %s", strerror(errno));
476
477
        WSS_destroy_subprotocols();
478
        WSS_destroy_extensions();
479
        pthread_mutex_destroy(&state.lock);
480
481
        return EXIT_FAILURE;
482
    }
483
484
    if (sigaction (SIGPIPE, &sa, NULL) == -1) {
485
        WSS_log_fatal("Unable to listen for SIGPIPE signal: %s", strerror(errno));
486
487
        WSS_destroy_subprotocols();
488
        WSS_destroy_extensions();
489
        pthread_mutex_destroy(&state.lock);
490
491
        return EXIT_FAILURE;
492
    }
493
494
    WSS_log_trace("Initializing sessions");
495
    if ( unlikely(WSS_SUCCESS != WSS_session_init_lock()) ) {
496
        WSS_log_fatal("Unable to initialize session mutex");
497
498
        WSS_destroy_subprotocols();
499
        WSS_destroy_extensions();
500
        pthread_mutex_destroy(&state.lock);
501
502
        return EXIT_FAILURE;
503
    }
504
505
    WSS_log_trace("Allocating memory for HTTP instance");
506
507
    if ( unlikely(NULL == (http = WSS_malloc(sizeof(wss_server_t)))) ) {
508
        WSS_log_fatal("Unable to allocate server structure");
509
510
        WSS_session_destroy_lock();
511
        WSS_destroy_subprotocols();
512
        WSS_destroy_extensions();
513
        pthread_mutex_destroy(&state.lock);
514
515
        return EXIT_FAILURE;
516
    }
517
    servers.http = http;
518
    servers.https = NULL;
519
520
    if ( unlikely(0 != (err = pthread_mutex_init(&http->lock, NULL))) ) {
521
        WSS_server_free(http);
522
        WSS_session_destroy_lock();
523
        WSS_destroy_subprotocols();
524
        WSS_destroy_extensions();
525
        pthread_mutex_destroy(&state.lock);
526
    }
527
528
    WSS_log_trace("Setting to running state");
529
530
    WSS_server_set_state(RUNNING);
531
532
    WSS_log_trace("Creating HTTP Instance");
533
534
    http->config       = config;
535
    http->port         = config->port_http;
536
    if ( unlikely(WSS_SUCCESS != WSS_http_server(http)) ) {
537
        WSS_log_fatal("Unable to initialize http server");
538
539
        WSS_server_free(http);
540
        WSS_session_destroy_lock();
541
        WSS_destroy_subprotocols();
542
        WSS_destroy_extensions();
543
        pthread_mutex_destroy(&state.lock);
544
545
        return EXIT_FAILURE;
546
    }
547
548
    if (ssl) {
549
        WSS_log_trace("Allocating memory for HTTPS instance");
550
551
        if ( unlikely(NULL == (https = (wss_server_t *) WSS_malloc(sizeof(wss_server_t)))) ) {
552
            WSS_server_free(http);
553
            WSS_session_destroy_lock();
554
            WSS_destroy_subprotocols();
555
            WSS_destroy_extensions();
556
            pthread_mutex_destroy(&state.lock);
557
558
            WSS_log_fatal("Unable to allocate https server structure");
559
560
            return EXIT_FAILURE;
561
        }
562
563
        servers.https = https;
564
565
        WSS_log_trace("Creating HTTPS Instance");
566
567
        https->config = config;
568
        https->port   = config->port_https;
569
        if ( unlikely(WSS_SUCCESS != WSS_http_ssl(https)) ) {
570
            WSS_server_free(https);
571
            WSS_server_free(http);
572
            WSS_session_destroy_lock();
573
            WSS_destroy_subprotocols();
574
            WSS_destroy_extensions();
575
            pthread_mutex_destroy(&state.lock);
576
577
            WSS_log_fatal("Unable to establish ssl context");
578
579
            return EXIT_FAILURE;
580
        }
581
582
        if ( unlikely(WSS_SUCCESS != WSS_http_server(https)) ) {
583
            WSS_server_free(https);
584
            WSS_server_free(http);
585
            WSS_session_destroy_lock();
586
            WSS_destroy_subprotocols();
587
            WSS_destroy_extensions();
588
            pthread_mutex_destroy(&state.lock);
589
590
            WSS_log_fatal("Unable to initialize https server");
591
592
            return EXIT_FAILURE;
593
        }
594
    }
595
596
    WSS_log_trace("Creating HTTP cleanup thread");
597
598
    if ( unlikely(pthread_create(&cleanup_thread_id, NULL, WSS_cleanup, NULL) != 0) ) {
599
        if (ssl) {
600
            WSS_server_free(https);
601
        }
602
        WSS_server_free(http);
603
        WSS_session_destroy_lock();
604
        WSS_destroy_subprotocols();
605
        WSS_destroy_extensions();
606
        pthread_mutex_destroy(&state.lock);
607
608
        WSS_log_fatal("Unable to create cleanup thread");
609
        return EXIT_FAILURE;
610
    }
611
612
    WSS_log_trace("Joining server threads");
613
614
    pthread_join(cleanup_thread_id, (void **) &err);
615
    if ( unlikely(WSS_SUCCESS != err) ) {
616
        WSS_log_error("Cleanup thread returned with error: %s", strerror(err));
617
        WSS_server_set_state(HALT_ERROR);
618
    }
619
620
    WSS_log_trace("Cleanup thread has shutdown");
621
622
    pthread_join(http->thread_id, (void **) &err);
623
    if ( unlikely(WSS_SUCCESS != err) ) {
624
        WSS_log_error("HTTP Server thread returned with error: %s", strerror(err));
625
        WSS_server_set_state(HALT_ERROR);
626
    }
627
628
    WSS_log_trace("HTTP server thread has shutdown");
629
630
    if (ssl) {
631
        pthread_join(https->thread_id, (void **) &err);
632
        if ( unlikely(WSS_SUCCESS != err) ) {
633
            WSS_log_error("HTTPS Server thread returned with error: %s", strerror(err));
634
            WSS_server_set_state(HALT_ERROR);
635
        }
636
637
        WSS_log_trace("HTTPS server thread has shutdown");
638
    }
639
640
    pthread_join(http->thread_id, (void **) &err);
641
642
    if ( unlikely(WSS_poll_close(http) != WSS_SUCCESS) ) {
643
        WSS_server_set_state(HALT_ERROR);
644
    }
645
646
    if ( unlikely(WSS_server_free(http) != 0) ) {
647
        WSS_server_set_state(HALT_ERROR);
648
    }
649
650
    WSS_log_trace("Freed memory associated with HTTP server instance");
651
652
    if (ssl) {
653
        if ( unlikely(WSS_poll_close(https) != WSS_SUCCESS) ) {
654
            WSS_server_set_state(HALT_ERROR);
655
        }
656
657
        if ( unlikely(WSS_server_free(https) != 0) ) {
658
            WSS_server_set_state(HALT_ERROR);
659
        }
660
661
        WSS_log_trace("Freed memory associated with HTTPS server instance");
662
    }
663
664
    if ( unlikely(WSS_SUCCESS != WSS_session_delete_all()) ) {
665
        WSS_server_set_state(HALT_ERROR);
666
    }
667
668
    WSS_log_trace("Freed all sessions");
669
670
    if ( unlikely(WSS_SUCCESS != WSS_session_destroy_lock()) ) {
671
        WSS_server_set_state(HALT_ERROR);
672
    }
673
674
    WSS_log_trace("Destroyed session lock");
675
676
    if ( unlikely(HALT_ERROR == state.state) ) {
677
        ret = EXIT_FAILURE;
678
    }
679
680
    WSS_destroy_subprotocols();
681
682
    WSS_destroy_extensions();
683
684
    pthread_mutex_destroy(&state.lock);
685
686
    return ret;
687
}