iodine 0.2.17 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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
@@ -4,17 +4,20 @@ license: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
7
- #ifndef HTTP1_H
8
- #define HTTP1_H
9
-
7
+ #ifndef H_HTTP1_H
8
+ #define H_HTTP1_H
10
9
  #include "http.h"
11
10
 
12
11
  #ifndef HTTP1_MAX_HEADER_SIZE
13
- /** Sets the maximum allowed size of a requests header section
12
+ /**
13
+ Sets the maximum allowed size of a requests header section
14
14
  (all cookies, headers and other data that isn't the request's "body").
15
15
 
16
- Defaults to ~8Kb headers per request */
17
- #define HTTP1_MAX_HEADER_SIZE (8 * 1024) /* ~ 8kb */
16
+ This value includes the request line itself.
17
+
18
+ Defaults to ~16Kb headers per request.
19
+ */
20
+ #define HTTP1_MAX_HEADER_SIZE (16 * 1024) /* ~16kb */
18
21
  #endif
19
22
 
20
23
  #ifndef HTTP1_MAX_HEADER_COUNT
@@ -32,30 +35,18 @@ Any concurrent HTTP1 connections over this amount will be dynamically allocated.
32
35
  #define HTTP1_POOL_SIZE (64) /* should be ~0.5Mb with default values*/
33
36
  #endif
34
37
 
35
- extern char *HTTP1;
38
+ /** The HTTP/1.1 protocol identifier. */
39
+ extern char *HTTP1_Protocol_String;
36
40
 
37
- /**
38
- Allocates memory for an HTTP/1.1 protocol.
39
-
40
- The protocol self destructs when the `on_close` callback is called.
41
-
42
- To free the protocol manually, it's possible to call it's `on_close` callback.
41
+ /* *****************************************************************************
42
+ HTTP listening helpers
43
43
  */
44
- protocol_s *http1_alloc(intptr_t fd, http_settings_s *settings);
45
44
 
46
45
  /**
47
- Listens for incoming HTTP/1.1 connections on the specified posrt and address,
48
- implementing the requested settings.
49
- */
50
- int http1_listen(const char *port, const char *address,
51
- http_settings_s settings);
52
-
53
- #define http1_listen(port, address, ...) \
54
- http1_listen((port), (address), (http_settings_s){__VA_ARGS__})
46
+ Allocates memory for an upgradable HTTP/1.1 protocol.
55
47
 
56
- #endif
48
+ The protocol self destructs when the `on_close` callback is called.
49
+ */
50
+ protocol_s *http1_on_open(intptr_t fd, http_settings_s *settings);
57
51
 
58
- /** Should be defined by `http.h` ... if it was removed, use a lower value. */
59
- #ifndef HTTP_BUSY_UNLESS_HAS_FDS
60
- #define HTTP_BUSY_UNLESS_HAS_FDS 8
61
52
  #endif
