X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fplugin_transport_http_server.c;h=a8731907eafe7f3892a6b6ead986bcdde748e2f2;hb=a03f3a1884c6f423cde604ba5b0bba86f43a7113;hp=2ce844307eb1e14c5d026866055984270579dd04;hpb=3e9940cb3859618e37cc35cb45ef6d62ff92677c;p=oweals%2Fgnunet.git diff --git a/src/transport/plugin_transport_http_server.c b/src/transport/plugin_transport_http_server.c index 2ce844307..a8731907e 100644 --- a/src/transport/plugin_transport_http_server.c +++ b/src/transport/plugin_transport_http_server.c @@ -25,15 +25,10 @@ */ #include "platform.h" -#include "gnunet_protocols.h" -#include "gnunet_connection_lib.h" +#include "gnunet_common.h" #include "gnunet_server_lib.h" -#include "gnunet_service_lib.h" #include "gnunet_statistics_service.h" -#include "gnunet_transport_service.h" #include "gnunet_transport_plugin.h" - -#include "gnunet_container_lib.h" #include "gnunet_nat_lib.h" #include "plugin_transport_http_common.h" #include "microhttpd.h" @@ -55,6 +50,7 @@ */ struct Plugin; + /** * Session handle for connections. */ @@ -131,6 +127,11 @@ struct Session */ int session_passed; + /** + * Did we immediately end the session in disconnect_cb + */ + int session_ended; + /** * Absolute time when to receive data again * Used for receive throttling @@ -143,6 +144,7 @@ struct Session GNUNET_SCHEDULER_TaskIdentifier timeout_task; }; + struct ServerConnection { /* _RECV or _SEND */ @@ -151,6 +153,9 @@ struct ServerConnection /* Should this connection get disconnected? GNUNET_YES/NO */ int disconnect; + /* For PUT connections: Is this the first or last callback with size 0 */ + int connected; + /* The session this server connection belongs to */ struct Session *session; @@ -161,6 +166,7 @@ struct ServerConnection struct MHD_Daemon *mhd_daemon; }; + /** * Encapsulation of all of the state of the plugin. */ @@ -209,12 +215,27 @@ struct HTTP_Server_Plugin */ unsigned int cur_connections; + /** + * Did we immediately end the session in disconnect_cb + */ + int in_shutdown; + + /** + * Length of peer id + */ + int peer_id_length; + /** * External hostname the plugin can be connected to, can be different to * the host's FQDN, used e.g. for reverse proxying */ char *ext_addr; + /** + * Notify transport only about external address + */ + unsigned int external_only; + /** * External address length */ @@ -324,6 +345,7 @@ struct HTTP_Server_Plugin }; + /** * Wrapper to manage addresses */ @@ -344,6 +366,7 @@ struct HttpAddressWrapper size_t addrlen; }; + /** * Message to send using http */ @@ -374,6 +397,11 @@ struct HTTP_Message */ size_t size; + /** + * HTTP/S specific overhead + */ + size_t overhead; + /** * Continuation function to call once the transmission buffer * has again space available. NULL if there is no @@ -388,35 +416,55 @@ struct HTTP_Message }; +/** + * The http_server plugin handle + */ static struct HTTP_Server_Plugin * p; + /** - * Start session timeout + * Start session timeout for session s + * @param s the session */ static void server_start_session_timeout (struct Session *s); + /** - * Increment session timeout due to activity + * Increment session timeout due to activity for session s + * @param s the session */ static void server_reschedule_session_timeout (struct Session *s); + /** - * Cancel timeout + * Cancel timeout for session s + * @param s the session */ static void server_stop_session_timeout (struct Session *s); + /** - * Disconnect a session + * Disconnect a session s + * @param s the session */ -int +static int server_disconnect (struct Session *s); -int + +/** + * Does session s exist? + * + * @param plugin the plugin handle + * @param s the session + * @return GNUNET_YES on success, GNUNET_NO on error + */ +static int server_exist_session (struct HTTP_Server_Plugin *plugin, struct Session *s); + /** * Reschedule the execution of both IPv4 and IPv6 server * @param plugin the plugin @@ -425,7 +473,9 @@ server_exist_session (struct HTTP_Server_Plugin *plugin, struct Session *s); * until timeout */ static void -server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, int now); +server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, + int now); + /** * Function that can be used by the transport service to transmit @@ -465,6 +515,7 @@ http_server_plugin_send (void *cls, struct HTTP_Server_Plugin *plugin = cls; struct HTTP_Message *msg; int bytes_sent = 0; + char *stat_txt; GNUNET_assert (plugin != NULL); GNUNET_assert (session != NULL); @@ -474,11 +525,21 @@ http_server_plugin_send (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - GNUNET_assert (NULL != session->server_send); + if (NULL == session->server_send) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_YES == session->server_send->disconnect) return GNUNET_SYSERR; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, session->plugin->name, + "Session %p/connection %p: Sending message with %u to peer `%s' with \n", + session, session->server_send, + msgbuf_size, GNUNET_i2s (&session->target)); + /* create new message and schedule */ bytes_sent = sizeof (struct HTTP_Message) + msgbuf_size; msg = GNUNET_malloc (bytes_sent); @@ -492,6 +553,11 @@ http_server_plugin_send (void *cls, GNUNET_CONTAINER_DLL_insert_tail (session->msg_head, session->msg_tail, msg); + GNUNET_asprintf (&stat_txt, "# bytes currently in %s_server buffers", plugin->protocol); + GNUNET_STATISTICS_update (plugin->env->stats, + stat_txt, msgbuf_size, GNUNET_NO); + GNUNET_free (stat_txt); + server_reschedule (session->plugin, session->server_send->mhd_daemon, GNUNET_YES); server_reschedule_session_timeout (session); @@ -529,7 +595,7 @@ http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *targ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, "Disconnecting session %p to `%s'\n", pos, GNUNET_i2s (target)); - GNUNET_assert (GNUNET_OK == server_disconnect (pos)); + server_disconnect (pos); } } @@ -549,14 +615,17 @@ http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *targ * and transport */ static int -http_server_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) +http_server_plugin_address_suggested (void *cls, const void *addr, + size_t addrlen) { struct HTTP_Server_Plugin *plugin = cls; struct HttpAddressWrapper *next; struct HttpAddressWrapper *pos; - if ((NULL != plugin->ext_addr) && GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, plugin->ext_addr, plugin->ext_addr_len))) + if ((NULL != plugin->ext_addr) && + GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, + plugin->ext_addr, plugin->ext_addr_len))) return GNUNET_OK; next = plugin->addr_head; @@ -574,6 +643,7 @@ http_server_plugin_address_suggested (void *cls, const void *addr, size_t addrle return GNUNET_NO; } + /** * Creates a new outbound session the transport * service will use to send data to the peer @@ -595,17 +665,15 @@ http_server_plugin_get_session (void *cls, /** * Deleting the session * Must not be used afterwards + * + * @param s the session to delete */ - -void +static void server_delete_session (struct Session *s) { struct HTTP_Server_Plugin *plugin = s->plugin; server_stop_session_timeout(s); - if (GNUNET_YES == s->session_passed) - plugin->env->session_end (plugin->env->cls, &s->target, s); - GNUNET_CONTAINER_DLL_remove (plugin->head, plugin->tail, s); struct HTTP_Message *msg = s->msg_head; struct HTTP_Message *tmp = NULL; @@ -617,7 +685,8 @@ server_delete_session (struct Session *s) GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); if (msg->transmit_cont != NULL) { - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); + msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR, + msg->size, msg->pos + msg->overhead); } GNUNET_free (msg); msg = tmp; @@ -639,7 +708,9 @@ server_delete_session (struct Session *s) /** -* Cancel timeout +* Cancel timeout for session s +* +* @param s the session */ static void server_stop_session_timeout (struct Session *s) @@ -654,17 +725,21 @@ server_stop_session_timeout (struct Session *s) } } + /** * Function that queries MHD's select sets and * starts the task waiting for them. * @param plugin plugin * @param daemon_handle the MHD daemon handle + * @param now schedule immediately * @return gnunet task identifier */ static GNUNET_SCHEDULER_TaskIdentifier -server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_handle, +server_schedule (struct HTTP_Server_Plugin *plugin, + struct MHD_Daemon *daemon_handle, int now); + /** * Reschedule the execution of both IPv4 and IPv6 server * @param plugin the plugin @@ -673,7 +748,8 @@ server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_ha * until timeout */ static void -server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, int now) +server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, + int now) { if ((server == plugin->server_v4) && (plugin->server_v4 != NULL)) { @@ -708,11 +784,24 @@ server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, } } -int + +/** + * Disconnect session s + * + * @param s the session + * @return GNUNET_OK on success + */ +static int server_disconnect (struct Session *s) { - struct ServerConnection * send; - struct ServerConnection * recv; + struct ServerConnection * send = NULL; + struct ServerConnection * recv = NULL; + + if (GNUNET_NO == server_exist_session (p, s)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } send = (struct ServerConnection *) s->server_send; if (s->server_send != NULL) @@ -772,131 +861,172 @@ server_mhd_connection_timeout (struct HTTP_Server_Plugin *plugin, struct Session #endif } +/** + * Parse incoming URL for tag and target + * + * @param plugin plugin + * @param url incoming url + * @param target where to store the target + * @param tag where to store the tag + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ -static struct ServerConnection * -server_lookup_connection (struct HTTP_Server_Plugin *plugin, - struct MHD_Connection *mhd_connection, const char *url, - const char *method) +static int +server_parse_url (struct HTTP_Server_Plugin *plugin, const char * url, struct GNUNET_PeerIdentity * target, uint32_t *tag) { - struct Session *s = NULL; - struct ServerConnection *sc = NULL; - const union MHD_ConnectionInfo *conn_info; - struct GNUNET_ATS_Information ats; - - char *addr; - size_t addr_len; + int debug = GNUNET_YES; - struct GNUNET_PeerIdentity target; - uint32_t tag = 0; - int direction = GNUNET_SYSERR; - int to; - - /* url parsing variables */ - size_t url_len; - char *url_end; - char *hash_start; - char *hash_end; - char *tag_start; - char *tag_end; - - conn_info = MHD_get_connection_info (mhd_connection, - MHD_CONNECTION_INFO_CLIENT_ADDRESS); - if ((conn_info->client_addr->sa_family != AF_INET) && - (conn_info->client_addr->sa_family != AF_INET6)) - return NULL; + char * tag_start = NULL; + char * tag_end = NULL; + char * target_start = NULL; + char * separator = NULL; + char hash[plugin->peer_id_length+1]; + int hash_length; + unsigned long int ctag; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "New %s connection from %s\n", method, url); /* URL parsing - * URL is valid if it is in the form [peerid[103];tag]*/ - url_len = strlen (url); - url_end = (char *) &url[url_len]; + * URL is valid if it is in the form [prefix with (multiple) '/'][peerid[103];tag]*/ - if (url_len < 105) - { - GNUNET_break (0); - goto error; /* too short */ - } - hash_start = strrchr (url, '/'); - if (NULL == hash_start) + if (NULL == url) { GNUNET_break (0); - goto error; /* '/' delimiter not found */ + return GNUNET_SYSERR; } - if (hash_start >= url_end) + /* convert tag */ + + /* find separator */ + separator = strrchr (url, ';'); + + if (NULL == separator) { - GNUNET_break (0); - goto error; /* mal formed */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - hash_start++; + tag_start = separator + 1; - hash_end = strrchr (hash_start, ';'); - if (NULL == hash_end) + if (strlen (tag_start) == 0) { - GNUNET_break (0); - goto error; /* ';' delimiter not found */ + /* No tag after separator */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - - if (hash_end >= url_end) + ctag = strtoul (tag_start, &tag_end, 10); + if (ctag == 0) { - GNUNET_break (0); - goto error; /* mal formed */ + /* tag == 0 , invalid */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - - if (hash_start >= hash_end) + if ((ctag == ULONG_MAX) && (ERANGE == errno)) { - GNUNET_break (0); - goto error; /* mal formed */ + /* out of range: > ULONG_MAX */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - - if ((strlen(hash_start) - strlen(hash_end)) != 103) + if (ctag > UINT32_MAX) { - GNUNET_break (0); - goto error; /* invalid hash length */ + /* out of range: > UINT32_MAX */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - - char hash[104]; - memcpy (hash, hash_start, 103); - hash[103] = '\0'; - if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target.hashPubKey))) + (*tag) = (uint32_t) ctag; + if (NULL == tag_end) { - GNUNET_break (0); - goto error; /* mal formed */ + /* no char after tag */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - - if (hash_end >= url_end) + if (url[strlen(url)] != tag_end[0]) { - GNUNET_break (0); - goto error; /* mal formed */ + /* there are more not converted chars after tag */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } + if (debug) + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Found tag `%u' in url\n", (*tag)); - tag_start = &hash_end[1]; - /* Converting tag */ - tag_end = NULL; - tag = strtoul (tag_start, &tag_end, 10); - if (tag == 0) + /* convert peer id */ + target_start = strrchr (url, '/'); + if (NULL == target_start) { - GNUNET_break (0); - goto error; /* mal formed */ + /* no leading '/' */ + target_start = (char *) url; } - if (tag_end == NULL) + target_start++; + hash_length = separator - target_start; + if (hash_length != plugin->peer_id_length) { - GNUNET_break (0); - goto error; /* mal formed */ + /* no char after tag */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - if (tag_end != url_end) + memcpy (hash, target_start, hash_length); + hash[hash_length] = '\0'; + + if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target->hashPubKey))) { - GNUNET_break (0); - goto error; /* mal formed */ + /* hash conversion failed */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Found target `%s' in url\n", GNUNET_h2s_full(&target->hashPubKey)); + return GNUNET_OK; +} + + +/** + * Lookup a mhd connection and create one if none is found + * + * @param plugin the plugin handle + * @param mhd_connection the incoming mhd_connection + * @param url incoming requested URL + * @param method PUT or GET + * @return the server connecetion + */ +static struct ServerConnection * +server_lookup_connection (struct HTTP_Server_Plugin *plugin, + struct MHD_Connection *mhd_connection, const char *url, + const char *method) +{ + struct Session *s = NULL; + struct ServerConnection *sc = NULL; + const union MHD_ConnectionInfo *conn_info; + struct GNUNET_ATS_Information ats; + + char *addr; + size_t addr_len; + + struct GNUNET_PeerIdentity target; + uint32_t tag = 0; + int direction = GNUNET_SYSERR; + int to; + + conn_info = MHD_get_connection_info (mhd_connection, + MHD_CONNECTION_INFO_CLIENT_ADDRESS); + if ((conn_info->client_addr->sa_family != AF_INET) && + (conn_info->client_addr->sa_family != AF_INET6)) + return NULL; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "New %s connection from %s\n", method, url); + + if (GNUNET_SYSERR == server_parse_url (plugin, url, &target, &tag)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Invalid url %s\n", url); + return NULL; + } if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) direction = _RECEIVE; else if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) direction = _SEND; else { - goto error; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Invalid method %s connection from %s\n", method, url); + return NULL; } plugin->cur_connections++; @@ -905,7 +1035,6 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, method, GNUNET_i2s (&target), tag, plugin->cur_connections, plugin->max_connections); - /* find duplicate session */ s = plugin->head; while (s != NULL) @@ -923,7 +1052,8 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, "Duplicate PUT connection from `%s' tag %u, dismissing new connection\n", GNUNET_i2s (&target), tag); - goto error; + return NULL; + } if ((_SEND == direction) && (NULL != s->server_send)) { @@ -931,7 +1061,7 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, "Duplicate GET connection from `%s' tag %u, dismissing new connection\n", GNUNET_i2s (&target), tag); - goto error; + return NULL; } } else @@ -951,9 +1081,8 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, break; default: GNUNET_break (0); - goto error; + return NULL; } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, "Creating new session for peer `%s' connecting from `%s'\n", GNUNET_i2s (&target), @@ -970,10 +1099,10 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, s->server_recv = NULL; s->server_send = NULL; s->session_passed = GNUNET_NO; + s->session_ended = GNUNET_NO; server_start_session_timeout(s); GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); } - sc = GNUNET_malloc (sizeof (struct ServerConnection)); if (conn_info->client_addr->sa_family == AF_INET) sc->mhd_daemon = plugin->server_v4; @@ -981,12 +1110,12 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, sc->mhd_daemon = plugin->server_v6; sc->mhd_conn = mhd_connection; sc->direction = direction; + sc->connected = GNUNET_NO; sc->session = s; if (direction == _SEND) s->server_send = sc; if (direction == _RECEIVE) s->server_recv = sc; - #if MHD_VERSION >= 0x00090E00 if ((NULL == s->server_recv) || (NULL == s->server_send)) { @@ -1007,14 +1136,16 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, "Setting timeout for %p to %u sec.\n", sc, to); #endif return sc; - -/* Error condition */ - error: - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Invalid connection request\n"); - return NULL; } + +/** + * Lookup a session for a server connection + * + * @param plugin the plugin + * @param sc the server connection + * @return the session found or NULL + */ static struct Session * server_lookup_session (struct HTTP_Server_Plugin *plugin, struct ServerConnection * sc) @@ -1046,6 +1177,7 @@ server_exist_session (struct HTTP_Server_Plugin *plugin, struct Session *s) /** * Callback called by MHD when it needs data to send + * * @param cls current session * @param pos position in buffer * @param buf the buffer to write data to @@ -1058,6 +1190,7 @@ server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) struct Session *s = cls; ssize_t bytes_read = 0; struct HTTP_Message *msg; + char *stat_txt; GNUNET_assert (NULL != p); if (GNUNET_NO == server_exist_session (p, s)) @@ -1076,23 +1209,35 @@ server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) { GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); if (NULL != msg->transmit_cont) - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK); + msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK, + msg->size, msg->size + msg->overhead); GNUNET_free (msg); } } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, + if (0 < bytes_read) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, "Sent %u bytes to peer `%s' with session %p \n", bytes_read, GNUNET_i2s (&s->target), s); - - - + GNUNET_asprintf (&stat_txt, "# bytes currently in %s_server buffers", p->protocol); + GNUNET_STATISTICS_update (p->env->stats, + stat_txt, -bytes_read, GNUNET_NO); + GNUNET_free (stat_txt); + GNUNET_asprintf (&stat_txt, "# bytes transmitted via %s_server", p->protocol); + GNUNET_STATISTICS_update (p->env->stats, + stat_txt, bytes_read, GNUNET_NO); + GNUNET_free (stat_txt); + } return bytes_read; } + /** * Callback called by MessageStreamTokenizer when a message has arrived + * * @param cls current session as closure - * @param client clien + * @param client client * @param message the message to be forwarded to transport service + * @return GNUNET_OK */ static int server_receive_mst_cb (void *cls, void *client, @@ -1101,6 +1246,7 @@ server_receive_mst_cb (void *cls, void *client, struct Session *s = cls; struct GNUNET_ATS_Information atsi[2]; struct GNUNET_TIME_Relative delay; + char *stat_txt; GNUNET_assert (NULL != p); if (GNUNET_NO == server_exist_session(p, s)) @@ -1114,11 +1260,18 @@ server_receive_mst_cb (void *cls, void *client, atsi[1].value = s->ats_address_network_type; GNUNET_break (s->ats_address_network_type != ntohl (GNUNET_ATS_NET_UNSPECIFIED)); + delay = plugin->env->receive (plugin->env->cls, &s->target, message, (const struct GNUNET_ATS_Information *) &atsi, 2, s, s->addr, s->addrlen); + + GNUNET_asprintf (&stat_txt, "# bytes received via %s_server", plugin->protocol); + GNUNET_STATISTICS_update (plugin->env->stats, + stat_txt, ntohs (message->size), GNUNET_NO); + GNUNET_free (stat_txt); + s->session_passed = GNUNET_YES; s->next_receive = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); if (delay.rel_value > 0) @@ -1133,6 +1286,20 @@ server_receive_mst_cb (void *cls, void *client, return GNUNET_OK; } + +/** + * MHD callback for a new incoming connection + * + * @param cls the plugin handle + * @param mhd_connection the mhd connection + * @param url the requested URL + * @param method GET or PUT + * @param version HTTP version + * @param upload_data upload data + * @param upload_data_size sizeof upload data + * @param httpSessionCache the session cache to remember the connection + * @return MHD_YES if connection is accepted, MHD_NO on reject + */ static int server_access_cb (void *cls, struct MHD_Connection *mhd_connection, const char *url, const char *method, const char *version, @@ -1146,13 +1313,21 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, struct Session *s; struct MHD_Response *response; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + _("Access from connection %p (%u of %u) for `%s' `%s' url `%s' with upload data size %u\n"), + sc, + plugin->cur_connections, plugin->max_connections, + method, version, url, (*upload_data_size)); + GNUNET_assert (cls != NULL); if (sc == NULL) { /* new connection */ sc = server_lookup_connection (plugin, mhd_connection, url, method); if (sc != NULL) + { (*httpSessionCache) = sc; + } else { response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO); @@ -1174,12 +1349,11 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, /* existing connection */ sc = (*httpSessionCache); s = sc->session; - GNUNET_assert (NULL != s); /* connection is to be disconnected */ if (sc->disconnect == GNUNET_YES) { - /* Sent HTTP/1.1: 200 OK as PUT Response\ */ + /* Sent HTTP/1.1: 200 OK as response */ response = MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!", MHD_NO, MHD_NO); @@ -1187,14 +1361,7 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, MHD_destroy_response (response); return MHD_YES; } - GNUNET_assert (s != NULL); - /* Check if both directions are connected */ - if ((sc->session->server_recv == NULL) || (sc->session->server_send == NULL)) - { - /* Delayed read from since not both semi-connections are connected */ - return MHD_YES; - } if (sc->direction == _SEND) { @@ -1208,22 +1375,44 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, } if (sc->direction == _RECEIVE) { - if (*upload_data_size == 0) + if ((*upload_data_size == 0) && (sc->connected == GNUNET_NO)) { + /* (*upload_data_size == 0) first callback when header are passed */ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Peer `%s' PUT on address `%s' connected\n", + "Session %p / Connection %p: Peer `%s' PUT on address `%s' connected\n", + s, sc, GNUNET_i2s (&s->target), http_common_plugin_address_to_string (NULL, s->addr, s->addrlen)); + sc->connected = GNUNET_YES; return MHD_YES; } - - /* Receiving data */ - if ((*upload_data_size > 0)) + else if ((*upload_data_size == 0) && (sc->connected == GNUNET_YES)) + { + /* (*upload_data_size == 0) when upload is complete */ + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Session %p / Connection %p: Peer `%s' PUT on address `%s' finished upload\n", + s, sc, + GNUNET_i2s (&s->target), + http_common_plugin_address_to_string (NULL, + s->addr, + s->addrlen)); + sc->connected = GNUNET_NO; + /* Sent HTTP/1.1: 200 OK as PUT Response\ */ + response = MHD_create_response_from_data (strlen ("Thank you!"), + "Thank you!", + MHD_NO, MHD_NO); + res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + return MHD_YES; + } + else if ((*upload_data_size > 0) && (sc->connected == GNUNET_YES)) { + /* (*upload_data_size > 0) for every segment received */ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Peer `%s' PUT on address `%s' received %u bytes\n", + "Session %p / Connection %p: Peer `%s' PUT on address `%s' received %u bytes\n", + s, sc, GNUNET_i2s (&s->target), http_common_plugin_address_to_string (NULL, s->addr, @@ -1250,17 +1439,28 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%p no inbound bandwidth available! Next read was delayed by %llu ms\n", - s, now.abs_value - s->next_receive.abs_value); + "Session %p / Connection %p: no inbound bandwidth available! Next read was delayed by %llu ms\n", + s, sc, now.abs_value - s->next_receive.abs_value); } return MHD_YES; } else + { + GNUNET_break (0); return MHD_NO; + } } return res; } + +/** + * Callback from MHD when a connection disconnects + * + * @param cls closure + * @param connection the disconnected MHD connection + * @param httpSessionCache the pointer to distinguish + */ static void server_disconnect_cb (void *cls, struct MHD_Connection *connection, void **httpSessionCache) @@ -1270,6 +1470,9 @@ server_disconnect_cb (void *cls, struct MHD_Connection *connection, struct Session *t = NULL; struct HTTP_Server_Plugin *plugin = NULL; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, p->name, + "Disconnect for connection %p \n", sc); + if (sc == NULL) return; @@ -1312,6 +1515,7 @@ server_disconnect_cb (void *cls, struct MHD_Connection *connection, GNUNET_i2s (&s->target), s->server_recv, http_common_plugin_address_to_string (NULL, s->addr, s->addrlen)); s->server_recv = NULL; + /* Do not terminate session when PUT disconnects if (NULL != (s->server_send)) { s->server_send->disconnect = GNUNET_YES; @@ -1321,7 +1525,7 @@ server_disconnect_cb (void *cls, struct MHD_Connection *connection, 1); #endif server_reschedule (plugin, s->server_send->mhd_daemon, GNUNET_NO); - } + }*/ if (s->msg_tk != NULL) { GNUNET_SERVER_mst_destroy (s->msg_tk); @@ -1331,6 +1535,7 @@ server_disconnect_cb (void *cls, struct MHD_Connection *connection, GNUNET_free (sc); plugin->cur_connections--; + if ((s->server_send == NULL) && (s->server_recv == NULL)) { GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, @@ -1338,13 +1543,21 @@ server_disconnect_cb (void *cls, struct MHD_Connection *connection, GNUNET_i2s (&s->target), http_common_plugin_address_to_string (NULL, s->addr, s->addrlen)); + if ((GNUNET_YES == s->session_passed) && (GNUNET_NO == s->session_ended)) + { + /* Notify transport immediately that this session is invalid */ + s->session_ended = GNUNET_YES; + plugin->env->session_end (plugin->env->cls, &s->target, s); + } server_delete_session (s); } + } + /** * Check if incoming connection is accepted. - * NOTE: Here every connection is accepted + * @param cls plugin as closure * @param addr address of incoming connection * @param addr_len address length of incoming connection @@ -1357,7 +1570,13 @@ server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len) struct HTTP_Server_Plugin *plugin = cls; if (plugin->cur_connections <= plugin->max_connections) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + _("Accepting connection (%u of %u) from `%s'\n"), + plugin->cur_connections, plugin->max_connections, + GNUNET_a2s (addr, addr_len)); return MHD_YES; + } else { GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name, @@ -1429,9 +1648,12 @@ server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } +#define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG + /** * Function that queries MHD's select sets and * starts the task waiting for them. + * * @param plugin plugin * @param daemon_handle the MHD daemon handle * @return gnunet task identifier @@ -1449,12 +1671,15 @@ server_schedule (struct HTTP_Server_Plugin *plugin, struct GNUNET_NETWORK_FDSet *wws; struct GNUNET_NETWORK_FDSet *wes; int max; - unsigned MHD_LONG_LONG timeout; + UNSIGNED_MHD_LONG_LONG timeout; static unsigned long long last_timeout = 0; int haveto; struct GNUNET_TIME_Relative tv; + if (GNUNET_YES == plugin->in_shutdown) + return GNUNET_SCHEDULER_NO_TASK; + ret = GNUNET_SCHEDULER_NO_TASK; FD_ZERO (&rs); FD_ZERO (&ws); @@ -1469,14 +1694,16 @@ server_schedule (struct HTTP_Server_Plugin *plugin, { if (timeout != last_timeout) { -#if VERBOSE_SERVER + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, "SELECT Timeout changed from %llu to %llu\n", last_timeout, timeout); -#endif last_timeout = timeout; } - tv.rel_value = (uint64_t) timeout; + if (timeout <= GNUNET_TIME_UNIT_SECONDS.rel_value) + tv.rel_value = (uint64_t) timeout; + else + tv = GNUNET_TIME_UNIT_SECONDS; } else tv = GNUNET_TIME_UNIT_SECONDS; @@ -1527,6 +1754,12 @@ server_schedule (struct HTTP_Server_Plugin *plugin, #if BUILD_HTTPS +/** + * Load ssl certificate from file + * + * @param file filename + * @return content of the file + */ static char * server_load_file (const char *file) { @@ -1560,18 +1793,36 @@ server_load_file (const char *file) #if BUILD_HTTPS - +/** + * Load ssl certificate + * + * @param plugin the plugin + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ static int server_load_certificate (struct HTTP_Server_Plugin *plugin) { int res = GNUNET_OK; + char *sh; char *key_file; char *cert_file; /* Get crypto init string from config * If not present just use default values */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, + "PATHS", + "SERVICEHOME", + &sh)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + "Failed to get servicehome!\n"); + return GNUNET_SYSERR; + } + + if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name, @@ -1588,16 +1839,19 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, "KEY_FILE", &key_file)) { - key_file = GNUNET_strdup ("https_key.key"); + GNUNET_break (0); + GNUNET_asprintf (&key_file, "%s/%s", sh, "https_key.key"); } + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, "CERT_FILE", &cert_file)) { - GNUNET_asprintf (&cert_file, "%s", "https_cert.crt"); + GNUNET_break (0); + GNUNET_asprintf (&cert_file, "%s/%s", sh, "https_cert.crt"); } - + GNUNET_free (sh); /* read key & certificates from file */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to loading TLS certificate from key-file `%s' cert-file`%s'\n", @@ -1672,7 +1926,14 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) } #endif -int + +/** + * Start the HTTP server + * + * @param plugin the plugin handle + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +static int server_start (struct HTTP_Server_Plugin *plugin) { unsigned int timeout; @@ -1802,18 +2063,6 @@ server_start (struct HTTP_Server_Plugin *plugin) void server_stop (struct HTTP_Server_Plugin *plugin) { - if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v4_task); - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; - } - - if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v6_task); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; - } - if (plugin->server_v4 != NULL) { MHD_stop_daemon (plugin->server_v4); @@ -1825,6 +2074,18 @@ server_stop (struct HTTP_Server_Plugin *plugin) plugin->server_v6 = NULL; } + + if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->server_v4_task); + plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; + } + + if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->server_v6_task); + plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; + } p = NULL; #if BUILD_HTTPS @@ -1837,6 +2098,15 @@ server_stop (struct HTTP_Server_Plugin *plugin) "%s server component stopped\n", plugin->name); } + +/** + * Add an address to the server's set of addresses and notify transport + * + * @param cls the plugin handle + * @param add_remove GNUNET_YES on add, GNUNET_NO on remove + * @param addr the address + * @param addrlen address length + */ static void server_add_address (void *cls, int add_remove, const struct sockaddr *addr, socklen_t addrlen) @@ -1857,11 +2127,22 @@ server_add_address (void *cls, int add_remove, const struct sockaddr *addr, GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, "Notifying transport to add address `%s'\n", http_common_plugin_address_to_string(NULL, w->addr, w->addrlen)); - +#if BUILD_HTTPS + plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "https_client"); +#else plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "http_client"); +#endif } +/** + * Remove an address from the server's set of addresses and notify transport + * + * @param cls the plugin handle + * @param add_remove GNUNET_YES on add, GNUNET_NO on remove + * @param addr the address + * @param addrlen address length + */ static void server_remove_address (void *cls, int add_remove, const struct sockaddr *addr, socklen_t addrlen) @@ -1889,7 +2170,11 @@ server_remove_address (void *cls, int add_remove, const struct sockaddr *addr, "Notifying transport to remove address `%s'\n", http_common_plugin_address_to_string (NULL, w->addr, w->addrlen)); GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w); +#if BUILD_HTTPS + plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "https_client"); +#else plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "http_client"); +#endif GNUNET_free (w->addr); GNUNET_free (w); } @@ -1964,6 +2249,16 @@ server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr * } +/** + * Get valid server addresses + * + * @param plugin the plugin handle + * @param serviceName the servicename + * @param cfg configuration handle + * @param addrs addresses + * @param addr_lens address length + * @return number of addresses + */ static int server_get_addresses (struct HTTP_Server_Plugin *plugin, const char *serviceName, @@ -2141,6 +2436,12 @@ server_get_addresses (struct HTTP_Server_Plugin *plugin, return resi; } + +/** + * Ask NAT for addresses + * + * @param plugin the plugin handle + */ static void server_start_report_addresses (struct HTTP_Server_Plugin *plugin) { @@ -2176,6 +2477,11 @@ server_start_report_addresses (struct HTTP_Server_Plugin *plugin) } +/** + * Stop NAT for addresses + * + * @param plugin the plugin handle + */ static void server_stop_report_addresses (struct HTTP_Server_Plugin *plugin) { @@ -2198,6 +2504,9 @@ server_stop_report_addresses (struct HTTP_Server_Plugin *plugin) /** * Check if IPv6 supported on this system + * + * @param plugin the plugin handle + * @return GNUNET_YES on success, else GNUNET_NO */ static int server_check_ipv6_support (struct HTTP_Server_Plugin *plugin) @@ -2253,12 +2562,25 @@ server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskCo plugin->ext_addr_len = strlen (plugin->ext_addr) + 1; GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, "Notifying transport about external hostname address `%s'\n", plugin->ext_addr); + +#if BUILD_HTTPS + plugin->env->notify_address (plugin->env->cls, GNUNET_YES, + plugin->ext_addr, plugin->ext_addr_len, + "https_client"); +#else plugin->env->notify_address (plugin->env->cls, GNUNET_YES, plugin->ext_addr, plugin->ext_addr_len, "http_client"); +#endif } +/** + * Configure the plugin + * + * @param plugin plugin handle + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ static int server_configure_plugin (struct HTTP_Server_Plugin *plugin) { @@ -2383,15 +2705,37 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name, "EXTERNAL_HOSTNAME", &plugin->external_hostname)) { + char * tmp = NULL; + if (NULL != strstr(plugin->external_hostname, "://")) + { + tmp = strdup(&strstr(plugin->external_hostname, "://")[3]); + GNUNET_free (plugin->external_hostname); + plugin->external_hostname = tmp; + + } GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, _("Using external hostname `%s'\n"), plugin->external_hostname); plugin->notify_ext_task = GNUNET_SCHEDULER_add_now (&server_notify_external_hostname, plugin); + + /* Use only configured external hostname */ + if (GNUNET_CONFIGURATION_have_value + (plugin->env->cfg, plugin->name, "EXTERNAL_HOSTNAME_ONLY")) + { + plugin->external_only = + GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name, + "EXTERNAL_HOSTNAME_ONLY"); + } + else + plugin->external_only = GNUNET_NO; + + if (GNUNET_YES == plugin->external_only) + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + _("Notifying transport only about hostname `%s'\n"), plugin->external_hostname); } else GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, "No external hostname configured\n"); - /* Optional parameters */ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, plugin->name, @@ -2402,13 +2746,20 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, _("Maximum number of connections is %u\n"), plugin->max_connections); + + + plugin->peer_id_length = strlen (GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey)); + return GNUNET_OK; } + /** * Session was idle, so disconnect it + * + * @param cls the session + * @param tc task context */ - static void server_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { @@ -2424,8 +2775,11 @@ server_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc GNUNET_assert (GNUNET_OK == server_disconnect (s)); } + /** -* Start session timeout +* Start session timeout for session s +* +* @param s the session */ static void server_start_session_timeout (struct Session *s) @@ -2442,7 +2796,9 @@ server_start_session_timeout (struct Session *s) /** -* Increment session timeout due to activity +* Increment session timeout due to activity session s +* +* @param s the session */ static void server_reschedule_session_timeout (struct Session *s) @@ -2459,8 +2815,12 @@ server_reschedule_session_timeout (struct Session *s) s, (unsigned long long) SERVER_SESSION_TIMEOUT.rel_value); } + /** * Exit point from the plugin. + * + * @param cls api + * @return NULL */ void * LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) @@ -2476,7 +2836,7 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) GNUNET_free (api); return NULL; } - + plugin->in_shutdown = GNUNET_YES; GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, _("Shutting down plugin `%s'\n"), plugin->name); @@ -2494,23 +2854,39 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) http_common_plugin_address_to_string (NULL, plugin->ext_addr, plugin->ext_addr_len)); +#if BUILD_HTTPS plugin->env->notify_address (plugin->env->cls, GNUNET_NO, plugin->ext_addr, plugin->ext_addr_len, - "http_client"); + "https_client"); +#else + plugin->env->notify_address (plugin->env->cls, + GNUNET_NO, + plugin->ext_addr, + plugin->ext_addr_len, + "http_client"); +#endif + } /* Stop to report addresses to transport service */ server_stop_report_addresses (plugin); - server_stop (plugin); - next = plugin->head; while (NULL != (pos = next)) { next = pos->next; - GNUNET_CONTAINER_DLL_remove( plugin->head, plugin->tail, pos); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Removing left over session %p\n", pos); + + if ((GNUNET_YES == pos->session_passed) && (GNUNET_NO == pos->session_ended)) + { + /* Notify transport immediately that this session is invalid */ + pos->session_ended = GNUNET_YES; + plugin->env->session_end (plugin->env->cls, &pos->target, pos); + } + server_delete_session (pos); } @@ -2532,6 +2908,9 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) /** * Entry point for the plugin. + * + * @param cls env + * @return api */ void * LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) @@ -2587,7 +2966,8 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) plugin->use_ipv6 = server_check_ipv6_support (plugin); /* Report addresses to transport service */ - server_start_report_addresses (plugin); + if (GNUNET_NO == plugin->external_only) + server_start_report_addresses (plugin); if (GNUNET_SYSERR == server_start (plugin)) { @@ -2598,7 +2978,4 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) return api; } - - - /* end of plugin_transport_http_server.c */