GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/header.c Lines: 290 301 96.3 %
Date: 2020-12-10 21:44:00 Branches: 219 272 80.5 %

Line Branch Exec Source
1
#include <stddef.h>             /* size_t */
2
#include <math.h>               /* log10 */
3
#include <stdio.h> 				/* printf, fflush, fprintf, fopen, fclose */
4
#include <string.h>             /* strerror, memset, strncpy, memcpy, strlen,
5
                                   strtok, strtok_r */
6
7
#include <sys/types.h>
8
#include <ctype.h>              /* isspace */
9
10
#include "header.h"
11
#include "alloc.h"
12
#include "b64.h"
13
#include "httpstatuscodes.h"
14
#include "session.h"
15
#include "log.h"
16
#include "str.h"
17
#include "subprotocols.h"
18
#include "extensions.h"
19
#include "predict.h"
20
21
/**
22
 * Acceptable HTTP methods
23
 */
24
const char *methods[] = {
25
    "GET",
26
//    "OPTIONS",
27
//    "HEAD",
28
//    "POST",
29
//    "PUT",
30
//    "DELETE",
31
//    "TRACE",
32
//    "CONNECT"
33
};
34
35
/**
36
 * Acceptable HTTP versions
37
 */
38
const char *versions[] = {
39
//    "HTTP/0.9",
40
//    "HTTP/1.0",
41
    "HTTP/1.1",
42
//    "HTTP/2.0",
43
};
44
45
/**
46
 * Trims a string for leading and trailing whitespace.
47
 *
48
 * @param   str     [char *]    "The string to trim"
49
 * @return          [char *]    "The input string without leading and trailing spaces"
50
 */
51
130
static inline char *trim(char* str)
52
{
53
130
    if ( unlikely(NULL == str) ) {
54
        return NULL;
55
    }
56
57
105
    if ( unlikely(str[0] == '\0') ) {
58
        return str;
59
    }
60
105
    int start, end = strlen(str);
61
160
    for (start = 0; isspace(str[start]); ++start) {}
62
105
    if (str[start]) {
63

109
        while (end > 0 && isspace(str[end-1]))
64
4
            --end;
65
105
        memmove(str, &str[start], end - start);
66
    }
67
105
    str[end - start] = '\0';
68
69
105
    return str;
70
}
71
72
13
static inline void header_set_version(wss_header_t *header, char *v) {
73
13
    int version = strtol(strtok_r(NULL, "", &v), (char **) NULL, 10);
74
75
13
    if ( likely(header->ws_version < version) ) {
76
13
        if ( unlikely(version == 4) ) {
77
2
            header->ws_type = HYBI04;
78
2
            header->ws_version = version;
79
11
        } else if( unlikely(version == 5) ) {
80
2
            header->ws_type = HYBI05;
81
2
            header->ws_version = version;
82
9
        } else if( unlikely(version == 6) ) {
83
2
            header->ws_type = HYBI06;
84
2
            header->ws_version = version;
85
7
        } else if( unlikely(version == 7) ) {
86
2
            header->ws_type = HYBI07;
87
2
            header->ws_version = version;
88
5
        } else if( unlikely(version == 8) ) {
89
2
            header->ws_type = HYBI10;
90
2
            header->ws_version = version;
91
3
        } else if( likely(version == 13) ) {
92
3
            header->ws_type = RFC6455;
93
3
            header->ws_version = version;
94
        }
95
    }
96
13
}
97
98
99
/**
100
 * Parses a HTTP header into a header structure and returns the status code
101
 * appropriate.
102
 *
103
 * @param   fd        [int]                    "The filedescriptor"
104
 * @param   header    [wss_header_t *]         "The header structure to fill"
105
 * @param   config    [wss_config_t *]         "The configuration of the server"
106
 * @return            [enum HttpStatus_Code]   "The status code to return to the client"
107
 */
