iodine 0.2.17 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +36 -3
  4. data/bin/config.ru +23 -2
  5. data/bin/http-hello +1 -1
  6. data/bin/ws-shootout +5 -0
  7. data/ext/iodine/defer.c +468 -0
  8. data/ext/iodine/defer.h +105 -0
  9. data/ext/iodine/evio.c +263 -0
  10. data/ext/iodine/evio.h +133 -0
  11. data/ext/iodine/extconf.rb +2 -1
  12. data/ext/iodine/facil.c +958 -0
  13. data/ext/iodine/facil.h +423 -0
  14. data/ext/iodine/http.c +90 -0
  15. data/ext/iodine/http.h +50 -12
  16. data/ext/iodine/http1.c +200 -267
  17. data/ext/iodine/http1.h +17 -26
  18. data/ext/iodine/http1_request.c +81 -0
  19. data/ext/iodine/http1_request.h +58 -0
  20. data/ext/iodine/http1_response.c +403 -0
  21. data/ext/iodine/http1_response.h +90 -0
  22. data/ext/iodine/http1_simple_parser.c +124 -108
  23. data/ext/iodine/http1_simple_parser.h +8 -3
  24. data/ext/iodine/http_request.c +104 -0
  25. data/ext/iodine/http_request.h +58 -102
  26. data/ext/iodine/http_response.c +212 -208
  27. data/ext/iodine/http_response.h +89 -252
  28. data/ext/iodine/iodine_core.c +57 -46
  29. data/ext/iodine/iodine_core.h +3 -1
  30. data/ext/iodine/iodine_http.c +105 -81
  31. data/ext/iodine/iodine_websocket.c +17 -13
  32. data/ext/iodine/iodine_websocket.h +1 -0
  33. data/ext/iodine/rb-call.c +9 -7
  34. data/ext/iodine/{rb-libasync.h → rb-defer.c} +57 -49
  35. data/ext/iodine/rb-rack-io.c +12 -6
  36. data/ext/iodine/rb-rack-io.h +1 -1
  37. data/ext/iodine/rb-registry.c +5 -2
  38. data/ext/iodine/sock.c +1159 -0
  39. data/ext/iodine/{libsock.h → sock.h} +138 -142
  40. data/ext/iodine/spnlock.inc +77 -0
  41. data/ext/iodine/websockets.c +101 -112
  42. data/ext/iodine/websockets.h +38 -19
  43. data/iodine.gemspec +3 -3
  44. data/lib/iodine/version.rb +1 -1
  45. data/lib/rack/handler/iodine.rb +6 -6
  46. metadata +23 -19
  47. data/ext/iodine/http_response_http1.h +0 -382
  48. data/ext/iodine/libasync.c +0 -570
  49. data/ext/iodine/libasync.h +0 -122
  50. data/ext/iodine/libreact.c +0 -350
  51. data/ext/iodine/libreact.h +0 -244
  52. data/ext/iodine/libserver.c +0 -957
  53. data/ext/iodine/libserver.h +0 -481
  54. data/ext/iodine/libsock.c +0 -1025
  55. data/ext/iodine/spnlock.h +0 -243
@@ -6,12 +6,32 @@ Feel free to copy, use and enjoy according to the license provided.
6
6
  */
7
7
  #ifndef HTTP_H
8
8
  #define HTTP_H
9
+ #define LIB_FACIL_HTTP_VERSION_MAJOR 0
10
+ #define LIB_FACIL_HTTP_VERSION_MINOR 4
11
+ #define LIB_FACIL_HTTP_VERSION_PATCH 0
12
+ #include "facil.h"
13
+
14
+ /** an HTTP/1.1 vs. HTTP/2 identifier. */
15
+ enum HTTP_VERSION { HTTP_V1 = 0, HTTP_V2 = 1 };
16
+
17
+ /** HTTP header information */
18
+ typedef struct {
19
+ const char *name;
20
+ union {
21
+ const char *data;
22
+ const char *value;
23
+ };
24
+ uint32_t name_len;
25
+ union {
26
+ uint32_t data_len;
27
+ uint32_t value_len;
28
+ };
29
+ } http_header_s;
9
30
 
