summaryrefslogtreecommitdiff
path: root/opentracker.c
diff options
context:
space:
mode:
Diffstat (limited to 'opentracker.c')
-rw-r--r--opentracker.c202
1 files changed, 50 insertions, 152 deletions
diff --git a/opentracker.c b/opentracker.c
index 6160696..2f2746d 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -29,12 +29,14 @@
29#include "ot_ip.h" 29#include "ot_ip.h"
30#include "ot_livesync.h" 30#include "ot_livesync.h"
31#include "ot_mutex.h" 31#include "ot_mutex.h"
32#include "ot_workqueue.h"
32#include "ot_random.h" 33#include "ot_random.h"
33#include "ot_scan.h" 34#include "ot_scan.h"
34#include "ot_event.h" 35#include "ot_event.h"
35#include "ot_fdstate.h" 36#include "ot_fdstate.h"
36#include "ot_socket.h" 37#include "ot_socket.h"
37#include "ot_stats.h" 38#include "ot_stats.h"
39#include "ot_tcp.h"
38#include "ot_udp.h" 40#include "ot_udp.h"
39#include "trackerlogic.h" 41#include "trackerlogic.h"
40 42
@@ -96,129 +98,6 @@ static void install_signal_handlers(void) {
96} 98}
97 99
98 100
99static ssize_t header_complete(char *request, ssize_t byte_count) {
100 ssize_t i = 0, state = 0;
101
102 for (i = 1; i < byte_count; i += 2)
103 if (request[i] <= 13) {
104 i--;
105 for (state = 0; i < byte_count; ++i) {
106 char c = request[i];
107 if (c == '\r' || c == '\n')
108 state = (state >> 2) | ((c << 6) & 0xc0);
109 else
110 break;
111 if (state >= 0xa0 || state == 0x99)
112 return i + 1;
113 }
114 }
115 return 0;
116}
117
118static void handle_read(const int64_t sock, struct ot_workstruct *ws) {
119 struct http_data *cookie = fdstate_cookie(sock);
120 ssize_t byte_count = read((int)sock, ws->inbuf, G_INBUF_SIZE);
121
122 if (byte_count == 0 || (byte_count == -1 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)) {
123 http_close(sock);
124 return;
125 }
126
127 if (byte_count == -1)
128 return;
129
130 ws->request = NULL;
131 ws->request_size = 0;
132 ws->header_size = 0;
133 ws->keep_alive = 0;
134
135 /* If we get the whole request in one packet, handle it without copying */
136 if (!cookie->request.data) {
137 if ((ws->header_size = header_complete(ws->inbuf, byte_count))) {
138 ws->request = ws->inbuf;
139 ws->request_size = byte_count;
140 http_handle_request(sock, ws);
141 } else if (buf_cat(&cookie->request, ws->inbuf, (size_t)byte_count))
142 http_close(sock);
143 return;
144 }
145
146 if (buf_cat(&cookie->request, ws->inbuf, (size_t)byte_count) || cookie->request.size > G_INBUF_SIZE) {
147 http_issue_error(sock, ws, CODE_HTTPERROR_500);
148 return;
149 }
150
151 while ((ws->header_size = header_complete(cookie->request.data, (ssize_t)cookie->request.size))) {
152 ws->request = cookie->request.data;
153 ws->request_size = (ssize_t)cookie->request.size;
154 http_handle_request(sock, ws);
155#ifdef WANT_KEEPALIVE
156 if (!ws->keep_alive)
157#endif
158 return;
159 }
160}
161
162static void handle_write(const int64_t sock) {
163 struct http_data *cookie = fdstate_cookie(sock);
164 int chunked = 0;
165 int keep_alive = 0;
166
167 if (cookie) {
168 chunked = (cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER) != 0;
169 keep_alive = (cookie->flag & STRUCT_HTTP_FLAG_KEEPALIVE) != 0;
170
171 if (cookie->batch) {
172 if (batch_send(sock, cookie->batch) == BATCH_FAILED) {
173 http_close(sock);
174 return;
175 }
176 if (batch_pending(cookie->batch))
177 return;
178 }
179 }
180
181 /* In a chunked transfer, wait for the next piece once everything queued has gone out */
182 if (chunked)
183 http_interest_drop(sock, cookie, EVENT_OUT);
184 else if (keep_alive) {
185 /* The reply is out and the client asked to keep the connection, so stop
186 waiting to write and go back to waiting for the next request */
187 http_interest_drop(sock, cookie, EVENT_OUT);
188 http_interest_add(sock, cookie, EVENT_IN);
189 fdstate_set_expiry(sock, time_future_seconds(OT_CLIENT_TIMEOUT));
190 } else
191 http_close(sock);
192}
193
194static void handle_accept(const int64_t serversocket, struct ot_workstruct *ws) {
195 struct http_data *cookie;
196 int64_t sock;
197 ot_ip6 ip;
198 uint16_t port;
199
200 while ((sock = socket_accept(serversocket, ip, &port)) != -1) {
201
202 socket_set_blocking((int)sock, 0);
203
204 if (!(cookie = (struct http_data *)calloc(1, sizeof(struct http_data)))) {
205 close((int)sock);
206 continue;
207 }
208 memcpy(cookie->ip, ip, sizeof(ot_ip6));
209 cookie->interest = EVENT_IN;
210
211 if (event_add((int)sock, EVENT_IN, fdstate_open(sock, cookie, time_future_seconds(OT_CLIENT_TIMEOUT))) == -1) {
212 fdstate_close(sock);
213 free(cookie);
214 close((int)sock);
215 continue;
216 }
217
218 stats_issue_event(ws, EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip);
219 }
220}
221
222/* How many ready descriptors one wait hands back at a time */ 101/* How many ready descriptors one wait hands back at a time */
223#define OT_EVENT_BATCH 512 102#define OT_EVENT_BATCH 512
224 103
@@ -231,20 +110,9 @@ static void *server_mainloop(void *args) {
231 110
232 (void)args; 111 (void)args;
233 112
234 /* Initialize our "thread local storage" */ 113 if (workstruct_init(&ws) == -1)
235 memset(&ws, 0, sizeof(ws));
236 stats_counters_register(&ws);
237 ws.inbuf = malloc(G_INBUF_SIZE);
238 ws.outbuf = malloc(G_OUTBUF_SIZE);
239#ifdef _DEBUG_HTTPERROR
240 ws.debugbuf = malloc(G_DEBUGBUF_SIZE);
241#endif
242
243 if (!ws.inbuf || !ws.outbuf)
244 panic("Initializing worker failed"); 114 panic("Initializing worker failed");
245 115
246 random_bytes(ws.rand48_state, sizeof(ws.rand48_state));
247
248 while (g_opentracker_running) { 116 while (g_opentracker_running) {
249 ot_event ready[OT_EVENT_BATCH]; 117 ot_event ready[OT_EVENT_BATCH];
250 int64_t sock; 118 int64_t sock;
@@ -264,22 +132,24 @@ static void *server_mainloop(void *args) {
264 cookie = fdstate_cookie((int)sock); 132 cookie = fdstate_cookie((int)sock);
265 133
266 if ((intptr_t)cookie == FLAG_TCP) 134 if ((intptr_t)cookie == FLAG_TCP)
267 handle_accept(sock, &ws); 135 tcp_accept(sock, &ws);
268 else if ((intptr_t)cookie == FLAG_UDP) 136 else if ((intptr_t)cookie == FLAG_UDP)
269 handle_udp6(sock, &ws); 137 handle_udp6(sock, &ws);
270 else if ((intptr_t)cookie == FLAG_SELFPIPE) 138 else if ((intptr_t)cookie == FLAG_SELFPIPE)
271 while (read((int)sock, ws.inbuf, G_INBUF_SIZE) > 0) 139 while (read((int)sock, ws.inbuf, G_INBUF_SIZE) > 0)
272 ; 140 ;
273 else { 141 else if (fdstate_claim((int)sock, ready[i].generation)) {
274 if (ready[i].what & EVENT_IN) 142 if (ready[i].what & EVENT_IN)
275 handle_read(sock, &ws); 143 tcp_read(sock, &ws);
276 /* The read may have closed it, so look again before writing */ 144 /* The read may have closed it, so look again before writing */
277 if ((ready[i].what & EVENT_OUT) && fdstate_generation((int)sock) == ready[i].generation) 145 if ((ready[i].what & EVENT_OUT) && fdstate_generation((int)sock) == ready[i].generation)
278 handle_write(sock); 146 tcp_write(sock);
147 if (fdstate_generation((int)sock) == ready[i].generation)
148 fdstate_release((int)sock);
279 } 149 }
280 } 150 }
281 151
282 while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) 152 while ((sock = workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1)
283 http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); 153 http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial);
284 154
285 if (time_now_seconds() > next_timeout_check) { 155 if (time_now_seconds() > next_timeout_check) {
@@ -295,13 +165,7 @@ static void *server_mainloop(void *args) {
295 165
296 /* This work struct is about to go out of scope, so hand its tally over 166 /* This work struct is about to go out of scope, so hand its tally over
297 before anything can read it there */ 167 before anything can read it there */
298 stats_counters_retire(&ws); 168 workstruct_deinit(&ws);
299
300 free(ws.inbuf);
301 free(ws.outbuf);
302#ifdef _DEBUG_HTTPERROR
303 free(ws.debugbuf);
304#endif
305 return 0; 169 return 0;
306} 170}
307 171
@@ -310,7 +174,7 @@ static void *server_mainloop(void *args) {
310 member asked for it before binding, the first one included, so whether 174 member asked for it before binding, the first one included, so whether
311 this entry will have workers has to be known here. */ 175 this entry will have workers has to be known here. */
312static socket_sharing listen_sharing_for(const ot_listen *entry) { 176static socket_sharing listen_sharing_for(const ot_listen *entry) {
313 if (entry->proto == FLAG_UDP && entry->workers > 1 && socket_can_balance()) 177 if (entry->proto != FLAG_MCA && entry->workers > 1 && socket_can_balance())
314 return SOCKET_BALANCED; 178 return SOCKET_BALANCED;
315 return entry->shared ? SOCKET_SHARED : SOCKET_EXCLUSIVE; 179 return entry->shared ? SOCKET_SHARED : SOCKET_EXCLUSIVE;
316} 180}
@@ -342,6 +206,7 @@ static void listen_bind_one(ot_listen *entry) {
342 if (entry->proto == FLAG_TCP) { 206 if (entry->proto == FLAG_TCP) {
343 if ((entry->sock = socket_tcp_listen(entry->ip, entry->port, sharing)) == -1) 207 if ((entry->sock = socket_tcp_listen(entry->ip, entry->port, sharing)) == -1)
344 panic("socket_tcp_listen"); 208 panic("socket_tcp_listen");
209 socket_defer_accept((int)entry->sock);
345 } else { 210 } else {
346 if ((entry->sock = socket_udp_bind(entry->ip, entry->port, sharing)) == -1) 211 if ((entry->sock = socket_udp_bind(entry->ip, entry->port, sharing)) == -1)
347 panic("socket_udp_bind"); 212 panic("socket_udp_bind");
@@ -365,10 +230,41 @@ static void listen_bind_all(void) {
365 listen_bind_one(entries + i); 230 listen_bind_one(entries + i);
366} 231}
367 232
368/* The socket the event loop was using becomes the first worker's. Where the 233/* The socket the event loop was using is passed to the first worker.
369 kernel spreads datagrams, every further worker binds one of its own, so 234 If start up tests show that the kernel can spread connections over
370 none of them share a receive queue. 235 a group bound to one address, every worker binds one of its own.
371 All others read from the first */ 236 If not, all workers call accept on the first. */
237static void listen_start_tcp_workers(void) {
238 size_t count, i;
239 ot_listen *entries = config_listen_entries(&count);
240
241 for (i = 0; i < count; ++i) {
242 ot_listen *entry = entries + i;
243 int balance = listen_sharing_for(entry) == SOCKET_BALANCED;
244 int worker;
245
246 if (entry->proto != FLAG_TCP || entry->workers <= 0)
247 continue;
248
249 event_del((int)entry->sock);
250 tcp_worker_install(entry->sock);
251
252 for (worker = 1; worker < entry->workers; ++worker) {
253 int64_t sock = entry->sock;
254
255 if (balance) {
256 sock = socket_tcp_listen(entry->ip, entry->port, SOCKET_BALANCED);
257 if (sock == -1) {
258 fprintf(stderr, "Warning: could not bind another socket for tcp worker %d: %s. Sharing the first instead.\n", worker,
259 strerror(errno));
260 sock = entry->sock;
261 }
262 }
263 tcp_worker_install(sock);
264 }
265 }
266}
267
372static void listen_start_udp_workers(void) { 268static void listen_start_udp_workers(void) {
373 size_t count, i; 269 size_t count, i;
374 ot_listen *entries = config_listen_entries(&count); 270 ot_listen *entries = config_listen_entries(&count);
@@ -529,9 +425,11 @@ int main(int argc, char **argv) {
529 425
530 install_signal_handlers(); 426 install_signal_handlers();
531 427
428 listen_start_tcp_workers();
532 listen_start_udp_workers(); 429 listen_start_udp_workers();
533 430
534 server_mainloop(0); 431 server_mainloop(0);
432 tcp_workers_deinit();
535 udp_workers_deinit(); 433 udp_workers_deinit();
536 434
537 /* No thread is serving any more, so the teardown can run where a debugger 435 /* No thread is serving any more, so the teardown can run where a debugger