108
60
enum HttpStatus_Code WSS_parse_header(int fd, wss_header_t *header, wss_config_t *config) {
109
60
    bool valid, in_use, double_clrf = false;
110
60
    size_t i, line_length;
111
60
    char *lineptr, *temp, *line, *sep, *accepted;
112
60
    char *tokenptr = NULL, *sepptr = NULL, *paramptr = NULL;
113
60
    char *token, *name;
114
60
    wss_subprotocol_t *proto;
115
60
    wss_extension_t *ext;
116
60
    char *exts = NULL;
117
60
    unsigned int header_size = 0;
118
60
    size_t extensions_length = 0;
119
120
60
    WSS_log_trace("Parsing HTTP header");
121
122
60
    if ( unlikely(NULL == header->content) ) {
123
1
        WSS_log_trace("No header content");
124
1
        return HttpStatus_BadRequest;
125
    }
126
127
59
    token = strtok_r(header->content, "\r", &tokenptr);
128
129
59
    if ( likely(tokenptr[0] == '\n') ) {
130
57
        tokenptr++;
131
2
    } else if ( unlikely(tokenptr[0] != '\0') ) {
132
1
        WSS_log_trace("Line shall always end with newline character");
133
1
        return HttpStatus_BadRequest;
134
    }
135
136
58
    if ( unlikely(NULL == token) ) {
137
1
        WSS_log_trace("No first line of header");
138
1
        return HttpStatus_BadRequest;
139
    }
140
141
    /**
142
     * Receiving and checking method of the request
143
     */
144
57
    if ( unlikely(NULL == (header->method = strtok_r(token, " ", &lineptr))) ) {
145
1
        WSS_log_trace("Unable to parse header method");
146
1
        return HttpStatus_BadRequest;
147
    }
148
149
56
    if ( unlikely(strinarray(header->method, methods,
150
                sizeof(methods)/sizeof(methods[0])) != 0) ) {
151
2
        WSS_log_trace("Method that the client is using is unknown.");
152
2
        return HttpStatus_MethodNotAllowed;
153
    }
154
155
    /**
156
     * Receiving the path of the request
157
     */
158
54
    if ( unlikely(NULL == (header->path = strtok_r(NULL, " ", &lineptr))) ) {
159
1
        WSS_log_trace("Unable to parse header path");
160
1
        return HttpStatus_BadRequest;
161
    }
162
163
53
    if ( unlikely(strlen(header->path) > config->size_uri) ) {
164
1
        WSS_log_trace("The size of the request URI was too large");
165
1
        return HttpStatus_URITooLong;
166


52
    } else if ( unlikely(strncmp("/", header->path, sizeof(char)*1) != 0 &&
167
            strncasecmp("ws://", header->path, sizeof(char)*5) != 0 &&
168
            strncasecmp("wss://", header->path, sizeof(char)*6) != 0 &&
169
            strncasecmp("http://", header->path, sizeof(char)*7) != 0 &&
170
            strncasecmp("https://", header->path, sizeof(char)*8) != 0) ) {
171
1
        WSS_log_trace("The request URI is not absolute URI nor relative path.");
172
1
        return HttpStatus_BadRequest;
173
    }
174
175
    /**
176
     * Receiving and checking version of the request
177
     */
178
51
    if ( unlikely(NULL == (header->version = strtok_r(NULL, " ", &lineptr))) ) {
179
1
        WSS_log_trace("Unable to parse header version");
180
1
        return HttpStatus_BadRequest;
181
    }
182
183
50
    if ( unlikely(strinarray(header->version, versions,
184
                sizeof(versions)/sizeof(versions[0])) != 0) ) {
185
1
        WSS_log_trace("HTTP version that the client is using is unknown.");
186
1
        return HttpStatus_HTTPVersionNotSupported;
187
    }
188
189
    // We've reached the payload
190

49
    if ( unlikely(strlen(token) >= 2 && tokenptr[0] == '\r' && tokenptr[1] == '\n') ) {
191
22
        tokenptr += 2;
192
193
22
        if ( unlikely(strlen(tokenptr) > config->size_payload) ) {
194
1
            WSS_log_trace("Payload size received is too large.");
195
1
            return HttpStatus_PayloadTooLarge;
196
        }
197
198
21
        double_clrf = true;
199
21
        header->payload = tokenptr;
200
21
        token = NULL;
201
    } else {
202
27
        token = strtok_r(NULL, "\r", &tokenptr);
203
204
27
        if ( likely(tokenptr[0] == '\n') ) {
205
25
            tokenptr++;
206
2
        } else if ( unlikely(tokenptr[0] != '\0') ) {
207
1
            WSS_log_trace("Line shall always end with newline character");
208
1
            return HttpStatus_BadRequest;
209
        }
210
    }
211
212
185
    while ( likely(NULL != token) ) {
213
143
        header_size += strlen(token);
214
215
143
        if ( unlikely(header_size > config->size_header) ) {
216
1
            WSS_log_trace("Header size received is too large.");
217
1
            return HttpStatus_RequestHeaderFieldsTooLarge;
218
        }
219
220
142
        line = strtok_r(token, ":", &lineptr);
221
222
142
        if ( likely(line != NULL) ) {
223
142
            line_length = strlen(line);
224
142
            if ( line_length == strlen(SEC_WEBSOCKET_VERSION) &&
225
14
                strncasecmp(SEC_WEBSOCKET_VERSION, line, line_length) == 0 ) {
226
                // The |Sec-WebSocket-Version| header field MUST NOT appear more than once in an HTTP request.
227
14
                if ( unlikely(header->ws_version != 0) ) {
228
1
                    WSS_log_trace("Sec-WebSocket-Version must only appear once in header");
229
1
                    return HttpStatus_BadRequest;
230
                }
231
232
13
                sep = trim(strtok_r(strtok_r(NULL, "", &lineptr), ",", &sepptr));
233
26
                while (NULL != sep) {
234
13
                    header_set_version(header, sep);
235
236
13
                    sep = trim(strtok_r(NULL, ",", &sepptr));
237
                }
238
128
            } else if ( line_length == strlen(UPGRADE_STRING) &&
239
20
                        strncasecmp(UPGRADE_STRING, line, line_length) == 0 ) {
240
20
                header->ws_upgrade = (strtok_r(NULL, "", &lineptr)+1);
241
108
            } else if ( line_length == strlen(ORIGIN_STRING) &&
242
16
                        strncasecmp(ORIGIN_STRING, line, line_length) == 0 ) {
243
14
                header->ws_origin = (strtok_r(NULL, "", &lineptr)+1);
244
94
            } else if ( line_length == strlen(CONNECTION_STRING) &&
245
20
                        strncasecmp(CONNECTION_STRING, line, line_length) == 0 ) {
246
20
                header->ws_connection = (strtok_r(NULL, "", &lineptr)+1);
247
74
            } else if ( line_length == strlen(COOKIE_STRING) &&
248
2
                        strncasecmp(COOKIE_STRING, line, line_length) == 0 ) {
249
2
                header->cookies = (strtok_r(NULL, "", &lineptr)+1);
250
72
            } else if ( line_length == strlen(SEC_WEBSOCKET_PROTOCOL) &&
251
14
                        strncasecmp(SEC_WEBSOCKET_PROTOCOL, line, line_length) == 0 ) {
252
14
                if (NULL == header->ws_protocol) {
253
14
                    sep = trim(strtok_r(strtok_r(NULL, "", &lineptr), ",", &sepptr));
254

14
                    if (NULL != sep && NULL != (proto = WSS_find_subprotocol(sep))) {
255
2
                        header->ws_protocol = proto;
256
                    } else {
257
18
                        while (NULL != (sep = trim(strtok_r(NULL, ",", &sepptr)))) {
258
12
                            if (NULL != (proto = WSS_find_subprotocol(sep))) {
259
6
                                header->ws_protocol = proto;
260
6
                                break;
261
                            }
262
                        }
263
                    }
264
                }
265
58
            } else if ( line_length == strlen(SEC_WEBSOCKET_ORIGIN) &&
266
6
                        strncasecmp(SEC_WEBSOCKET_ORIGIN, line, line_length) == 0 ) {
267
6
                header->ws_origin = (strtok_r(NULL, "", &lineptr)+1);
268
52
            } else if ( line_length == strlen(SEC_WEBSOCKET_KEY) &&
269
14
                        strncasecmp(SEC_WEBSOCKET_KEY, line, line_length) == 0 ) {
270
                //The |Sec-WebSocket-Key| header field MUST NOT appear more than once in an HTTP request.
271
14
                if ( unlikely(header->ws_key != NULL) ) {
272
1
                    WSS_log_trace("Sec-WebSocket-Key must only appear once in header");
273
1
                    return HttpStatus_BadRequest;
274
                }
275
13
                header->ws_key = (strtok_r(NULL, "", &lineptr)+1);
276
38
            } else if ( line_length == strlen(SEC_WEBSOCKET_EXTENSIONS) &&
277
8
                        strncasecmp(SEC_WEBSOCKET_EXTENSIONS, line, line_length) == 0 ) {
278
8
                line = trim(strtok_r(NULL, "", &lineptr));
279
8
                line_length = strlen(line);
280
8
                if ( NULL == (exts = WSS_realloc((void **)&exts, extensions_length, (extensions_length+line_length+1)*sizeof(char))) ) {
281
                    WSS_log_error("Unable to allocate space for extensions string");
282
                    return HttpStatus_InternalServerError;
283
                }
284
285
8
                memcpy(exts+extensions_length, line, line_length);
286
8
                extensions_length += line_length;
287
30
            } else if ( line_length == strlen(HOST_STRING) &&
288
22
                        strncasecmp(HOST_STRING, line, line_length) == 0 ) {
289
20
                header->host = (strtok_r(NULL, "", &lineptr)+1);
290
10
            } else if ( line_length == strlen(WEBSOCKET_PROTOCOL_STRING) &&
291
8
                        strncasecmp(WEBSOCKET_PROTOCOL_STRING, line, line_length) == 0 ) {
292
4
                header->ws_type = HIXIE75;
293
4
                if (NULL == header->ws_protocol) {
294
4
                    sep = trim(strtok_r(strtok_r(NULL, "", &lineptr), ",", &sepptr));
295

4
                    if (NULL != sep && NULL != (proto = WSS_find_subprotocol(sep))) {
296
2
                        header->ws_protocol = proto;
297
                    } else {
298
2
                        while (NULL != (sep = trim(strtok_r(NULL, ",", &sepptr)))) {
299
2
                            if (NULL != (proto = WSS_find_subprotocol(sep))) {
300
2
                                header->ws_protocol = proto;
301
2
                                break;
302
                            }
303
                        }
304
                    }
305
                }
306
6
            } else if ( line_length == strlen(SEC_WEBSOCKET_KEY1) &&
307
4
                        strncasecmp(SEC_WEBSOCKET_KEY1, line, line_length) == 0 ) {
308
2
                header->ws_type = HIXIE76;
309
2
                header->ws_key1 = (strtok_r(NULL, "", &lineptr)+1);
310
4
            } else if ( line_length == strlen(SEC_WEBSOCKET_KEY2) &&
311
2
                        strncasecmp(SEC_WEBSOCKET_KEY2, line, line_length) == 0 ) {
312
2
                header->ws_type = HIXIE76;
313
2
                header->ws_key2 = (strtok_r(NULL, "", &lineptr)+1);
314
            }
315
        }
316
317
        // We've reached the payload
318

140
        if ( unlikely(strlen(tokenptr) >= 2 && tokenptr[0] == '\r' && tokenptr[1] == '\n') ) {
319
21
            tokenptr += 2;
320
321
21
            if ( unlikely(strlen(tokenptr) > config->size_payload) ) {
322
1
                WSS_log_trace("Payload size received is too large.");
323
1
                return HttpStatus_PayloadTooLarge;
324
            }
325
326
20
            double_clrf = true;
327
20
            header->payload = tokenptr;
328
20
            temp = NULL;
329
        } else {
330
119
            temp = strtok_r(NULL, "\r", &tokenptr);
331
332
119
            if ( likely(tokenptr[0] == '\n') ) {
333
118
                tokenptr++;
334
1
            } else if ( unlikely(tokenptr[0] != '\0') ) {
335
1
                WSS_log_trace("Line shall always end with newline character");
336
1
                return HttpStatus_BadRequest;
337
            }
338
        }
339
340

138
        if ( unlikely(temp == NULL && header->ws_type == HIXIE76) ) {
341
2
            header->ws_type = HIXIE76;
342
2
            header->ws_key3 = header->payload;
343
        }
344
345
        token = temp;
346
    }
347
348
42
    if ( !double_clrf ) {
349
1
        WSS_log_trace("Double CLRF required to distinguish between header and body");
350
1
        return HttpStatus_BadRequest;
351
    }
352
353
41
    if ( NULL != exts ) {
354
2
        sep = trim(strtok_r(exts, ",", &sepptr));
355
8
        while(NULL != sep) {
356
6
            in_use = false;
357
6
            sep = trim(strtok_r(sep, ";", &paramptr));
358
359
6
            if ( NULL == (name = WSS_copy(sep, strlen(sep)+1)) ) {
360
                WSS_log_error("Unable to allocate space for extension structure");
361
                return HttpStatus_InternalServerError;
362
            }
363
364
6
            if (NULL != (ext = WSS_find_extension(name))) {
365
4
                for (i = 0; i < header->ws_extensions_count; i++) {
366
2
                    if ( unlikely(header->ws_extensions[i]->ext == ext) ) {
367
                        in_use = true;
368
                        break;
369
                    }
370
                }
371
372
4
                if ( likely(! in_use) ) {
373
2
                    ext->open(fd, trim(paramptr), &accepted, &valid);
374
2
                    if ( likely(valid) ) {
375
2
                        if ( unlikely(NULL == (header->ws_extensions = WSS_realloc((void **) &header->ws_extensions, header->ws_extensions_count*sizeof(wss_ext_t *), (header->ws_extensions_count+1)*sizeof(wss_ext_t *)))) ) {
376
                            WSS_log_error("Unable to allocate space for extension");
377
                            return HttpStatus_InternalServerError;
378
                        }
379
380
2
                        if ( unlikely(NULL == (header->ws_extensions[header->ws_extensions_count] = WSS_malloc(sizeof(wss_ext_t))))) {
381
                            WSS_log_error("Unable to allocate space for extension structure");
382
                            return HttpStatus_InternalServerError;
383
                        }
384
2
                        header->ws_extensions[header->ws_extensions_count]->ext = ext;
385
2
                        header->ws_extensions[header->ws_extensions_count]->name = name;
386
2
                        header->ws_extensions[header->ws_extensions_count]->accepted = accepted;
387
2
                        header->ws_extensions_count += 1;
388
                    }
389
                }
390
            }
391
392
6
            sep = trim(strtok_r(NULL, ",", &sepptr));
393
        }
394
2
        WSS_free((void **)&exts);
395
    }
396
397






41
    if ( unlikely(header->ws_type == UNKNOWN
398
            && header->ws_version == 0
399
            && header->ws_key1 == NULL
400
            && header->ws_key2 == NULL
401
            && header->ws_key3 == NULL
402
            && header->ws_key == NULL
403
            && header->ws_upgrade != NULL
404
            && header->ws_connection != NULL
405
            && strlen(header->ws_upgrade) == strlen(WEBSOCKET_STRING)
406
            && strncasecmp(WEBSOCKET_STRING, header->ws_upgrade, strlen(WEBSOCKET_STRING)) == 0
407
            && strlen(header->ws_connection) == strlen(UPGRADE_STRING)
408
            && strncasecmp(UPGRADE_STRING, header->ws_connection, strlen(UPGRADE_STRING)) == 0
409
            && header->host != NULL
410
            && header->ws_origin != NULL) ) {
411
4
        header->ws_type = HIXIE75;
412
    }
413
414
    return HttpStatus_OK;
415
}
416
417
/**
418
 * Upgrades a HTTP header, that is returns switching protocols response if
419
 * the header contains the required options.
420
 *
421
 * @param   header    [wss_header_t *]         "The header structure to fill"
422
 * @param   config    [wss_config_t *]         "The configuration of the server"
423
 * @param   re        [regex_t *]              "The regex to validate path"
424
 * @return            [enum HttpStatus_Code]   "The status code to return to the client"
425
 */
