GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/http.c Lines: 90 144 62.5 %
Date: 2020-12-10 21:44:00 Branches: 52 105 49.5 %

Line Branch Exec Source
1
#include <errno.h> 				/* errno */
2
#include <stdio.h> 				/* printf, fflush, fprintf, fopen, fclose */
3
#include <string.h>             /* strerror, memset, strncpy, memcpy, strlen */
4
#include <stdlib.h>             /* atoi, malloc, free, realloc */
5
#include <stdbool.h>
6
#include <math.h> 				/* log10 */
7
#include <unistd.h> 			/* close */
8
#include <regex.h>              /* regex_t, regcomp, regexec */
9
10
#include <sys/socket.h>         /* socket, setsockopt, inet_ntoa, accept */
11
#include <netinet/in.h>         /* sockaddr_in, inet_ntoa */
12
#include <arpa/inet.h>          /* htonl, htons, inet_ntoa */
13
14
#include "http.h"
15
#include "event.h"
16
#include "alloc.h"
17
#include "config.h"
18
#include "error.h"
19
#include "log.h"
20
#include "socket.h"
21
#include "predict.h"
22
#include "ssl.h"
23
24
/**
25
 * Generates a regular expression pattern to match the request uri of the header.
26
 *
27
 * @param   config    [wss_config_t *]  "The configuration of the server"
28
 * @param   ssl       [bool]            "Whether server uses SSL"
29
 * @param   port      [int]             "The server port"
30
 * @return            [char *]          "The request uri regex pattern"
31
 */
32
29
static char *generate_request_uri(wss_config_t * config, bool ssl, int port) {
33
29
    int i, j, k;
34
29
    size_t iw = 0, jw = 0, kw = 0;
35
29
    size_t request_uri_length = 0;
36
29
    size_t sum_host_length = 0;
37
29
    size_t sum_path_length = 0;
38
29
    size_t sum_query_length = 0;
39
29
    char *request_uri;
40
29
    char *host = "";
41
29
    char *path = "";
42
29
    char *query = "";
43
29
    char *s = "";
44
45
203
    for (i = 0; i < config->hosts_length; i++) {
46
174
        sum_host_length += strlen(config->hosts[i]);
47
    }
48
29
    sum_host_length += MAX(config->hosts_length-1, 0);
49
50
87
    for (j = 0; j < config->paths_length; j++) {
51
58
        sum_path_length += strlen(config->paths[j]);
52
    }
53
29
    sum_path_length += MAX(config->paths_length-1, 0);
54
55
87
    for (k = 0; k < config->queries_length; k++) {
56
58
        sum_query_length += strlen(config->queries[k]);
57
    }
58
29
    sum_query_length += MAX(config->queries_length-1, 0);
59
60
29
    if (sum_host_length+sum_path_length+sum_query_length == 0) {
61
        return NULL;
62
    }
63
64
29
    request_uri_length += strlen(REQUEST_URI)*sizeof(char)-12*sizeof(char);
65
29
    request_uri_length += ssl*sizeof(char);
66
29
    request_uri_length += (log10(port)+1)*sizeof(char);
67
29
    request_uri_length += sum_host_length*sizeof(char);
68
29
    request_uri_length += sum_path_length*sizeof(char);
69
29
    request_uri_length += sum_query_length*sizeof(char);
70
29
    request_uri_length += sum_query_length*sizeof(char);
71
29
    request_uri = (char *) WSS_malloc(request_uri_length+1*sizeof(char));
72
73
29
    if (ssl) {
74
        s = "s";
75
    }
76
77

29
    if ( unlikely(sum_host_length > 0 && NULL == (host = WSS_malloc(sum_host_length+1))) ) {
78
        return NULL;
79
    }
80
81

29
    if ( unlikely(sum_path_length > 0 && NULL == (path = WSS_malloc(sum_path_length+1))) ) {
82
        return NULL;
83
    }
84
85

29
    if ( unlikely(sum_query_length > 0 && NULL == (query = WSS_malloc(sum_query_length+1))) ) {
86
        return NULL;
87
    }
88
89
203
    for (i = 0; likely(i < config->hosts_length); i++) {
90
174
        if ( unlikely(i+1 == config->hosts_length) ) {
91
29
            sprintf(host+iw, "%s", config->hosts[i]);
92
        } else {
93
145
            sprintf(host+iw, "%s|", config->hosts[i]);
94
145
            iw++;
95
        }
96
174
        iw += strlen(config->hosts[i]);
97
    }
98
99
87
    for (j = 0; likely(j < config->paths_length); j++) {
100
58
        if ( unlikely(j+1 == config->paths_length) ) {
101
29
            sprintf(path+jw, "%s", config->paths[j]);
102
        } else {
103
29
            sprintf(path+jw, "%s|", config->paths[j]);
104
29
            jw++;
105
        }
106
58
        jw += strlen(config->paths[j]);
107
    }
108
109
87
    for (k = 0; likely(k < config->queries_length); k++) {
110
58
        if ( unlikely(k+1 == config->queries_length) ) {
111
29
            sprintf(query+kw, "%s", config->queries[k]);
112
        } else {
113
29
            sprintf(query+kw, "%s|", config->queries[k]);
114
29
            kw++;
115
        }
116
58
        kw += strlen(config->queries[k]);
117
    }
118
119
29
    sprintf(request_uri, REQUEST_URI, s, host, port, path, query, query);
120
121
29
    if ( likely(strlen(host) > 0) ) {
122
29
        WSS_free((void **) &host);
123
    }
124
125
29
    if ( likely(strlen(path) > 0) ) {
126
29
        WSS_free((void **) &path);
127
    }
128
129
29
    if ( likely(strlen(query) > 0) ) {
130
29
        WSS_free((void **) &query);
131
    }
132
133
    return request_uri;
134
}
135
136
/**
137
 * Function that initializes a regex for the server instance that can be used
138
 * to validate the connecting path of the client.
139
 *
140
 * @param   server	[wss_server_t *] 	"The server instance"
141
 * @return 			[wss_error_t]       "The error status"
142
 */
