]> rtime.felk.cvut.cz Git - coffee/mt-apps.git/blob - mt_server.c
each line is sent only once, default is empty
[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_vhost *vhost = lws_get_vhost(wsi);
147     const struct lws_protocols *prot = lws_get_protocol(wsi);
148     /*
149     per_session_data__merica_terminal *pss =
150         (per_session_data__merica_terminal *)user;
151     */
152     per_vhost_data__merica_terminal *vhd =
153         (per_vhost_data__merica_terminal *)
154         lws_protocol_vh_priv_get(vhost, prot);
155
156     int n, m;
157
158     list *lines = (list *)prot->user;
159     char *line;
160
161     switch (reason) {
162         case LWS_CALLBACK_PROTOCOL_INIT:
163             vhd = lws_protocol_vh_priv_zalloc(
164                       vhost, prot,
165                       sizeof(struct per_vhost_data__merica_terminal)
166                   );
167             vhd->context = lws_get_context(wsi);
168             vhd->protocol = prot;
169             vhd->vhost = vhost;
170             break;
171
172         case LWS_CALLBACK_PROTOCOL_DESTROY:
173             if (!vhd) {
174                 break;
175             }
176             break;
177
178         case LWS_CALLBACK_ESTABLISHED:
179             break;
180
181         case LWS_CALLBACK_SERVER_WRITEABLE:
182             if (lines->first) {
183                 line = lines->first->line;
184                 n = strlen(line);
185                 m = lws_write(wsi, (unsigned char *)line, n, LWS_WRITE_TEXT);
186                 list_remove(lines);
187                 if (lines->first) {
188                     lws_callback_on_writable_all_protocol(lws_get_context(wsi),
189                         &protocols[PROTOCOL_MERICA_TERMINAL]);
190                 }
191             } else {
192                 line = copy_line(JSON_EMPTY);
193                 if (line) {
194                     n = strlen(line);
195                     m = lws_write(wsi, (unsigned char *)line, n, LWS_WRITE_TEXT);
196                     free_line(line);
197                 } else {
198                     return -1;
199                 }
200             }
201             if (m < n) {
202                 fprintf(stderr, "ERROR %d writing to di socket\n", n);
203                 return -1;
204             }
205             break;
206
207         case LWS_CALLBACK_RECEIVE:
208             if (strcmp((const char *)in, "reset") == 0) {
209                 line = copy_line(JSON_EMPTY);
210                 if (line) {
211                     if (list_add(lines, line) == 0) {
212                         lws_callback_on_writable_all_protocol(lws_get_context(wsi),
213                             &protocols[PROTOCOL_MERICA_TERMINAL]);
214                     } else {
215                         free_line(line);
216                     }
217                 }
218             } else if (strcmp((const char *)in, "close") == 0) {
219                 fprintf(stderr, "closing websocket\n");
220                 lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
221                                  (unsigned char *)"seeya", 5);
222                 return -1;
223             }
224             break;
225
226         default:
227             break;
228     }
229
230     return 0;
231 }
232
233 static void fd_cb(EV_P_ ev_io *w_, int revents)
234 {
235     ev_io_ws *w = (ev_io_ws *)w_;
236
237     char *pos = w->pos++;
238
239     read(w->w.fd, pos, 1);
240
241     if (*pos == '\n' || (w->pos - w->text) == INPUT_LINE_LENGTH) {
242         *pos = 0;
243         char *line = new_line();
244         if (line) {
245             if (list_add(w->lines, w->text) == 0) {
246                 w->text = line;
247                 w->pos = w->text;
248                 lws_callback_on_writable_all_protocol(w->context,
249                     &protocols[PROTOCOL_MERICA_TERMINAL]);
250                 return;
251             }
252             free_line(line);
253         }
254         fprintf(stderr, "cannot malloc new line\n");
255     }
256 }
257
258 int mt_server_init(mt_server_t *self, struct ev_loop *loop, int fd)
259 {
260     struct lws_context_creation_info info;
261
262     memset(&info, 0, sizeof(info));
263     info.port = HTTP_PORT;
264     info.mounts = &mount;
265     info.protocols = protocols;
266     info.max_http_header_pool = 1;
267     info.options |= LWS_SERVER_OPTION_LIBEV;
268
269     struct lws_context *context = lws_create_context(&info);
270     if (!context) {
271         fprintf(stderr, "lws_create_context failed\n");
272         return -1;
273     }
274
275     self->context = context;
276
277     ev_io_ws *w = &(self->fd_watcher);
278     w->context = context;
279     w->lines = list_init();
280     if (!w->lines) {
281         return -1;
282     }
283     protocols[PROTOCOL_MERICA_TERMINAL].user = (void *)w->lines;
284     char *line = new_line();
285     if (!line) {
286         list_deinit(w->lines);
287         return -1;
288     }
289     w->text = line;
290     w->pos = w->text;
291     ev_io_init(&(w->w), fd_cb, fd, EV_READ);
292     ev_io_start(loop, (ev_io *)w);
293
294     return lws_ev_initloop(context, loop, 0);
295 }
296
297 void mt_server_deinit(mt_server_t *self)
298 {
299     free_line(self->fd_watcher.text);
300     list_deinit(self->fd_watcher.lines);
301     lws_context_destroy(self->context);
302 }
303
304 #ifndef NO_MAIN
305 int main(int argc, const char **argv)
306 {
307     struct ev_loop *loop = EV_DEFAULT;
308     mt_server_t server;
309
310     set_signal_exit(loop);
311
312     if (mt_server_init(&server, loop, STDIN_FILENO) != 0) {
313         return -1;
314     }
315
316     ev_run(loop, 0);
317
318     mt_server_deinit(&server);
319     ev_loop_destroy(loop);
320
321     return 0;
322 }
323 #endif