summaryrefslogtreecommitdiff
path: root/opentracker.c
diff options
context:
space:
mode:
Diffstat (limited to 'opentracker.c')
-rw-r--r--opentracker.c214
1 files changed, 49 insertions, 165 deletions
diff --git a/opentracker.c b/opentracker.c
index b47759e..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,142 +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_set(sock, cookie, 0);
184 else if (keep_alive) {
185 /* The reply is out and the client asked to keep the connection, so go
186 back to waiting for the next request */
187 http_interest_set(sock, cookie, EVENT_IN);
188 fdstate_set_expiry(sock, time_future_seconds(OT_CLIENT_TIMEOUT));
189 } else
190 http_close(sock);
191}
192
193/* The fast path, assuming that the complete request has usually arrived by the time a
194 connection is accepted, so it is answered here outside the event loop. The more load
195 a tracker has, the more likely it is that by the time it accepts, the roundtrip to
196 the peer has completed.
197 If the request could not be completely handled, the stack cookie is promoted to heap
198 and the connection handed over to the event loop to finish */
199static void handle_accept(const int64_t serversocket, struct ot_workstruct *ws) {
200 int64_t sock;
201 ot_ip6 ip;
202 uint16_t port;
203
204 while ((sock = socket_accept(serversocket, ip, &port)) != -1) {
205 struct http_data cookie;
206 uint32_t generation;
207
208 memset(&cookie, 0, sizeof(cookie));
209 memcpy(cookie.ip, ip, sizeof(ot_ip6));
210 cookie.interest = EVENT_IN;
211 cookie.flag = STRUCT_HTTP_FLAG_ONSTACK;
212
213 generation = fdstate_open(sock, &cookie, time_future_seconds(OT_CLIENT_TIMEOUT));
214 if (!fdstate_claim((int)sock, generation)) {
215 fdstate_close(sock);
216 event_close((int)sock);
217 continue;
218 }
219
220 stats_issue_event(ws, EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip);
221
222 handle_read(sock, ws);
223
224 /* Gone already if it was answered and closed inline */
225 if (fdstate_generation((int)sock) != generation)
226 continue;
227
228 if (http_adopt(sock, &cookie) == -1)
229 http_close(sock);
230 else
231 fdstate_release((int)sock);
232 }
233}
234
235/* How many ready descriptors one wait hands back at a time */ 101/* How many ready descriptors one wait hands back at a time */
236#define OT_EVENT_BATCH 512 102#define OT_EVENT_BATCH 512
237 103
@@ -244,20 +110,9 @@ static void *server_mainloop(void *args) {
244 110
245 (void)args; 111 (void)args;
246 112
247 /* Initialize our "thread local storage" */ 113 if (workstruct_init(&ws) == -1)
248 memset(&ws, 0, sizeof(ws));
249 stats_counters_register(&ws);
250 ws.inbuf = malloc(G_INBUF_SIZE);
251 ws.outbuf = malloc(G_OUTBUF_SIZE);
252#ifdef _DEBUG_HTTPERROR
253 ws.debugbuf = malloc(G_DEBUGBUF_SIZE);
254#endif
255
256 if (!ws.inbuf || !ws.outbuf)
257 panic("Initializing worker failed"); 114 panic("Initializing worker failed");
258 115
259 random_bytes(ws.rand48_state, sizeof(ws.rand48_state));
260
261 while (g_opentracker_running) { 116 while (g_opentracker_running) {
262 ot_event ready[OT_EVENT_BATCH]; 117 ot_event ready[OT_EVENT_BATCH];
263 int64_t sock; 118 int64_t sock;
@@ -277,22 +132,24 @@ static void *server_mainloop(void *args) {
277 cookie = fdstate_cookie((int)sock); 132 cookie = fdstate_cookie((int)sock);
278 133
279 if ((intptr_t)cookie == FLAG_TCP) 134 if ((intptr_t)cookie == FLAG_TCP)
280 handle_accept(sock, &ws); 135 tcp_accept(sock, &ws);
281 else if ((intptr_t)cookie == FLAG_UDP) 136 else if ((intptr_t)cookie == FLAG_UDP)
282 handle_udp6(sock, &ws); 137 handle_udp6(sock, &ws);
283 else if ((intptr_t)cookie == FLAG_SELFPIPE) 138 else if ((intptr_t)cookie == FLAG_SELFPIPE)
284 while (read((int)sock, ws.inbuf, G_INBUF_SIZE) > 0) 139 while (read((int)sock, ws.inbuf, G_INBUF_SIZE) > 0)
285 ; 140 ;
286 else { 141 else if (fdstate_claim((int)sock, ready[i].generation)) {
287 if (ready[i].what & EVENT_IN) 142 if (ready[i].what & EVENT_IN)
288 handle_read(sock, &ws); 143 tcp_read(sock, &ws);
289 /* The read may have closed it, so look again before writing */ 144 /* The read may have closed it, so look again before writing */
290 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)
291 handle_write(sock); 146 tcp_write(sock);
147 if (fdstate_generation((int)sock) == ready[i].generation)
148 fdstate_release((int)sock);
292 } 149 }
293 } 150 }
294 151
295 while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) 152 while ((sock = workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1)
296 http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); 153 http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial);
297 154
298 if (time_now_seconds() > next_timeout_check) { 155 if (time_now_seconds() > next_timeout_check) {
@@ -308,13 +165,7 @@ static void *server_mainloop(void *args) {
308 165
309 /* 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
310 before anything can read it there */ 167 before anything can read it there */
311 stats_counters_retire(&ws); 168 workstruct_deinit(&ws);
312
313 free(ws.inbuf);
314 free(ws.outbuf);
315#ifdef _DEBUG_HTTPERROR
316 free(ws.debugbuf);
317#endif
318 return 0; 169 return 0;
319} 170}
320 171
@@ -323,7 +174,7 @@ static void *server_mainloop(void *args) {
323 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
324 this entry will have workers has to be known here. */ 175 this entry will have workers has to be known here. */
325static socket_sharing listen_sharing_for(const ot_listen *entry) { 176static socket_sharing listen_sharing_for(const ot_listen *entry) {
326 if (entry->proto == FLAG_UDP && entry->workers > 1 && socket_can_balance()) 177 if (entry->proto != FLAG_MCA && entry->workers > 1 && socket_can_balance())
327 return SOCKET_BALANCED; 178 return SOCKET_BALANCED;
328 return entry->shared ? SOCKET_SHARED : SOCKET_EXCLUSIVE; 179 return entry->shared ? SOCKET_SHARED : SOCKET_EXCLUSIVE;
329} 180}
@@ -379,10 +230,41 @@ static void listen_bind_all(void) {
379 listen_bind_one(entries + i); 230 listen_bind_one(entries + i);
380} 231}
381 232
382/* 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.
383 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
384 none of them share a receive queue. 235 a group bound to one address, every worker binds one of its own.
385 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
386static void listen_start_udp_workers(void) { 268static void listen_start_udp_workers(void) {
387 size_t count, i; 269 size_t count, i;
388 ot_listen *entries = config_listen_entries(&count); 270 ot_listen *entries = config_listen_entries(&count);
@@ -543,9 +425,11 @@ int main(int argc, char **argv) {
543 425
544 install_signal_handlers(); 426 install_signal_handlers();
545 427
428 listen_start_tcp_workers();
546 listen_start_udp_workers(); 429 listen_start_udp_workers();
547 430
548 server_mainloop(0); 431 server_mainloop(0);
432 tcp_workers_deinit();
549 udp_workers_deinit(); 433 udp_workers_deinit();
550 434
551 /* 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