2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @author Martin Schanzenbach
22 * @file src/rest/gnunet-rest-server.c
23 * @brief REST service for GNUnet services
27 #include <microhttpd.h>
28 #include "gnunet_util_lib.h"
29 #include "gnunet_rest_plugin.h"
33 * Default Socks5 listen port.
35 #define GNUNET_REST_SERVICE_PORT 7776
38 * Maximum supported length for a URI.
39 * Should die. @deprecated
41 #define MAX_HTTP_URI_LENGTH 2048
44 * Port for plaintext HTTP.
51 #define HTTPS_PORT 443
54 * After how long do we clean up unused MHD SSL/TLS instances?
56 #define MHD_CACHE_TIMEOUT \
57 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
59 #define GN_REST_STATE_INIT 0
60 #define GN_REST_STATE_PROCESSING 1
65 static struct GNUNET_SCHEDULER_Task *httpd_task;
68 * The address to bind to
70 static in_addr_t address;
73 * The IPv6 address to bind to
75 static struct in6_addr address6;
78 * The port the service is running on (default 7776)
80 static unsigned long long port = GNUNET_REST_SERVICE_PORT;
83 * The listen socket of the service for IPv4
85 static struct GNUNET_NETWORK_Handle *lsock4;
88 * The listen socket of the service for IPv6
90 static struct GNUNET_NETWORK_Handle *lsock6;
93 * The listen task ID for IPv4
95 static struct GNUNET_SCHEDULER_Task *ltask4;
98 * The listen task ID for IPv6
100 static struct GNUNET_SCHEDULER_Task *ltask6;
105 static struct MHD_Daemon *httpd;
108 * Response we return on failures.
110 static struct MHD_Response *failure_response;
115 static const struct GNUNET_CONFIGURATION_Handle *cfg;
118 * Map of loaded plugins.
120 static struct GNUNET_CONTAINER_MultiHashMap *plugin_map;
123 * Echo request Origin in CORS
125 static int echo_origin;
128 * Allowed Origins (CORS)
130 static char *allow_origins;
133 * Allowed Headers (CORS)
135 static char *allow_headers;
138 * Allowed Credentials (CORS)
140 static char *allow_credentials;
143 * MHD Connection handle
145 struct MhdConnectionHandle
147 struct MHD_Connection *con;
149 struct MHD_Response *response;
151 struct GNUNET_REST_Plugin *plugin;
153 struct GNUNET_REST_RequestHandle *data_handle;
155 struct MHD_PostProcessor *pp;
162 /* ************************* Global helpers ********************* */
166 * Task run whenever HTTP server operations are pending.
171 do_httpd (void *cls);
175 * Run MHD now, we have extra data ready for the callback.
180 if (NULL != httpd_task)
182 GNUNET_SCHEDULER_cancel (httpd_task);
185 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
190 * Plugin result callback
192 * @param cls closure (MHD connection handle)
193 * @param data the data to return to the caller
194 * @param len length of the data
195 * @param status #GNUNET_OK if successful
198 plugin_callback (void *cls, struct MHD_Response *resp, int status)
200 struct MhdConnectionHandle *handle = cls;
202 handle->status = status;
203 handle->response = resp;
204 MHD_resume_connection (handle->con);
210 cleanup_url_map (void *cls, const struct GNUNET_HashCode *key, void *value)
212 GNUNET_free_non_null (value);
218 cleanup_handle (struct MhdConnectionHandle *handle)
220 if (NULL != handle->response)
221 MHD_destroy_response (handle->response);
222 if (NULL != handle->data_handle)
224 if (NULL != handle->data_handle->header_param_map)
226 GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle
230 GNUNET_CONTAINER_multihashmap_destroy (
231 handle->data_handle->header_param_map);
233 if (NULL != handle->data_handle->url_param_map)
235 GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle->url_param_map,
238 GNUNET_CONTAINER_multihashmap_destroy (
239 handle->data_handle->url_param_map);
241 GNUNET_free (handle->data_handle);
243 GNUNET_free (handle);
248 header_iterator (void *cls,
249 enum MHD_ValueKind kind,
253 struct GNUNET_REST_RequestHandle *handle = cls;
254 struct GNUNET_HashCode hkey;
258 lowerkey = GNUNET_strdup (key);
259 GNUNET_STRINGS_utf8_tolower (key, lowerkey);
260 GNUNET_CRYPTO_hash (lowerkey, strlen (lowerkey), &hkey);
261 GNUNET_asprintf (&val, "%s", value);
262 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
263 handle->header_param_map,
266 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
268 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
269 "Could not load add header `%s'=%s\n",
273 GNUNET_free (lowerkey);
279 url_iterator (void *cls,
280 enum MHD_ValueKind kind,
284 struct GNUNET_REST_RequestHandle *handle = cls;
285 struct GNUNET_HashCode hkey;
288 GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
289 GNUNET_asprintf (&val, "%s", value);
290 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
291 handle->url_param_map,
294 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
296 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
297 "Could not load add url param `%s'=%s\n",
306 post_data_iter (void *cls,
307 enum MHD_ValueKind kind,
309 const char *filename,
310 const char *content_type,
311 const char *transfer_encoding,
316 struct GNUNET_REST_RequestHandle *handle = cls;
317 struct GNUNET_HashCode hkey;
320 if (MHD_POSTDATA_KIND != kind)
323 GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
324 GNUNET_asprintf (&val, "%s", data);
325 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
326 handle->url_param_map,
329 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
332 "Could not load add url param '%s'=%s\n",
341 /* ********************************* MHD response generation ******************* */
344 * Main MHD callback for handling requests.
347 * @param con MHD connection handle
348 * @param url the url in the request
349 * @param meth the HTTP method used ("GET", "PUT", etc.)
350 * @param ver the HTTP version string (i.e. "HTTP/1.1")
351 * @param upload_data the data being uploaded (excluding HEADERS,
352 * for a POST that fits into memory and that is encoded
353 * with a supported encoding, the POST data will NOT be
354 * given in upload_data and is instead available as
355 * part of MHD_get_connection_values; very large POST
356 * data *will* be made available incrementally in
358 * @param upload_data_size set initially to the size of the
359 * @a upload_data provided; the method must update this
360 * value to the number of bytes NOT processed;
361 * @param con_cls pointer to location where we store the 'struct Request'
362 * @return MHD_YES if the connection was handled successfully,
363 * MHD_NO if the socket must be closed due to a serious
364 * error while handling the request
367 create_response (void *cls,
368 struct MHD_Connection *con,
372 const char *upload_data,
373 size_t *upload_data_size,
378 struct GNUNET_HashCode key;
379 struct MhdConnectionHandle *con_handle;
380 struct GNUNET_REST_RequestHandle *rest_conndata_handle;
382 con_handle = *con_cls;
384 if (NULL == *con_cls)
386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New connection %s\n", url);
387 char tmp_url[strlen (url) + 1];
388 strcpy (tmp_url, url);
389 con_handle = GNUNET_new (struct MhdConnectionHandle);
390 con_handle->con = con;
391 con_handle->state = GN_REST_STATE_INIT;
392 *con_cls = con_handle;
394 plugin_name = strtok (tmp_url, "/");
396 if (NULL != plugin_name)
398 GNUNET_CRYPTO_hash (plugin_name, strlen (plugin_name), &key);
400 con_handle->plugin = GNUNET_CONTAINER_multihashmap_get (plugin_map, &key);
402 if (NULL == con_handle->plugin)
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Queueing response with MHD\n");
405 GNUNET_free (con_handle);
406 return MHD_queue_response (con, MHD_HTTP_NOT_FOUND, failure_response);
411 if (GN_REST_STATE_INIT == con_handle->state)
413 rest_conndata_handle = GNUNET_new (struct GNUNET_REST_RequestHandle);
414 rest_conndata_handle->method = meth;
415 rest_conndata_handle->url = url;
416 rest_conndata_handle->data = upload_data;
417 rest_conndata_handle->data_size = *upload_data_size;
418 rest_conndata_handle->url_param_map =
419 GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
420 rest_conndata_handle->header_param_map =
421 GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
422 con_handle->data_handle = rest_conndata_handle;
423 MHD_get_connection_values (con,
424 MHD_GET_ARGUMENT_KIND,
425 (MHD_KeyValueIterator) & url_iterator,
426 rest_conndata_handle);
427 MHD_get_connection_values (con,
429 (MHD_KeyValueIterator) & header_iterator,
430 rest_conndata_handle);
431 con_handle->pp = MHD_create_post_processor (con,
434 rest_conndata_handle);
435 if (*upload_data_size)
437 MHD_post_process (con_handle->pp, upload_data, *upload_data_size);
439 MHD_destroy_post_processor (con_handle->pp);
441 con_handle->state = GN_REST_STATE_PROCESSING;
442 con_handle->plugin->process_request (rest_conndata_handle,
445 *upload_data_size = 0;
449 if (NULL == con_handle->response)
451 // Suspend connection until plugin is done
452 MHD_suspend_connection (con_handle->con);
455 MHD_resume_connection (con_handle->con);
456 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
457 "Queueing response from plugin with MHD\n");
458 // Handle Preflights for extensions
459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking origin\n");
460 GNUNET_CRYPTO_hash ("origin", strlen ("origin"), &key);
461 origin = GNUNET_CONTAINER_multihashmap_get (con_handle->data_handle
466 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Origin: %s\n", origin);
467 // Only echo for browser plugins
468 if (GNUNET_YES == echo_origin)
471 strncmp ("moz-extension://", origin, strlen ("moz-extension://"))) ||
472 (0 == strncmp ("chrome-extension://",
474 strlen ("chrome-extension://"))))
476 MHD_add_response_header (con_handle->response,
477 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
481 if (NULL != allow_origins)
483 char *tmp = GNUNET_strdup (allow_origins);
484 char *allow_origin = strtok (tmp, ",");
485 while (NULL != allow_origin)
487 if (0 == strncmp (allow_origin, origin, strlen (allow_origin)))
489 MHD_add_response_header (con_handle->response,
490 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
494 allow_origin = strtok (NULL, ",");
499 if (NULL != allow_credentials)
501 MHD_add_response_header (con_handle->response,
502 "Access-Control-Allow-Credentials",
505 if (NULL != allow_headers)
507 MHD_add_response_header (con_handle->response,
508 "Access-Control-Allow-Headers",
512 int ret = MHD_queue_response (con, con_handle->status, con_handle->response);
513 cleanup_handle (con_handle);
518 /* ******************** MHD HTTP setup and event loop ******************** */
521 * Function called when MHD decides that we are done with a connection.
524 * @param connection connection handle
525 * @param con_cls value as set by the last call to
526 * the MHD_AccessHandlerCallback, should be our handle
527 * @param toe reason for request termination (ignored)
530 mhd_completed_cb (void *cls,
531 struct MHD_Connection *connection,
533 enum MHD_RequestTerminationCode toe)
535 if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
536 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
537 "MHD encountered error handling request: %d\n",
543 * Kill the MHD daemon.
550 MHD_stop_daemon (httpd);
553 if (NULL != httpd_task)
555 GNUNET_SCHEDULER_cancel (httpd_task);
560 GNUNET_SCHEDULER_cancel (ltask4);
565 GNUNET_SCHEDULER_cancel (ltask6);
571 GNUNET_NETWORK_socket_close (lsock4);
576 GNUNET_NETWORK_socket_close (lsock6);
583 * Schedule MHD. This function should be called initially when an
584 * MHD is first getting its client socket, and will then automatically
585 * always be called later whenever there is work to be done.
587 * @param hd the daemon to schedule
595 struct GNUNET_NETWORK_FDSet *wrs;
596 struct GNUNET_NETWORK_FDSet *wws;
599 MHD_UNSIGNED_LONG_LONG timeout;
600 struct GNUNET_TIME_Relative tv;
606 if (MHD_YES != MHD_get_fdset (httpd, &rs, &ws, &es, &max))
611 haveto = MHD_get_timeout (httpd, &timeout);
612 if (MHD_YES == haveto)
613 tv.rel_value_us = (uint64_t) timeout * 1000LL;
615 tv = GNUNET_TIME_UNIT_FOREVER_REL;
618 wrs = GNUNET_NETWORK_fdset_create ();
619 wws = GNUNET_NETWORK_fdset_create ();
620 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
621 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
628 if (NULL != httpd_task)
630 GNUNET_SCHEDULER_cancel (httpd_task);
633 if ((MHD_YES == haveto) || (-1 != max))
635 httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
643 GNUNET_NETWORK_fdset_destroy (wrs);
645 GNUNET_NETWORK_fdset_destroy (wws);
650 * Task run whenever HTTP server operations are pending.
664 * Accept new incoming connections
666 * @param cls the closure with the lsock4 or lsock6
667 * @param tc the scheduler context
670 do_accept (void *cls)
672 struct GNUNET_NETWORK_Handle *lsock = cls;
673 struct GNUNET_NETWORK_Handle *s;
675 const struct sockaddr *addr;
678 GNUNET_assert (NULL != lsock);
681 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
686 else if (lsock == lsock6)
688 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
695 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
698 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
701 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
702 "Got an inbound connection, waiting for data\n");
703 fd = GNUNET_NETWORK_get_fd (s);
704 addr = GNUNET_NETWORK_get_addr (s);
705 len = GNUNET_NETWORK_get_addrlen (s);
706 if (MHD_YES != MHD_add_connection (httpd, fd, addr, len))
708 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
709 _ ("Failed to pass client to MHD\n"));
718 * Task run on shutdown
723 do_shutdown (void *cls)
725 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutting down...\n");
727 GNUNET_free_non_null (allow_credentials);
728 GNUNET_free_non_null (allow_headers);
733 * Create an IPv4 listen socket bound to our port.
735 * @return NULL on error
737 static struct GNUNET_NETWORK_Handle *
740 struct GNUNET_NETWORK_Handle *ls;
741 struct sockaddr_in sa4;
744 memset (&sa4, 0, sizeof(sa4));
745 sa4.sin_family = AF_INET;
746 sa4.sin_port = htons (port);
747 sa4.sin_addr.s_addr = address;
748 #if HAVE_SOCKADDR_IN_SIN_LEN
749 sa4.sin_len = sizeof(sa4);
751 ls = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
754 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls,
755 (const struct sockaddr *) &sa4,
759 GNUNET_NETWORK_socket_close (ls);
768 * Create an IPv6 listen socket bound to our port.
770 * @return NULL on error
772 static struct GNUNET_NETWORK_Handle *
775 struct GNUNET_NETWORK_Handle *ls;
776 struct sockaddr_in6 sa6;
779 memset (&sa6, 0, sizeof(sa6));
780 sa6.sin6_family = AF_INET6;
781 sa6.sin6_port = htons (port);
782 sa6.sin6_addr = address6;
783 #if HAVE_SOCKADDR_IN_SIN_LEN
784 sa6.sin6_len = sizeof(sa6);
786 ls = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_STREAM, 0);
789 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls,
790 (const struct sockaddr *) &sa6,
794 GNUNET_NETWORK_socket_close (ls);
803 * Callback for plugin load
806 * @param libname the name of the library loaded
807 * @param lib_ret the object returned by the plugin initializer
810 load_plugin (void *cls, const char *libname, void *lib_ret)
812 struct GNUNET_REST_Plugin *plugin = lib_ret;
813 struct GNUNET_HashCode key;
817 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
818 "Could not load plugin `%s'\n",
822 GNUNET_assert (1 < strlen (plugin->name));
823 GNUNET_assert ('/' == *plugin->name);
824 GNUNET_CRYPTO_hash (plugin->name + 1, strlen (plugin->name + 1), &key);
825 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
829 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
832 "Could not load add plugin `%s'\n",
836 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded plugin `%s'\n", libname);
841 * Main function that will be run
844 * @param args remaining command-line arguments
845 * @param cfgfile name of the configuration file used (for saving, can be NULL)
846 * @param c configuration
852 const struct GNUNET_CONFIGURATION_Handle *c)
857 plugin_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
859 /* Get port to bind to */
861 GNUNET_CONFIGURATION_get_value_number (cfg, "rest", "HTTP_PORT", &port))
863 // No address specified
864 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Using default port...\n");
865 port = GNUNET_REST_SERVICE_PORT;
868 /* Get address to bind to */
870 GNUNET_CONFIGURATION_get_value_string (cfg, "rest", "BIND_TO", &addr_str))
872 // No address specified
873 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Don't know what to bind to...\n");
874 GNUNET_SCHEDULER_shutdown ();
877 if (1 != inet_pton (AF_INET, addr_str, &address))
879 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
880 "Unable to parse address %s\n",
882 GNUNET_free (addr_str);
883 GNUNET_SCHEDULER_shutdown ();
886 GNUNET_free (addr_str);
887 /* Get address to bind to */
888 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
893 // No address specified
894 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Don't know what to bind6 to...\n");
895 GNUNET_SCHEDULER_shutdown ();
898 if (1 != inet_pton (AF_INET6, addr_str, &address6))
900 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
901 "Unable to parse IPv6 address %s\n",
903 GNUNET_free (addr_str);
904 GNUNET_SCHEDULER_shutdown ();
907 GNUNET_free (addr_str);
910 /* Get CORS data from cfg */
912 GNUNET_CONFIGURATION_get_value_yesno (cfg,
914 "REST_ECHO_ORIGIN_WEBEXT");
915 allow_origins = NULL;
916 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
921 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
922 "No CORS Access-Control-Allow-Origin header will be sent...\n");
925 GNUNET_CONFIGURATION_get_value_string (cfg,
927 "REST_ALLOW_CREDENTIALS",
930 // No origin specified
931 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
932 "No CORS Credential Header will be sent...\n");
935 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
937 "REST_ALLOW_HEADERS",
940 // No origin specified
941 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
942 "No CORS Access-Control-Allow-Headers Header will be sent...\n");
945 /* Open listen socket proxy */
949 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
953 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
955 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
956 GNUNET_NETWORK_socket_close (lsock6);
961 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
970 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
974 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
976 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
977 GNUNET_NETWORK_socket_close (lsock4);
982 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
988 if ((NULL == lsock4) && (NULL == lsock6))
990 GNUNET_SCHEDULER_shutdown ();
993 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service listens on port %llu\n", port);
994 httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET
995 | MHD_ALLOW_SUSPEND_RESUME,
1001 MHD_OPTION_CONNECTION_TIMEOUT,
1003 MHD_OPTION_NOTIFY_COMPLETED,
1009 GNUNET_SCHEDULER_shutdown ();
1013 GNUNET_PLUGIN_load_all ("libgnunet_plugin_rest",
1017 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1023 * The main function for gnunet-rest-service
1025 * @param argc number of arguments from the cli
1026 * @param argv command line arguments
1027 * @return 0 ok, 1 on error
1031 main (int argc, char *const *argv)
1033 struct GNUNET_GETOPT_CommandLineOption options[] =
1034 { GNUNET_GETOPT_OPTION_END };
1035 static const char *err_page = "{}";
1038 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1040 GNUNET_log_setup ("gnunet-rest-server", "WARNING", NULL);
1041 failure_response = MHD_create_response_from_buffer (strlen (err_page),
1043 MHD_RESPMEM_PERSISTENT);
1044 ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc,
1046 "gnunet-rest-server",
1047 _ ("GNUnet REST server"),
1053 MHD_destroy_response (failure_response);
1054 GNUNET_free_non_null ((char *) argv);
1059 /* end of gnunet-rest-server.c */