summaryrefslogtreecommitdiff
path: root/opentracker.c
diff options
context:
space:
mode:
Diffstat (limited to 'opentracker.c')
-rw-r--r--opentracker.c191
1 files changed, 44 insertions, 147 deletions
diff --git a/opentracker.c b/opentracker.c
index ec92fe0..e5efff5 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -35,6 +35,7 @@
35#include "ot_fdstate.h" 35#include "ot_fdstate.h"
36#include "ot_socket.h" 36#include "ot_socket.h"
37#include "ot_stats.h" 37#include "ot_stats.h"
38#include "ot_tcp.h"
38#include "ot_udp.h" 39#include "ot_udp.h"
39#include "trackerlogic.h" 40#include "trackerlogic.h"
40 41
@@ -96,126 +97,6 @@ static void install_signal_handlers(void) {
96} 97}
97 98
98 99
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
193static void handle_accept(const int64_t serversocket, struct ot_workstruct *ws) {
194 struct http_data *cookie;
195 int64_t sock;
196 ot_ip6 ip;
197 uint16_t port;
198
199 while ((sock = socket_accept(serversocket, ip, &port)) != -1) {
200
201 if (!(cookie = (struct http_data *)calloc(1, sizeof(struct http_data)))) {
202 close((int)sock);
203 continue;
204 }
205 memcpy(cookie->ip, ip, sizeof(ot_ip6));
206 cookie->interest = EVENT_IN;
207
208 if (event_add((int)sock, EVENT_IN, fdstate_open(sock, cookie, time_future_seconds(OT_CLIENT_TIMEOUT))) == -1) {
209 fdstate_close(sock);
210 free(cookie);
211 close((int)sock);
212 continue;
213 }
214
215 stats_issue_event(ws, EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip);
216 }
217}
218
219/* How many ready descriptors one wait hands back at a time */ 100/* How many ready descriptors one wait hands back at a time */
220#define OT_EVENT_BATCH 512 101#define OT_EVENT_BATCH 512
221 102
@@ -228,20 +109,9 @@ static void *server_mainloop(void *args) {
228 109
229 (void)args; 110 (void)args;
230 111
231 /* Initialize our "thread local storage" */ 112 if (workstruct_init(&ws) == -1)
232 memset(&ws, 0, sizeof(ws));
233 stats_counters_register(&ws);
234 ws.inbuf = malloc(G_INBUF_SIZE);
235 ws.outbuf = malloc(G_OUTBUF_SIZE);
236#ifdef _DEBUG_HTTPERROR
237 ws.debugbuf = malloc(G_DEBUGBUF_SIZE);
238#endif
239
240 if (!ws.inbuf || !ws.outbuf)
241 panic("Initializing worker failed"); 113 panic("Initializing worker failed");
242 114
243 random_bytes(ws.rand48_state, sizeof(ws.rand48_state));
244
245 while (g_opentracker_running) { 115 while (g_opentracker_running) {
246 ot_event ready[OT_EVENT_BATCH]; 116 ot_event ready[OT_EVENT_BATCH];
247 int64_t sock; 117 int64_t sock;
@@ -261,7 +131,7 @@ static void *server_mainloop(void *args) {
261 cookie = fdstate_cookie((int)sock); 131 cookie = fdstate_cookie((int)sock);
262 132
263 if ((intptr_t)cookie == FLAG_TCP) 133 if ((intptr_t)cookie == FLAG_TCP)
264 handle_accept(sock, &ws); 134 tcp_accept(sock, &ws);
265 else if ((intptr_t)cookie == FLAG_UDP) 135 else if ((intptr_t)cookie == FLAG_UDP)
266 handle_udp6(sock, &ws); 136 handle_udp6(sock, &ws);
267 else if ((intptr_t)cookie == FLAG_SELFPIPE) 137 else if ((intptr_t)cookie == FLAG_SELFPIPE)
@@ -269,10 +139,10 @@ static void *server_mainloop(void *args) {
269 ; 139 ;
270 else { 140 else {
271 if (ready[i].what & EVENT_IN) 141 if (ready[i].what & EVENT_IN)
272 handle_read(sock, &ws); 142 tcp_read(sock, &ws);
273 /* The read may have closed it, so look again before writing */ 143 /* The read may have closed it, so look again before writing */
274 if ((ready[i].what & EVENT_OUT) && fdstate_generation((int)sock) == ready[i].generation) 144 if ((ready[i].what & EVENT_OUT) && fdstate_generation((int)sock) == ready[i].generation)
275 handle_write(sock); 145 tcp_write(sock);
276 } 146 }
277 } 147 }
278 148
@@ -292,13 +162,7 @@ static void *server_mainloop(void *args) {
292 162
293 /* This work struct is about to go out of scope, so hand its tally over 163 /* This work struct is about to go out of scope, so hand its tally over
294 before anything can read it there */ 164 before anything can read it there */
295 stats_counters_retire(&ws); 165 workstruct_deinit(&ws);
296
297 free(ws.inbuf);
298 free(ws.outbuf);
299#ifdef _DEBUG_HTTPERROR
300 free(ws.debugbuf);
301#endif
302 return 0; 166 return 0;
303} 167}
304 168
@@ -307,7 +171,7 @@ static void *server_mainloop(void *args) {
307 member asked for it before binding, the first one included, so whether 171 member asked for it before binding, the first one included, so whether
308 this entry will have workers has to be known here. */ 172 this entry will have workers has to be known here. */
309static socket_sharing listen_sharing_for(const ot_listen *entry) { 173static socket_sharing listen_sharing_for(const ot_listen *entry) {
310 if (entry->proto == FLAG_UDP && entry->workers > 1 && socket_can_balance()) 174 if (entry->proto != FLAG_MCA && entry->workers > 1 && socket_can_balance())
311 return SOCKET_BALANCED; 175 return SOCKET_BALANCED;
312 return entry->shared ? SOCKET_SHARED : SOCKET_EXCLUSIVE; 176 return entry->shared ? SOCKET_SHARED : SOCKET_EXCLUSIVE;
313} 177}
@@ -363,10 +227,41 @@ static void listen_bind_all(void) {
363 listen_bind_one(entries + i); 227 listen_bind_one(entries + i);
364} 228}
365 229
366/* The socket the event loop was using becomes the first worker's. Where the 230/* The socket the event loop was using is passed to the first worker.
367 kernel spreads datagrams, every further worker binds one of its own, so 231 If start up tests show that the kernel can spread connections over
368 none of them share a receive queue. 232 a group bound to one address, every worker binds one of its own.
369 All others read from the first */ 233 If not, all workers call accept on the first. */
234static void listen_start_tcp_workers(void) {
235 size_t count, i;
236 ot_listen *entries = config_listen_entries(&count);
237
238 for (i = 0; i < count; ++i) {
239 ot_listen *entry = entries + i;
240 int balance = listen_sharing_for(entry) == SOCKET_BALANCED;
241 int worker;
242
243 if (entry->proto != FLAG_TCP || entry->workers <= 0)
244 continue;
245
246 event_del((int)entry->sock);
247 tcp_worker_install(entry->sock);
248
249 for (worker = 1; worker < entry->workers; ++worker) {
250 int64_t sock = entry->sock;
251
252 if (balance) {
253 sock = socket_tcp_listen(entry->ip, entry->port, SOCKET_BALANCED);
254 if (sock == -1) {
255 fprintf(stderr, "Warning: could not bind another socket for tcp worker %d: %s. Sharing the first instead.\n", worker,
256 strerror(errno));
257 sock = entry->sock;
258 }
259 }
260 tcp_worker_install(sock);
261 }
262 }
263}
264
370static void listen_start_udp_workers(void) { 265static void listen_start_udp_workers(void) {
371 size_t count, i; 266 size_t count, i;
372 ot_listen *entries = config_listen_entries(&count); 267 ot_listen *entries = config_listen_entries(&count);
@@ -527,9 +422,11 @@ int main(int argc, char **argv) {
527 422
528 install_signal_handlers(); 423 install_signal_handlers();
529 424
425 listen_start_tcp_workers();
530 listen_start_udp_workers(); 426 listen_start_udp_workers();
531 427
532 server_mainloop(0); 428 server_mainloop(0);
429 tcp_workers_deinit();
533 udp_workers_deinit(); 430 udp_workers_deinit();
534 431
535 /* No thread is serving any more, so the teardown can run where a debugger 432 /* No thread is serving any more, so the teardown can run where a debugger