iodine 0.4.19 → 0.5.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/http1_request.c
DELETED
@@ -1,81 +0,0 @@
|
|
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
|
-
}
|
data/ext/iodine/http1_request.h
DELETED
@@ -1,58 +0,0 @@
|
|
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 */
|
data/ext/iodine/http1_response.c
DELETED
@@ -1,417 +0,0 @@
|
|
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 "spnlock.inc"
|
8
|
-
|
9
|
-
#include "http1_response.h"
|
10
|
-
|
11
|
-
#include <string.h>
|
12
|
-
#include <strings.h>
|
13
|
-
/**
|
14
|
-
The padding for the status line (62 + 2 for the extra \r\n after the headers).
|
15
|
-
*/
|
16
|
-
#define H1P_HEADER_START 80
|
17
|
-
#define H1P_OVERFLOW_PADDING 128
|
18
|
-
|
19
|
-
/* *****************************************************************************
|
20
|
-
Response object & Initialization
|
21
|
-
***************************************************************************** */
|
22
|
-
|
23
|
-
typedef struct {
|
24
|
-
http_response_s response;
|
25
|
-
size_t buffer_start;
|
26
|
-
size_t buffer_end;
|
27
|
-
spn_lock_i lock;
|
28
|
-
uint8_t use_count;
|
29
|
-
uint8_t dest_count;
|
30
|
-
char buffer[HTTP1_MAX_HEADER_SIZE];
|
31
|
-
} http1_response_s;
|
32
|
-
|
33
|
-
static struct {
|
34
|
-
spn_lock_i lock;
|
35
|
-
uint8_t init;
|
36
|
-
http1_response_s *next;
|
37
|
-
http1_response_s pool_mem[HTTP1_POOL_SIZE];
|
38
|
-
} http1_response_pool = {.lock = SPN_LOCK_INIT, .init = 0};
|
39
|
-
|
40
|
-
static inline void http1_response_clear(http1_response_s *rs,
|
41
|
-
http_request_s *request) {
|
42
|
-
rs->response = (http_response_s){
|
43
|
-
.http_version = HTTP_V1,
|
44
|
-
.request = request,
|
45
|
-
.fd = request->fd,
|
46
|
-
.status = 200,
|
47
|
-
.date = facil_last_tick(),
|
48
|
-
.should_close = (request->connection && request->connection_len == 5) ||
|
49
|
-
(!request->connection && request->version &&
|
50
|
-
(request->version_len < 8 ||
|
51
|
-
(request->version[request->version_len - 1] == '0' &&
|
52
|
-
request->version[request->version_len - 3] == '1')))};
|
53
|
-
rs->buffer_end = rs->buffer_start = H1P_HEADER_START;
|
54
|
-
rs->use_count = 1;
|
55
|
-
rs->lock = SPN_LOCK_INIT;
|
56
|
-
}
|
57
|
-
/** Creates / allocates a protocol version's response object. */
|
58
|
-
http_response_s *http1_response_create(http_request_s *request) {
|
59
|
-
http1_response_s *rs;
|
60
|
-
spn_lock(&http1_response_pool.lock);
|
61
|
-
if (!http1_response_pool.next)
|
62
|
-
goto use_malloc;
|
63
|
-
rs = http1_response_pool.next;
|
64
|
-
http1_response_pool.next = (void *)rs->response.request;
|
65
|
-
spn_unlock(&http1_response_pool.lock);
|
66
|
-
http1_response_clear(rs, request);
|
67
|
-
return (http_response_s *)rs;
|
68
|
-
use_malloc:
|
69
|
-
if (http1_response_pool.init == 0)
|
70
|
-
goto initialize;
|
71
|
-
spn_unlock(&http1_response_pool.lock);
|
72
|
-
rs = malloc(sizeof(*rs));
|
73
|
-
http1_response_clear(rs, request);
|
74
|
-
return (http_response_s *)rs;
|
75
|
-
initialize:
|
76
|
-
http1_response_pool.init = 1;
|
77
|
-
for (size_t i = 1; i < (HTTP1_POOL_SIZE - 1); i++) {
|
78
|
-
http1_response_pool.pool_mem[i].response.request =
|
79
|
-
(void *)(http1_response_pool.pool_mem + (i + 1));
|
80
|
-
}
|
81
|
-
http1_response_pool.pool_mem[HTTP1_POOL_SIZE - 1].response.request = NULL;
|
82
|
-
http1_response_pool.next = http1_response_pool.pool_mem + 1;
|
83
|
-
spn_unlock(&http1_response_pool.lock);
|
84
|
-
http1_response_clear(http1_response_pool.pool_mem, request);
|
85
|
-
return (http_response_s *)http1_response_pool.pool_mem;
|
86
|
-
}
|
87
|
-
|
88
|
-
static void http1_response_deferred_destroy(void *rs_, void *ignr) {
|
89
|
-
(void)(ignr);
|
90
|
-
http1_response_s *rs = rs_;
|
91
|
-
if (spn_trylock(&rs->lock)) {
|
92
|
-
defer(http1_response_deferred_destroy, rs, NULL);
|
93
|
-
return;
|
94
|
-
}
|
95
|
-
rs->use_count -= 1;
|
96
|
-
if (rs->use_count) {
|
97
|
-
spn_unlock(&rs->lock);
|
98
|
-
return;
|
99
|
-
}
|
100
|
-
|
101
|
-
if (rs->response.request_dupped)
|
102
|
-
http_request_destroy(rs->response.request);
|
103
|
-
|
104
|
-
if ((uintptr_t)rs < (uintptr_t)http1_response_pool.pool_mem ||
|
105
|
-
(uintptr_t)rs >
|
106
|
-
(uintptr_t)(http1_response_pool.pool_mem + (HTTP1_POOL_SIZE - 1)))
|
107
|
-
goto use_free;
|
108
|
-
spn_lock(&http1_response_pool.lock);
|
109
|
-
rs->response.request = (void *)http1_response_pool.next;
|
110
|
-
http1_response_pool.next = (void *)rs;
|
111
|
-
spn_unlock(&http1_response_pool.lock);
|
112
|
-
return;
|
113
|
-
use_free:
|
114
|
-
free(rs);
|
115
|
-
return;
|
116
|
-
}
|
117
|
-
|
118
|
-
/** Destroys the response object. No data is sent.*/
|
119
|
-
void http1_response_destroy(http_response_s *rs) {
|
120
|
-
((http1_response_s *)rs)->dest_count++;
|
121
|
-
http1_response_deferred_destroy(rs, NULL);
|
122
|
-
}
|
123
|
-
|
124
|
-
/* *****************************************************************************
|
125
|
-
Writing and Finishing Helpers + `http1_response_finish`
|
126
|
-
***************************************************************************** */
|
127
|
-
|
128
|
-
static int h1p_protected_copy(http1_response_s *rs, void *buff, size_t len) {
|
129
|
-
if (len + rs->buffer_end >= HTTP1_MAX_HEADER_SIZE - H1P_OVERFLOW_PADDING)
|
130
|
-
return -1;
|
131
|
-
memcpy(rs->buffer + rs->buffer_end, buff, len);
|
132
|
-
rs->buffer_end += len;
|
133
|
-
return 0;
|
134
|
-
}
|
135
|
-
|
136
|
-
static void http1_response_finalize_headers(http1_response_s *rs) {
|
137
|
-
if (rs->response.headers_sent)
|
138
|
-
return;
|
139
|
-
rs->response.headers_sent = 1;
|
140
|
-
const char *status = http_response_status_str(rs->response.status);
|
141
|
-
if (!status) {
|
142
|
-
rs->response.status = 500;
|
143
|
-
status = http_response_status_str(rs->response.status);
|
144
|
-
}
|
145
|
-
|
146
|
-
/* write the content length header, unless forced not to (<0) */
|
147
|
-
if (rs->response.content_length_written == 0 &&
|
148
|
-
!(rs->response.content_length < 0) && rs->response.status >= 200 &&
|
149
|
-
rs->response.status != 204 && rs->response.status != 304) {
|
150
|
-
h1p_protected_copy(rs, "Content-Length: ", 16);
|
151
|
-
rs->buffer_end +=
|
152
|
-
http_ul2a(rs->buffer + rs->buffer_end, rs->response.content_length);
|
153
|
-
/* write the header seperator (`\r\n`) */
|
154
|
-
rs->buffer[rs->buffer_end++] = '\r';
|
155
|
-
rs->buffer[rs->buffer_end++] = '\n';
|
156
|
-
}
|
157
|
-
/* write the date, if missing */
|
158
|
-
if (!rs->response.date_written) {
|
159
|
-
if (!rs->response.last_modified)
|
160
|
-
rs->response.last_modified = rs->response.date;
|
161
|
-
else if (rs->response.date < rs->response.last_modified)
|
162
|
-
rs->response.date = rs->response.last_modified;
|
163
|
-
/* date header */
|
164
|
-
h1p_protected_copy(rs, "Date: ", 6);
|
165
|
-
rs->buffer_end +=
|
166
|
-
http_time2str(rs->buffer + rs->buffer_end, rs->response.date);
|
167
|
-
rs->buffer[rs->buffer_end++] = '\r';
|
168
|
-
rs->buffer[rs->buffer_end++] = '\n';
|
169
|
-
/* last-modified header */
|
170
|
-
h1p_protected_copy(rs, "Last-Modified: ", 15);
|
171
|
-
rs->buffer_end +=
|
172
|
-
http_time2str(rs->buffer + rs->buffer_end, rs->response.last_modified);
|
173
|
-
rs->buffer[rs->buffer_end++] = '\r';
|
174
|
-
rs->buffer[rs->buffer_end++] = '\n';
|
175
|
-
}
|
176
|
-
/* write the keep-alive (connection) header, if missing */
|
177
|
-
if (!rs->response.connection_written) {
|
178
|
-
if (rs->response.should_close) {
|
179
|
-
h1p_protected_copy(rs, "Connection: close\r\n", 19);
|
180
|
-
} else {
|
181
|
-
// h1p_protected_copy(rs,
|
182
|
-
// "Connection:keep-alive\r\n"
|
183
|
-
// "Keep-Alive:timeout=2\r\n",
|
184
|
-
// 45);
|
185
|
-
h1p_protected_copy(rs, "Connection: keep-alive\r\n", 24);
|
186
|
-
}
|
187
|
-
}
|
188
|
-
/* write the headers completion marker (empty line - `\r\n`) */
|
189
|
-
rs->buffer[rs->buffer_end++] = '\r';
|
190
|
-
rs->buffer[rs->buffer_end++] = '\n';
|
191
|
-
|
192
|
-
/* write the status string is "HTTP/1.1 xxx <...>\r\n" length == 15 +
|
193
|
-
* strlen(status) */
|
194
|
-
|
195
|
-
size_t tmp = strlen(status);
|
196
|
-
rs->buffer_start = H1P_HEADER_START - (15 + tmp);
|
197
|
-
memcpy(rs->buffer + rs->buffer_start, "HTTP/1.1 ### ", 13);
|
198
|
-
memcpy(rs->buffer + rs->buffer_start + 13, status, tmp);
|
199
|
-
rs->buffer[H1P_HEADER_START - 1] = '\n';
|
200
|
-
rs->buffer[H1P_HEADER_START - 2] = '\r';
|
201
|
-
tmp = rs->response.status / 10;
|
202
|
-
*(rs->buffer + rs->buffer_start + 9) = '0' + (tmp / 10);
|
203
|
-
*(rs->buffer + rs->buffer_start + 11) =
|
204
|
-
'0' + (rs->response.status - (10 * tmp));
|
205
|
-
*(rs->buffer + rs->buffer_start + 10) = '0' + (tmp - (10 * (tmp / 10)));
|
206
|
-
}
|
207
|
-
|
208
|
-
int http1_response_send_headers(http1_response_s *rs) {
|
209
|
-
if (!rs->buffer_end)
|
210
|
-
return 0;
|
211
|
-
http1_response_finalize_headers(rs);
|
212
|
-
spn_lock(&rs->lock);
|
213
|
-
rs->use_count++;
|
214
|
-
spn_unlock(&rs->lock);
|
215
|
-
if (sock_write2(.uuid = rs->response.fd, .buffer = rs,
|
216
|
-
.offset = ((uintptr_t)(rs->buffer + rs->buffer_start) -
|
217
|
-
(uintptr_t)rs),
|
218
|
-
.length = rs->buffer_end - rs->buffer_start, .move = 1,
|
219
|
-
.dealloc = (void (*)(void *))http1_response_destroy) < 0)
|
220
|
-
return -1;
|
221
|
-
rs->buffer_end = 0;
|
222
|
-
return 0;
|
223
|
-
}
|
224
|
-
|
225
|
-
/** Sends the data and destroys the response object.*/
|
226
|
-
void http1_response_finish(http_response_s *rs) {
|
227
|
-
if (!rs->headers_sent)
|
228
|
-
http1_response_send_headers((http1_response_s *)rs);
|
229
|
-
if (rs->should_close)
|
230
|
-
sock_close(rs->fd);
|
231
|
-
http1_response_deferred_destroy(rs, NULL);
|
232
|
-
}
|
233
|
-
|
234
|
-
/* *****************************************************************************
|
235
|
-
Writing data to the response object
|
236
|
-
***************************************************************************** */
|
237
|
-
|
238
|
-
/**
|
239
|
-
Writes a header to the response. This function writes only the requested
|
240
|
-
number of bytes from the header name and the requested number of bytes from
|
241
|
-
the header value. It can be used even when the header name and value don't
|
242
|
-
contain NULL terminating bytes by passing the `.name_len` or `.value_len` data
|
243
|
-
in the `http_headers_s` structure.
|
244
|
-
|
245
|
-
If the header buffer is full or the headers were already sent (new headers
|
246
|
-
cannot be sent), the function will return -1.
|
247
|
-
|
248
|
-
On success, the function returns 0.
|
249
|
-
*/
|
250
|
-
int http1_response_write_header_fn(http_response_s *rs_, http_header_s header) {
|
251
|
-
http1_response_s *rs = (http1_response_s *)rs_;
|
252
|
-
if (rs->buffer_end + header.name_len + header.value_len >=
|
253
|
-
HTTP1_MAX_HEADER_SIZE - H1P_OVERFLOW_PADDING - 5)
|
254
|
-
return -1;
|
255
|
-
size_t org_pos = rs->buffer_end;
|
256
|
-
if (h1p_protected_copy(rs, (void *)header.name, header.name_len))
|
257
|
-
goto error;
|
258
|
-
rs->buffer[rs->buffer_end++] = ':';
|
259
|
-
rs->buffer[rs->buffer_end++] = ' ';
|
260
|
-
if (h1p_protected_copy(rs, (void *)header.value, header.value_len))
|
261
|
-
goto error;
|
262
|
-
rs->buffer[rs->buffer_end++] = '\r';
|
263
|
-
rs->buffer[rs->buffer_end++] = '\n';
|
264
|
-
return 0;
|
265
|
-
error:
|
266
|
-
rs->buffer_end = org_pos;
|
267
|
-
return -1;
|
268
|
-
}
|
269
|
-
|
270
|
-
/**
|
271
|
-
Set / Delete a cookie using this helper function.
|
272
|
-
|
273
|
-
This function writes a cookie header to the response. Only the requested
|
274
|
-
number of bytes from the cookie value and name are written (if none are
|
275
|
-
provided, a terminating NULL byte is assumed).
|
276
|
-
|
277
|
-
Both the name and the value of the cookie are checked for validity (legal
|
278
|
-
characters), but other properties aren't reviewed (domain/path) - please make
|
279
|
-
sure to use only valid data, as HTTP imposes restrictions on these things.
|
280
|
-
|
281
|
-
If the header buffer is full or the headers were already sent (new headers
|
282
|
-
cannot be sent), the function will return -1.
|
283
|
-
|
284
|
-
On success, the function returns 0.
|
285
|
-
*/
|
286
|
-
int http1_response_set_cookie(http_response_s *rs, http_cookie_s cookie) {
|
287
|
-
http1_response_s *const rs1 = (http1_response_s *)rs;
|
288
|
-
if (rs->headers_sent ||
|
289
|
-
(rs1->buffer_end + cookie.value_len + cookie.name_len >=
|
290
|
-
(HTTP1_MAX_HEADER_SIZE - H1P_OVERFLOW_PADDING)))
|
291
|
-
return -1; /* must have a cookie name. */
|
292
|
-
size_t org_pos = rs1->buffer_end;
|
293
|
-
|
294
|
-
/* write the header's name to the buffer */
|
295
|
-
if (h1p_protected_copy(rs1, "Set-Cookie: ", 12))
|
296
|
-
goto error;
|
297
|
-
if (h1p_protected_copy(rs1, cookie.name, cookie.name_len))
|
298
|
-
goto error;
|
299
|
-
|
300
|
-
/* seperate name from value */
|
301
|
-
rs1->buffer[rs1->buffer_end++] = '=';
|
302
|
-
/* write the cookie value, if any */
|
303
|
-
if (cookie.value) {
|
304
|
-
if (h1p_protected_copy(rs1, cookie.value, cookie.value_len))
|
305
|
-
goto error;
|
306
|
-
} else {
|
307
|
-
cookie.max_age = -1;
|
308
|
-
}
|
309
|
-
/* complete value data */
|
310
|
-
rs1->buffer[rs1->buffer_end++] = ';';
|
311
|
-
if (cookie.max_age) {
|
312
|
-
rs1->buffer_end +=
|
313
|
-
sprintf(rs1->buffer + rs1->buffer_end, "Max-Age=%d;", cookie.max_age);
|
314
|
-
}
|
315
|
-
if (cookie.domain) {
|
316
|
-
memcpy(rs1->buffer + rs1->buffer_end, "domain=", 7);
|
317
|
-
rs1->buffer_end += 7;
|
318
|
-
if (h1p_protected_copy(rs1, cookie.domain, cookie.domain_len))
|
319
|
-
return -1;
|
320
|
-
rs1->buffer[rs1->buffer_end++] = ';';
|
321
|
-
}
|
322
|
-
if (cookie.path) {
|
323
|
-
memcpy(rs1->buffer + rs1->buffer_end, "path=", 5);
|
324
|
-
rs1->buffer_end += 5;
|
325
|
-
if (h1p_protected_copy(rs1, cookie.path, cookie.path_len))
|
326
|
-
return -1;
|
327
|
-
rs1->buffer[rs1->buffer_end++] = ';';
|
328
|
-
}
|
329
|
-
if (cookie.http_only) {
|
330
|
-
memcpy(rs1->buffer + rs1->buffer_end, "HttpOnly;", 9);
|
331
|
-
rs1->buffer_end += 9;
|
332
|
-
}
|
333
|
-
if (cookie.secure) {
|
334
|
-
memcpy(rs1->buffer + rs1->buffer_end, "secure;", 7);
|
335
|
-
rs1->buffer_end += 7;
|
336
|
-
}
|
337
|
-
rs1->buffer[rs1->buffer_end++] = '\r';
|
338
|
-
rs1->buffer[rs1->buffer_end++] = '\n';
|
339
|
-
return 0;
|
340
|
-
|
341
|
-
error:
|
342
|
-
rs1->buffer_end = org_pos;
|
343
|
-
return -1;
|
344
|
-
}
|
345
|
-
|
346
|
-
/**
|
347
|
-
Sends the headers (if they weren't previously sent) and writes the data to the
|
348
|
-
underlying socket.
|
349
|
-
|
350
|
-
The body will be copied to the server's outgoing buffer.
|
351
|
-
|
352
|
-
If the connection was already closed, the function will return -1. On success,
|
353
|
-
the function returns 0.
|
354
|
-
*/
|
355
|
-
int http1_response_write_body(http_response_s *rs, const char *body,
|
356
|
-
size_t length) {
|
357
|
-
if (!sock_isvalid(rs->fd))
|
358
|
-
return -1;
|
359
|
-
http1_response_s *rs1 = (http1_response_s *)rs;
|
360
|
-
size_t tmp;
|
361
|
-
if (!rs->headers_sent) {
|
362
|
-
http1_response_finalize_headers(rs1);
|
363
|
-
tmp = (length + rs1->buffer_end >= HTTP1_MAX_HEADER_SIZE)
|
364
|
-
? HTTP1_MAX_HEADER_SIZE - rs1->buffer_end
|
365
|
-
: length;
|
366
|
-
memcpy(rs1->buffer + rs1->buffer_end, body, tmp);
|
367
|
-
rs1->buffer_end += tmp;
|
368
|
-
http1_response_send_headers(rs1);
|
369
|
-
length -= tmp;
|
370
|
-
body += tmp;
|
371
|
-
}
|
372
|
-
if (length)
|
373
|
-
return (sock_write(rs->fd, body, length) >= 0);
|
374
|
-
return 0;
|
375
|
-
}
|
376
|
-
|
377
|
-
/**
|
378
|
-
Sends the headers (if they weren't previously sent) and writes the data to the
|
379
|
-
underlying socket.
|
380
|
-
|
381
|
-
The server's outgoing buffer will take ownership of the file and close it
|
382
|
-
using `close` once the data was sent.
|
383
|
-
|
384
|
-
If the connection was already closed, the function will return -1. On success,
|
385
|
-
the function returns 0.
|
386
|
-
*/
|
387
|
-
int http1_response_sendfile(http_response_s *rs, int source_fd, off_t offset,
|
388
|
-
size_t length) {
|
389
|
-
if (!sock_isvalid(rs->fd) || !length) {
|
390
|
-
close(source_fd);
|
391
|
-
return -1;
|
392
|
-
}
|
393
|
-
http1_response_s *rs1 = (http1_response_s *)rs;
|
394
|
-
ssize_t tmp;
|
395
|
-
if (!rs->headers_sent) {
|
396
|
-
http1_response_finalize_headers(rs1);
|
397
|
-
tmp = (length + rs1->buffer_end >= HTTP1_MAX_HEADER_SIZE)
|
398
|
-
? HTTP1_MAX_HEADER_SIZE - rs1->buffer_end
|
399
|
-
: length;
|
400
|
-
tmp = pread(source_fd, rs1->buffer + rs1->buffer_end, tmp, offset);
|
401
|
-
if (tmp <= 0) {
|
402
|
-
close(source_fd);
|
403
|
-
return -1;
|
404
|
-
}
|
405
|
-
rs1->buffer_end += tmp;
|
406
|
-
http1_response_send_headers(rs1);
|
407
|
-
length -= tmp;
|
408
|
-
offset += tmp;
|
409
|
-
}
|
410
|
-
if (length)
|
411
|
-
return (sock_write2(.uuid = rs->fd, .data_fd = source_fd, .length = length,
|
412
|
-
.offset = offset, .is_fd = 1, .move = 1) >= 0);
|
413
|
-
else
|
414
|
-
close(source_fd);
|
415
|
-
|
416
|
-
return 0;
|
417
|
-
}
|