143
29
wss_error_t WSS_http_regex_init(wss_server_t *server) {
144
29
    int err;
145
29
    char *request_uri;
146
29
    bool ssl = false;
147
148
29
    if (NULL != server->ssl_ctx) {
149
        ssl = true;
150
    }
151
152
29
    request_uri = generate_request_uri(server->config, ssl, server->port);
153
29
    if ( likely(request_uri != NULL) ) {
154
29
        if ( NULL == (server->re = WSS_malloc(sizeof(regex_t)))) {
155
            return WSS_MEMORY_ERROR;
156
        }
157
158
29
        if ( unlikely((err = regcomp(server->re, request_uri, REG_EXTENDED|REG_NOSUB)) != 0) ) {
159
            return WSS_REGEX_ERROR;
160
        }
161
162
29
        WSS_free((void **) &request_uri);
163
    }
164
165
    return WSS_SUCCESS;
166
}
167
168
/**
169
 * Function that initializes a http server instance and creating thread where
170
 * the instance is being run.
171
 *
172
 * @param   server	[wss_server_t *] 	"The server instance"
173
 * @return 			[wss_error_t]       "The error status"
174
 */
175
wss_error_t WSS_http_server(wss_server_t *server) {
176
    int err;
177
    char straddr[INET6_ADDRSTRLEN];
178
179
    if (NULL == server->ssl_ctx) {
180
        WSS_log_trace("Starting HTTP instance");
181
    } else {
182
        WSS_log_trace("Starting HTTPS instance");
183
    }
184
185
    WSS_log_trace("Assigning server to port %d", server->port);
186
187
    /**
188
     * Setting port and initializes filedescriptors to -1 in the server
189
     * structure.
190
     */
191
    server->fd = -1;
192
    server->poll_fd = -1;
193
194
    WSS_log_trace("Creating socket filedescriptor");
195
    if ( unlikely((err = WSS_socket_create(server)) != WSS_SUCCESS) ) {
196
        return err;
197
    }
198
199
    WSS_log_trace("Allowing reuse of socket");
200
    if ( unlikely((err = WSS_socket_reuse(server->fd)) != WSS_SUCCESS) ) {
201
        return err;
202
    }
203
204
    WSS_log_trace("Creating socket structure for instance");
205
    if ( unlikely((err = WSS_socket_bind(server)) != WSS_SUCCESS) ) {
206
        return err;
207
    }
208
209
    if ( likely(NULL != inet_ntop(AF_INET6, (const void *)&server->info, straddr, sizeof(straddr)))) {
210
        WSS_log_trace("Binding address of server to: ", straddr, server->port);
211
    }
212
213
    WSS_log_trace("Making server socket non-blocking");
214
    if ( unlikely((err = WSS_socket_non_blocking(server->fd)) != WSS_SUCCESS) ) {
215
        return err;
216
    }
217
218
    WSS_log_trace("Starts listening to the server socket");
219
    if ( unlikely((err = WSS_socket_listen(server->fd)) != WSS_SUCCESS) ) {
220
        return err;
221
    }
222
223
    WSS_log_trace("Creating threadpool");
224
    if ( unlikely((err = WSS_socket_threadpool(server)) != WSS_SUCCESS) ) {
225
        return err;
226
    }
227
228
    WSS_log_trace("Initializing server regexp");
229
    if ( unlikely((err = WSS_http_regex_init(server)) != WSS_SUCCESS) ) {
230
        return err;
231
    }
232
233
    WSS_log_trace("Initializing server poll");
234
    if ( unlikely(WSS_SUCCESS != (err = WSS_poll_init(server))) ) {
235
        return err;
236
    }
237
238
    WSS_log_trace("Creating server thread");
239
    if ( unlikely(pthread_create(&server->thread_id, NULL, WSS_server_run, (void *) server) != 0) ) {
240
        WSS_log_error("Unable to create server thread", strerror(errno));
241
        return WSS_THREAD_CREATE_ERROR;
242
    }
243
244
    return WSS_SUCCESS;
245
}
246
247
/**
248
 * Function that free op space allocated for the http server and closes the
249
 * filedescriptors in use..
250
 *
251
 * @param   server	[wss_server_t *] 	"The http server"
252
 * @return 			[wss_error_t]       "The error status"
253
 */
