/*
This file is part of GNUnet.
- Copyright (C) 2012-2015 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2012-2015 GNUnet e.V.
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
You should have received a copy of the GNU General Public License
along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
*/
/**
* @author Martin Schanzenbach
/**
* The task ID
*/
-struct GNUNET_SCHEDULER_Task * httpd_task;
-
-/**
- * is this an ssl daemon? //TODO
- */
-int is_ssl;
+static struct GNUNET_SCHEDULER_Task *httpd_task;
/**
* The port the service is running on (default 7776)
*/
-static unsigned long port = GNUNET_REST_SERVICE_PORT;
+static unsigned long long port = GNUNET_REST_SERVICE_PORT;
/**
* The listen socket of the service for IPv4
/**
* Map of loaded plugins.
*/
-struct GNUNET_CONTAINER_MultiHashMap *plugin_map;
+static struct GNUNET_CONTAINER_MultiHashMap *plugin_map;
+
+/**
+ * Allowed Origins (CORS)
+ */
+static char* allow_origin;
/**
- * MHD Connection handle
+ * Allowed Headers (CORS)
+ */
+static char* allow_headers;
+
+/**
+ * Allowed Credentials (CORS)
+ */
+static char* allow_credentials;
+
+/**
+ * MHD Connection handle
*/
struct MhdConnectionHandle
{
struct GNUNET_REST_Plugin *plugin;
- struct RestConnectionDataHandle *data_handle;
+ struct GNUNET_REST_RequestHandle *data_handle;
+
+ struct MHD_PostProcessor *pp;
int status;
* Task run whenever HTTP server operations are pending.
*
* @param cls NULL
- * @param tc sched context
*/
static void
-do_httpd (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc);
+do_httpd (void *cls);
/**
static void
run_mhd_now ()
{
- if (NULL !=
- httpd_task)
+ if (NULL != httpd_task)
+ {
GNUNET_SCHEDULER_cancel (httpd_task);
+ httpd_task = NULL;
+ }
httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
NULL);
+
}
/**
* @param cls closure (MHD connection handle)
* @param data the data to return to the caller
* @param len length of the data
- * @param status GNUNET_OK if successful
+ * @param status #GNUNET_OK if successful
*/
-void
+static void
plugin_callback (void *cls,
- const char *data,
- size_t len,
+ struct MHD_Response *resp,
int status)
{
struct MhdConnectionHandle *handle = cls;
- struct MHD_Response *resp = MHD_create_response_from_buffer (len,
- (void*)data,
- MHD_RESPMEM_MUST_COPY);
- (void) MHD_add_response_header (resp,MHD_HTTP_HEADER_CONTENT_TYPE,"application/json");
handle->status = status;
handle->response = resp;
- run_mhd_now();
+ run_mhd_now();
}
-int
+
+static int
cleanup_url_map (void *cls,
const struct GNUNET_HashCode *key,
void *value)
return GNUNET_YES;
}
-void
+
+static void
cleanup_handle (struct MhdConnectionHandle *handle)
{
if (NULL != handle->response)
MHD_destroy_response (handle->response);
if (NULL != handle->data_handle)
{
+
+ if (NULL != handle->data_handle->header_param_map)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle->header_param_map,
+ &cleanup_url_map,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (handle->data_handle->header_param_map);
+ }
if (NULL != handle->data_handle->url_param_map)
{
GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle->url_param_map,
GNUNET_free (handle->data_handle);
}
GNUNET_free (handle);
+}
+
+static int
+header_iterator (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *value)
+{
+ struct GNUNET_REST_RequestHandle *handle = cls;
+ struct GNUNET_HashCode hkey;
+ char *val;
+ char *lowerkey;
+ lowerkey = GNUNET_strdup (key);
+ GNUNET_STRINGS_utf8_tolower (key, lowerkey);
+ GNUNET_CRYPTO_hash (lowerkey, strlen (lowerkey), &hkey);
+ GNUNET_asprintf (&val, "%s", value);
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_multihashmap_put (handle->header_param_map,
+ &hkey,
+ val,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not load add header `%s'=%s\n",
+ lowerkey, value);
+ }
+ GNUNET_free (lowerkey);
+ return MHD_YES;
}
-int
+
+static int
url_iterator (void *cls,
enum MHD_ValueKind kind,
const char *key,
const char *value)
{
- struct RestConnectionDataHandle *handle = cls;
+ struct GNUNET_REST_RequestHandle *handle = cls;
struct GNUNET_HashCode hkey;
char *val;
-
+
GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
GNUNET_asprintf (&val, "%s", value);
if (GNUNET_OK !=
return MHD_YES;
}
+static int
+post_data_iter (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *filename,
+ const char *content_type,
+ const char *transfer_encoding,
+ const char *data,
+ uint64_t off,
+ size_t size)
+{
+ struct GNUNET_REST_RequestHandle *handle = cls;
+ struct GNUNET_HashCode hkey;
+ char *val;
+
+ if (MHD_POSTDATA_KIND != kind)
+ return MHD_YES;
+
+ GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
+ GNUNET_asprintf (&val, "%s", data);
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_multihashmap_put (handle->url_param_map,
+ &hkey,
+ val,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not load add url param '%s'=%s\n",
+ key, data);
+ GNUNET_free(val);
+ }
+ return MHD_YES;
+
+}
+
/* ********************************* MHD response generation ******************* */
/**
char *plugin_name;
struct GNUNET_HashCode key;
struct MhdConnectionHandle *con_handle;
- struct RestConnectionDataHandle *rest_conndata_handle;
+ struct GNUNET_REST_RequestHandle *rest_conndata_handle;
con_handle = *con_cls;
con_handle->plugin = GNUNET_CONTAINER_multihashmap_get (plugin_map,
&key);
}
- else
- con_handle->plugin = NULL;
-
if (NULL == con_handle->plugin)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Queueing response with MHD\n");
GNUNET_free (con_handle);
- MHD_queue_response (con,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- failure_response);
+ return MHD_queue_response (con,
+ MHD_HTTP_NOT_FOUND,
+ failure_response);
}
+
return MHD_YES;
}
if (GN_REST_STATE_INIT == con_handle->state)
{
- rest_conndata_handle = GNUNET_new (struct RestConnectionDataHandle);
+ rest_conndata_handle = GNUNET_new (struct GNUNET_REST_RequestHandle);
rest_conndata_handle->method = meth;
rest_conndata_handle->url = url;
rest_conndata_handle->data = upload_data;
rest_conndata_handle->data_size = *upload_data_size;
rest_conndata_handle->url_param_map = GNUNET_CONTAINER_multihashmap_create (16,
GNUNET_NO);
+ rest_conndata_handle->header_param_map = GNUNET_CONTAINER_multihashmap_create (16,
+ GNUNET_NO);
con_handle->data_handle = rest_conndata_handle;
MHD_get_connection_values (con,
MHD_GET_ARGUMENT_KIND,
&url_iterator,
rest_conndata_handle);
+ MHD_get_connection_values (con,
+ MHD_HEADER_KIND,
+ &header_iterator,
+ rest_conndata_handle);
+ con_handle->pp = MHD_create_post_processor(con,
+ 65536,
+ post_data_iter,
+ rest_conndata_handle);
+ if (*upload_data_size)
+ {
+ MHD_post_process(con_handle->pp, upload_data, *upload_data_size);
+ }
+ MHD_destroy_post_processor(con_handle->pp);
+
con_handle->state = GN_REST_STATE_PROCESSING;
con_handle->plugin->process_request (rest_conndata_handle,
&plugin_callback,
con_handle);
*upload_data_size = 0;
-
}
if (NULL != con_handle->response)
{
-
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Queueing response from plugin with MHD\n");
- if (GNUNET_OK == con_handle->status) {
- return MHD_queue_response (con,
- MHD_HTTP_OK,
- con_handle->response);
- } else {
- return MHD_queue_response (con,
- MHD_HTTP_BAD_REQUEST,
- con_handle->response);
+ //Handle Preflights
+ if (NULL != allow_origin)
+ {
+ MHD_add_response_header (con_handle->response,
+ MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+ allow_origin);
+ }
+ if (NULL != allow_credentials)
+ {
+ MHD_add_response_header (con_handle->response,
+ "Access-Control-Allow-Credentials",
+ allow_credentials);
}
+ if (NULL != allow_headers)
+ {
+ MHD_add_response_header (con_handle->response,
+ "Access-Control-Allow-Headers",
+ allow_headers);
+ }
+ //Always add JSONAPI content type. TODO
+ MHD_add_response_header (con_handle->response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ "application/vnd.api+json");
+ int ret = MHD_queue_response (con,
+ con_handle->status,
+ con_handle->response);
cleanup_handle (con_handle);
+ return ret;
}
return MHD_YES;
}
+
/* ******************** MHD HTTP setup and event loop ******************** */
/**
void **con_cls,
enum MHD_RequestTerminationCode toe)
{
-
if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"MHD encountered error handling request: %d\n",
toe);
- *con_cls = NULL;
}
+
/**
* Kill the MHD daemon.
*/
httpd = NULL;
}
if (NULL != httpd_task)
- {
+ {
GNUNET_SCHEDULER_cancel (httpd_task);
httpd_task = NULL;
}
-}
+ if (NULL != ltask4)
+ {
+ GNUNET_SCHEDULER_cancel (ltask4);
+ ltask4 = NULL;
+ }
+ if (NULL != ltask6)
+ {
+ GNUNET_SCHEDULER_cancel (ltask6);
+ ltask6 = NULL;
+ }
+
+ if (NULL != lsock4)
+ {
+ GNUNET_NETWORK_socket_close (lsock4);
+ lsock4 = NULL;
+ }
+ if (NULL != lsock6)
+ {
+ GNUNET_NETWORK_socket_close (lsock6);
+ lsock6 = NULL;
+ }
+ }
+
-/**
- * Task run whenever HTTP server is idle for too long. Kill it.
- *
- * @param cls NULL
- * @param tc sched context
- */
-static void
-kill_httpd_task (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- httpd_task = NULL;
- kill_httpd ();
-}
/**
* Schedule MHD. This function should be called initially when an
* MHD is first getting its client socket, and will then automatically
wws = NULL;
}
if (NULL != httpd_task)
- GNUNET_SCHEDULER_cancel (httpd_task);
- if ( (MHD_YES != haveto) &&
- (-1 == max))
{
- /* daemon is idle, kill after timeout */
- httpd_task = GNUNET_SCHEDULER_add_delayed (MHD_CACHE_TIMEOUT,
- &kill_httpd_task,
- NULL);
+ GNUNET_SCHEDULER_cancel (httpd_task);
+ httpd_task = NULL;
}
- else
+ if ( (MHD_YES == haveto) ||
+ (-1 != max))
{
httpd_task =
GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
tv, wrs, wws,
&do_httpd, NULL);
+
}
if (NULL != wrs)
GNUNET_NETWORK_fdset_destroy (wrs);
* Task run whenever HTTP server operations are pending.
*
* @param cls NULL
- * @param tc scheduler context
*/
static void
-do_httpd (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_httpd (void *cls)
{
httpd_task = NULL;
MHD_run (httpd);
schedule_httpd ();
}
+
/**
* Accept new incoming connections
*
* @param tc the scheduler context
*/
static void
-do_accept (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_accept (void *cls)
{
struct GNUNET_NETWORK_Handle *lsock = cls;
struct GNUNET_NETWORK_Handle *s;
const struct sockaddr *addr;
socklen_t len;
+ GNUNET_assert (NULL != lsock);
if (lsock == lsock4)
- ltask4 = NULL;
- else
- ltask6 = NULL;
- if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
- return;
- if (lsock == lsock4)
+ {
ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
lsock,
&do_accept, lsock);
- else
+
+ }
+ else if (lsock == lsock6)
+ {
ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
lsock,
&do_accept, lsock);
+
+ }
+ else
+ GNUNET_assert (0);
s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
if (NULL == s)
{
_("Failed to pass client to MHD\n"));
return;
}
-
+ GNUNET_free(s);
schedule_httpd ();
}
+
/**
* Task run on shutdown
*
* @param cls closure
- * @param tc task context
*/
static void
-do_shutdown (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_shutdown (void *cls)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Shutting down...\n");
kill_httpd ();
+ GNUNET_free_non_null (allow_origin);
+ GNUNET_free_non_null (allow_credentials);
+ GNUNET_free_non_null (allow_headers);
}
+
/**
* Create an IPv4 listen socket bound to our port.
*
sa4.sin_port = htons (port);
#if HAVE_SOCKADDR_IN_SIN_LEN
sa4.sin_len = sizeof (sa4);
-#endif
+#endif
ls = GNUNET_NETWORK_socket_create (AF_INET,
SOCK_STREAM,
0);
return ls;
}
+
/**
* Create an IPv6 listen socket bound to our port.
*
sa6.sin6_port = htons (port);
#if HAVE_SOCKADDR_IN_SIN_LEN
sa6.sin6_len = sizeof (sa6);
-#endif
+#endif
ls = GNUNET_NETWORK_socket_create (AF_INET6,
SOCK_STREAM,
0);
return ls;
}
+
/**
* Callback for plugin load
*
* @param libname the name of the library loaded
* @param lib_ret the object returned by the plugin initializer
*/
-void
+static void
load_plugin (void *cls,
const char *libname,
void *lib_ret)
struct GNUNET_HashCode key;
if (NULL == lib_ret)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Could not load plugin `%s'\n",
libname);
return;
plugin,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Could not load add plugin `%s'\n",
libname);
return;
libname);
}
+
/**
* Main function that will be run
*
* @param c configuration
*/
static void
-run (void *cls, char *const *args, const char *cfgfile,
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *c)
{
cfg = c;
plugin_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
+ /* Get CORS data from cfg */
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "rest",
+ "REST_ALLOW_ORIGIN",
+ &allow_origin))
+ {
+ //No origin specified
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No CORS Access-Control-Allow-Origin Header will be sent...\n");
+ }
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "rest",
+ "REST_ALLOW_CREDENTIALS",
+ &allow_credentials))
+ {
+ //No origin specified
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No CORS Access-Control-Allow-Origin Header will be sent...\n");
+ }
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "rest",
+ "REST_ALLOW_HEADERS",
+ &allow_headers))
+ {
+ //No origin specified
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No CORS Access-Control-Allow-Headers Header will be sent...\n");
+ }
+
/* Open listen socket proxy */
lsock6 = bind_v6 ();
if (NULL == lsock6)
+ {
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
+ }
else
{
if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
{
ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
lsock6, &do_accept, lsock6);
+
}
}
lsock4 = bind_v4 ();
if (NULL == lsock4)
+ {
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
+ }
else
{
if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
{
ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
lsock4, &do_accept, lsock4);
+
}
}
if ( (NULL == lsock4) &&
{
GNUNET_SCHEDULER_shutdown ();
return;
- }
+ }
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Service listens on port %u\n",
+ "Service listens on port %llu\n",
port);
httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
0,
(void *) cfg,
&load_plugin,
NULL);
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
- &do_shutdown, NULL);
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
}
+
/**
*
* The main function for gnunet-rest-service
int
main (int argc, char *const *argv)
{
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- {'p', "port", NULL,
- gettext_noop ("listen on specified port (default: 7776)"), 1,
- &GNUNET_GETOPT_set_ulong, &port},
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_option_ulong ('p',
+ "port",
+ "PORT",
+ gettext_noop ("listen on specified port (default: 7776)"),
+ &port),
GNUNET_GETOPT_OPTION_END
};
static const char* err_page =
if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
return 2;
- GNUNET_log_setup ("gnunet-rest-service", "WARNING", NULL);
+ GNUNET_log_setup ("gnunet-rest-server", "WARNING", NULL);
failure_response = MHD_create_response_from_buffer (strlen(err_page),
(void*)err_page,
MHD_RESPMEM_PERSISTENT);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Start\n");
ret =
(GNUNET_OK ==
GNUNET_PROGRAM_run (argc, argv, "gnunet-rest-server",