Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 316 lines (279 sloc) 11.077 kb
eeebb66 jayridge initial import of pubsub
jayridge authored
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
4 #include <time.h>
e91b668 Matt Reiferson refactor stats collection into simplehttp; add generic logging to simple...
mreiferson authored
5 #include <simplehttp/queue.h>
6 #include <simplehttp/simplehttp.h>
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
7 #include "http-internal.h"
eeebb66 jayridge initial import of pubsub
jayridge authored
8
09802dd Nathan Folkman Adding support for multipart.
nathanfolkman authored
9 #define BOUNDARY "xXPubSubXx"
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
10 #define MAX_PENDING_DATA 1024*1024*50
9eca153 Nathan Folkman Added BUFSIZE.
nathanfolkman authored
11
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
12 int ps_debug = 0;
13
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
14 enum kick_client_enum {
fff2d9d jayridge Cleaned up warnings.
jayridge authored
15 CLIENT_OK = 0,
16 KICK_CLIENT = 1,
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
17 };
eeebb66 jayridge initial import of pubsub
jayridge authored
18 typedef struct cli {
0e581ad jayridge added multipart=0
jayridge authored
19 int multipart;
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
20 int websocket;
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
21 enum kick_client_enum kick_client;
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
22 uint64_t connection_id;
23 time_t connect_time;
eeebb66 jayridge initial import of pubsub
jayridge authored
24 struct evbuffer *buf;
25 struct evhttp_request *req;
26 TAILQ_ENTRY(cli) entries;
27 } cli;
28 TAILQ_HEAD(, cli) clients;
29
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
30 uint64_t totalConns = 0;
31 uint64_t currentConns = 0;
1a34ab1 Jehiah Czebotar updating pubsub reader code; moving client code to pubsubclient folder
jehiah authored
32 uint64_t kickedClients = 0;
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
33 uint64_t msgRecv = 0;
34 uint64_t msgSent = 0;
1604d50 Nathan Folkman Added stats.
nathanfolkman authored
35
0e581ad jayridge added multipart=0
jayridge authored
36
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
37
38 int
39 is_slow(struct cli *client) {
40 if (client->kick_client == KICK_CLIENT) { return 1; }
41 struct evhttp_connection *evcon;
42 unsigned long output_buffer_length;
43
44 evcon = (struct evhttp_connection *)client->req->evcon;
fff2d9d jayridge Cleaned up warnings.
jayridge authored
45 output_buffer_length = evcon->output_buffer ? (unsigned long)EVBUFFER_LENGTH(evcon->output_buffer) : 0;
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
46 if (output_buffer_length > MAX_PENDING_DATA) {
1a34ab1 Jehiah Czebotar updating pubsub reader code; moving client code to pubsubclient folder
jehiah authored
47 kickedClients+=1;
fff2d9d jayridge Cleaned up warnings.
jayridge authored
48 fprintf(stdout, "%llu >> kicking client with %lu pending data\n", client->connection_id, output_buffer_length);
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
49 client->kick_client = KICK_CLIENT;
50 // clear the clients output buffer
51 evbuffer_drain(evcon->output_buffer, EVBUFFER_LENGTH(evcon->output_buffer));
fff2d9d jayridge Cleaned up warnings.
jayridge authored
52 evbuffer_add_printf(evcon->output_buffer, "ERROR_TOO_SLOW. kicked for having %lu pending bytes\n", output_buffer_length);
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
53 return 1;
54 }
55 return 0;
56 }
57
58 int
59 can_kick(struct cli *client) {
60 if (client->kick_client == CLIENT_OK){return 0;}
61 // if the buffer length is back to zero, we can kick now
62 // our error notice has been pushed to the client
63 struct evhttp_connection *evcon;
64 evcon = (struct evhttp_connection *)client->req->evcon;
65 if (EVBUFFER_LENGTH(evcon->output_buffer) == 0){
66 return 1;
67 }
68 return 0;
69 }
70
0e581ad jayridge added multipart=0
jayridge authored
71 void
72 argtoi(struct evkeyvalq *args, char *key, int *val, int def)
73 {
74 char *tmp;
75
76 *val = def;
77 tmp = (char *)evhttp_find_header(args, (const char *)key);
78 if (tmp) {
79 *val = atoi(tmp);
80 }
81 }
82
1604d50 Nathan Folkman Added stats.
nathanfolkman authored
83 void
1b59fb8 Nathan Folkman Added new /clients callback.
nathanfolkman authored
84 clients_cb(struct evhttp_request *req, struct evbuffer *evb, void *ctx)
85 {
86 struct cli *client;
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
87 struct tm *time_struct;
88 char buf[248];
89 unsigned long output_buffer_length;
90 struct evhttp_connection *evcon;
1b59fb8 Nathan Folkman Added new /clients callback.
nathanfolkman authored
91
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
92 if (TAILQ_EMPTY(&clients)) {
93 evbuffer_add_printf(evb, "no /sub connections\n");
94 }
1b59fb8 Nathan Folkman Added new /clients callback.
nathanfolkman authored
95 TAILQ_FOREACH(client, &clients, entries) {
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
96 evcon = (struct evhttp_connection *)client->req->evcon;
97
98 time_struct = gmtime(&client->connect_time);
99 strftime(buf, 248, "%Y-%m-%d %H:%M:%S", time_struct);
100 output_buffer_length = (unsigned long)EVBUFFER_LENGTH(evcon->output_buffer);
fff2d9d jayridge Cleaned up warnings.
jayridge authored
101 evbuffer_add_printf(evb, "%s:%d connected at %s. output buffer size:%lu state:%d\n",
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
102 client->req->remote_host,
103 client->req->remote_port,
104 buf,
105 output_buffer_length,
106 (int)evcon->state);
1b59fb8 Nathan Folkman Added new /clients callback.
nathanfolkman authored
107 }
108
109 evhttp_send_reply(req, HTTP_OK, "OK", evb);
110 }
111
112 void
38c69fd Nathan Folkman Moved stats to HTTP response headers.
nathanfolkman authored
113 stats_cb(struct evhttp_request *req, struct evbuffer *evb, void *ctx)
1604d50 Nathan Folkman Added stats.
nathanfolkman authored
114 {
5b0770f Nathan Folkman Fixed args.
nathanfolkman authored
115 struct evkeyvalq args;
38c69fd Nathan Folkman Moved stats to HTTP response headers.
nathanfolkman authored
116 char buf[33];
076e8df Matt Reiferson updated pubsub with json stats, parse query args before accessing, accep...
mreiferson authored
117 const char *reset;
118 const char *format;
38c69fd Nathan Folkman Moved stats to HTTP response headers.
nathanfolkman authored
119
1a34ab1 Jehiah Czebotar updating pubsub reader code; moving client code to pubsubclient folder
jehiah authored
120 sprintf(buf, "%llu", totalConns);
38c69fd Nathan Folkman Moved stats to HTTP response headers.
nathanfolkman authored
121 evhttp_add_header(req->output_headers, "X-PUBSUB-TOTAL-CONNECTIONS", buf);
1a34ab1 Jehiah Czebotar updating pubsub reader code; moving client code to pubsubclient folder
jehiah authored
122 sprintf(buf, "%llu", currentConns);
8ee367b Nathan Folkman Fixed stat name.
nathanfolkman authored
123 evhttp_add_header(req->output_headers, "X-PUBSUB-ACTIVE-CONNECTIONS", buf);
1a34ab1 Jehiah Czebotar updating pubsub reader code; moving client code to pubsubclient folder
jehiah authored
124 sprintf(buf, "%llu", msgRecv);
38c69fd Nathan Folkman Moved stats to HTTP response headers.
nathanfolkman authored
125 evhttp_add_header(req->output_headers, "X-PUBSUB-MESSAGES-RECEIVED", buf);
1a34ab1 Jehiah Czebotar updating pubsub reader code; moving client code to pubsubclient folder
jehiah authored
126 sprintf(buf, "%llu", msgSent);
38c69fd Nathan Folkman Moved stats to HTTP response headers.
nathanfolkman authored
127 evhttp_add_header(req->output_headers, "X-PUBSUB-MESSAGES-SENT", buf);
1a34ab1 Jehiah Czebotar updating pubsub reader code; moving client code to pubsubclient folder
jehiah authored
128 sprintf(buf, "%llu", kickedClients);
129 evhttp_add_header(req->output_headers, "X-PUBSUB-KICKED-CLIENTS", buf);
38c69fd Nathan Folkman Moved stats to HTTP response headers.
nathanfolkman authored
130
076e8df Matt Reiferson updated pubsub with json stats, parse query args before accessing, accep...
mreiferson authored
131 evhttp_parse_query(req->uri, &args);
132 format = (char *)evhttp_find_header(&args, "format");
133
134 if ((format != NULL) && (strcmp(format, "json") == 0)) {
135 evbuffer_add_printf(evb, "{");
136 evbuffer_add_printf(evb, "\"current_connections\": %llu,", currentConns);
137 evbuffer_add_printf(evb, "\"total_connections\": %llu,", totalConns);
138 evbuffer_add_printf(evb, "\"messages_received\": %llu,", msgRecv);
139 evbuffer_add_printf(evb, "\"messages_sent\": %llu,", msgSent);
140 evbuffer_add_printf(evb, "\"kicked_clients\": %llu,", kickedClients);
141 evbuffer_add_printf(evb, "}\n");
142 } else {
143 evbuffer_add_printf(evb, "Active connections: %llu\n", currentConns);
144 evbuffer_add_printf(evb, "Total connections: %llu\n", totalConns);
145 evbuffer_add_printf(evb, "Messages received: %llu\n", msgRecv);
146 evbuffer_add_printf(evb, "Messages sent: %llu\n", msgSent);
147 evbuffer_add_printf(evb, "Kicked clients: %llu\n", kickedClients);
148 }
149
38c69fd Nathan Folkman Moved stats to HTTP response headers.
nathanfolkman authored
150 reset = (char *)evhttp_find_header(&args, "reset");
b1d1329 Nathan Folkman Trying to fix new stats command.
nathanfolkman authored
151 if (reset) {
1604d50 Nathan Folkman Added stats.
nathanfolkman authored
152 msgRecv = 0;
153 msgSent = 0;
154 }
076e8df Matt Reiferson updated pubsub with json stats, parse query args before accessing, accep...
mreiferson authored
155
38c69fd Nathan Folkman Moved stats to HTTP response headers.
nathanfolkman authored
156 evhttp_send_reply(req, HTTP_OK, "OK", evb);
157 evhttp_clear_headers(&args);
1604d50 Nathan Folkman Added stats.
nathanfolkman authored
158 }
eeebb66 jayridge initial import of pubsub
jayridge authored
159
160 void on_close(struct evhttp_connection *evcon, void *ctx)
161 {
162 struct cli *client = (struct cli *)ctx;
163
164 if (client) {
d5089c8 Jehiah Czebotar %lu -> %llu formating
jehiah authored
165 fprintf(stdout, "%llu >> close from %s:%d\n", client->connection_id, evcon->address, evcon->port);
1604d50 Nathan Folkman Added stats.
nathanfolkman authored
166 currentConns--;
eeebb66 jayridge initial import of pubsub
jayridge authored
167 TAILQ_REMOVE(&clients, client, entries);
168 evbuffer_free(client->buf);
169 free(client);
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
170 } else {
171 fprintf(stdout, "[unknown] >> close from %s:%d\n", evcon->address, evcon->port);
eeebb66 jayridge initial import of pubsub
jayridge authored
172 }
173 }
174
175 void pub_cb(struct evhttp_request *req, struct evbuffer *evb, void *ctx)
176 {
177 int i = 0;
178 struct cli *client;
1604d50 Nathan Folkman Added stats.
nathanfolkman authored
179
180 msgRecv++;
5b0770f Nathan Folkman Fixed args.
nathanfolkman authored
181 totalConns++;
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
182
eeebb66 jayridge initial import of pubsub
jayridge authored
183 TAILQ_FOREACH(client, &clients, entries) {
1604d50 Nathan Folkman Added stats.
nathanfolkman authored
184 msgSent++;
eeebb66 jayridge initial import of pubsub
jayridge authored
185 evbuffer_drain(client->buf, EVBUFFER_LENGTH(client->buf));
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
186 if (is_slow(client)) {
187 if (can_kick(client)) {
188 evhttp_connection_free(client->req->evcon);
189 continue;
190 }
191 continue;
192 }
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
193 if (client->websocket) {
194 // set to non-chunked so that send_reply_chunked doesn't add \r\n before/after this block
195 client->req->chunked = 0;
196 // write the frame. a websocket frame is \x00 + msg + \xFF
eae7c73 jayridge initial import of sorted file db
jayridge authored
197 evbuffer_add(client->buf, "\0", 1);
198 evbuffer_add(client->buf, EVBUFFER_DATA(req->input_buffer), EVBUFFER_LENGTH(req->input_buffer));
199 evbuffer_add(client->buf, "\xFF", 1);
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
200 }
201 else if (client->multipart) {
202 /* chunked */
0e581ad jayridge added multipart=0
jayridge authored
203 evbuffer_add_printf(client->buf,
204 "content-type: %s\r\ncontent-length: %d\r\n\r\n",
205 "*/*",
206 (int)EVBUFFER_LENGTH(req->input_buffer));
eae7c73 jayridge initial import of sorted file db
jayridge authored
207 evbuffer_add(client->buf, EVBUFFER_DATA(req->input_buffer), EVBUFFER_LENGTH(req->input_buffer));
0e581ad jayridge added multipart=0
jayridge authored
208 evbuffer_add_printf(client->buf, "\r\n--%s\r\n", BOUNDARY);
209 } else {
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
210 /* new line terminated */
eae7c73 jayridge initial import of sorted file db
jayridge authored
211 evbuffer_add(client->buf, EVBUFFER_DATA(req->input_buffer), EVBUFFER_LENGTH(req->input_buffer));
0e581ad jayridge added multipart=0
jayridge authored
212 evbuffer_add_printf(client->buf, "\n");
213 }
eeebb66 jayridge initial import of pubsub
jayridge authored
214 evhttp_send_reply_chunk(client->req, client->buf);
215 i++;
216 }
5b0770f Nathan Folkman Fixed args.
nathanfolkman authored
217
eeebb66 jayridge initial import of pubsub
jayridge authored
218 evbuffer_add_printf(evb, "Published to %d clients.\n", i);
219 evhttp_send_reply(req, HTTP_OK, "OK", evb);
220 }
221
222 void sub_cb(struct evhttp_request *req, struct evbuffer *evb, void *ctx)
223 {
224 struct cli *client;
0e581ad jayridge added multipart=0
jayridge authored
225 struct evkeyvalq args;
226 char *uri;
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
227 char *ws_origin;
228 char *ws_upgrade;
229 char *host;
230 char buf[248];
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
231 struct tm *time_struct;
eeebb66 jayridge initial import of pubsub
jayridge authored
232
1604d50 Nathan Folkman Added stats.
nathanfolkman authored
233 currentConns++;
234 totalConns++;
40373bf Jehiah Czebotar dont double-decode uri
jehiah authored
235 evhttp_parse_query(req->uri, &args);
eeebb66 jayridge initial import of pubsub
jayridge authored
236 client = calloc(1, sizeof(*client));
0e581ad jayridge added multipart=0
jayridge authored
237 argtoi(&args, "multipart", &client->multipart, 1);
eeebb66 jayridge initial import of pubsub
jayridge authored
238 client->req = req;
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
239 client->connection_id = totalConns;
240 client->connect_time = time(NULL);
241 time_struct = gmtime(&client->connect_time);
eeebb66 jayridge initial import of pubsub
jayridge authored
242 client->buf = evbuffer_new();
7cbcd85 Jehiah Czebotar kick slow clients and write a message (after dropping their pending data...
jehiah authored
243 client->kick_client = CLIENT_OK;
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
244
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
245 strftime(buf, 248, "%Y-%m-%d %H:%M:%S", time_struct);
246
247 // print out info about this connection
d5089c8 Jehiah Czebotar %lu -> %llu formating
jehiah authored
248 fprintf(stdout, "%llu >> /sub connection from %s:%d %s\n", client->connection_id, req->remote_host, req->remote_port, buf);
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
249
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
250 // Connection: Upgrade
251 // Upgrade: WebSocket
252 ws_upgrade = (char *) evhttp_find_header(req->input_headers, "Upgrade");
253 ws_origin = (char *) evhttp_find_header(req->input_headers, "Origin");
254 host = (char *) evhttp_find_header(req->input_headers, "Host");
255
256 if (ps_debug && ws_upgrade) {
d5089c8 Jehiah Czebotar %lu -> %llu formating
jehiah authored
257 fprintf(stderr, "%llu >> upgrade header is %s\n", client->connection_id, ws_upgrade);
258 fprintf(stderr, "%llu >> multipart is %d\n", client->connection_id, client->multipart);
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
259 }
260
261 if (ws_upgrade && strstr(ws_upgrade, "WebSocket") != NULL) {
262 if (ps_debug) {
d5089c8 Jehiah Czebotar %lu -> %llu formating
jehiah authored
263 fprintf(stderr, "%llu >> upgrading connection to a websocket\n", client->connection_id);
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
264 }
265 client->websocket = 1;
266 client->req->major = 1;
267 client->req->minor = 1;
268 evhttp_add_header(client->req->output_headers, "Upgrade", "WebSocket");
269 evhttp_add_header(client->req->output_headers, "Connection", "Upgrade");
270 evhttp_add_header(client->req->output_headers, "Server", "simplehttp/pubsub");
271 if (ws_origin) {
272 evhttp_add_header(client->req->output_headers, "WebSocket-Origin", ws_origin);
273 }
274 if (host) {
275 sprintf(buf, "ws://%s%s", host, req->uri);
276 if (ps_debug) {
d5089c8 Jehiah Czebotar %lu -> %llu formating
jehiah authored
277 fprintf(stderr, "%llu >> setting WebSocket-Location to %s\n", client->connection_id, buf);
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
278 }
279 evhttp_add_header(client->req->output_headers, "WebSocket-Location", buf);
280 }
281 // evbuffer_add_printf(client->buf, "\r\n");
282 }
283 else if (client->multipart) {
0e581ad jayridge added multipart=0
jayridge authored
284 evhttp_add_header(client->req->output_headers, "content-type",
285 "multipart/x-mixed-replace; boundary=" BOUNDARY);
286 evbuffer_add_printf(client->buf, "--%s\r\n", BOUNDARY);
287 } else {
288 evhttp_add_header(client->req->output_headers, "content-type",
289 "application/json");
290 evbuffer_add_printf(client->buf, "\r\n");
291 }
6db1157 Jehiah Czebotar pubsub server speaking websocket's
jehiah authored
292 if (client->websocket) {
293 evhttp_send_reply_start(client->req, 101, "Web Socket Protocol Handshake");
294 } else {
295 evhttp_send_reply_start(client->req, HTTP_OK, "OK");
296 }
09802dd Nathan Folkman Adding support for multipart.
nathanfolkman authored
297 evhttp_send_reply_chunk(client->req, client->buf);
eeebb66 jayridge initial import of pubsub
jayridge authored
298 TAILQ_INSERT_TAIL(&clients, client, entries);
299 evhttp_connection_set_closecb(req->evcon, on_close, (void *)client);
0e581ad jayridge added multipart=0
jayridge authored
300 evhttp_clear_headers(&args);
eeebb66 jayridge initial import of pubsub
jayridge authored
301 }
302
303 int
304 main(int argc, char **argv)
305 {
306 TAILQ_INIT(&clients);
307 simplehttp_init();
308 simplehttp_set_cb("/pub*", pub_cb, NULL);
309 simplehttp_set_cb("/sub*", sub_cb, NULL);
076e8df Matt Reiferson updated pubsub with json stats, parse query args before accessing, accep...
mreiferson authored
310 simplehttp_set_cb("/stats*", stats_cb, NULL);
ac22e1f Jehiah Czebotar connection stats that include the size of outgoing buffers for connected...
jehiah authored
311 simplehttp_set_cb("/clients", clients_cb, NULL);
eeebb66 jayridge initial import of pubsub
jayridge authored
312 simplehttp_main(argc, argv);
313
314 return 0;
315 }
Something went wrong with that request. Please try again.