]> rtime.felk.cvut.cz Git - coffee/mt-apps.git/blob - mt_server.c
data stored in context
[coffee/mt-apps.git] / mt_server.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4
5 #include "mt_server.h"
6 #include "signal_exit.h"
7
8 char *new_line()
9 {
10     char *line = (char *)malloc((LWS_PRE + INPUT_LINE_LENGTH)*sizeof(char));
11     if (!line) {
12         perror("malloc");
13         return NULL;
14     }
15     return line + LWS_PRE;
16 }
17
18 char *copy_line(char *line) {
19     char *copy = new_line();
20     if (!copy) {
21         return NULL;
22     }
23     strncpy(copy, line, INPUT_LINE_LENGTH);
24 }
25
26 void free_line(char *line)
27 {
28     free(line - LWS_PRE);
29 }
30
31 node *node_init(char *line)
32 {
33     node *res = (node *)malloc(sizeof(node));
34     if (!res) {
35         perror("malloc");
36         return res;
37     }
38     res->line = line;;
39     res->next = NULL;
40     return res;
41 }
42
43 list *list_init()
44 {
45     list *res = (list *)calloc(1, sizeof(list));
46     if (!res) {
47         perror("malloc");
48     }
49     return res;
50 }
51
52 int list_add(list *in, char *line)
53 {
54     node *new = node_init(line);
55     if (!new) {
56         return -1;
57     }
58     if (!in->first) {
59         in->first = new;
60     } else {
61         in->last->next = new;
62     }
63     in->last = new;
64     return 0;
65 }
66
67 void list_remove(list *in)
68 {
69     node *tmp = in->first;
70     if (tmp) {
71         in->first = tmp->next;
72         if (!tmp->next) {
73             in->last = NULL;
74         }
75         free_line(tmp->line);
76         free(tmp);
77     }
78 }
79
80 void list_deinit(list *in)
81 {
82     while (in->first) {
83         list_remove(in);
84     }
85     free(in);
86 }
87
88 static const struct lws_http_mount mount = {
89     /* .mount_next */            NULL,         /* linked-list "next" */
90     /* .mountpoint */            HTTP_MOUNTPOINT,
91     /* .origin */                HTTP_ORIGIN,  /* serve from dir */
92     /* .def */                   HTTP_DEFAULT, /* default filename */
93     /* .protocol */              NULL,
94     /* .cgienv */                NULL,
95     /* .extra_mimetypes */       NULL,
96     /* .interpret */             NULL,
97     /* .cgi_timeout */           0,
98     /* .cache_max_age */         0,
99     /* .auth_mask */             0,
100     /* .cache_reusable */        0,
101     /* .cache_revalidate */      0,
102     /* .cache_intermediaries */  0,
103     /* .origin_protocol */       LWSMPRO_FILE, /* files in a dir */
104     /* .mountpoint_len */        1,            /* char count */
105     /* .basic_auth_login_file */ NULL,
106 };
107
108 typedef struct per_vhost_data__merica_terminal {
109     struct lws_context *context;
110     struct lws_vhost *vhost;
111     const struct lws_protocols *protocol;
112 } per_vhost_data__merica_terminal;
113
114 /*
115 typedef struct per_session_data__merica_terminal {
116     int number;
117 } per_session_data__merica_terminal;
118 */
119
120 enum protocols {
121     PROTOCOL_HTTP = 0, // always first
122     PROTOCOL_MERICA_TERMINAL
123 };
124
125 static int callback_merica_terminal(struct lws *wsi,
126                                     enum lws_callback_reasons reason,
127                                     void *user, void *in, size_t len);
128
129 // list of supported protocols and callbacks
130 static struct lws_protocols protocols[] = {
131     // first protocol must always be HTTP handler
132     {"http", lws_callback_http_dummy, 0, 0},
133     {
134         "merica-terminal-protocol",
135         callback_merica_terminal,
136         0, //sizeof(struct per_session_data__merica_terminal),
137         MT_PROTOCOL_RX_BUFFER_SIZE
138     },
139     {NULL, NULL, 0, 0} // terminator
140 };
141
142 static int callback_merica_terminal(struct lws *wsi,
143                                     enum lws_callback_reasons reason,
144                                     void *user, void *in, size_t len)
145 {
146     struct lws_context *context = lws_get_context(wsi);
147     struct lws_vhost *vhost = lws_get_vhost(wsi);
148     const struct lws_protocols *prot = lws_get_protocol(wsi);
149     /*
150     per_session_data__merica_terminal *pss =
151         (per_session_data__merica_terminal *)user;
152     */
153     per_vhost_data__merica_terminal *vhd =
154         (per_vhost_data__merica_terminal *)
155         lws_protocol_vh_priv_get(vhost, prot);
156
157     int n, m;
158
159     list *lines = (list *)lws_context_user(context);
160     char *line;
161
162     switch (reason) {
163         case LWS_CALLBACK_PROTOCOL_INIT:
164             vhd = lws_protocol_vh_priv_zalloc(
165                       vhost, prot,
166                       sizeof(struct per_vhost_data__merica_terminal)
167                   );
168             vhd->context = context;
169             vhd->protocol = prot;
170             vhd->vhost = vhost;
171             break;
172
173         case LWS_CALLBACK_PROTOCOL_DESTROY:
174             if (!vhd) {
175                 break;
176             }
177             break;
178
179         case LWS_CALLBACK_ESTABLISHED:
180             break;
181
182         case LWS_CALLBACK_SERVER_WRITEABLE:
183             if (lines->first) {
184                 line = lines->first->line;
185                 n = strlen(line);
186                 m = lws_write(wsi, (unsigned char *)line, n, LWS_WRITE_TEXT);
187                 list_remove(lines);
188                 if (lines->first) {
189                     lws_callback_on_writable_all_protocol(context,
190                         &protocols[PROTOCOL_MERICA_TERMINAL]);
191                 }
192             } else {
193                 line = copy_line(JSON_EMPTY);
194                 if (line) {
195                     n = strlen(line);
196                     m = lws_write(wsi, (unsigned char *)line, n, LWS_WRITE_TEXT);
197                     free_line(line);
198                 } else {
199                     return -1;
200                 }
201             }
202             if (m < n) {
203                 fprintf(stderr, "ERROR %d writing to di socket\n", n);
204                 return -1;
205             }
206             break;
207
208         case LWS_CALLBACK_RECEIVE:
209             if (strcmp((const char *)in, "reset") == 0) {
210                 line = copy_line(JSON_EMPTY);
211                 if (line) {
212                     if (list_add(lines, line) == 0) {
213                         lws_callback_on_writable_all_protocol(context,
214                             &protocols[PROTOCOL_MERICA_TERMINAL]);
215                     } else {
216                         free_line(line);
217                     }
218                 }
219             } else if (strcmp((const char *)in, "close") == 0) {
220                 fprintf(stderr, "closing websocket\n");
221                 lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
222                                  (unsigned char *)"seeya", 5);
223                 return -1;
224             }
225             break;
226
227         default:
228             break;
229     }
230
231     return 0;
232 }
233
234 static void fd_cb(EV_P_ ev_io *w_, int revents)
235 {
236     ev_io_ws *w = (ev_io_ws *)w_;
237
238     char *pos = w->pos++;
239
240     read(w->w.fd, pos, 1);
241
242     if (*pos == '\n' || (w->pos - w->text) == INPUT_LINE_LENGTH) {
243         *pos = 0;
244         char *line = new_line();
245         if (line) {
246             if (list_add(w->lines, w->text) == 0) {
247                 w->text = line;
248                 w->pos = w->text;
249                 lws_callback_on_writable_all_protocol(w->context,
250                     &protocols[PROTOCOL_MERICA_TERMINAL]);
251                 return;
252             }
253             free_line(line);
254         }
255         fprintf(stderr, "cannot malloc new line\n");
256     }
257 }
258
259 int mt_server_init(mt_server_t *self, struct ev_loop *loop, int fd)
260 {
261     struct lws_context_creation_info info;
262
263     list *l = list_init();
264     if (!l) {
265         return -1;
266     }
267
268     memset(&info, 0, sizeof(info));
269     info.port = HTTP_PORT;
270     info.mounts = &mount;
271     info.protocols = protocols;
272     info.max_http_header_pool = 1;
273     info.options |= LWS_SERVER_OPTION_LIBEV;
274     info.user = l;
275
276     struct lws_context *context = lws_create_context(&info);
277     if (!context) {
278         fprintf(stderr, "lws_create_context failed\n");
279         list_deinit(l);
280         return -1;
281     }
282
283     self->context = context;
284
285     ev_io_ws *w = &(self->fd_watcher);
286     w->context = context;
287     w->lines = l;
288
289     char *line = new_line();
290     if (!line) {
291         list_deinit(l);
292         return -1;
293     }
294     w->text = line;
295     w->pos = w->text;
296     ev_io_init(&(w->w), fd_cb, fd, EV_READ);
297     ev_io_start(loop, (ev_io *)w);
298
299     return lws_ev_initloop(context, loop, 0);
300 }
301
302 void mt_server_deinit(mt_server_t *self)
303 {
304     free_line(self->fd_watcher.text);
305     list_deinit(self->fd_watcher.lines);
306     lws_context_destroy(self->context);
307 }
308
309 #ifndef NO_MAIN
310 int main(int argc, const char **argv)
311 {
312     struct ev_loop *loop = EV_DEFAULT;
313     mt_server_t server;
314
315     set_signal_exit(loop);
316
317     if (mt_server_init(&server, loop, STDIN_FILENO) != 0) {
318         return -1;
319     }
320
321     ev_run(loop, 0);
322
323     mt_server_deinit(&server);
324     ev_loop_destroy(loop);
325
326     return 0;
327 }
328 #endif