254
9
wss_error_t WSS_http_server_free(wss_server_t *server) {
255
9
    int err;
256
9
    int res = WSS_SUCCESS;
257
258
9
    if ( likely(NULL != server) ) {
259
        /**
260
         * Shutting down socket, such that no more reads is allowed.
261
         */
262
9
        if ( likely(server->fd > -1) ) {
263
7
            if ( unlikely(shutdown(server->fd, SHUT_RD) != 0) ) {
264
4
                WSS_log_error("Unable to shutdown for reads of server socket: %s", strerror(errno));
265
4
                res = WSS_SOCKET_SHUTDOWN_ERROR;
266
            }
267
        }
268
269
        /**
270
         * Shutting down threadpool gracefully
271
         */
272
9
        if ( likely(NULL != server->pool) ) {
273
1
            if ( unlikely((err = threadpool_destroy(server->pool, threadpool_graceful)) != 0) ) {
274
                WSS_log_error("Unable to destroy threadpool gracefully: %s", threadpool_strerror(errno));
275
276
                switch (err) {
277
                    case threadpool_invalid:
278
                        res = WSS_THREADPOOL_LOCK_ERROR;
279
                        break;
280
                    case threadpool_lock_failure:
281
                        res = WSS_THREADPOOL_LOCK_ERROR;
282
                        break;
283
                    case threadpool_queue_full:
284
                        res = WSS_THREADPOOL_FULL_ERROR;
285
                        break;
286
                    case threadpool_shutdown:
287
                        res = WSS_THREADPOOL_SHUTDOWN_ERROR;
288
                        break;
289
                    case threadpool_thread_failure:
290
                        res = WSS_THREADPOOL_THREAD_ERROR;
291
                        break;
292
                    default:
293
                        res = WSS_THREADPOOL_ERROR;
294
                        break;
295
                }
296
9
            }
297
        }
298
299
9
        if ( NULL != server->re ) {
300
            regfree(server->re);
301
            WSS_free((void **) &server->re);
302
        }
303
304
        /**
305
         * Freeing epoll structures
306
         */
307
9
        WSS_free((void **) &server->events);
308
309
        /**
310
         * Closing epoll
311
         */
312
9
        if ( likely(server->poll_fd > -1) ) {
313
9
            if ( unlikely(close(server->poll_fd) != 0) ) {
314
                WSS_log_error("Unable to close servers epoll filedescriptor: %s", strerror(errno));
315
                res = WSS_SOCKET_CLOSE_ERROR;
316
            }
317
318
9
            server->poll_fd = -1;
319
        }
320
321
        /**
322
         * Closing socket
323
         */
324
9
        if ( likely(server->fd > -1)) {
325
7
            if ( unlikely(close(server->fd) != 0) ) {
326
                WSS_log_error("Unable to close servers filedescriptor: %s", strerror(errno));
327
                res = WSS_SOCKET_CLOSE_ERROR;
328
            }
329
7
            server->fd = -1;
330
        }
331
332
9
        if (NULL != server->ssl_ctx) {
333
            WSS_http_ssl_free(server);
334
        }
335
    }
336
337
9
    return res;
338
}