summaryrefslogtreecommitdiff
path: root/opentracker.c
diff options
context:
space:
mode:
Diffstat (limited to 'opentracker.c')
-rw-r--r--opentracker.c198
1 files changed, 49 insertions, 149 deletions
diff --git a/opentracker.c b/opentracker.c
index ec92fe0..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,126 +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
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 */ 101/* How many ready descriptors one wait hands back at a time */
220#define OT_EVENT_BATCH 512 102#define OT_EVENT_BATCH 512
221 103
@@ -228,20 +110,9 @@ static void *server_mainloop(void *args) {
228 110
229 (void)args; 111 (void)args;
230 112
231 /* Initialize our "thread local storage" */ 113 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"); 114 panic("Initializing worker failed");
242 115
243 random_bytes(ws.rand48_state, sizeof(ws.rand48_state));
244
245 while (g_opentracker_running) { 116 while (g_opentracker_running) {
246 ot_event ready[OT_EVENT_BATCH]; 117 ot_event ready[OT_EVENT_BATCH];
247 int64_t sock; 118 int64_t sock;
@@ -261,22 +132,24 @@ static void *server_mainloop(void *args) {
261 cookie = fdstate_cookie((int)sock); 132 cookie = fdstate_cookie((int)sock);
262 133
263 if ((intptr_t)cookie == FLAG_TCP) 134 if ((intptr_t)cookie == FLAG_TCP)
264 handle_accept(sock, &ws); 135 tcp_accept(sock, &ws);
265 else if ((intptr_t)cookie == FLAG_UDP) 136 else if ((intptr_t)cookie == FLAG_UDP)
266 handle_udp6(sock, &ws); 137 handle_udp6(sock, &ws);
267 else if ((intptr_t)cookie == FLAG_SELFPIPE) 138 else if ((intptr_t)cookie == FLAG_SELFPIPE)
268 while (read((int)sock, ws.inbuf, G_INBUF_SIZE) > 0) 139 while (read((int)sock, ws.inbuf, G_INBUF_SIZE) > 0)
269 ; 140 ;
270 else { 141 else if (fdstate_claim((int)sock, ready[i].generation)) {
271 if (ready[i].what & EVENT_IN) 142 if (ready[i].what & EVENT_IN)
272 handle_read(sock, &ws); 143 tcp_read(sock, &ws);
273 /* The read may have closed it, so look again before writing */ 144 /* 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) 145 if ((ready[i].what & EVENT_OUT) && fdstate_generation((int)sock) == ready[i].generation)
275 handle_write(sock); 146 tcp_write(sock);
147 if (fdstate_generation((int)sock) == ready[i].generation)
148 fdstate_release((int)sock);
276 } 149 }
277 } 150 }
278 151
279 while ((sock = mutex_workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) 152 while ((sock = workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1)
280 http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); 153 http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial);
281 154
282 if (time_now_seconds() > next_timeout_check) { 155 if (time_now_seconds() > next_timeout_check) {
@@ -292,13 +165,7 @@ static void *server_mainloop(void *args) {
292 165
293 /* 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
294 before anything can read it there */ 167 before anything can read it there */
295 stats_counters_retire(&ws); 168 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; 169 return 0;
303} 170}
304 171
@@ -307,7 +174,7 @@ static void *server_mainloop(void *args) {
307 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
308 this entry will have workers has to be known here. */ 175 this entry will have workers has to be known here. */
309static socket_sharing listen_sharing_for(const ot_listen *entry) { 176static socket_sharing listen_sharing_for(const ot_listen *entry) {
310 if (entry->proto == FLAG_UDP && entry->workers > 1 && socket_can_balance()) 177 if (entry->proto != FLAG_MCA && entry->workers > 1 && socket_can_balance())
311 return SOCKET_BALANCED; 178 return SOCKET_BALANCED;
312 return entry->shared ? SOCKET_SHARED : SOCKET_EXCLUSIVE; 179 return entry->shared ? SOCKET_SHARED : SOCKET_EXCLUSIVE;
313} 180}
@@ -363,10 +230,41 @@ static void listen_bind_all(void) {
363 listen_bind_one(entries + i); 230 listen_bind_one(entries + i);
364} 231}
365 232
366/* 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.
367 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
368 none of them share a receive queue. 235 a group bound to one address, every worker binds one of its own.
369 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
370static void listen_start_udp_workers(void) { 268static void listen_start_udp_workers(void) {
371 size_t count, i; 269 size_t count, i;
372 ot_listen *entries = config_listen_entries(&count); 270 ot_listen *entries = config_listen_entries(&count);
@@ -527,9 +425,11 @@ int main(int argc, char **argv) {
527 425
528 install_signal_handlers(); 426 install_signal_handlers();
529 427
428 listen_start_tcp_workers();
530 listen_start_udp_workers(); 429 listen_start_udp_workers();
531 430
532 server_mainloop(0); 431 server_mainloop(0);
432 tcp_workers_deinit();
533 udp_workers_deinit(); 433 udp_workers_deinit();
534 434
535 /* 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