source: roaraudio/plugins/universal/protocol-http.c @ 5725:a6b8cb206c2b

Last change on this file since 5725:a6b8cb206c2b was 5725:a6b8cb206c2b, checked in by phi, 11 years ago

added working uste support

File size: 25.6 KB
Line 
1//protocol-http.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011-2012
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include <roaraudio.h>
27#ifdef ROAR_HAVE_LIBUSTE
28#include <uste.h>
29#endif
30
31struct resource_handle;
32
33enum ctl_cmd {
34 CMD_SET_CLIENTNAME
35};
36
37enum status {
38 STATUS_WAITING_FOR_HEADERS = 0,
39 STATUS_END_OF_HEADER,
40 STATUS_BAD_HEADER,
41 STATUS_RUNNING_DATA,
42 STATUS_DONE
43};
44
45struct input_source {
46 int userdata_si;
47 void * userdata_vp;
48 ssize_t offset;
49 struct roar_vio_calls * vio;
50 struct roar_vio_calls vio_store;
51};
52
53struct http_client {
54 enum status status;
55 struct roar_buffer * input_buffer;
56 char * method;
57 char * proto;
58 char * uri;
59 char * path, * query_string;
60 void * headersstore;
61 struct roar_keyval * headers;
62 ssize_t headerslen;
63 const struct resource_handle * resource;
64 struct input_source input;
65 struct roar_dl_librarypara * pluginpara;
66 int clientid;
67};
68
69struct resource_handle {
70 const char  * uri;
71 const char  * rawdata;
72 const ssize_t rawlen;
73 const char  * content_type;
74 int (*header_send)(struct http_client * self, struct roar_buffer ** obuffer);
75 int (*body_send)(struct http_client * self, struct roar_buffer ** obuffer);
76};
77
78static struct {
79 enum {
80  HOSTTYPE_GENERIC = 0,
81  HOSTTYPE_ROARD
82 } hosttype;
83 union {
84  struct {
85   int (*clients_set_name)(int id, const char * name);
86  } roard;
87 } hostspec;
88} __host = {.hosttype = HOSTTYPE_GENERIC};
89
90static int header_send(struct roar_buffer ** obuffer, int status, const char * msg, const char * content_type, ssize_t len);
91static void send_errorpage(struct http_client * self, struct roar_buffer ** obuffer, int error, const char * msg);
92
93static int slow_zero_stream(struct http_client * self, struct roar_buffer ** obuffer) {
94 struct roar_buffer * buffer;
95 void * data;
96
97 self->status = STATUS_RUNNING_DATA;
98
99 if ( roar_buffer_new_data(&buffer, 1, &data) == -1 )
100  return -1;
101
102 *((char*)data) = 0;
103
104 if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
105  roar_buffer_free(buffer);
106  return -1;
107 }
108
109 return 0;
110}
111
112static const char * __res_vio_content_type_get(struct http_client * self) {
113 const char * point = rindex(self->path, '.');
114
115 if ( point == NULL )
116  return NULL;
117
118 point++;
119
120 if ( !strcmp(point, "txt") || !strcmp(point, "text") ) {
121  return "text/plain";
122 } else if ( !strcmp(point, "htm") || !strcmp(point, "html") ) {
123  return "text/html";
124 } else if ( !strcmp(point, "css") ) {
125  return "text/css";
126 } else if ( !strcmp(point, "png") ) {
127  return "image/png";
128 } else if ( !strcmp(point, "jpg") || !strcmp(point, "jpeg") ) {
129  return "image/jpeg";
130 }
131
132 return NULL;
133}
134
135#ifdef ROAR_HAVE_LIBUSTE
136static uste_var_t __res_vio_handle_uste_build_headers(struct http_client * self) {
137 uste_var_t root = uste_var_new_kv();
138 uste_var_t var;
139 ssize_t i;
140
141 if ( root == NULL )
142  return NULL;
143
144 uste_var_set_key(root, "header");
145
146 for (i = 0; i < self->headerslen; i++) {
147  var = uste_var_new_str(self->headers[i].value == NULL ? "" : self->headers[i].value);
148  if ( self->headers[i].key != NULL )
149   uste_var_set_key(var, self->headers[i].key);
150  uste_var_push(root, var);
151  uste_var_unref(var);
152 }
153
154 return root;
155}
156static uste_var_t __res_vio_handle_uste_build_rootkv(struct http_client * self) {
157 uste_var_t root = uste_var_new_kv();
158 uste_var_t subroot, subsubroot;
159 uste_var_t var;
160
161 if ( root == NULL )
162  return NULL;
163
164#define __new_member_prefix(prefix,name) \
165   if ( prefix->name != NULL ) { \
166    var = uste_var_new_str(prefix->name); \
167    uste_var_set_key(var, #name); \
168    uste_var_push(subsubroot, var); \
169    uste_var_unref(var); \
170   }
171#define __new_member(name) __new_member_prefix(self,name)
172
173 subroot = uste_var_new_kv();
174 if ( subroot != NULL ) {
175  uste_var_set_key(subroot, "__client__");
176  uste_var_push(root, subroot);
177
178  var = uste_var_new_int(self->clientid);
179  uste_var_set_key(var, "clientid");
180  uste_var_push(subroot, var);
181  uste_var_unref(var);
182
183  subsubroot = uste_var_new_kv();
184  if ( subsubroot != NULL ) {
185   uste_var_set_key(subsubroot, "http");
186   uste_var_push(subroot, subsubroot);
187
188   __new_member(method);
189   __new_member(proto);
190   __new_member(uri);
191   __new_member(path);
192   __new_member(query_string);
193
194   var = __res_vio_handle_uste_build_headers(self);
195   uste_var_push(subsubroot, var);
196   uste_var_unref(var);
197
198   uste_var_unref(subsubroot);
199  }
200
201  uste_var_unref(subroot);
202 }
203
204 subroot = uste_var_new_kv();
205 if ( subroot != NULL ) {
206  uste_var_set_key(subroot, "__host__");
207  uste_var_push(root, subroot);
208
209  subsubroot = subroot;
210  __new_member_prefix(self->pluginpara, appname);
211  __new_member_prefix(self->pluginpara, abiversion);
212
213  uste_var_unref(subroot);
214 }
215
216 return root;
217}
218
219static void __res_vio_handle_uste(struct http_client * self, struct roar_buffer ** obuffer, const char * content_type) {
220 struct roar_buffer * buffer = NULL;
221 uste_renderer_t renderer;
222 uste_parser_t parser;
223 uste_var_t rootkv;
224 uste_node_t root;
225 int err;
226
227
228 parser = uste_parser_new(self->input.vio);
229 err = roar_error;
230 roar_vio_close(self->input.vio);
231 if ( parser == NULL ) {
232  send_errorpage(self, obuffer, err, NULL);
233  return;
234 }
235
236 if ( uste_parser_parse(parser) == -1 ) {
237  send_errorpage(self, obuffer, roar_error, NULL);
238  return;
239 }
240
241 root = uste_parser_get_rootnode(parser);
242 err = roar_error;
243 uste_parser_unref(parser);
244
245 if ( root == NULL ) {
246  send_errorpage(self, obuffer, err, NULL);
247  return;
248 }
249
250 renderer = uste_renderer_new();
251 err = roar_error;
252 if ( renderer == NULL ) {
253  uste_node_unref(root);
254  send_errorpage(self, obuffer, err, NULL);
255  return;
256 }
257
258 uste_stdfunc_register_all(renderer);
259
260 rootkv = __res_vio_handle_uste_build_rootkv(self);
261 err = roar_error;
262 if ( rootkv == NULL ) {
263  uste_node_unref(root);
264  uste_renderer_unref(renderer);
265  send_errorpage(self, obuffer, err, NULL);
266  return;
267 }
268
269 uste_renderer_set_rootvar(renderer, rootkv);
270 uste_var_unref(rootkv);
271
272 buffer = uste_node_render(root, renderer);
273 err = roar_error;
274 uste_node_unref(root);
275 uste_renderer_unref(renderer);
276
277 if ( buffer == NULL ) {
278  send_errorpage(self, obuffer, err, NULL);
279  return;
280 }
281
282 header_send(obuffer, 200, NULL, content_type, -1);
283
284 if ( buffer != NULL ) {
285  if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
286   roar_buffer_free(buffer);
287   return;
288  }
289 }
290
291 self->status = STATUS_DONE;
292}
293#endif
294
295static int __res_vio_check_uste(struct http_client * self, struct roar_buffer ** obuffer, const char * content_type) {
296 const size_t len = 14;
297 struct roar_buffer * buffer;
298 void * data;
299 ssize_t ret;
300
301 // TODO: use content type for some basic filtering.
302 (void)content_type;
303
304 *obuffer = NULL;
305
306 if ( roar_buffer_new_data(&buffer, len, &data) == -1 )
307  return -1;
308
309 ret = roar_vio_read(self->input.vio, data, len);
310
311 if ( ret < 1 ) {
312  roar_buffer_free(buffer);
313  return 0;
314 }
315
316 *obuffer = buffer;
317
318 if ( ret == (ssize_t)len ) {
319  return !strncmp(data, "@@@TEMPLATE@@@", len);
320 } else {
321  if ( roar_buffer_set_len(buffer, ret) == -1 ) {
322   *obuffer = NULL;
323   roar_buffer_free(buffer);
324   return -1;
325  }
326  // is this really a nice idea?
327  return 0;
328 }
329}
330
331static int __res_vio_header_send(struct http_client * self, struct roar_buffer ** obuffer) {
332 struct roar_buffer * buffer;
333 struct roar_keyval * path;
334 char filename[1024];
335 const char * slash = "/";
336 const char * content_type = NULL;
337
338 if ( self->pluginpara == NULL ) {
339  roar_err_set(ROAR_ERROR_INVAL);
340  return -1;
341 }
342
343 path = roar_keyval_lookup(self->pluginpara->argv, "webroot", self->pluginpara->argc, 1);
344 if ( path == NULL ) {
345  if ( roar_error == ROAR_ERROR_NOENT )
346   roar_err_set(ROAR_ERROR_INVAL);
347  return -1;
348 }
349
350 if ( path->value == NULL ) {
351  roar_err_set(ROAR_ERROR_INVAL);
352  return -1;
353 }
354
355 if ( strstr(self->path, "..") != NULL || strstr(self->path, "#") != NULL ) {
356  roar_err_set(ROAR_ERROR_INVAL);
357  return -1;
358 }
359
360 if ( (roar_mm_strlen(path->value) + roar_mm_strlen(self->path) + 2) > sizeof(filename) ) {
361  roar_err_set(ROAR_ERROR_NAMETOOLONG);
362  return -1;
363 }
364
365 if ( self->path[0] == '/' || path->value[strlen(path->value)-1] == '/' )
366  slash = "";
367
368 snprintf(filename, sizeof(filename), "%s%s%s", path->value, slash, self->path);
369
370
371 self->input.vio = &(self->input.vio_store);
372 if ( roar_vio_open_dstr_simple(self->input.vio, filename, ROAR_VIOF_READ|ROAR_VIOF_NONBLOCK) == -1 ) {
373  return -1;
374 }
375
376 if ( roar_vio_ctl(self->input.vio, ROAR_VIO_CTL_GET_MIMETYPE, &content_type) == -1 )
377  content_type = NULL;
378
379 if ( content_type == NULL )
380  content_type = __res_vio_content_type_get(self);
381
382 if ( __res_vio_check_uste(self, &buffer, content_type) == 1 ) {
383  if ( buffer != NULL )
384   roar_buffer_free(buffer);
385#ifdef ROAR_HAVE_LIBUSTE
386  __res_vio_handle_uste(self, obuffer, content_type);
387  return 0;
388#else
389  roar_vio_close(self->input.vio);
390  send_errorpage(self, obuffer, ROAR_ERROR_NOSYS, NULL);
391  return 0;
392#endif
393 }
394
395 header_send(obuffer, 200, NULL, content_type, -1);
396
397 if ( buffer != NULL ) {
398  if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
399   roar_buffer_free(buffer);
400   return -1;
401  }
402 }
403
404 self->status = STATUS_RUNNING_DATA;
405 return 0;
406}
407
408static int __res_vio_body_send(struct http_client * self, struct roar_buffer ** obuffer) {
409 const size_t len = 1024;
410 struct roar_buffer * buffer;
411 void * data;
412 ssize_t ret;
413
414 if ( self->status == STATUS_DONE )
415  return 0;
416
417 if ( roar_buffer_new_data(&buffer, len, &data) == -1 )
418  return -1;
419
420 ret = roar_vio_read(self->input.vio, data, len);
421
422 if ( ret == -1 && roar_error == ROAR_ERROR_AGAIN ) {
423  ret = 0;
424 } else if ( ret < 1 ) {
425  roar_buffer_free(buffer);
426  if ( roar_error != ROAR_ERROR_AGAIN ) {
427   self->status = STATUS_DONE;
428   roar_vio_close(self->input.vio);
429  }
430  return 0;
431 }
432
433 if ( roar_buffer_set_len(buffer, ret) == -1 ) {
434  roar_buffer_free(buffer);
435  return -1;
436 }
437
438 if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
439  roar_buffer_free(buffer);
440  return -1;
441 }
442
443 return 0;
444}
445
446static const struct resource_handle _g_resources[] = {
447 {
448  .uri = "/test/*",
449  .rawdata = "Hello world!\n",
450  .rawlen  = 13,
451  .content_type = NULL,
452  .header_send = NULL,
453  .body_send = NULL
454 },
455 {
456  .uri = "/szs/*",
457  .rawdata = NULL,
458  .rawlen  = -1,
459  .content_type = NULL,
460  .header_send = NULL,
461  .body_send = slow_zero_stream
462 },
463 {
464  .uri = "*",
465  .rawdata = NULL,
466  .rawlen  = -1,
467  .content_type = NULL,
468  .header_send = __res_vio_header_send,
469  .body_send = __res_vio_body_send
470 }
471};
472
473
474static inline int __ret(struct http_client * self, struct roar_buffer ** obuffer) {
475 ROAR_DBG("__ret(self=%p, obuffer=%p{%p}): self->status=%i", self, obuffer, *obuffer, (int)self->status);
476 if ( *obuffer != NULL )
477  return 0;
478 if ( self->status == STATUS_DONE )
479  return -1;
480 return 0;
481}
482
483static int __ctl(struct http_client * self, enum ctl_cmd cmd, void * argp) {
484 switch (cmd) {
485  case CMD_SET_CLIENTNAME:
486    if ( argp == NULL ) {
487     roar_err_set(ROAR_ERROR_FAULT);
488     return -1;
489    }
490    ROAR_DBG("__ctl(self=%p, cmd=%i, argp='%s') = ?", self, (int)cmd, (const char *)argp);
491    switch (__host.hosttype) {
492     case HOSTTYPE_GENERIC:
493       roar_err_set(ROAR_ERROR_BADHOST);
494       return -1;
495      break;
496     case HOSTTYPE_ROARD:
497       __host.hostspec.roard.clients_set_name(self->clientid, argp);
498      break;
499    }
500   break;
501 }
502
503 roar_err_set(ROAR_ERROR_BADRQC);
504 return -1;
505}
506
507static void __init(struct roar_dl_librarypara * para) {
508 // TODO add some init code here.
509
510 if ( para == NULL )
511  return;
512
513 if ( !roar_dl_para_check_version(para, "roard <0/RoarAudio>", "1.0beta8") ) {
514  __host.hosttype = HOSTTYPE_ROARD;
515  __host.hostspec.roard.clients_set_name = roar_dl_getsym(ROAR_DL_HANDLE_APPLICATION, "clients_set_name", -1);
516
517  // check if *all* function have been found:
518  if ( __host.hostspec.roard.clients_set_name == NULL ) {
519   __host.hosttype = HOSTTYPE_GENERIC;
520  }
521 }
522}
523
524// this function is a wrapper for __init() with the code which should be inlined.
525static inline void _init(struct roar_dl_librarypara * para) {
526 static int inited = 0;
527 if (inited) return;
528 __init(para);
529 inited = 1;
530}
531
532static const struct resource_handle * resource_lookup_by_uri(const char * uri) {
533 size_t i;
534
535 for (i = 0; i < (sizeof(_g_resources)/sizeof(*_g_resources)); i++) {
536  if ( !roar_mm_strselcmp(_g_resources[i].uri, uri) ) {
537   return &(_g_resources[i]);
538  }
539 }
540
541 roar_err_set(ROAR_ERROR_NOENT);
542 return NULL;
543}
544
545static int resource_header_send(struct http_client * self, struct roar_buffer ** obuffer) {
546 if ( self->resource->header_send != NULL )
547  return self->resource->header_send(self, obuffer);
548
549 return header_send(obuffer, 200, NULL, self->resource->content_type, self->resource->rawlen);
550}
551
552static int resource_body_send(struct http_client * self, struct roar_buffer ** obuffer) {
553 struct roar_buffer * buffer;
554 void * data;
555 int ret;
556
557 if ( self->resource->body_send != NULL )
558  return self->resource->body_send(self, obuffer);
559
560 if ( self->resource->rawlen == -1 ) {
561  self->status = STATUS_DONE;
562  roar_err_set(ROAR_ERROR_INVAL);
563  return -1;
564 }
565
566 if ( roar_buffer_new_data(&buffer, self->resource->rawlen, &data) == -1 ) {
567  self->status = STATUS_DONE;
568  return -1;
569 }
570
571 memcpy(data, self->resource->rawdata, self->resource->rawlen);
572
573 ret = roar_buffer_moveintoqueue(obuffer, &buffer);
574 self->status = STATUS_DONE;
575
576 return ret;
577}
578
579static int header_parse(struct http_client * self) {
580 void * data;
581 size_t len;
582 size_t i;
583 char * start, * end;
584 char * start_uri;
585 char * start_headers, * end_headers = NULL;
586 char * tmp;
587 struct roar_keyval * c;
588
589 if ( roar_buffer_get_datalen(self->input_buffer, &data, &len) == -1 )
590  return -1;
591
592 ROAR_DBG("header_parse(self=%p): data='%*s'", self, (int)len, (const char*)data);
593
594 if ( len < 4 )
595  return 0;
596
597 for (i = 0; i < (len - 3); i++) {
598  if ( ((const char*)data)[i] == '\r' ) {
599   ROAR_DBG("header_parse(self=%p): i=%i", self, (int)i);
600   if ( !strncmp(data + i, "\r\n\r\n", 4) ) {
601    self->status = STATUS_END_OF_HEADER;
602    end_headers = data + i;
603    break;
604   }
605  }
606 }
607
608 if ( self->status != STATUS_END_OF_HEADER ) {
609  for (i = 0; i < (len - 1); i++) {
610   if ( ((const char*)data)[i] == '\n' ) {
611    ROAR_DBG("header_parse(self=%p): i=%i", self, (int)i);
612    if ( !strncmp(data + i, "\n\n", 2) ) {
613     self->status = STATUS_END_OF_HEADER;
614     end_headers = data + i;
615     break;
616    }
617   }
618  }
619 }
620
621 if ( self->status != STATUS_END_OF_HEADER )
622  return 0;
623
624 *end_headers = 0;
625
626 end = strstr(data, " ");
627 if ( end == NULL ) {
628  self->status = STATUS_BAD_HEADER;
629  return 1;
630 }
631
632 *end = 0;
633 end++;
634 self->method = roar_mm_strdup(data);
635
636 start = end;
637
638 end = strstr(start, " ");
639 if ( end == NULL ) {
640  self->status = STATUS_BAD_HEADER;
641  return 1;
642 }
643
644 *end = 0;
645 end++;
646 self->uri = roar_mm_strdup(start);
647 start_uri = start;
648
649 start = end;
650
651 end = strstr(start, "\r");
652 if ( end == NULL )
653  end = strstr(start, "\n");
654 if ( end == NULL ) {
655  self->status = STATUS_BAD_HEADER;
656  return 1;
657 }
658
659 *end = 0;
660 end++;
661 self->proto = roar_mm_strdup(start);
662
663 start_headers = end;
664
665 start = start_uri;
666
667 end = strstr(start, "?");
668 if ( end == NULL ) {
669  self->path = roar_mm_strdup(start);
670 } else {
671  *end = 0;
672  end++;
673  self->path = roar_mm_strdup(start);
674  self->query_string = roar_mm_strdup(end);
675 }
676
677 self->headerslen = 0;
678
679 i = end_headers - start_headers + 1;
680 self->headersstore = roar_mm_memdup(start_headers, i);
681 if ( self->headersstore == NULL ) {
682  self->status = STATUS_BAD_HEADER; // TODO: FIXME: not the right error, but works. ;)
683  return -1;
684 }
685
686 start_headers = self->headersstore;
687 end_headers   = start_headers + i;
688
689 for (tmp = start_headers; tmp < end_headers;) {
690  for (; tmp < end_headers && (*tmp == '\r' || *tmp == '\n'); tmp++);
691  if ( tmp == end_headers )
692   break;
693  self->headerslen++;
694  for (; tmp < end_headers && !(*tmp == '\r' || *tmp == '\n'); tmp++);
695 }
696
697 ROAR_DBG("header_parse(self=%p): self->headerslen=%i", self, (int)self->headerslen);
698
699 self->headers = roar_mm_malloc(sizeof(struct roar_keyval)*(self->headerslen+1));
700 if ( self->headers == NULL ) {
701  self->status = STATUS_BAD_HEADER; // TODO: FIXME: not the right error, but works. ;)
702  return -1;
703 }
704 memset(self->headers, 0, sizeof(struct roar_keyval)*(self->headerslen+1));
705 c = self->headers;
706
707 for (tmp = start_headers; tmp < end_headers;) {
708  ROAR_DBG("header_parse(self=%p): tmp='%s'", self, tmp);
709  for (; tmp < end_headers && (*tmp == '\r' || *tmp == '\n'); tmp++) *tmp = '\0';
710  if ( tmp == end_headers )
711   break;
712  ROAR_DBG("header_parse(self=%p): tmp='%s'", self, tmp);
713  c->key = tmp;
714  ROAR_DBG("header_parse(self=%p): c->key='%s'", self, c->key);
715  for (; tmp < end_headers && !(*tmp == '\r' || *tmp == '\n' || *tmp == ':'); tmp++);
716  c->value = tmp;
717  for (; tmp < end_headers && !(*tmp == '\r' || *tmp == '\n'); tmp++);
718  c++;
719 }
720
721 c->key = NULL;
722 c->value = NULL;
723
724 for (i = 0; i < (size_t)self->headerslen; i++) {
725  c = &(self->headers[i]);
726  if ( c->value[0] == '\0' ) {
727   c->value = NULL;
728   continue;
729  }
730
731  c->value[0] = '\0';
732  c->value++;
733
734  for (; c->value[0] == ' '; c->value++);
735
736  if ( c->value[0] == '\0' )
737   c->value = NULL;
738 }
739
740 return 1;
741}
742
743static int header_send(struct roar_buffer ** obuffer, int status, const char * msg, const char * content_type, ssize_t len) {
744 struct roar_buffer * buffer;
745 size_t bufferlen = 1024;
746 void * data;
747 char buffer_len[64];
748
749 if ( roar_buffer_new_data(&buffer, bufferlen, &data) == -1 )
750  return -1;
751
752 if ( msg == NULL ) {
753  switch (status) {
754   case 200: msg = "OK"; break;
755   case 400: msg = "Bad Request"; break;
756   case 404: msg = "File not found"; break;
757   case 500: msg = "Internal server error"; break;
758   default:  msg = "<<<unknown status code>>>"; break;
759  }
760 }
761
762 if ( content_type == NULL )
763  content_type = "text/plain";
764
765 if ( len == (ssize_t)-1 ) {
766  buffer_len[0] = 0;
767 } else {
768  snprintf(buffer_len, sizeof(buffer_len), "Content-Length: %lu\r\n", (long unsigned int)len);
769 }
770
771/*
772Date: Sun, 29 Jul 2012 01:08:15 GMT
773Cache-Control: no-cache,no-store
774Content-Type: text/html; charset=%s
775*/
776 snprintf(data, bufferlen, "HTTP/1.0 %i %s\r\n"
777                           "Server: protocol-http (libroar plugin)\r\n"
778                           "Connection: close\r\n"
779                           "Content-Type: %s\r\n"
780                           "%s"
781                           "\r\n",
782                           status, msg, content_type, buffer_len
783                           );
784
785 if ( roar_buffer_set_len(buffer, roar_mm_strlen(data)) == -1 ) {
786  roar_buffer_free(buffer);
787  return -1;
788 }
789
790 if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
791  roar_buffer_free(buffer);
792  return -1;
793 }
794
795 return 0;
796}
797
798static void send_errorpage(struct http_client * self, struct roar_buffer ** obuffer, int error, const char * msg) {
799 struct roar_buffer * buffer;
800 const size_t bufferlen = 1024;
801 void * data;
802 int httperror;
803
804 if ( roar_err_convert(&httperror, ROAR_ERROR_TYPE_HTTP, error, ROAR_ERROR_TYPE_ROARAUDIO) == -1 )
805  httperror = 500;
806
807 if ( msg == NULL )
808  msg = roar_error2str(error);
809
810 // send header and mark as done early so we can just return in case some of the later calls fail.
811 header_send(obuffer, httperror, msg, "text/html", -1);
812 self->status = STATUS_DONE;
813
814 if ( roar_buffer_new_data(&buffer, bufferlen, &data) == -1 )
815  return;
816
817 snprintf(data, bufferlen, "<html>\n"
818                           " <head><title>%i - %s</title></head>\n"
819                           " <body>\n"
820                           "  <h1>%i - %s</h1><hr>\n"
821                           " </body>\n"
822                           "</html>",
823                           httperror, msg, httperror, msg);
824
825 _LIBROAR_IGNORE_RET(roar_buffer_set_len(buffer, roar_mm_strlen(data)));
826
827 if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 )
828  roar_buffer_free(buffer);
829}
830
831static void handle_client(struct http_client * self, struct roar_buffer ** obuffer) {
832 struct roar_keyval * kv;
833
834 // meta stuff:
835 // try to set client name.
836 kv = roar_keyval_lookup(self->headers, "user-agent", self->headerslen, 0);
837 if ( kv != NULL && kv->value != NULL ) {
838  __ctl(self, CMD_SET_CLIENTNAME, kv->value);
839 }
840
841 // first try lookup including query string. if nothing is found
842 // retry search with only resource path.
843 self->resource = resource_lookup_by_uri(self->uri);
844 if ( self->resource == NULL )
845  self->resource = resource_lookup_by_uri(self->path);
846
847 if ( self->resource == NULL ) {
848  send_errorpage(self, obuffer, roar_error, NULL);
849  return;
850 }
851
852 if ( resource_header_send(self, obuffer) == -1 ) {
853  send_errorpage(self, obuffer, roar_error, NULL);
854  return;
855 }
856
857 resource_body_send(self, obuffer);
858
859 //send_errorpage(self, obuffer, ROAR_ERROR_CAUSALITY, NULL);
860// send_errorpage(self, obuffer, roar_random_uint16() % ROAR_ERROR_BADLICENSE, NULL);
861 return;
862}
863
864
865static int _set_proto(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
866 struct http_client * self = roar_mm_malloc(sizeof(struct http_client));
867
868 (void)vio, (void)protopara, (void)protoparalen;
869
870 if ( self == NULL )
871  return -1;
872
873 _init(pluginpara);
874
875 memset(self, 0, sizeof(*self));
876 self->status = STATUS_WAITING_FOR_HEADERS;
877 self->headerslen = (ssize_t)-1;
878 self->clientid = client;
879
880 if ( pluginpara != NULL ) {
881  roar_dl_para_ref(pluginpara);
882  self->pluginpara = pluginpara;
883 }
884
885 *userdata = self;
886
887 return __ret(self, obuffer);
888}
889
890static int _unset_proto(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
891 struct http_client * self = *userdata;
892
893 (void)client, (void)vio, (void)obuffer, (void)protopara, (void)protoparalen, (void)pluginpara;
894
895 if ( self->input_buffer != NULL )
896  roar_buffer_free(self->input_buffer);
897
898 if ( self->method != NULL )
899  roar_mm_free(self->method);
900 if ( self->proto != NULL )
901  roar_mm_free(self->proto);
902 if ( self->uri != NULL )
903  roar_mm_free(self->uri);
904 if ( self->path != NULL )
905  roar_mm_free(self->path);
906 if ( self->query_string != NULL )
907  roar_mm_free(self->query_string);
908 if ( self->headersstore != NULL )
909  roar_mm_free(self->headersstore);
910 if ( self->headers != NULL )
911  roar_mm_free(self->headers);
912
913 if ( self->pluginpara != NULL )
914  roar_dl_para_unref(self->pluginpara);
915
916 roar_mm_free(self);
917 *userdata = NULL;
918
919 return 0;
920}
921
922#define _INCREMENT 256
923static int _handle(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
924 struct http_client * self = *userdata;
925 void * data;
926 size_t oldlen;
927 ssize_t ret;
928
929 (void)client, (void)protopara, (void)protoparalen, (void)pluginpara;
930
931 if ( self->status != STATUS_WAITING_FOR_HEADERS )
932  return __ret(self, obuffer);
933
934 if ( self->input_buffer == NULL ) {
935  if ( roar_buffer_new_data(&(self->input_buffer), _INCREMENT, &data) == -1 )
936   return -1;
937  oldlen = 0;
938 } else {
939  if ( roar_buffer_get_len(self->input_buffer, &oldlen) == -1 )
940   return -1;
941  if ( roar_buffer_set_len(self->input_buffer, oldlen + _INCREMENT) == -1 )
942   return -1;
943  if ( roar_buffer_get_data(self->input_buffer, &data) == -1 )
944   return -1;
945 }
946
947 data += oldlen;
948
949 ret = roar_vio_read(vio, data, _INCREMENT);
950 if ( ret == (ssize_t)-1 ) {
951  // we can safely ignore return value here as we return error anyway.
952  _LIBROAR_IGNORE_RET(roar_buffer_set_len(self->input_buffer, oldlen));
953  return -1;
954 }
955
956 if ( roar_buffer_set_len(self->input_buffer, oldlen + ret) == -1 ) {
957  ROAR_WARN("_handle(*): Can not reset buffer length. BAD. Error was: %s", roar_errorstring);
958  return -1;
959 }
960
961 header_parse(self);
962
963 if ( self->status == STATUS_BAD_HEADER ) {
964  header_send(obuffer, 400, NULL, NULL, -1);
965  self->status = STATUS_DONE;
966 } else if ( self->status == STATUS_END_OF_HEADER ) {
967  handle_client(self, obuffer);
968 }
969
970 return __ret(self, obuffer);
971}
972
973// this is a dummy function only used to kill the client after all data has been flushed.
974static int _flushed(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
975 struct http_client * self = *userdata;
976
977 (void)client, (void)vio, (void)protopara, (void)protoparalen, (void)pluginpara;
978
979 ROAR_DBG("_flushed(*) = ?");
980
981 if ( self->status == STATUS_RUNNING_DATA )
982  resource_body_send(self, obuffer);
983
984 return __ret(self, obuffer);
985}
986
987static const struct roar_dl_proto proto = {
988 .proto = ROAR_PROTO_HTTP,
989 .description = "Hyper Text Transfer Protocol",
990 .flags = ROAR_DL_PROTO_FLAGS_NONE,
991 .set_proto = _set_proto,
992 .unset_proto = _unset_proto,
993 .handle = _handle,
994 .flush = NULL,
995 .flushed = _flushed,
996 .status = NULL
997};
998
999static int __reg_proto(struct roar_dl_librarypara * para, struct roar_dl_libraryinst * lib) {
1000 (void)para, (void)lib;
1001 ROAR_DL_PLUGIN_REG_FN(ROAR_DL_PROTO_SUBTYPE, proto, ROAR_DL_PROTO_VERSION);
1002 return 0;
1003}
1004
1005ROAR_DL_PLUGIN_START(protocol_http) {
1006 ROAR_DL_PLUGIN_META_PRODUCT_NIV("protocol-http", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO);
1007 ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING);
1008 ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0);
1009 ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org");
1010 ROAR_DL_PLUGIN_META_DESC("Implementation of the HTTP Protocol");
1011
1012 ROAR_DL_PLUGIN_REG(ROAR_DL_FN_PROTO, __reg_proto);
1013} ROAR_DL_PLUGIN_END
1014
1015//ll
Note: See TracBrowser for help on using the repository browser.