summaryrefslogtreecommitdiff
path: root/ot_tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_tcp.c')
-rw-r--r--ot_tcp.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/ot_tcp.c b/ot_tcp.c
new file mode 100644
index 0000000..8647a47
--- /dev/null
+++ b/ot_tcp.c
@@ -0,0 +1,157 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <errno.h>
8#include <string.h>
9#include <unistd.h>
10
11/* Opentracker */
12#include "ot_batch.h"
13#include "ot_buf.h"
14#include "ot_event.h"
15#include "ot_fdstate.h"
16#include "ot_http.h"
17#include "ot_mutex.h"
18#include "ot_socket.h"
19#include "ot_stats.h"
20#include "ot_tcp.h"
21#include "trackerlogic.h"
22
23static ssize_t header_complete(char *request, ssize_t byte_count) {
24 ssize_t i = 0, state = 0;
25
26 for (i = 1; i < byte_count; i += 2)
27 if (request[i] <= 13) {
28 i--;
29 for (state = 0; i < byte_count; ++i) {
30 char c = request[i];
31 if (c == '\r' || c == '\n')
32 state = (state >> 2) | ((c << 6) & 0xc0);
33 else
34 break;
35 if (state >= 0xa0 || state == 0x99)
36 return i + 1;
37 }
38 }
39 return 0;
40}
41
42void tcp_read(const int64_t sock, struct ot_workstruct *ws) {
43 struct http_data *cookie = fdstate_cookie(sock);
44 ssize_t byte_count = read((int)sock, ws->inbuf, G_INBUF_SIZE);
45
46 if (byte_count == 0 || (byte_count == -1 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)) {
47 http_close(sock);
48 return;
49 }
50
51 if (byte_count == -1)
52 return;
53
54 ws->request = NULL;
55 ws->request_size = 0;
56 ws->header_size = 0;
57 ws->keep_alive = 0;
58
59 /* If we get the whole request in one packet, handle it without copying */
60 if (!cookie->request.data) {
61 if ((ws->header_size = header_complete(ws->inbuf, byte_count))) {
62 ws->request = ws->inbuf;
63 ws->request_size = byte_count;
64 http_handle_request(sock, ws);
65 } else if (buf_cat(&cookie->request, ws->inbuf, (size_t)byte_count))
66 http_close(sock);
67 return;
68 }
69
70 if (buf_cat(&cookie->request, ws->inbuf, (size_t)byte_count) || cookie->request.size > G_INBUF_SIZE) {
71 http_issue_error(sock, ws, CODE_HTTPERROR_500);
72 return;
73 }
74
75 while ((ws->header_size = header_complete(cookie->request.data, (ssize_t)cookie->request.size))) {
76 ws->request = cookie->request.data;
77 ws->request_size = (ssize_t)cookie->request.size;
78 http_handle_request(sock, ws);
79#ifdef WANT_KEEPALIVE
80 if (!ws->keep_alive)
81#endif
82 return;
83 }
84}
85
86void tcp_write(const int64_t sock) {
87 struct http_data *cookie = fdstate_cookie(sock);
88 int chunked = 0;
89 int keep_alive = 0;
90
91 if (cookie) {
92 chunked = (cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER) != 0;
93 keep_alive = (cookie->flag & STRUCT_HTTP_FLAG_KEEPALIVE) != 0;
94
95 if (cookie->batch) {
96 if (batch_send(sock, cookie->batch) == BATCH_FAILED) {
97 http_close(sock);
98 return;
99 }
100 if (batch_pending(cookie->batch))
101 return;
102 }
103 }
104
105 /* In a chunked transfer, wait for the next piece once everything queued has gone out */
106 if (chunked)
107 http_interest_set(sock, cookie, 0);
108 else if (keep_alive) {
109 /* The reply is out and the client asked to keep the connection, so go
110 back to waiting for the next request */
111 http_interest_set(sock, cookie, EVENT_IN);
112 fdstate_set_expiry(sock, time_future_seconds(OT_CLIENT_TIMEOUT));
113 } else
114 http_close(sock);
115}
116
117/* The fast path, assuming that the complete request has usually arrived by the time a
118 connection is accepted, so it is answered here outside the event loop. The more load
119 a tracker has, the more likely it is that by the time it accepts, the roundtrip to
120 the peer has completed.
121 If the request could not be completely handled, the stack cookie is promoted to heap
122 and the connection handed over to the event loop to finish */
123void tcp_accept(const int64_t serversocket, struct ot_workstruct *ws) {
124 int64_t sock;
125 ot_ip6 ip;
126 uint16_t port;
127
128 while ((sock = socket_accept(serversocket, ip, &port)) != -1) {
129 struct http_data cookie;
130 uint32_t generation;
131
132 memset(&cookie, 0, sizeof(cookie));
133 memcpy(cookie.ip, ip, sizeof(ot_ip6));
134 cookie.interest = EVENT_IN;
135 cookie.flag = STRUCT_HTTP_FLAG_ONSTACK;
136
137 generation = fdstate_open(sock, &cookie, time_future_seconds(OT_CLIENT_TIMEOUT));
138 if (!fdstate_claim((int)sock, generation)) {
139 fdstate_close(sock);
140 event_close((int)sock);
141 continue;
142 }
143
144 stats_issue_event(ws, EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip);
145
146 tcp_read(sock, ws);
147
148 /* Gone already if it was answered and closed inline */
149 if (fdstate_generation((int)sock) != generation)
150 continue;
151
152 if (http_adopt(sock, &cookie) == -1)
153 http_close(sock);
154 else
155 fdstate_release((int)sock);
156 }
157}