10
31
  /* *****************************************************************************
11
32
  Core include files
12
33
  */
13
34
  // clang-format off
14
- #include "libserver.h"
15
35
  #include <time.h>
16
36
  #include "http_request.h"
17
37
  #include "http_response.h"
@@ -32,7 +52,7 @@ Hard Coded Settings
32
52
  #endif
33
53
 
34
54
  /* *****************************************************************************
35
- HTTP settings / core data structure
55
+ HTTP Core API & data structure
36
56
  */
37
57
 
38
58
  /** Manages protocol settings for the HTTP protocol */
@@ -63,11 +83,39 @@ typedef struct {
63
83
  /** An HTTP connection timeout. For HTTP/1.1 this defaults to ~5 seconds.*/
64
84
  uint8_t timeout;
65
85
  /**
86
+ The default HTTP version which a new connection will use. At the moment, only
87
+ version HTTP/1.1 is supported.
88
+ */
89
+ enum HTTP_VERSION version;
90
+ /**
66
91
  internal flag for library use.
67
92
  */
68
93
  uint8_t private_metaflags;
69
94
  } http_settings_s;
70
95
 
96
+ typedef protocol_s *(*http_on_open_func)(intptr_t, void *);
97
+ typedef void (*http_on_finish_func)(void *);
98
+ /**
99
+ Return the callback used for creating the HTTP protocol in the `settings`.
100
+ */
101
+ http_on_open_func http_get_on_open_func(http_settings_s *settings);
102
+ /**
103
+ Return the callback used for freeing the HTTP protocol in the `settings`.
104
+ */
105
+ http_on_finish_func http_get_on_finish_func(http_settings_s *settings);
106
+
107
+ /**
108
+ Listens for incoming HTTP connections on the specified posrt and address,
109
+ implementing the requested settings.
110
+
111
+ Since facil.io doesn't support native TLS/SLL
112
+ */
113
+ int http_listen(const char *port, const char *address,
114
+ http_settings_s settings);
115
+
116
+ #define http_listen(port, address, ...) \
117
+ http_listen((port), (address), (http_settings_s){__VA_ARGS__})
118
+
71
119
  /* *****************************************************************************
72
120
  HTTP Helper functions that might be used globally
73
121
  */
@@ -122,14 +170,4 @@ ssize_t http_decode_url_unsafe(char *dest, const char *url_data);
122
170
  /** Decodes a URL encoded string. */
123
171
  ssize_t http_decode_url(char *dest, const char *url_data, size_t length);
124
172
 
125
- /* *****************************************************************************
126
- HTTP versions (they depend on the settings / core data structure)
127
- */
128
-
129
- #include "http1.h"
130
-
131
- /* *****************************************************************************
132
- HTTP listening helpers
133
- */
134
-
135
173
  #endif
@@ -4,256 +4,160 @@ license: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
7
+ #include "spnlock.inc"
8
+
9
+ #include "http.h"
7
10
  #include "http1.h"
8
- #include "errno.h"
11
+ #include "http1_request.h"
9
12
  #include "http1_simple_parser.h"
13
+
10
14
  #include <fcntl.h>
11
15
  #include <stdio.h>
12
16
  #include <string.h>
13
- #include <sys/mman.h>
14
17
  #include <sys/stat.h>
15
18
 
19
+ char *HTTP1_Protocol_String = "facil_http/1.1_protocol";
16
20
  /* *****************************************************************************
17
- HTTP/1.1 data structure
21
+ HTTP/1.1 data structures
18
22
  */
19
23
 
20
- typedef struct {
24
+ typedef struct http1_protocol_s {
21
25
  protocol_s protocol;
22
26
  http_settings_s *settings;
23
- char buffer[HTTP1_MAX_HEADER_SIZE];
24
- size_t buffer_pos;
25
27
  void (*on_request)(http_request_s *request);
26
- http_request_s
27
- request; /* MUST be last, as it has memory extensions for the headers*/
28
- } http1_protocol_s; /* ~ 8416 bytes for (8 * 1024) buffer size and 64 headers */
29
-
30
- #define HTTP1_PROTOCOL_SIZE \
31
- (sizeof(http1_protocol_s) + HTTP_REQUEST_SIZE(HTTP1_MAX_HEADER_COUNT))
32
-
33
- char *HTTP1 = "http1";
28
+ struct http1_protocol_s *next;
29
+ http1_request_s request;
30
+ } http1_protocol_s;
34
31
 
