- refactor kx sending, unify under send_kx
[oweals/gnunet.git] / src / rest / gnunet-rest-server.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 Christian Grothoff (and other contributing authors)
4
5    GNUnet is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published
7    by the Free Software Foundation; either version 3, or (at your
8    option) any later version.
9
10    GNUnet is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with GNUnet; see the file COPYING.  If not, write to the
17    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.
19    */
20 /**
21  * @author Martin Schanzenbach
22  * @file src/rest/gnunet-rest-server.c
23  * @brief REST service for GNUnet services
24  *
25  */
26 #include "platform.h"
27 #include <microhttpd.h>
28 #include "gnunet_util_lib.h"
29 #include "gnunet_rest_plugin.h"
30
31
32 /**
33  * Default Socks5 listen port.
34  */
35 #define GNUNET_REST_SERVICE_PORT 7776
36
37 /**
38  * Maximum supported length for a URI.
39  * Should die. @deprecated
40  */
41 #define MAX_HTTP_URI_LENGTH 2048
42
43 /**
44  * Port for plaintext HTTP.
45  */
46 #define HTTP_PORT 80
47
48 /**
49  * Port for HTTPS.
50  */
51 #define HTTPS_PORT 443
52
53 /**
54  * After how long do we clean up unused MHD SSL/TLS instances?
55  */
56 #define MHD_CACHE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
57
58 #define GN_REST_STATE_INIT 0
59 #define GN_REST_STATE_PROCESSING 1
60
61 /**
62  * The task ID
63  */
64 static struct GNUNET_SCHEDULER_Task * httpd_task;
65
66 /**
67  * The port the service is running on (default 7776)
68  */
69 static unsigned long port = GNUNET_REST_SERVICE_PORT;
70
71 /**
72  * The listen socket of the service for IPv4
73  */
74 static struct GNUNET_NETWORK_Handle *lsock4;
75
76 /**
77  * The listen socket of the service for IPv6
78  */
79 static struct GNUNET_NETWORK_Handle *lsock6;
80
81 /**
82  * The listen task ID for IPv4
83  */
84 static struct GNUNET_SCHEDULER_Task * ltask4;
85
86 /**
87  * The listen task ID for IPv6
88  */
89 static struct GNUNET_SCHEDULER_Task * ltask6;
90
91 /**
92  * Daemon for HTTP
93  */
94 static struct MHD_Daemon *httpd;
95
96 /**
97  * Response we return on failures.
98  */
99 static struct MHD_Response *failure_response;
100
101 /**
102  * Our configuration.
103  */
104 static const struct GNUNET_CONFIGURATION_Handle *cfg;
105
106 /**
107  * Map of loaded plugins.
108  */
109 static struct GNUNET_CONTAINER_MultiHashMap *plugin_map;
110
111 /**
112  * MHD Connection handle 
113  */
114 struct MhdConnectionHandle
115 {
116   struct MHD_Connection *con;
117
118   struct MHD_Response *response;
119
120   struct GNUNET_REST_Plugin *plugin;
121
122   struct RestConnectionDataHandle *data_handle;
123
124   int status;
125
126   int state;
127 };
128
129 /* ************************* Global helpers ********************* */
130
131
132 /**
133  * Task run whenever HTTP server operations are pending.
134  *
135  * @param cls NULL
136  * @param tc sched context
137  */
138 static void
139 do_httpd (void *cls,
140           const struct GNUNET_SCHEDULER_TaskContext *tc);
141
142
143 /**
144  * Run MHD now, we have extra data ready for the callback.
145  */
146 static void
147 run_mhd_now ()
148 {
149   if (NULL !=
150       httpd_task)
151     GNUNET_SCHEDULER_cancel (httpd_task);
152   httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
153                                          NULL);
154 }
155
156 /**
157  * Plugin result callback
158  *
159  * @param cls closure (MHD connection handle)
160  * @param data the data to return to the caller
161  * @param len length of the data
162  * @param status #GNUNET_OK if successful
163  */
164 static void
165 plugin_callback (void *cls,
166                  struct MHD_Response *resp,
167                  int status)
168 {
169   struct MhdConnectionHandle *handle = cls;
170   handle->status = status;
171   handle->response = resp;
172   run_mhd_now(); 
173 }
174
175
176 static int
177 cleanup_url_map (void *cls,
178                  const struct GNUNET_HashCode *key,
179                  void *value)
180 {
181   GNUNET_free_non_null (value);
182   return GNUNET_YES;
183 }
184
185
186 static void
187 cleanup_handle (struct MhdConnectionHandle *handle)
188 {
189   if (NULL != handle->response)
190     MHD_destroy_response (handle->response);
191   if (NULL != handle->data_handle)
192   {
193     if (NULL != handle->data_handle->url_param_map)
194     {
195       GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle->url_param_map,
196                                              &cleanup_url_map,
197                                              NULL);
198       GNUNET_CONTAINER_multihashmap_destroy (handle->data_handle->url_param_map);
199     }
200     GNUNET_free (handle->data_handle);
201   }
202   GNUNET_free (handle);
203 }
204
205
206 static int
207 url_iterator (void *cls,
208               enum MHD_ValueKind kind,
209               const char *key,
210               const char *value)
211 {
212   struct RestConnectionDataHandle *handle = cls;
213   struct GNUNET_HashCode hkey;
214   char *val;
215   
216   GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
217   GNUNET_asprintf (&val, "%s", value);
218   if (GNUNET_OK !=
219       GNUNET_CONTAINER_multihashmap_put (handle->url_param_map,
220                                          &hkey,
221                                          val,
222                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
223   {
224     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
225                 "Could not load add url param `%s'=%s\n",
226                 key, value);
227   }
228   return MHD_YES;
229 }
230
231 /* ********************************* MHD response generation ******************* */
232
233 /**
234  * Main MHD callback for handling requests.
235  *
236  * @param cls unused
237  * @param con MHD connection handle
238  * @param url the url in the request
239  * @param meth the HTTP method used ("GET", "PUT", etc.)
240  * @param ver the HTTP version string (i.e. "HTTP/1.1")
241  * @param upload_data the data being uploaded (excluding HEADERS,
242  *        for a POST that fits into memory and that is encoded
243  *        with a supported encoding, the POST data will NOT be
244  *        given in upload_data and is instead available as
245  *        part of MHD_get_connection_values; very large POST
246  *        data *will* be made available incrementally in
247  *        upload_data)
248  * @param upload_data_size set initially to the size of the
249  *        @a upload_data provided; the method must update this
250  *        value to the number of bytes NOT processed;
251  * @param con_cls pointer to location where we store the 'struct Request'
252  * @return MHD_YES if the connection was handled successfully,
253  *         MHD_NO if the socket must be closed due to a serious
254  *         error while handling the request
255  */
256 static int
257 create_response (void *cls,
258                  struct MHD_Connection *con,
259                  const char *url,
260                  const char *meth,
261                  const char *ver,
262                  const char *upload_data,
263                  size_t *upload_data_size,
264                  void **con_cls)
265 {
266   char *plugin_name;
267   struct GNUNET_HashCode key;
268   struct MhdConnectionHandle *con_handle;
269   struct RestConnectionDataHandle *rest_conndata_handle;
270
271   con_handle = *con_cls;
272
273   if (NULL == *con_cls)
274   {
275     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
276                 "New connection %s\n", url);
277     char tmp_url[strlen(url)+1];
278     strcpy (tmp_url, url);
279     con_handle = GNUNET_new (struct MhdConnectionHandle);
280     con_handle->con = con;
281     con_handle->state = GN_REST_STATE_INIT;
282     *con_cls = con_handle;
283
284     plugin_name = strtok(tmp_url, "/");
285
286     if (NULL != plugin_name)
287     {
288       GNUNET_CRYPTO_hash (plugin_name, strlen (plugin_name), &key);
289
290       con_handle->plugin = GNUNET_CONTAINER_multihashmap_get (plugin_map,
291                                                               &key);
292     }
293     if (NULL == con_handle->plugin)
294     {
295       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296                   "Queueing response with MHD\n");
297       GNUNET_free (con_handle);
298       return MHD_queue_response (con,
299                                  MHD_HTTP_NOT_FOUND,
300                                  failure_response);
301     }
302     return MHD_YES;
303   }
304   if (GN_REST_STATE_INIT == con_handle->state)
305   {
306     rest_conndata_handle = GNUNET_new (struct RestConnectionDataHandle);
307     rest_conndata_handle->method = meth;
308     rest_conndata_handle->url = url;
309     rest_conndata_handle->data = upload_data;
310     rest_conndata_handle->data_size = *upload_data_size;
311     rest_conndata_handle->url_param_map = GNUNET_CONTAINER_multihashmap_create (16,
312                                                                                 GNUNET_NO);
313     con_handle->data_handle = rest_conndata_handle;
314     MHD_get_connection_values (con,
315                                MHD_GET_ARGUMENT_KIND,
316                                &url_iterator,
317                                rest_conndata_handle);
318     con_handle->state = GN_REST_STATE_PROCESSING;
319     con_handle->plugin->process_request (rest_conndata_handle,
320                                          &plugin_callback,
321                                          con_handle);
322     *upload_data_size = 0;
323   }
324   if (NULL != con_handle->response)
325   {
326     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327                 "Queueing response from plugin with MHD\n");
328     /* FIXME: this is a bit dangerous... only for testing. */
329     MHD_add_response_header (con_handle->response,
330                              "Access-Control-Allow-Origin",
331                              "*");
332     int ret = MHD_queue_response (con,
333                                  con_handle->status,
334                                  con_handle->response);
335     cleanup_handle (con_handle);
336     return ret;
337   }
338   return MHD_YES;
339 }
340
341
342 /* ******************** MHD HTTP setup and event loop ******************** */
343
344 /**
345  * Function called when MHD decides that we are done with a connection.
346  *
347  * @param cls NULL
348  * @param connection connection handle
349  * @param con_cls value as set by the last call to
350  *        the MHD_AccessHandlerCallback, should be our handle
351  * @param toe reason for request termination (ignored)
352  */
353 static void
354 mhd_completed_cb (void *cls,
355                   struct MHD_Connection *connection,
356                   void **con_cls,
357                   enum MHD_RequestTerminationCode toe)
358 {
359   if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
360     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
361                 "MHD encountered error handling request: %d\n",
362                 toe);
363 }
364
365
366 /**
367  * Kill the MHD daemon.
368  */
369 static void
370 kill_httpd ()
371 {
372   if (NULL != httpd)
373   {
374     MHD_stop_daemon (httpd);
375     httpd = NULL;
376   }
377   if (NULL != httpd_task)
378   { 
379     GNUNET_SCHEDULER_cancel (httpd_task);
380     httpd_task = NULL;
381   }
382 }
383
384
385 /**
386  * Task run whenever HTTP server is idle for too long. Kill it.
387  *
388  * @param cls NULL
389  * @param tc sched context
390  */
391 static void
392 kill_httpd_task (void *cls,
393                  const struct GNUNET_SCHEDULER_TaskContext *tc)
394 {
395   httpd_task = NULL;
396   kill_httpd ();
397 }
398
399
400 /**
401  * Schedule MHD.  This function should be called initially when an
402  * MHD is first getting its client socket, and will then automatically
403  * always be called later whenever there is work to be done.
404  *
405  * @param hd the daemon to schedule
406  */
407 static void
408 schedule_httpd ()
409 {
410   fd_set rs;
411   fd_set ws;
412   fd_set es;
413   struct GNUNET_NETWORK_FDSet *wrs;
414   struct GNUNET_NETWORK_FDSet *wws;
415   int max;
416   int haveto;
417   MHD_UNSIGNED_LONG_LONG timeout;
418   struct GNUNET_TIME_Relative tv;
419
420   FD_ZERO (&rs);
421   FD_ZERO (&ws);
422   FD_ZERO (&es);
423   max = -1;
424   if (MHD_YES != MHD_get_fdset (httpd, &rs, &ws, &es, &max))
425   {
426     kill_httpd ();
427     return;
428   }
429   haveto = MHD_get_timeout (httpd, &timeout);
430   if (MHD_YES == haveto)
431     tv.rel_value_us = (uint64_t) timeout * 1000LL;
432   else
433     tv = GNUNET_TIME_UNIT_FOREVER_REL;
434   if (-1 != max)
435   {
436     wrs = GNUNET_NETWORK_fdset_create ();
437     wws = GNUNET_NETWORK_fdset_create ();
438     GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
439     GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
440   }
441   else
442   {
443     wrs = NULL;
444     wws = NULL;
445   }
446   if (NULL != httpd_task)
447     GNUNET_SCHEDULER_cancel (httpd_task);
448   if ( (MHD_YES != haveto) &&
449        (-1 == max))
450   {
451     /* daemon is idle, kill after timeout */
452     httpd_task = GNUNET_SCHEDULER_add_delayed (MHD_CACHE_TIMEOUT,
453                                                &kill_httpd_task,
454                                                NULL);
455   }
456   else
457   {
458     httpd_task =
459       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
460                                    tv, wrs, wws,
461                                    &do_httpd, NULL);
462   }
463   if (NULL != wrs)
464     GNUNET_NETWORK_fdset_destroy (wrs);
465   if (NULL != wws)
466     GNUNET_NETWORK_fdset_destroy (wws);
467 }
468
469 /**
470  * Task run whenever HTTP server operations are pending.
471  *
472  * @param cls NULL
473  * @param tc scheduler context
474  */
475 static void
476 do_httpd (void *cls,
477           const struct GNUNET_SCHEDULER_TaskContext *tc)
478 {
479   httpd_task = NULL;
480   MHD_run (httpd);
481   schedule_httpd ();
482 }
483
484
485 /**
486  * Accept new incoming connections
487  *
488  * @param cls the closure with the lsock4 or lsock6
489  * @param tc the scheduler context
490  */
491 static void
492 do_accept (void *cls,
493            const struct GNUNET_SCHEDULER_TaskContext *tc)
494 {
495   struct GNUNET_NETWORK_Handle *lsock = cls;
496   struct GNUNET_NETWORK_Handle *s;
497   int fd;
498   const struct sockaddr *addr;
499   socklen_t len;
500
501   if (lsock == lsock4)
502     ltask4 = NULL;
503   else
504     ltask6 = NULL;
505   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
506     return; 
507   if (lsock == lsock4)
508     ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
509                                             lsock,
510                                             &do_accept, lsock);
511   else
512     ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
513                                             lsock,
514                                             &do_accept, lsock);
515   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
516   if (NULL == s)
517   {
518     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
519     return;
520   }
521   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
522               "Got an inbound connection, waiting for data\n");
523   fd = GNUNET_NETWORK_get_fd (s);
524   addr = GNUNET_NETWORK_get_addr (s);
525   len = GNUNET_NETWORK_get_addrlen (s);
526   if (MHD_YES != MHD_add_connection (httpd, fd, addr, len))
527   {
528     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
529                 _("Failed to pass client to MHD\n"));
530     return;
531   }
532
533   schedule_httpd ();
534 }
535
536
537 /**
538  * Task run on shutdown
539  *
540  * @param cls closure
541  * @param tc task context
542  */
543 static void
544 do_shutdown (void *cls,
545              const struct GNUNET_SCHEDULER_TaskContext *tc)
546 {
547   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
548               "Shutting down...\n");
549   kill_httpd ();
550 }
551
552
553 /**
554  * Create an IPv4 listen socket bound to our port.
555  *
556  * @return NULL on error
557  */
558 static struct GNUNET_NETWORK_Handle *
559 bind_v4 ()
560 {
561   struct GNUNET_NETWORK_Handle *ls;
562   struct sockaddr_in sa4;
563   int eno;
564
565   memset (&sa4, 0, sizeof (sa4));
566   sa4.sin_family = AF_INET;
567   sa4.sin_port = htons (port);
568 #if HAVE_SOCKADDR_IN_SIN_LEN
569   sa4.sin_len = sizeof (sa4);
570 #endif 
571   ls = GNUNET_NETWORK_socket_create (AF_INET,
572                                      SOCK_STREAM,
573                                      0);
574   if (NULL == ls)
575     return NULL;
576   if (GNUNET_OK !=
577       GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4,
578                                   sizeof (sa4)))
579   {
580     eno = errno;
581     GNUNET_NETWORK_socket_close (ls);
582     errno = eno;
583     return NULL;
584   }
585   return ls;
586 }
587
588
589 /**
590  * Create an IPv6 listen socket bound to our port.
591  *
592  * @return NULL on error
593  */
594 static struct GNUNET_NETWORK_Handle *
595 bind_v6 ()
596 {
597   struct GNUNET_NETWORK_Handle *ls;
598   struct sockaddr_in6 sa6;
599   int eno;
600
601   memset (&sa6, 0, sizeof (sa6));
602   sa6.sin6_family = AF_INET6;
603   sa6.sin6_port = htons (port);
604 #if HAVE_SOCKADDR_IN_SIN_LEN
605   sa6.sin6_len = sizeof (sa6);
606 #endif 
607   ls = GNUNET_NETWORK_socket_create (AF_INET6,
608                                      SOCK_STREAM,
609                                      0);
610   if (NULL == ls)
611     return NULL;
612   if (GNUNET_OK !=
613       GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa6,
614                                   sizeof (sa6)))
615   {
616     eno = errno;
617     GNUNET_NETWORK_socket_close (ls);
618     errno = eno;
619     return NULL;
620   }
621   return ls;
622 }
623
624
625 /**
626  * Callback for plugin load
627  *
628  * @param cls NULL
629  * @param libname the name of the library loaded
630  * @param lib_ret the object returned by the plugin initializer
631  */
632 static void
633 load_plugin (void *cls,
634              const char *libname,
635              void *lib_ret)
636 {
637   struct GNUNET_REST_Plugin *plugin = lib_ret;
638   struct GNUNET_HashCode key;
639   if (NULL == lib_ret)
640   {
641     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
642                 "Could not load plugin `%s'\n",
643                 libname);
644     return;
645   }
646   GNUNET_assert (1 < strlen (plugin->name));
647   GNUNET_assert ('/' == *plugin->name);
648   GNUNET_CRYPTO_hash (plugin->name+1, strlen (plugin->name+1), &key);
649   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (plugin_map,
650                                                       &key,
651                                                       plugin,
652                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
653   {
654     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
655                 "Could not load add plugin `%s'\n",
656                 libname);
657     return;
658   }
659   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660               "Loaded plugin `%s'\n",
661               libname);
662 }
663
664
665 /**
666  * Main function that will be run
667  *
668  * @param cls closure
669  * @param args remaining command-line arguments
670  * @param cfgfile name of the configuration file used (for saving, can be NULL)
671  * @param c configuration
672  */
673 static void
674 run (void *cls, 
675      char *const *args, 
676      const char *cfgfile,
677      const struct GNUNET_CONFIGURATION_Handle *c)
678 {
679   cfg = c;
680   plugin_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
681
682   /* Open listen socket proxy */
683   lsock6 = bind_v6 ();
684   if (NULL == lsock6)
685   {
686     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
687   }
688   else
689   {
690     if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
691     {
692       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
693       GNUNET_NETWORK_socket_close (lsock6);
694       lsock6 = NULL;
695     }
696     else
697     {
698       ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
699                                               lsock6, &do_accept, lsock6);
700     }
701   }
702   lsock4 = bind_v4 ();
703   if (NULL == lsock4)
704   {
705     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
706   }
707   else
708   {
709     if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
710     {
711       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
712       GNUNET_NETWORK_socket_close (lsock4);
713       lsock4 = NULL;
714     }
715     else
716     {
717       ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
718                                               lsock4, &do_accept, lsock4);
719     }
720   }
721   if ( (NULL == lsock4) &&
722        (NULL == lsock6) )
723   {
724     GNUNET_SCHEDULER_shutdown ();
725     return;
726   } 
727   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
728               "Service listens on port %u\n",
729               port);
730   httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
731                             0,
732                             NULL, NULL,
733                             &create_response, NULL,
734                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
735                             MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
736                             MHD_OPTION_END);
737   if (NULL == httpd)
738   {
739     GNUNET_SCHEDULER_shutdown ();
740     return;
741   }
742   /* Load plugins */
743   GNUNET_PLUGIN_load_all ("libgnunet_plugin_rest",
744                           (void *) cfg,
745                           &load_plugin,
746                           NULL);
747   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
748                                 &do_shutdown, NULL);
749 }
750
751
752 /**
753  *
754  * The main function for gnunet-rest-service
755  *
756  * @param argc number of arguments from the cli
757  * @param argv command line arguments
758  * @return 0 ok, 1 on error
759  *
760  */
761 int
762 main (int argc, char *const *argv)
763 {
764   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
765     {'p', "port", NULL,
766       gettext_noop ("listen on specified port (default: 7776)"), 1,
767       &GNUNET_GETOPT_set_ulong, &port},
768     GNUNET_GETOPT_OPTION_END
769   };
770   static const char* err_page =
771     "{}";
772   int ret;
773
774   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
775     return 2;
776   GNUNET_log_setup ("gnunet-rest-server", "WARNING", NULL);
777   failure_response = MHD_create_response_from_buffer (strlen(err_page),
778                                                       (void*)err_page,
779                                                       MHD_RESPMEM_PERSISTENT);
780   ret =
781     (GNUNET_OK ==
782      GNUNET_PROGRAM_run (argc, argv, "gnunet-rest-server",
783                          _("GNUnet REST server"),
784                          options,
785                          &run, NULL)) ? 0: 1;
786   MHD_destroy_response (failure_response);
787   GNUNET_free_non_null ((char *) argv);
788   return ret;
789 }
790
791 /* end of gnunet-rest-server.c */