@@ -0,0 +1,81 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #include "http1_request.h"
8
+ #include "http1.h"
9
+
10
+ #include <string.h>
11
+ #include <strings.h>
12
+
13
+ /* *****************************************************************************
14
+ Initialization
15
+ ***************************************************************************** */
16
+
17
+ /** Creates / allocates a protocol version's request object. */
18
+ http_request_s *http1_request_create(void) {
19
+ http1_request_s *req = malloc(sizeof(*req));
20
+ req->request.body_file = 0;
21
+ http1_request_clear((http_request_s *)req);
22
+ return (http_request_s *)req;
23
+ }
24
+ /** Destroys the request object. */
25
+ void http1_request_destroy(http_request_s *request) {
26
+ http1_request_clear(request);
27
+ free(request);
28
+ }
29
+ /** Recycle's the request object, clearing it's data. */
30
+ void http1_request_clear(http_request_s *request) {
31
+ if (request->body_file)
32
+ close(request->body_file);
33
+ *request = (http_request_s){.http_version = HTTP_V1, .fd = request->fd};
34
+ ((http1_request_s *)request)->buffer_pos = 0;
35
+ ((http1_request_s *)request)->header_pos = 0;
36
+ }
37
+ /** Duplicates a request object. */
38
+ http_request_s *http1_request_dup(http_request_s *request) {
39
+ http1_request_s *req = (http1_request_s *)http1_request_create();
40
+ *req = *((http1_request_s *)request);
41
+ return (http_request_s *)req;
42
+ }
43
+
44
+ /* *****************************************************************************
45
+ Header Access
46
+ ***************************************************************************** */
47
+
48
+ /** searches for a header in the request's data store, returning a `header_s`
49
+ * structure with all it's data.*/
50
+ http_header_s http1_request_header_find(http_request_s *request,
51
+ const char *header, size_t header_len) {
52
+ for (size_t i = 0; i < request->headers_count; i++) {
53
+ if (((http1_request_s *)request)->headers[i].name_len == header_len &&
54
+ strncasecmp((char *)header,
55
+ (char *)((http1_request_s *)request)->headers[i].name,
56
+ header_len) == 0)
57
+ return ((http1_request_s *)request)->headers[i];
58
+ }
59
+ return (http_header_s){.name = NULL};
60
+ }
61
+ /** Starts itterating the header list, returning the first header. Header
62
+ * itteration is NOT thread-safe. */
63
+ http_header_s http1_request_header_first(http_request_s *request) {
64
+ ((http1_request_s *)request)->header_pos = 0;
65
+ if (!request->headers_count)
66
+ return (http_header_s){.name = NULL};
67
+ return ((http1_request_s *)request)->headers[0];
68
+ }
69
+ /**
70
+ * Continues itterating the header list.
71
+ *
72
+ * Returns NULL header data if at end of list (header.name == NULL);
73
+ *
74
+ * Header itteration is NOT thread-safe. */
75
+ http_header_s http1_request_header_next(http_request_s *request) {
76
+ ((http1_request_s *)request)->header_pos++;
77
+ if (((http1_request_s *)request)->header_pos >= request->headers_count)
78
+ return (http_header_s){.name = NULL};
79
+ return ((http1_request_s *)request)
80
+ ->headers[((http1_request_s *)request)->header_pos];
81
+ }
@@ -0,0 +1,58 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_HTTP1_REQUEST_H
8
+ #define H_HTTP1_REQUEST_H
9
+ #include "http1.h"
10
+ #include "http_request.h"
11
+
12
+ /* *****************************************************************************
13
+ Data Structure
14
+ ***************************************************************************** */
15
+ typedef struct {
16
+ http_request_s request;
17
+ size_t buffer_pos;
18
+ size_t header_pos;
19
+ http_header_s headers[HTTP1_MAX_HEADER_COUNT];
20
+ char buffer[HTTP1_MAX_HEADER_SIZE];
21
+ } http1_request_s;
22
+
23
+ /* *****************************************************************************
24
+ Initialization
25
+ ***************************************************************************** */
26
+
27
+ /** Creates / allocates a protocol version's request object. */
28
+ http_request_s *http1_request_create(void);
29
+ /** Destroys the request object. */
30
+ void http1_request_destroy(http_request_s *);
31
+ /** Recycle's the request object, clearing it's data. */
32
+ void http1_request_clear(http_request_s *request);
33
+ /** Duplicates a request object. */
34
+ http_request_s *http1_request_dup(http_request_s *);
35
+
36
+ /* *****************************************************************************
37
+ Header Access
38
+ ***************************************************************************** */
39
+
40
+ /** searches for a header in the request's data store, returning a `header_s`
41
+ * structure with all it's data.
42
+ *
43
+ * This doesn't effect header iteration.
44
+ */
45
+ http_header_s http1_request_header_find(http_request_s *request,
46
+ const char *header, size_t header_len);
47
+ /** Starts iterating the header list, returning the first header. Header
48
+ * iteration is NOT thread-safe. */
49
+ http_header_s http1_request_header_first(http_request_s *request);
50
+ /**
51
+ * Continues iterating the header list.
52
+ *
53
+ * Returns NULL header data if at end of list (header.name == NULL);
54
+ *
55
+ * Header itteration is NOT thread-safe. */
56
+ http_header_s http1_request_header_next(http_request_s *request);
57
+
58
+ #endif /* H_HTTP_REQUEST_H */
@@ -0,0 +1,403 @@
1
+ #include "spnlock.inc"
2
+
3
+ #include "http1_response.h"
4
+
5
+ #include <string.h>
6
+ #include <strings.h>
7
+ /**
8
+ The padding for the status line (62 + 2 for the extra \r\n after the headers).
9
+ */
10
+ #define H1P_HEADER_START 80
11
+ #define H1P_OVERFLOW_PADDING 128
12
+
13
+ /* *****************************************************************************
14
+ Response object & Initialization
15
+ ***************************************************************************** */
16
+
17
+ typedef struct {
18
+ http_response_s response;
19
+ size_t buffer_start;
20
+ size_t buffer_end;
21
+ spn_lock_i lock;
22
+ uint8_t use_count;
23
+ uint8_t dest_count;
24
+ char buffer[HTTP1_MAX_HEADER_SIZE];
25
+ } http1_response_s;
26
+
27
+ static struct {
28
+ spn_lock_i lock;
29
+ uint8_t init;
30
+ http1_response_s *next;
31
+ http1_response_s pool_mem[HTTP1_POOL_SIZE];
32
+ } http1_response_pool = {.lock = SPN_LOCK_INIT, .init = 0};
33
+
34
+ static inline void http1_response_clear(http1_response_s *rs,
35
+ http_request_s *request) {
36
+ rs->response = (http_response_s){
37
+ .http_version = HTTP_V1,
38
+ .request = request,
39
+ .fd = request->fd,
40
+ .status = 200,
41
+ .should_close = (request->connection && request->connection_len == 5)};
42
+ rs->buffer_end = rs->buffer_start = H1P_HEADER_START;
43
+ rs->use_count = 1;
44
+ rs->lock = SPN_LOCK_INIT;
45
+ }
46
+ /** Creates / allocates a protocol version's response object. */
47
+ http_response_s *http1_response_create(http_request_s *request) {
48
+ http1_response_s *rs;
49
+ spn_lock(&http1_response_pool.lock);
50
+ if (!http1_response_pool.next)
51
+ goto use_malloc;
52
+ rs = http1_response_pool.next;
53
+ http1_response_pool.next = (void *)rs->response.request;
54
+ spn_unlock(&http1_response_pool.lock);
55
+ http1_response_clear(rs, request);
56
+ return (http_response_s *)rs;
57
+ use_malloc:
58
+ if (http1_response_pool.init == 0)
59
+ goto initialize;
60
+ spn_unlock(&http1_response_pool.lock);
61
+ rs = malloc(sizeof(*rs));
62
+ http1_response_clear(rs, request);
63
+ return (http_response_s *)rs;
64
+ initialize:
65
+ http1_response_pool.init = 1;
66
+ for (size_t i = 1; i < (HTTP1_POOL_SIZE - 1); i++) {
67
+ http1_response_pool.pool_mem[i].response.request =
68
+ (void *)(http1_response_pool.pool_mem + (i + 1));
69
+ }
70
+ http1_response_pool.pool_mem[HTTP1_POOL_SIZE - 1].response.request = NULL;
71
+ http1_response_pool.next = http1_response_pool.pool_mem + 1;
72
+ spn_unlock(&http1_response_pool.lock);
73
+ http1_response_clear(http1_response_pool.pool_mem, request);
74
+ return (http_response_s *)http1_response_pool.pool_mem;
75
+ }
76
+
77
+ static void http1_response_deffered_destroy(void *rs_, void *ignr) {
78
+ (void)(ignr);
79
+ http1_response_s *rs = rs_;
80
+ if (spn_trylock(&rs->lock)) {
81
+ defer(http1_response_deffered_destroy, rs, NULL);
82
+ return;
83
+ }
84
+ rs->use_count -= 1;
85
+ if (rs->use_count) {
86
+ spn_unlock(&rs->lock);
87
+ return;
88
+ }
89
+
90
+ if (rs->response.request_dupped)
91
+ http_request_destroy(rs->response.request);
92
+
93
+ if ((uintptr_t)rs < (uintptr_t)http1_response_pool.pool_mem ||
94
+ (uintptr_t)rs >
95
+ (uintptr_t)(http1_response_pool.pool_mem + (HTTP1_POOL_SIZE - 1)))
96
+ goto use_free;
97
+ spn_lock(&http1_response_pool.lock);
98
+ rs->response.request = (void *)http1_response_pool.next;
99
+ http1_response_pool.next = (void *)rs;
100
+ spn_unlock(&http1_response_pool.lock);
101
+ return;
102
+ use_free:
103
+ free(rs);
104
+ return;
105
+ }
106
+
107
+ /** Destroys the response object. No data is sent.*/
108
+ void http1_response_destroy(http_response_s *rs) {
109
+ ((http1_response_s *)rs)->dest_count++;
110
+ http1_response_deffered_destroy(rs, NULL);
111
+ }
112
+
113
+ /* *****************************************************************************
114
+ Writing and Finishing Helpers + `http1_response_finish`
115
+ ***************************************************************************** */
116
+
117
+ static int h1p_protected_copy(http1_response_s *rs, void *buff, size_t len) {
118
+ if (len + rs->buffer_end >= HTTP1_MAX_HEADER_SIZE - H1P_OVERFLOW_PADDING)
119
+ return -1;
120
+ memcpy(rs->buffer + rs->buffer_end, buff, len);
121
+ rs->buffer_end += len;
122
+ return 0;
123
+ }
124
+
125
+ static void http1_response_finalize_headers(http1_response_s *rs) {
126
+ if (rs->response.headers_sent)
127
+ return;
128
+ rs->response.headers_sent = 1;
129
+ const char *status = http_response_status_str(rs->response.status);
130
+ if (!status) {
131
+ rs->response.status = 500;
132
+ status = http_response_status_str(rs->response.status);
133
+ }
134
+
135
+ /* write the content length header, unless forced not to (<0) */
136
+ if (rs->response.content_length_written == 0 &&
137
+ !(rs->response.content_length < 0) && rs->response.status >= 200 &&
138
+ rs->response.status != 204 && rs->response.status != 304) {
139
+ h1p_protected_copy(rs, "Content-Length:", 15);
140
+ rs->buffer_end +=
141
+ http_ul2a(rs->buffer + rs->buffer_end, rs->response.content_length);
142
+ /* write the header seperator (`\r\n`) */
143
+ rs->buffer[rs->buffer_end++] = '\r';
144
+ rs->buffer[rs->buffer_end++] = '\n';
145
+ }
146
+ /* write the date, if missing */
147
+ if (!rs->response.date_written) {
148
+ if (rs->response.date < rs->response.last_modified)
149
+ rs->response.date = rs->response.last_modified;
150
+ struct tm t;
151
+ /* date header */
152
+ http_gmtime(&rs->response.date, &t);
153
+ h1p_protected_copy(rs, "Date:", 5);
154
+ rs->buffer_end += http_date2str(rs->buffer + rs->buffer_end, &t);
155
+ rs->buffer[rs->buffer_end++] = '\r';
156
+ rs->buffer[rs->buffer_end++] = '\n';
157
+ /* last-modified header */
158
+ http_gmtime(&rs->response.last_modified, &t);
159
+ h1p_protected_copy(rs, "Last-Modified:", 14);
160
+ rs->buffer_end += http_date2str(rs->buffer + rs->buffer_end, &t);
161
+ rs->buffer[rs->buffer_end++] = '\r';
162
+ rs->buffer[rs->buffer_end++] = '\n';
163
+ }
164
+ /* write the keep-alive (connection) header, if missing */
165
+ if (!rs->response.connection_written) {
166
+ if (rs->response.should_close) {
167
+ h1p_protected_copy(rs, "Connection:close\r\n", 18);
168
+ } else {
169
+ h1p_protected_copy(rs,
170
+ "Connection:keep-alive\r\n"
171
+ "Keep-Alive:timeout=2\r\n",
172
+ 45);
173
+ }
174
+ }
175
+ /* write the headers completion marker (empty line - `\r\n`) */
176
+ rs->buffer[rs->buffer_end++] = '\r';
177
+ rs->buffer[rs->buffer_end++] = '\n';
178
+
179
+ /* write the status string is "HTTP/1.1 xxx <...>\r\n" length == 15 +
180
+ * strlen(status) */
181
+
182
+ size_t tmp = strlen(status);
183
+ rs->buffer_start = H1P_HEADER_START - (15 + tmp);
184
+ memcpy(rs->buffer + rs->buffer_start, "HTTP/1.1 ### ", 13);
185
+ memcpy(rs->buffer + rs->buffer_start + 13, status, tmp);
186
+ rs->buffer[H1P_HEADER_START - 1] = '\n';
187
+ rs->buffer[H1P_HEADER_START - 2] = '\r';
188
+ tmp = rs->response.status / 10;
189
+ *(rs->buffer + rs->buffer_start + 9) = '0' + (tmp / 10);
190
+ *(rs->buffer + rs->buffer_start + 11) =
191
+ '0' + (rs->response.status - (10 * tmp));
192
+ *(rs->buffer + rs->buffer_start + 10) = '0' + (tmp - (10 * (tmp / 10)));
193
+ }
194
+
195
+ int http1_response_send_headers(http1_response_s *rs) {
196
+ if (!rs->buffer_end)
197
+ return 0;
198
+ http1_response_finalize_headers(rs);
199
+ spn_lock(&rs->lock);
200
+ rs->use_count++;
201
+ spn_unlock(&rs->lock);
202
+ if (sock_write2(.uuid = rs->response.fd, .buffer = rs,
203
+ .offset = ((uintptr_t)(rs->buffer + rs->buffer_start) -
204
+ (uintptr_t)rs),
205
+ .length = rs->buffer_end - rs->buffer_start, .move = 1,
206
+ .dealloc = (void (*)(void *))http1_response_destroy) < 0)
207
+ return -1;
208
+ rs->buffer_end = 0;
209
+ return 0;
210
+ }
211
+
212
+ /** Sends the data and destroys the response object.*/
213
+ void http1_response_finish(http_response_s *rs) {
214
+ if (!rs->headers_sent)
215
+ http1_response_send_headers((http1_response_s *)rs);
216
+ if (rs->should_close)
217
+ sock_close(rs->fd);
218
+ http1_response_deffered_destroy(rs, NULL);
219
+ }
220
+
221
+ /* *****************************************************************************
222
+ Writing data to the response object
223
+ ***************************************************************************** */
224
+
225
+ /**
226
+ Writes a header to the response. This function writes only the requested
227
+ number of bytes from the header name and the requested number of bytes from
228
+ the header value. It can be used even when the header name and value don't
229
+ contain NULL terminating bytes by passing the `.name_len` or `.value_len` data
230
+ in the `http_headers_s` structure.
231
+
232
+ If the header buffer is full or the headers were already sent (new headers
233
+ cannot be sent), the function will return -1.
234
+
235
+ On success, the function returns 0.
236
+ */
237
+ int http1_response_write_header_fn(http_response_s *rs_, http_header_s header) {
238
+ http1_response_s *rs = (http1_response_s *)rs_;
239
+ if (rs->buffer_end + header.name_len + header.value_len >=
240
+ HTTP1_MAX_HEADER_SIZE - H1P_OVERFLOW_PADDING - 5)
241
+ return -1;
242
+ size_t org_pos = rs->buffer_end;
243
+ if (h1p_protected_copy(rs, (void *)header.name, header.name_len))
244
+ goto error;
245
+ rs->buffer[rs->buffer_end++] = ':';
246
+ if (h1p_protected_copy(rs, (void *)header.value, header.value_len))
247
+ goto error;
248
+ rs->buffer[rs->buffer_end++] = '\r';
249
+ rs->buffer[rs->buffer_end++] = '\n';
250
+ return 0;
251
+ error:
252
+ rs->buffer_end = org_pos;
253
+ return -1;
254
+ }
255
+
256
+ /**
257
+ Set / Delete a cookie using this helper function.
258
+
259
+ This function writes a cookie header to the response. Only the requested
260
+ number of bytes from the cookie value and name are written (if none are
261
+ provided, a terminating NULL byte is assumed).
262
+
263
+ Both the name and the value of the cookie are checked for validity (legal
264
+ characters), but other properties aren't reviewed (domain/path) - please make
265
+ sure to use only valid data, as HTTP imposes restrictions on these things.
266
+
267
+ If the header buffer is full or the headers were already sent (new headers
268
+ cannot be sent), the function will return -1.
269
+
270
+ On success, the function returns 0.
271
+ */
272
+ int http1_response_set_cookie(http_response_s *rs, http_cookie_s cookie) {
273
+ http1_response_s *const rs1 = (http1_response_s *)rs;
274
+ if (rs->headers_sent ||
275
+ (rs1->buffer_end + cookie.value_len + cookie.name_len >=
276
+ (HTTP1_MAX_HEADER_SIZE - H1P_OVERFLOW_PADDING)))
277
+ return -1; /* must have a cookie name. */
278
+ size_t org_pos = rs1->buffer_end;
279
+
280
+ /* write the header's name to the buffer */
281
+ if (h1p_protected_copy(rs1, "Set-Cookie:", 11))
282
+ goto error;
283
+ if (h1p_protected_copy(rs1, cookie.name, cookie.name_len))
284
+ goto error;
285
+
286
+ /* seperate name from value */
287
+ rs1->buffer[rs1->buffer_end++] = '=';
288
+ /* write the cookie value, if any */
289
+ if (cookie.value) {
290
+ if (h1p_protected_copy(rs1, cookie.value, cookie.value_len))
291
+ goto error;
292
+ } else {
293
+ cookie.max_age = -1;
294
+ }
295
+ /* complete value data */
296
+ rs1->buffer[rs1->buffer_end++] = ';';
297
+ if (cookie.max_age) {
298
+ rs1->buffer_end +=
299
+ sprintf(rs1->buffer + rs1->buffer_end, "Max-Age=%d;", cookie.max_age);
300
+ }
301
+ if (cookie.domain) {
302
+ memcpy(rs1->buffer + rs1->buffer_end, "domain=", 7);
303
+ rs1->buffer_end += 7;
304
+ if (h1p_protected_copy(rs1, cookie.domain, cookie.domain_len))
305
+ return -1;
306
+ rs1->buffer[rs1->buffer_end++] = ';';
307
+ }
308
+ if (cookie.path) {
309
+ memcpy(rs1->buffer + rs1->buffer_end, "path=", 5);
310
+ rs1->buffer_end += 5;
311
+ if (h1p_protected_copy(rs1, cookie.path, cookie.path_len))
312
+ return -1;
313
+ rs1->buffer[rs1->buffer_end++] = ';';
314
+ }
315
+ if (cookie.http_only) {
316
+ memcpy(rs1->buffer + rs1->buffer_end, "HttpOnly;", 9);
317
+ rs1->buffer_end += 9;
318
+ }
319
+ if (cookie.secure) {
320
+ memcpy(rs1->buffer + rs1->buffer_end, "secure;", 7);
321
+ rs1->buffer_end += 7;
322
+ }
323
+ rs1->buffer[rs1->buffer_end++] = '\r';
324
+ rs1->buffer[rs1->buffer_end++] = '\n';
325
+ return 0;
326
+
327
+ error:
328
+ rs1->buffer_end = org_pos;
329
+ return -1;
330
+ }
331
+
332
+ /**
333
+ Sends the headers (if they weren't previously sent) and writes the data to the
334
+ underlying socket.
335
+
336
+ The body will be copied to the server's outgoing buffer.
337
+
338
+ If the connection was already closed, the function will return -1. On success,
339
+ the function returns 0.
340
+ */
341
+ int http1_response_write_body(http_response_s *rs, const char *body,
342
+ size_t length) {
343
+ if (!sock_isvalid(rs->fd))
344
+ return -1;
345
+ http1_response_s *rs1 = (http1_response_s *)rs;
346
+ size_t tmp;
347
+ if (!rs->headers_sent) {
348
+ http1_response_finalize_headers(rs1);
349
+ tmp = (length + rs1->buffer_end >= HTTP1_MAX_HEADER_SIZE)
350
+ ? HTTP1_MAX_HEADER_SIZE - rs1->buffer_end
351
+ : length;
352
+ memcpy(rs1->buffer + rs1->buffer_end, body, tmp);
353
+ rs1->buffer_end += tmp;
354
+ http1_response_send_headers(rs1);
355
+ length -= tmp;
356
+ body += tmp;
357
+ }
358
+ if (length)
359
+ return (sock_write(rs->fd, body, length) >= 0);
360
+ return 0;
361
+ }
362
+
363
+ /**
364
+ Sends the headers (if they weren't previously sent) and writes the data to the
365
+ underlying socket.
366
+
367
+ The server's outgoing buffer will take ownership of the file and close it
368
+ using `close` once the data was sent.
369
+
370
+ If the connection was already closed, the function will return -1. On success,
371
+ the function returns 0.
372
+ */
373
+ int http1_response_sendfile(http_response_s *rs, int source_fd, off_t offset,
374
+ size_t length) {
375
+ if (!sock_isvalid(rs->fd) || !length) {
376
+ close(source_fd);
377
+ return -1;
378
+ }
379
+ http1_response_s *rs1 = (http1_response_s *)rs;
380
+ ssize_t tmp;
381
+ if (!rs->headers_sent) {
382
+ http1_response_finalize_headers(rs1);
383
+ tmp = (length + rs1->buffer_end >= HTTP1_MAX_HEADER_SIZE)
384
+ ? HTTP1_MAX_HEADER_SIZE - rs1->buffer_end
385
+ : length;
386
+ tmp = pread(source_fd, rs1->buffer + rs1->buffer_end, tmp, offset);
387
+ if (tmp <= 0) {
388
+ close(source_fd);
389
+ return -1;
390
+ }
391
+ rs1->buffer_end += tmp;
392
+ http1_response_send_headers(rs1);
393
+ length -= tmp;
394
+ offset += tmp;
395
+ }
396
+ if (length)
397
+ return (sock_write2(.uuid = rs->fd, .data_fd = source_fd, .length = length,
398
+ .offset = offset, .is_fd = 1, .move = 1) >= 0);
399
+ else
400
+ close(source_fd);
401
+
402
+ return 0;
403
+ }