35
- /* *****************************************************************************
36
- HTTP/1.1 callbacks
37
- */
38
32
  static void http1_on_data(intptr_t uuid, http1_protocol_s *protocol);
39
33
 
40
34
  /* *****************************************************************************
41
- HTTP/1.1 protocol pooling
35
+ HTTP/1.1 pool
42
36
  */
43
37
 
44
- #define HTTP1_POOL_MEMORY_SIZE (HTTP1_PROTOCOL_SIZE * HTTP1_POOL_SIZE)
45
-
46
- struct {
47
- void *memory;
48
- http1_protocol_s *pool;
38
+ static struct {
49
39
  spn_lock_i lock;
50
- } http1_pool = {.memory = NULL};
40
+ uint8_t init;
41
+ http1_protocol_s *next;
42
+ http1_protocol_s protocol_mem[HTTP1_POOL_SIZE];
43
+ } http1_pool = {.lock = SPN_LOCK_INIT, .init = 0};
51
44
 
52
- inline static http1_protocol_s *pool_pop() {
53
- http1_protocol_s *prot;
45
+ static void http1_free(http1_protocol_s *pr) {
46
+ if ((uintptr_t)pr < (uintptr_t)http1_pool.protocol_mem ||
47
+ (uintptr_t)pr >= (uintptr_t)(http1_pool.protocol_mem + HTTP1_POOL_SIZE))
48
+ goto use_free;
54
49
  spn_lock(&http1_pool.lock);
55
- prot = http1_pool.pool;
56
- if (prot)
57
- http1_pool.pool = prot->request.metadata.next;
50
+ pr->next = http1_pool.next;
51
+ http1_pool.next = pr;
58
52
  spn_unlock(&http1_pool.lock);
59
- return prot;
53
+ return;
54
+ use_free:
55
+ free(pr);
56
+ return;
60
57
  }
61
58
 
62
- inline static void pool_push(http1_protocol_s *protocol) {
63
- if (protocol == NULL)
64
- return;
65
- spn_lock(&http1_pool.lock);
66
- protocol->request.metadata.next = http1_pool.pool;
67
- http1_pool.pool = protocol;
68
- spn_unlock(&http1_pool.lock);
59
+ static inline void http1_set_protocol_data(http1_protocol_s *pr) {
60
+ pr->protocol =
61
+ (protocol_s){.on_data = (void (*)(intptr_t, protocol_s *))http1_on_data,
62
+ .on_close = (void (*)(protocol_s *))http1_free};
63
+ pr->request.request = (http_request_s){.fd = 0, .http_version = HTTP_V1};
64
+ pr->request.header_pos = pr->request.buffer_pos = 0;
69
65
  }
70
66
 
71
- static void http1_cleanup(void) {
72
- // free memory
73
- if (http1_pool.memory) {
74
- munmap(http1_pool.memory, HTTP1_POOL_MEMORY_SIZE);
75
- }
76
- }
77
-
78
- static void http1_init(void) {
79
- static spn_lock_i inner_lock = SPN_LOCK_INIT;
80
- spn_lock(&inner_lock);
81
- if (http1_pool.memory == NULL) {
82
- // Allocate the memory
83
- http1_pool.memory =
84
- mmap(NULL, HTTP1_POOL_MEMORY_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
85
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
86
- if (http1_pool.memory == NULL)
87
- return;
88
- // setup `atexit` cleanup rutine
89
- atexit(http1_cleanup);
90
-
91
- // initialize pool
92
- void *pos = http1_pool.memory;
93
- while (pos <
94
- http1_pool.memory + (HTTP1_POOL_MEMORY_SIZE - HTTP1_PROTOCOL_SIZE)) {
95
- pool_push(pos);
96
- pos += HTTP1_PROTOCOL_SIZE;
97
- }
98
- }
99
- spn_unlock(&inner_lock);
100
- }
101
-
102
- /** initializes the library if it wasn't already initialized. */
103
- #define validate_mem() \
104
- { \
105
- if (http1_pool.memory == NULL) \
106
- http1_init(); \
107
- }
108
-
109
- inline static void http1_free(http1_protocol_s *http) {
110
- http_request_clear(&http->request);
111
- validate_mem();
112
- if (((void *)http) >= http1_pool.memory &&
113
- ((void *)http) <= (http1_pool.memory + HTTP1_POOL_MEMORY_SIZE)) {
114
- pool_push(http);
115
- } else
116
- free(http);
117
- }
118
-
119
- protocol_s *http1_alloc(intptr_t fd, http_settings_s *settings) {
120
- validate_mem();
121
- // HTTP/1.1 should send a busy response
122
- // if there aren't enough available file descriptors.
123
- if (sock_max_capacity() - sock_uuid2fd(fd) <= HTTP_BUSY_UNLESS_HAS_FDS)
124
- goto is_busy;
125
- // get an http object from the pool
126
- http1_protocol_s *http = pool_pop();
127
- // of malloc one
128
- if (http == NULL)
129
- http = malloc(HTTP1_PROTOCOL_SIZE);
130
- // review allocation
131
- if (http == NULL)
132
- return NULL;
133
-
134
- // we shouldn't update the `http` protocol as a struct, as this will waste
135
- // time as the whole buffer will be zeroed out when there is no need.
136
-
137
- // setup parsing state
138
- http->buffer_pos = 0;
139
- // setup protocol callbacks
140
- http->protocol = (protocol_s){
141
- .service = HTTP1,
142
- .on_data = (void (*)(intptr_t, protocol_s *))http1_on_data,
143
- .on_close = (void (*)(protocol_s *))http1_free,
144
- };
145
- // setup request data
146
- http->request = (http_request_s){
147
- .metadata.max_headers = HTTP1_MAX_HEADER_COUNT,
148
- .metadata.fd = fd,
149
- .metadata.owner = http,
150
- };
151
- // update settings
152
- http->settings = settings;
153
- http->on_request = settings->on_request;
154
- // set the timeout
155
- server_set_timeout(fd, settings->timeout);
156
- return (protocol_s *)http;
157
-
158
- is_busy:
159
- if (settings->public_folder && settings->public_folder_length) {
160
- size_t p_len = settings->public_folder_length;
161
- struct stat file_data = {};
162
- char fname[p_len + 8 + 1];
163
- memcpy(fname, settings->public_folder, p_len);
164
- if (settings->public_folder[p_len - 1] == '/' ||
165
- settings->public_folder[p_len - 1] == '\\')
166
- p_len--;
167
- memcpy(fname + p_len, "/503.html", 9);
168
- p_len += 9;
169
- if (stat(fname, &file_data))
170
- goto busy_no_file;
171
- // check that we have a file and not something else
172
- if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode))
173
- goto busy_no_file;
174
- int file = open(fname, O_RDONLY);
175
- if (file == -1)
176
- goto busy_no_file;
177
- sock_packet_s *packet;
178
- packet = sock_checkout_packet();
179
- memcpy(packet->buffer, "HTTP/1.1 503 Service Unavailable\r\n"
180
- "Content-Type: text/html\r\n"
181
- "Connection: close\r\n"
182
- "Content-Length: ",
183
- 94);
184
- p_len = 94 + http_ul2a(packet->buffer + 94, file_data.st_size);
185
- memcpy(packet->buffer + p_len, "\r\n\r\n", 4);
186
- p_len += 4;
187
- if ((off_t)(BUFFER_PACKET_SIZE - p_len) > file_data.st_size) {
188
- if (read(file, packet->buffer + p_len, file_data.st_size) < 0) {
189
- close(file);
190
- sock_free_packet(packet);
191
- goto busy_no_file;
192
- }
193
- close(file);
194
- packet->length = p_len + file_data.st_size;
195
- sock_send_packet(fd, packet);
196
- } else {
197
- packet->length = p_len;
198
- sock_send_packet(fd, packet);
199
- sock_sendfile(fd, file, 0, file_data.st_size);
200
- }
201
- return NULL;
67
+ static http1_protocol_s *http1_alloc(void) {
68
+ http1_protocol_s *pr;
69
+ spn_lock(&http1_pool.lock);
70
+ if (!http1_pool.next)
71
+ goto use_malloc;
72
+ pr = http1_pool.next;
73
+ http1_pool.next = pr->next;
74
+ spn_unlock(&http1_pool.lock);
75
+ http1_request_clear(&pr->request.request);
76
+ return pr;
77
+ use_malloc:
78
+ if (http1_pool.init == 0)
79
+ goto initialize;
80
+ spn_unlock(&http1_pool.lock);
81
+ // fprintf(stderr, "using malloc\n");
82
+ pr = malloc(sizeof(*pr));
83
+ http1_set_protocol_data(pr);
84
+ return pr;
85
+ initialize:
86
+ http1_pool.init = 1;
87
+ for (size_t i = 1; i < (HTTP1_POOL_SIZE - 1); i++) {
88
+ http1_set_protocol_data(http1_pool.protocol_mem + i);
89
+ http1_pool.protocol_mem[i].next = http1_pool.protocol_mem + (i + 1);
202
90
  }
203
- busy_no_file:
204
- sock_write(fd, "HTTP/1.1 503 Service Unavailable\r\nContent-Length: "
205
- "13\r\n\r\nServer Busy.",
206
- 68);
207
- return NULL;
91
+ http1_pool.protocol_mem[HTTP1_POOL_SIZE - 1].next = NULL;
92
+ http1_pool.next = http1_pool.protocol_mem + 1;
93
+ spn_unlock(&http1_pool.lock);
94
+ http1_set_protocol_data(http1_pool.protocol_mem);
95
+ return http1_pool.protocol_mem;
208
96
  }
209
97
 
210
98
  /* *****************************************************************************
211
- HTTP/1.1 protocol bare-bones implementation
99
+ HTTP callbacks
212
100
  */
213
-
214
101
  #define HTTP_BODY_CHUNK_SIZE 3072 // 4096
215
102
 
103
+ static void http1_on_header_found(http_request_s *request,
104
+ http_header_s *header) {
105
+ ((http1_request_s *)request)
106
+ ->headers[((http1_request_s *)request)->header_pos] = *header;
107
+ ((http1_request_s *)request)->header_pos += 1;
108
+ }
109
+
216
110
  /* parse and call callback */
217
111
  static void http1_on_data(intptr_t uuid, http1_protocol_s *protocol) {
218
112
  ssize_t len = 0;
219
113
  ssize_t result;
220
114
  char buff[HTTP_BODY_CHUNK_SIZE];
221
115
  char *buffer;
222
- http_request_s *request = &protocol->request;
116
+ http1_request_s *request = &protocol->request;
223
117
  for (;;) {
224
118
  // handle requests with no file data
225
- if (request->body_file <= 0) {
119
+ if (request->request.body_file <= 0) {
226
120
  // request headers parsing
227
121
  if (len == 0) {
228
- buffer = protocol->buffer;
122
+ buffer = request->buffer;
229
123
  // make sure headers don't overflow
230
- len = sock_read(uuid, buffer + protocol->buffer_pos,
231
- HTTP1_MAX_HEADER_SIZE - protocol->buffer_pos);
124
+ len = sock_read(uuid, buffer + request->buffer_pos,
125
+ HTTP1_MAX_HEADER_SIZE - request->buffer_pos);
232
126
  // update buffer read position.
233
- protocol->buffer_pos += len;
127
+ request->buffer_pos += len;
128
+ // if (len > 0) {
129
+ // fprintf(stderr, "\n----\nRead from socket, %lu bytes, total
130
+ // %lu:\n",
131
+ // len, request->buffer_pos);
132
+ // for (size_t i = 0; i < request->buffer_pos; i++) {
133
+ // fprintf(stderr, "%c", buffer[i] ? buffer[i] : '-');
134
+ // }
135
+ // fprintf(stderr, "\n");
136
+ // }
234
137
  }
235
138
  if (len <= 0) {
236
139
  return;
237
140
  }
141
+
238
142
  // parse headers
239
143
  result =
240
- http1_parse_request_headers(buffer, protocol->buffer_pos, request);
144
+ http1_parse_request_headers(buffer, request->buffer_pos,
145
+ &request->request, http1_on_header_found);
241
146
  // review result
242
147
  if (result >= 0) { // headers comeplete
243
- // mark buffer position, for HTTP pipelining
244
- protocol->buffer_pos = result;
245
148
  // are we done?
246
- if (request->content_length == 0 || request->body_str) {
149
+ if (request->request.content_length == 0 || request->request.body_str) {
247
150
  goto handle_request;
248
151
  }
249
- if (request->content_length > protocol->settings->max_body_size) {
152
+ if (request->request.content_length >
153
+ protocol->settings->max_body_size) {
250
154
  goto body_to_big;
251
155
  }
252
156
  // initialize or submit body data
253
- result =
254
- http1_parse_request_body(buffer + result, len - result, request);
157
+ result = http1_parse_request_body(buffer + result, len - result,
158
+ (http_request_s *)request);
255
159
  if (result >= 0) {
256
- protocol->buffer_pos += result;
160
+ request->buffer_pos += result;
257
161
  goto handle_request;
258
162
  } else if (result == -1) // parser error
259
163
  goto parser_error;
@@ -264,17 +168,16 @@ static void http1_on_data(intptr_t uuid, http1_protocol_s *protocol) {
264
168
  len = 0;
265
169
  continue;
266
170
  }
267
- if (request->body_file > 0) {
171
+ if (request->request.body_file > 0) {
172
+ // fprintf(stderr, "Body File\n");
268
173
  parse_body:
269
174
  buffer = buff;
270
175
  // request body parsing
271
176
  len = sock_read(uuid, buffer, HTTP_BODY_CHUNK_SIZE);
272
177
  if (len <= 0)
273
178
  return;
274
- result = http1_parse_request_body(buffer, len, request);
179
+ result = http1_parse_request_body(buffer, len, &request->request);
275
180
  if (result >= 0) {
276
- // set buffer pos for piplining support
277
- protocol->buffer_pos = result;
278
181
  goto handle_request;
279
182
  } else if (result == -1) // parser error
280
183
  goto parser_error;
@@ -285,121 +188,151 @@ static void http1_on_data(intptr_t uuid, http1_protocol_s *protocol) {
285
188
  continue;
286
189
  handle_request:
287
190
  // review required headers / data
288
- if (request->host == NULL)
191
+ if (request->request.host == NULL)
289
192
  goto bad_request;
290
193
  http_settings_s *settings = protocol->settings;
291
194
  // call request callback
292
195
  if (protocol && settings &&
293
- (settings->public_folder == NULL ||
294
- http_response_sendfile2(NULL, request, settings->public_folder,
295
- settings->public_folder_length, request->path,
296
- request->path_len, settings->log_static))) {
297
- protocol->on_request(request);
196
+ (request->request.upgrade || settings->public_folder == NULL ||
197
+ http_response_sendfile2(
198
+ NULL, &request->request, settings->public_folder,
199
+ settings->public_folder_length, request->request.path,
200
+ request->request.path_len, settings->log_static))) {
201
+ protocol->on_request(&request->request);
202
+ // fprintf(stderr, "Called on_request\n");
298
203
  }
299
- // clear request state
300
- http_request_clear(request);
301
204
  // rotate buffer for HTTP pipelining
302
- if (result >= len) {
205
+ if ((ssize_t)request->buffer_pos <= result) {
303
206
  len = 0;
207
+ // fprintf(stderr, "\n----\nAll data consumed.\n");
304
208
  } else {
305
- memmove(protocol->buffer, buffer + protocol->buffer_pos, len - result);
306
- len -= result;
209
+ memmove(request->buffer, buffer + result, request->buffer_pos - result);
210
+ len = request->buffer_pos - result;
211
+ // fprintf(stderr, "\n----\ndata after move, %lu long:\n%.*s\n", len,
212
+ // (int)len, request->buffer);
307
213
  }
308
- // restart buffer position
309
- protocol->buffer_pos = 0;
310
- buffer = protocol->buffer;
214
+ // fprintf(stderr, "data in buffer, %lu long:\n%.*s\n", len, (int)len,
215
+ // request->buffer);
216
+ // clear request state
217
+ http1_request_clear(&request->request);
218
+ request->buffer_pos = len;
219
+ // make sure to use the correct buffer.
220
+ buffer = request->buffer;
311
221
  }
312
222
  // no routes lead here.
313
223
  fprintf(stderr,
314
224
  "I am lost on a deserted island, no code can reach me here :-)\n");
315
225
  return; // How did we get here?
316
226
  parser_error:
317
- if (request->headers_count == request->metadata.max_headers)
227
+ if (request->request.headers_count >= HTTP1_MAX_HEADER_COUNT)
318
228
  goto too_big;
319
229
  bad_request:
320
230
  /* handle generally bad requests */
321
231
  {
322
- http_response_s response = http_response_init(request);
323
- response.status = 400;
324
- http_response_write_body(&response, "Bad Request", 11);
325
- http_response_finish(&response);
232
+ http_response_s *response = http_response_create(&request->request);
233
+ response->status = 400;
234
+ http_response_write_body(response, "Bad Request", 11);
235
+ http_response_finish(response);
326
236
  sock_close(uuid);
327
- protocol->buffer_pos = 0;
237
+ request->buffer_pos = 0;
328
238
  return;
329
239
  }
330
240
  too_big:
331
241
  /* handle oversized headers */
332
242
  {
333
- http_response_s response = http_response_init(request);
334
- response.status = 431;
335
- http_response_write_body(&response, "Request Header Fields Too Large", 31);
336
- http_response_finish(&response);
243
+ http_response_s *response = http_response_create(&request->request);
244
+ response->status = 431;
245
+ http_response_write_body(response, "Request Header Fields Too Large", 31);
246
+ http_response_finish(response);
337
247
  sock_close(uuid);
338
- protocol->buffer_pos = 0;
248
+ request->buffer_pos = 0;
339
249
  return;
340
250
  body_to_big:
341
251
  /* handle oversized body */
342
252
  {
343
- http_response_s response = http_response_init(request);
344
- response.status = 413;
345
- http_response_write_body(&response, "Payload Too Large", 17);
346
- http_response_finish(&response);
253
+ http_response_s *response = http_response_create(&request->request);
254
+ response->status = 413;
255
+ http_response_write_body(response, "Payload Too Large", 17);
256
+ http_response_finish(response);
347
257
  sock_close(uuid);
348
- protocol->buffer_pos = 0;
258
+ request->buffer_pos = 0;
349
259
  return;
350
260
  }
351
261
  }
352
262
  }
353
263
 
354
264
  /* *****************************************************************************
355
- HTTP/1.1 listening API implementation
265
+ HTTP listening helpers
356
266
  */
357
267
 
358
- #undef http1_listen
268
+ /**
269
+ Allocates memory for an upgradable HTTP/1.1 protocol.
359
270
 
360
- static void http1_on_init(http_settings_s *settings) {
361
- if (settings->timeout == 0)
362
- settings->timeout = 5;
363
- if (settings->max_body_size == 0)
364
- settings->max_body_size = HTTP_DEFAULT_BODY_LIMIT;
365
- if (settings->public_folder) {
366
- settings->public_folder_length = strlen(settings->public_folder);
367
- if (settings->public_folder[0] == '~' &&
368
- settings->public_folder[1] == '/' && getenv("HOME")) {
369
- char *home = getenv("HOME");
370
- size_t home_len = strlen(home);
371
- char *tmp = malloc(settings->public_folder_length + home_len + 1);
372
- memcpy(tmp, home, home_len);
373
- if (home[home_len - 1] == '/')
374
- --home_len;
375
- memcpy(tmp + home_len, settings->public_folder + 1,
376
- settings->public_folder_length); // copy also the NULL
377
- settings->public_folder = tmp;
378
- settings->private_metaflags |= 1;
379
- settings->public_folder_length = strlen(settings->public_folder);
271
+ The protocol self destructs when the `on_close` callback is called.
272
+ */
273
+ protocol_s *http1_on_open(intptr_t fd, http_settings_s *settings) {
274
+ if (sock_uuid2fd(fd) >= (sock_max_capacity() - HTTP_BUSY_UNLESS_HAS_FDS))
275
+ goto is_busy;
276
+ http1_protocol_s *pr = http1_alloc();
277
+ pr->request.request.fd = fd;
278
+ pr->settings = settings;
279
+ pr->on_request = settings->on_request;
280
+ facil_set_timeout(fd, pr->settings->timeout);
281
+ return (protocol_s *)pr;
282
+
283
+ is_busy:
284
+ if (settings->public_folder && settings->public_folder_length) {
285
+ size_t p_len = settings->public_folder_length;
286
+ struct stat file_data = {.st_mode = 0};
287
+ char fname[p_len + 8 + 1];
288
+ memcpy(fname, settings->public_folder, p_len);
289
+ if (settings->public_folder[p_len - 1] == '/' ||
290
+ settings->public_folder[p_len - 1] == '\\')
291
+ p_len--;
292
+ memcpy(fname + p_len, "/503.html", 9);
293
+ p_len += 9;
294
+ if (stat(fname, &file_data))
295
+ goto busy_no_file;
296
+ // check that we have a file and not something else
297
+ if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode))
298
+ goto busy_no_file;
299
+ int file = open(fname, O_RDONLY);
300
+ if (file == -1)
301
+ goto busy_no_file;
302
+ sock_buffer_s *buffer;
303
+ buffer = sock_buffer_checkout();
304
+ memcpy(buffer->buf,
305
+ "HTTP/1.1 503 Service Unavailable\r\n"
306
+ "Content-Type: text/html\r\n"
307
+ "Connection: close\r\n"
308
+ "Content-Length: ",
309
+ 94);
310
+ p_len = 94 + http_ul2a((char *)buffer->buf + 94, file_data.st_size);
311
+ memcpy(buffer->buf + p_len, "\r\n\r\n", 4);
312
+ p_len += 4;
313
+ if ((off_t)(BUFFER_PACKET_SIZE - p_len) > file_data.st_size) {
314
+ if (read(file, buffer->buf + p_len, file_data.st_size) < 0) {
315
+ close(file);
316
+ sock_buffer_free(buffer);
317
+ goto busy_no_file;
318
+ }
319
+ close(file);
320
+ buffer->len = p_len + file_data.st_size;
321
+ sock_buffer_send(fd, buffer);
322
+ } else {
323
+ buffer->len = p_len;
324
+ sock_buffer_send(fd, buffer);
325
+ sock_sendfile(fd, file, 0, file_data.st_size);
326
+ sock_close(fd);
380
327
  }
328
+ return NULL;
381
329
  }
382
- }
383
- static void http1_on_finish(http_settings_s *settings) {
384
- if (settings->private_metaflags & 1)
385
- free((void *)settings->public_folder);
386
- if (settings->private_metaflags & 2)
387
- free(settings);
388
- }
389
330
 
390
- int http1_listen(const char *port, const char *address,
391
- http_settings_s settings) {
392
- if (settings.on_request == NULL) {
393
- fprintf(
394
- stderr,
395
- "ERROR: http1_listen requires the .on_request parameter to be set\n");
396
- exit(11);
397
- }
398
- http_settings_s *settings_copy = malloc(sizeof(*settings_copy));
399
- *settings_copy = settings;
400
- settings_copy->private_metaflags = 2;
401
- return server_listen(.port = port, .address = address,
402
- .on_start = (void *)http1_on_init,
403
- .on_finish = (void *)http1_on_finish,
404
- .on_open = (void *)http1_alloc, .udata = settings_copy);
331
+ busy_no_file:
332
+ sock_write(fd,
333
+ "HTTP/1.1 503 Service Unavailable\r\nContent-Length: "
334
+ "13\r\n\r\nServer Busy.",
335
+ 68);
336
+ sock_close(fd);
337
+ return NULL;
405
338
  }