426
57
enum HttpStatus_Code WSS_upgrade_header(wss_header_t *header, wss_config_t * config, regex_t *re) {
427
57
    int err;
428
57
    char msg[1024];
429
57
    char *sep;
430
57
    char *sepptr = NULL;
431
57
    unsigned char *key = NULL;
432
57
    unsigned long key_length;
433
434
57
    WSS_log_trace("Upgrading HTTP header");
435
436

57
    if ( unlikely(strncasecmp("http://", header->path, sizeof(char)*7) == 0 ||
437
         strncasecmp("https://", header->path, sizeof(char)*8) == 0) ) {
438
1
        WSS_log_trace("Header path does not contain valid protocol");
439
1
        return HttpStatus_UpgradeRequired;
440
    }
441
442
56
    WSS_log_trace("Validating request_uri");
443
444
56
    if (re != NULL) {
445
        // It is recommended to specify paths in the config file
446
56
        err = regexec(re, header->path, 0, NULL, 0);
447
56
        if ( unlikely(err == REG_NOMATCH) ) {
448
4
            WSS_log_trace("Path is not allowed: %s", header->path);
449
4
            return HttpStatus_NotFound;
450
        }
451
452
52
        if ( unlikely(err != 0) ) {
453
            regerror(err, re, msg, 1024);
454
            WSS_log_error("Unable to exec regex: %s", msg);
455
            return HttpStatus_InternalServerError;
456
        }
457
    }
458
459
52
    WSS_log_trace("Validating host");
460
461
    // It is recommended to specify hosts in the config file
462
52
    if ( likely(config->hosts_length > 0) ) {
463
52
        if ( unlikely(strinarray(header->host, (const char **)config->hosts, config->hosts_length) != 0) ) {
464
6
            WSS_log_trace("Host is not allowed: %s", header->host);
465
6
            return HttpStatus_BadRequest;
466
        }
467
    }
468
469
46
    WSS_log_trace("Validating upgrade header");
470
471

46
    if ( unlikely(NULL == header->ws_upgrade || strlen(header->ws_upgrade) < strlen(WEBSOCKET_STRING) ||
472

42
            strncasecmp(WEBSOCKET_STRING, header->ws_upgrade, strlen(WEBSOCKET_STRING)) != 0) || (header->ws_type <= HIXIE76 && strncasecmp(WEBSOCKET_UPPERCASE_STRING, header->ws_upgrade, strlen(WEBSOCKET_UPPERCASE_STRING)) != 0) ) {
473
4
        WSS_log_trace("Invalid upgrade header value");
474
4
        return HttpStatus_UpgradeRequired;
475
    }
476
477
42
    WSS_log_trace("Validating connection header");
478
479

42
    if ( unlikely(NULL == header->ws_connection || strlen(header->ws_connection) < strlen(UPGRADE_STRING)) ) {
480
4
        WSS_log_trace("Invalid connection header value");
481
4
        return HttpStatus_UpgradeRequired;
482
    }
483
484
38
    sep = trim(strtok_r(header->ws_connection, ",", &sepptr));
485
38
    if ( unlikely(NULL == sep) ) {
486
2
        WSS_log_trace("Invalid connection header value");
487
2
        return HttpStatus_UpgradeRequired;
488
    }
489
490
38
    do {
491
38
        if ( likely(strncasecmp(UPGRADE_STRING, sep, strlen(UPGRADE_STRING)) == 0) ) {
492
            break;
493
        }
494
495
4
        sep = trim(strtok_r(NULL, ",", &sepptr));
496
4
    } while ( likely(NULL != sep) );
497
498
36
    if ( unlikely(sep == NULL) ) {
499
2
        WSS_log_trace("Invalid connection header value");
500
2
        return HttpStatus_UpgradeRequired;
501
    }
502
503
34
    WSS_log_trace("Validating origin");
504
505
    // It is recommended to specify origins in the config file
506
34
    if ( likely(config->origins_length > 0) ) {
507
34
        if ( unlikely(strinarray(header->ws_origin, (const char **)config->origins, config->origins_length) != 0) ) {
508
4
            WSS_log_trace("Origin is not allowed: %s", header->ws_origin);
509
4
            return HttpStatus_Forbidden;
510
        }
511
    }
512
513
30
    WSS_log_trace("Validating websocket version");
514
515
30
    switch (header->ws_type) {
516
        case RFC6455:
517
        case HYBI10:
518
        case HYBI07:
519
12
            break;
520
18
        default:
521
18
        WSS_log_trace("Invalid websocket version");
522
18
        return HttpStatus_NotImplemented;
523
    }
524
525
12
    WSS_log_trace("Validating websocket key");
526
527
12
    if ( unlikely(NULL == header->ws_key) ) {
528
2
        WSS_log_trace("Invalid websocket key");
529
2
        WSS_free((void **) &key);
530
2
        return HttpStatus_UpgradeRequired;
531
    }
532
533
10
    key = b64_decode_ex(header->ws_key, strlen(header->ws_key), &key_length);
534

10
    if ( unlikely(NULL == key || key_length != SEC_WEBSOCKET_KEY_LENGTH) ) {
535
2
        WSS_log_trace("Invalid websocket key");
536
2
        WSS_free((void **) &key);
537
2
        return HttpStatus_UpgradeRequired;
538
    }
539
8
    WSS_free((void **) &key);
540
541
8
    WSS_log_trace("Accepted handshake, switching protocol");
542
543
8
    return HttpStatus_SwitchingProtocols;
544
}
545
546
/**
547
 * Frees a HTTP header structure
548
 *
549
 * @param   header    [wss_header_t *]  "The HTTP header to free"
550
 * @return            [void]
551
 */
552
60
void WSS_free_header(wss_header_t *header) {
553
60
    size_t i;
554
60
    if ( likely(NULL != header) ) {
555
62
        for (i = 0; i < header->ws_extensions_count; i++) {
556
2
            if ( likely(NULL != header->ws_extensions[i]) ) {
557
2
                WSS_free((void **) &header->ws_extensions[i]->accepted);
558
2
                WSS_free((void **) &header->ws_extensions[i]->name);
559
2
                WSS_free((void **) &header->ws_extensions[i]);
560
            }
561
        }
562
60
        WSS_free((void **) &header->ws_extensions);
563
60
        WSS_free((void **) &header->content);
564
    }
565
60
    WSS_free((void **) &header);
566
60
}