-
Notifications
You must be signed in to change notification settings - Fork 39
/
http.c
254 lines (233 loc) · 7.56 KB
/
http.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/keyvalq_struct.h>
#include "log.h"
#include "sha1.h"
#include "utp.h"
#include "base64.h"
#include "timer.h"
#include "network.h"
#include "constants.h"
#include "hash_table.h"
#include "http.h"
#include "bufferevent_utp.h"
evhttp_connection *connections[10];
void join_url_swarm(network *n, const char *url)
{
__block struct {
uint8_t url_hash[20];
} hash_state;
SHA1(hash_state.url_hash, (const unsigned char *)url, (uint)strlen(url));
// TODO: stop after 24hr
timer_callback cb = ^{
dht_announce(n->dht, hash_state.url_hash);
};
cb();
timer_repeating(n, 25 * 60 * 1000, cb);
}
void fetch_url_swarm(network *n, const char *url)
{
uint8_t url_hash[20];
SHA1(url_hash, (const unsigned char *)url, (uint)strlen(url));
dht_get_peers(n->dht, url_hash);
}
const char* evhttp_method(evhttp_cmd_type type)
{
switch (type) {
case EVHTTP_REQ_GET: return "GET";
case EVHTTP_REQ_POST: return "POST";
case EVHTTP_REQ_HEAD: return "HEAD";
case EVHTTP_REQ_PUT: return "PUT";
case EVHTTP_REQ_DELETE: return "DELETE";
case EVHTTP_REQ_OPTIONS: return "OPTIONS";
case EVHTTP_REQ_TRACE: return "TRACE";
case EVHTTP_REQ_CONNECT: return "CONNECT";
case EVHTTP_REQ_PATCH: return "PATCH";
case EVHTTP_REQ_PROPFIND: return "PROPFIND";
case EVHTTP_REQ_PROPPATCH: return "PROPPATCH";
case EVHTTP_REQ_MKCOL: return "MKCOL";
case EVHTTP_REQ_LOCK: return "LOCK";
case EVHTTP_REQ_UNLOCK: return "UNLOCK";
case EVHTTP_REQ_COPY: return "COPY";
case EVHTTP_REQ_MOVE: return "MOVE";
}
return NULL;
}
const char* evhttp_request_error_str(evhttp_request_error error)
{
switch (error) {
case EVREQ_HTTP_TIMEOUT: return "TIMEOUT";
case EVREQ_HTTP_EOF: return "EOF";
case EVREQ_HTTP_INVALID_HEADER: return "INVALID_HEADER";
case EVREQ_HTTP_BUFFER_ERROR: return "BUFFER_ERROR";
case EVREQ_HTTP_REQUEST_CANCEL: return "REQUEST_CANCEL";
case EVREQ_HTTP_DATA_TOO_LONG: return "DATA_TOO_LONG";
};
return NULL;
}
int get_port_for_scheme(const char *scheme)
{
addrinfo hints = {.ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM};
addrinfo *res;
int error = getaddrinfo(NULL, scheme, &hints, &res);
if (error) {
fprintf(stderr, "getaddrinfo failed %s\n", gai_strerror(error));
return -1;
}
int port = -1;
for (addrinfo *r = res; r; r = r->ai_next) {
char portstr[NI_MAXSERV];
error = getnameinfo(r->ai_addr, r->ai_addrlen, NULL, 0, portstr, sizeof(portstr), NI_NUMERICSERV);
if (error) {
fprintf(stderr, "getnameinfo failed %s\n", gai_strerror(error));
continue;
}
port = atoi(portstr);
if (port != -1) {
break;
}
}
freeaddrinfo(res);
return port;
}
void overwrite_kv_header(evkeyvalq *out, const char *key, const char *value)
{
while (evhttp_find_header(out, key)) {
evhttp_remove_header(out, key);
}
evhttp_add_header(out, key, value);
}
void overwrite_header(evhttp_request *to, const char *key, const char *value)
{
overwrite_kv_header(to->output_headers, key, value);
}
void copy_header(evhttp_request *from, evhttp_request *to, const char *key)
{
const char *value = evhttp_find_header(from->input_headers, key);
if (value) {
overwrite_header(to, key, value);
}
}
void copy_all_headers(evhttp_request *from, evhttp_request *to)
{
evkeyvalq *in = from->input_headers;
evkeyval *header;
TAILQ_FOREACH(header, in, next) {
overwrite_header(to, header->key, header->value);
}
}
evbuffer* build_request_buffer(int response_code, evkeyvalq *hdrs)
{
evbuffer *buf = evbuffer_new();
evbuffer_add_printf(buf, "%d\r\n", response_code);
assert(response_code);
const char *headers[] = hashed_headers;
for (size_t i = 0; i < lenof(headers); i++) {
const char *key = headers[i];
const char *value = evhttp_find_header(hdrs, key);
if (!value) {
continue;
}
evbuffer_add_printf(buf, "%s: %s\r\n", key, value);
}
return buf;
}
void merkle_tree_hash_request(merkle_tree *m, evhttp_request *req, evkeyvalq *hdrs)
{
evbuffer_auto_free evbuffer *buf = build_request_buffer(req->response_code, hdrs);
merkle_tree_add_evbuffer(m, buf);
}
evhttp_connection *make_connection(network *n, const evhttp_uri *uri)
{
const char *scheme = evhttp_uri_get_scheme(uri);
const char *host = evhttp_uri_get_host(uri);
if (!host) {
return NULL;
}
int port = evhttp_uri_get_port(uri);
if (port == -1) {
port = get_port_for_scheme(scheme);
}
for (size_t i = 0; i < lenof(connections); i++) {
evhttp_connection *evcon = connections[i];
if (evcon) {
const char *e_host;
ev_uint16_t e_port;
evhttp_connection_get_peer(evcon, &e_host, &e_port);
if (port == e_port && strcasecmp(host, e_host) == 0) {
connections[i] = NULL;
evhttp_connection_set_closecb(evcon, NULL, NULL);
debug("re-using %s:%d evcon:%p\n", e_host, e_port, evcon);
return evcon;
}
}
}
debug("connecting to %s:%d\n", host, port);
// XXX: doesn't handle SSL
// TODO: if the request is from a peer, use LEDBAT: setsocketopt(sock, SOL_SOCKET, O_TRAFFIC_CLASS, SO_TC_BK, sizeof(int))
evhttp_connection *evcon = evhttp_connection_base_new(n->evbase, n->evdns, host, (port_t)port);
// XXX: disable IPv6, since evdns waits for *both* and the v6 request often times out
evhttp_connection_set_family(evcon, AF_INET);
return evcon;
}
void evcon_close_cb(evhttp_connection *evcon, void *ctx)
{
for (size_t i = 0; i < lenof(connections); i++) {
if (connections[i] == evcon) {
connections[i] = NULL;
break;
}
}
evhttp_connection_free_on_completion(evcon);
evhttp_connection_set_closecb(evcon, NULL, NULL);
}
void return_connection(evhttp_connection *evcon)
{
for (size_t i = 0; i < lenof(connections); i++) {
if (!connections[i]) {
connections[i] = evcon;
evhttp_connection_set_closecb(evcon, evcon_close_cb, NULL);
return;
}
}
evhttp_connection_free(evcon);
}
uint64 utp_on_accept(utp_callback_arguments *a)
{
network *n = (network*)utp_context_get_userdata(a->context);
sockaddr_storage addr;
socklen_t addrlen = sizeof(addr);
if (utp_getpeername(a->socket, (sockaddr *)&addr, &addrlen) == -1) {
debug("utp_getpeername failed\n");
}
//debug("%s %p %s\n", __func__, a->socket, sockaddr_str((const sockaddr*)&addr));
bufferevent *bev = bufferevent_utp_new(n->evbase, n->utp, a->socket, BEV_OPT_CLOSE_ON_FREE);
evhttp_connection *evcon = evhttp_get_request(n->http, EVUTIL_INVALID_SOCKET, (sockaddr *)&addr, addrlen, bev);
if (!evcon) {
debug("%s evhttp_get_request failed\n", __func__);
bufferevent_free(bev);
return 0;
}
return 1;
}
bool http_setup(network *n)
{
n->http = evhttp_new(n->evbase);
if (!n->http) {
fprintf(stderr, "evhttp_new failed\n");
return false;
}
// don't add any content type automatically
evhttp_set_default_content_type(n->http, NULL);
evhttp_set_timeout(n->http, 50);
utp_set_callback(n->utp, UTP_ON_ACCEPT, &utp_on_accept);
return true;
}