-allow caller ID to differ from zone used for resolution
[oweals/gnunet.git] / src / transport / plugin_transport_http_server.c
index 19fd632b8bc7d5b4cb4dbb2a92214b29855f2763..29ae6224e68685345a2c02f3a3afd9224eb17eee 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet
-     (C) 2002-2013 Christian Grothoff (and other contributing authors)
+     (C) 2002-2014 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -22,6 +22,7 @@
  * @file transport/plugin_transport_http_server.c
  * @brief HTTP/S server transport plugin
  * @author Matthias Wachs
+ * @author David Barksdale
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
@@ -30,6 +31,7 @@
 #include "gnunet_nat_lib.h"
 #include "plugin_transport_http_common.h"
 #include <microhttpd.h>
+#include <regex.h>
 
 
 
 #define _SEND 1
 
 
-/**
- * Enable output for debbuging URL's of incoming requests
- */
-#define DEBUG_URL_PARSE GNUNET_NO
-
-
-/**
- * Encapsulation of all of the state of the plugin.
- */
-struct Plugin;
-
 /**
  * Session handle for connections.
  */
@@ -101,7 +92,7 @@ struct Session
   struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk;
 
   /**
-   * Client send handle
+   * Client recv handle
    */
   struct ServerConnection *server_recv;
 
@@ -113,12 +104,7 @@ struct Session
   /**
    * Address
    */
-  void *addr;
-
-  /**
-   * Address length
-   */
-  size_t addrlen;
+  struct GNUNET_HELLO_Address *address;
 
   /**
    * Unique HTTP/S connection tag for this connection
@@ -140,11 +126,6 @@ struct Session
    */
   int session_ended;
 
-  /**
-   * Are incoming connection established at the moment
-   */
-  int connect_in_progress;
-
   /**
    * Absolute time when to receive data again
    * Used for receive throttling
@@ -155,6 +136,11 @@ struct Session
    * Session timeout task
    */
   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
+
+  /**
+   * Should this session get disconnected? GNUNET_YES/NO
+   */
+  int disconnect;
 };
 
 
@@ -165,13 +151,9 @@ struct ServerConnection
    */
   int direction;
 
-  /**
-   * Should this connection get disconnected? GNUNET_YES/NO
-   */
-  int disconnect;
-
   /**
    * For PUT connections: Is this the first or last callback with size 0
+   * For GET connections: Have we sent a message
    */
   int connected;
 
@@ -189,6 +171,12 @@ struct ServerConnection
    * The MHD daemon
    */
   struct MHD_Daemon *mhd_daemon;
+
+  /**
+   * Options requested by peer
+   */
+  uint32_t options;
+#define OPTION_LONG_POLL 1 /* GET request wants long-poll semantics */
 };
 
 
@@ -263,18 +251,13 @@ struct HTTP_Server_Plugin
    * External hostname the plugin can be connected to, can be different to
    * the host's FQDN, used e.g. for reverse proxying
    */
-  struct HttpAddress *ext_addr;
+  struct GNUNET_HELLO_Address *ext_addr;
 
   /**
    * Notify transport only about external address
    */
   unsigned int external_only;
 
-  /**
-   * External address length
-   */
-  size_t ext_addr_len;
-
   /**
    * use IPv6
    */
@@ -354,6 +337,11 @@ struct HTTP_Server_Plugin
    */
   struct MHD_Daemon *server_v6;
 
+  /**
+   * Regex for parsing URLs
+   */
+  regex_t url_regex;
+
 #if BUILD_HTTPS
   /**
    * Crypto related
@@ -558,23 +546,8 @@ http_server_plugin_send (void *cls,
       GNUNET_break (0);
       return GNUNET_SYSERR;
   }
-  if (NULL == session->server_send)
-  {
-     if (GNUNET_NO == session->connect_in_progress)
-     {
-      GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, session->plugin->name,
-                       "Session %p/connection %p: Sending message with %u bytes to peer `%s' with FAILED\n",
-                       session, session->server_send,
-                       msgbuf_size, GNUNET_i2s (&session->target));
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-     }
-  }
-  else
-  {
-    if (GNUNET_YES == session->server_send->disconnect)
-      return GNUNET_SYSERR;
-  }
+  if (session->disconnect)
+    return GNUNET_SYSERR;
 
 
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, session->plugin->name,
@@ -605,7 +578,6 @@ http_server_plugin_send (void *cls,
     server_reschedule (session->plugin,
                        session->server_send->mhd_daemon,
                        GNUNET_YES);
-    server_reschedule_session_timeout (session);
   }
   return bytes_sent;
 }
@@ -669,8 +641,8 @@ http_server_plugin_address_suggested (void *cls,
 
   if ((NULL != plugin->ext_addr) &&
       GNUNET_YES == (http_common_cmp_addresses (addr, addrlen,
-                                                plugin->ext_addr,
-                                                plugin->ext_addr_len)))
+                                                plugin->ext_addr->address,
+                                                plugin->ext_addr->address_length)))
   {
     /* Checking HTTP_OPTIONS_VERIFY_CERTIFICATE option for external hostname */
     if ((ntohl (haddr->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE) !=
@@ -744,7 +716,7 @@ server_delete_session (void *cls,
     GNUNET_SERVER_mst_destroy (s->msg_tk);
     s->msg_tk = NULL;
   }
-  GNUNET_free (s->addr);
+  GNUNET_HELLO_address_free (s->address);
   GNUNET_free_non_null (s->server_recv);
   GNUNET_free_non_null (s->server_send);
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
@@ -854,15 +826,14 @@ http_server_plugin_disconnect_session (void *cls,
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-
+  s->disconnect = GNUNET_YES;
   send = (struct ServerConnection *) s->server_send;
-  if (s->server_send != NULL)
+  if (send != NULL)
   {
     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
                      "Server: %p / %p Terminating inbound PUT session to peer `%s'\n",
-                     s, s->server_send, GNUNET_i2s (&s->target));
+                     s, send, GNUNET_i2s (&s->target));
 
-    send->disconnect = GNUNET_YES;
     MHD_set_connection_option (send->mhd_conn,
                                MHD_CONNECTION_OPTION_TIMEOUT,
                                1);
@@ -874,9 +845,8 @@ http_server_plugin_disconnect_session (void *cls,
   {
     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
                      "Server: %p / %p Terminating inbound GET session to peer `%s'\n",
-                     s, s->server_recv, GNUNET_i2s (&s->target));
+                     s, recv, GNUNET_i2s (&s->target));
 
-    recv->disconnect = GNUNET_YES;
     MHD_set_connection_option (recv->mhd_conn,
                                MHD_CONNECTION_OPTION_TIMEOUT,
                                1);
@@ -891,7 +861,7 @@ http_server_plugin_disconnect_session (void *cls,
  * GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to
  * calculate the interval between keepalive packets.
  *
- * @param cls closure with the `struct Plugin`
+ * @param cls closure with the `struct HTTP_Server_Plugin`
  * @return keepalive factor
  */
 static unsigned int
@@ -900,6 +870,19 @@ http_server_query_keepalive_factor (void *cls)
   return 3;
 }
 
+static void
+http_server_plugin_update_session_timeout (void *cls,
+                                  const struct GNUNET_PeerIdentity *peer,
+                                  struct Session *session)
+{
+  struct HTTP_Server_Plugin *plugin = cls;
+
+  if (GNUNET_NO == server_exist_session (plugin, session))
+      return;
+
+  server_reschedule_session_timeout (session);
+}
+
 
 /**
  * Tell MHD that the connection should timeout after @a to seconds.
@@ -944,111 +927,124 @@ server_mhd_connection_timeout (struct HTTP_Server_Plugin *plugin,
  * @param url incoming url
  * @param target where to store the target
  * @param tag where to store the tag
+ * @param options where to store the options
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
 server_parse_url (struct HTTP_Server_Plugin *plugin,
                  const char *url,
                  struct GNUNET_PeerIdentity *target,
-                 uint32_t *tag)
+                 uint32_t *tag,
+                 uint32_t *options)
 {
-  char * tag_start = NULL;
-  char * tag_end = NULL;
-  char * target_start = NULL;
-  char * separator = NULL;
-  unsigned int hash_length;
-  unsigned long int ctag;
-
-  /* URL parsing
-   * URL is valid if it is in the form [prefix with (multiple) '/'][peerid[103];tag]*/
+  regmatch_t matches[4];
+  const char *tag_start;
+  const char *target_start;
+  char *tag_end;
+  char *options_end;
+  size_t hash_length;
+  unsigned long int rc;
+
+  /* URL parsing */
+#define URL_REGEX \
+  ("^.*/([0-9A-V]+);([0-9]+)(,[0-9]+)?$")
 
   if (NULL == url)
   {
       GNUNET_break (0);
       return GNUNET_SYSERR;
   }
-  /* convert tag */
-
-  /* find separator */
-  separator = strrchr (url, ';');
 
-  if (NULL == separator)
+  if (regexec(&plugin->url_regex, url, 4, matches, 0))
   {
-      if (DEBUG_URL_PARSE) GNUNET_break (0);
-      return GNUNET_SYSERR;
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+       "URL `%s' did not match regex\n", url);
+    return GNUNET_SYSERR;
   }
-  tag_start = separator + 1;
 
-  if (strlen (tag_start) == 0)
+  target_start = &url[matches[1].rm_so];
+  tag_start = &url[matches[2].rm_so];
+
+  /* convert tag */
+  rc = strtoul (tag_start, &tag_end, 10);
+  if (&url[matches[2].rm_eo] != tag_end)
   {
-    /* No tag after separator */
-    if (DEBUG_URL_PARSE) GNUNET_break (0);
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+                     "URL tag did not line up with submatch\n");
     return GNUNET_SYSERR;
   }
-  ctag = strtoul (tag_start, &tag_end, 10);
-  if (ctag == 0)
+  if (rc == 0)
   {
-    /* tag == 0 , invalid */
-    if (DEBUG_URL_PARSE) GNUNET_break (0);
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+                     "URL tag is zero\n");
     return GNUNET_SYSERR;
   }
-  if ((ctag == ULONG_MAX) && (ERANGE == errno))
+  if ((rc == ULONG_MAX) && (ERANGE == errno))
   {
-    /* out of range: > ULONG_MAX */
-    if (DEBUG_URL_PARSE) GNUNET_break (0);
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+                     "URL tag > ULONG_MAX\n");
     return GNUNET_SYSERR;
   }
-  if (ctag > UINT32_MAX)
+  if (rc > UINT32_MAX)
   {
-    /* out of range: > UINT32_MAX */
-    if (DEBUG_URL_PARSE) GNUNET_break (0);
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+                     "URL tag > UINT32_MAX\n");
     return GNUNET_SYSERR;
   }
-  (*tag) = (uint32_t) ctag;
-  if (NULL == tag_end)
-  {
-      /* no char after tag */
-      if (DEBUG_URL_PARSE) GNUNET_break (0);
-      return GNUNET_SYSERR;
-  }
-  if (url[strlen(url)] != tag_end[0])
-  {
-      /* there are more not converted chars after tag */
-      if (DEBUG_URL_PARSE) GNUNET_break (0);
-      return GNUNET_SYSERR;
-  }
-  if (DEBUG_URL_PARSE)
-    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
-       "Found tag `%u' in url\n", (*tag));
+  (*tag) = (uint32_t)rc;
+  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+     "Found tag `%u' in url\n", *tag);
 
   /* convert peer id */
-  target_start = strrchr (url, '/');
-  if (NULL == target_start)
-  {
-      /* no leading '/' */
-      target_start = (char *) url;
-  }
-  target_start++;
-  hash_length = separator - target_start;
+  hash_length = matches[1].rm_eo - matches[1].rm_so;
   if (hash_length != plugin->peer_id_length)
   {
-      /* no char after tag */
-      if (DEBUG_URL_PARSE) GNUNET_break (0);
-      return GNUNET_SYSERR;
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+                     "URL target is %u bytes, expecting %u\n",
+                     hash_length, plugin->peer_id_length);
+    return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
       GNUNET_CRYPTO_eddsa_public_key_from_string (target_start,
                                                     hash_length,
                                                     &target->public_key))
-    {
-      /* hash conversion failed */
-      if (DEBUG_URL_PARSE) GNUNET_break (0);
-      return GNUNET_SYSERR;
+  {
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+                     "URL target conversion failed\n");
+    return GNUNET_SYSERR;
   }
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
                   plugin->name,
                   "Found target `%s' in URL\n",
                   GNUNET_i2s_full (target));
+
+  /* convert options */
+  if (-1 == matches[3].rm_so) {
+      *options = 0;
+  } else {
+    rc = strtoul (&url[matches[3].rm_so + 1], &options_end, 10);
+    if (&url[matches[3].rm_eo] != options_end)
+    {
+      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+                       "URL options did not line up with submatch\n");
+      return GNUNET_SYSERR;
+    }
+    if ((rc == ULONG_MAX) && (ERANGE == errno))
+    {
+      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+                       "URL options > ULONG_MAX\n");
+      return GNUNET_SYSERR;
+    }
+    if (rc > UINT32_MAX)
+    {
+      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+                       "URL options > UINT32_MAX\n");
+      return GNUNET_SYSERR;
+    }
+    (*options) = (uint32_t)rc;
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
+       "Found options `%u' in url\n", *options);
+  }
   return GNUNET_OK;
 }
 
@@ -1070,11 +1066,13 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin,
   struct Session *s = NULL;
   struct ServerConnection *sc = NULL;
   const union MHD_ConnectionInfo *conn_info;
-  struct GNUNET_ATS_Information ats;
   struct HttpAddress *addr;
-  size_t addr_len;
+
+  struct GNUNET_ATS_Information ats;
   struct GNUNET_PeerIdentity target;
+  size_t addr_len;
   uint32_t tag = 0;
+  uint32_t options;
   int direction = GNUNET_SYSERR;
   unsigned int to;
 
@@ -1086,7 +1084,7 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin,
   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))
+  if (GNUNET_SYSERR == server_parse_url (plugin, url, &target, &tag, &options))
   {
       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                        "Invalid url %s\n", url);
@@ -1141,6 +1139,7 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin,
   else
   {
     /* create new session */
+    addr = NULL;
     switch (conn_info->client_addr->sa_family)
     {
     case (AF_INET):
@@ -1163,8 +1162,8 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin,
     s = GNUNET_new (struct Session);
     memcpy (&s->target, &target, sizeof (struct GNUNET_PeerIdentity));
     s->plugin = plugin;
-    s->addr = addr;
-    s->addrlen = addr_len;
+    s->address = GNUNET_HELLO_address_allocate (&s->target, PLUGIN_NAME,
+        addr, addr_len, GNUNET_HELLO_ADDRESS_INFO_INBOUND);
     s->ats_address_network_type = ats.value;
     s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS;
     s->tag = tag;
@@ -1172,7 +1171,6 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin,
     s->server_send = NULL;
     s->session_passed = GNUNET_NO;
     s->session_ended = GNUNET_NO;
-    s->connect_in_progress = GNUNET_YES;
     server_start_session_timeout(s);
     GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
 
@@ -1182,6 +1180,7 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin,
                      http_common_plugin_address_to_string (NULL,
                                                            plugin->protocol,
                                                            addr, addr_len));
+    GNUNET_free_non_null (addr);
   }
   sc = GNUNET_new (struct ServerConnection);
   if (conn_info->client_addr->sa_family == AF_INET)
@@ -1192,6 +1191,7 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin,
   sc->direction = direction;
   sc->connected = GNUNET_NO;
   sc->session = s;
+  sc->options = options;
   if (direction == _SEND)
     s->server_send = sc;
   if (direction == _RECEIVE)
@@ -1199,8 +1199,7 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin,
 
   if ((NULL != s->server_send) && (NULL != s->server_recv))
   {
-    s->connect_in_progress = GNUNET_NO; /* PUT and GET are connected */
-    plugin->env->session_start (NULL, &s->target, PLUGIN_NAME, NULL, 0 ,s, NULL, 0);
+    plugin->env->session_start (NULL, s->address ,s, NULL, 0);
   }
 
   if ((NULL == s->server_recv) || (NULL == s->server_send))
@@ -1271,12 +1270,16 @@ static ssize_t
 server_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
 {
   struct Session *s = cls;
+  struct ServerConnection *sc;
   ssize_t bytes_read = 0;
   struct HTTP_Message *msg;
   char *stat_txt;
 
   if (GNUNET_NO == server_exist_session (s->plugin, s))
     return 0;
+  sc = s->server_send;
+  if (NULL == sc)
+    return 0;
   msg = s->msg_head;
   if (NULL != msg)
   {
@@ -1298,6 +1301,7 @@ server_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
   }
   if (0 < bytes_read)
   {
+    sc->connected = GNUNET_YES;
     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",
@@ -1310,6 +1314,11 @@ server_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
     GNUNET_STATISTICS_update (s->plugin->env->stats,
                               stat_txt, bytes_read, GNUNET_NO);
     GNUNET_free (stat_txt);
+  } else if ((sc->options & OPTION_LONG_POLL) && sc->connected) {
+    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
+                     "Completing GET response to peer `%s' with session %p \n",
+                     GNUNET_i2s (&s->target), s);
+    return MHD_CONTENT_READER_END_OF_STREAM;
   }
   return bytes_read;
 }
@@ -1341,15 +1350,8 @@ server_receive_mst_cb (void *cls, void *client,
   atsi.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,
-                                s, NULL, 0);
-
-  plugin->env->update_address_metrics (plugin->env->cls,
-                                      &s->target,
-                                      NULL, 0, s, &atsi, 1);
+  delay = plugin->env->receive (plugin->env->cls, s->address, s, message);
+  plugin->env->update_address_metrics (plugin->env->cls, s->address, s, &atsi, 1);
 
   GNUNET_asprintf (&stat_txt, "# bytes received via %s_server", plugin->protocol);
   GNUNET_STATISTICS_update (plugin->env->stats,
@@ -1364,8 +1366,8 @@ server_receive_mst_cb (void *cls, void *client,
                      "Peer `%s' address `%s' next read delayed for %s\n",
                      GNUNET_i2s (&s->target),
                      http_common_plugin_address_to_string (NULL,
-                                                           plugin->protocol,
-                                                           s->addr, s->addrlen),
+                         plugin->protocol, s->address->address,
+                         s->address->address_length),
                      GNUNET_STRINGS_relative_time_to_string (delay,
                                                             GNUNET_YES));
   }
@@ -1373,6 +1375,23 @@ server_receive_mst_cb (void *cls, void *client,
   return GNUNET_OK;
 }
 
+/**
+ * Add headers to a request indicating that we allow Cross-Origin Resource
+ * Sharing.
+ */
+static void
+add_cors_headers(struct MHD_Response *response)
+{
+  MHD_add_response_header (response,
+                           "Access-Control-Allow-Origin",
+                           "*");
+  MHD_add_response_header (response,
+                           "Access-Control-Allow-Methods",
+                           "GET, PUT, OPTIONS");
+  MHD_add_response_header (response,
+                           "Access-Control-Max-Age",
+                           "86400");
+}
 
 /**
  * MHD callback for a new incoming connection
@@ -1409,6 +1428,16 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
   GNUNET_assert (cls != NULL);
   if (sc == NULL)
   {
+    /* CORS pre-flight request */
+    if (0 == strcmp (MHD_HTTP_METHOD_OPTIONS, method))
+    {
+      response = MHD_create_response_from_buffer (0, NULL,
+                                                  MHD_RESPMEM_PERSISTENT);
+      add_cors_headers(response);
+      res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+      return res;
+    }
     /* new connection */
     sc = server_lookup_connection (plugin, mhd_connection, url, method);
     if (sc != NULL)
@@ -1421,6 +1450,7 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
       MHD_add_response_header (response,
                               MHD_HTTP_HEADER_CONTENT_TYPE,
                               "text/html");
+      add_cors_headers(response);
       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
       MHD_destroy_response (response);
       return res;
@@ -1441,12 +1471,13 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
   s = sc->session;
   GNUNET_assert (NULL != s);
   /* connection is to be disconnected */
-  if (sc->disconnect == GNUNET_YES)
+  if (s->disconnect == GNUNET_YES)
   {
     /* Sent HTTP/1.1: 200 OK as response */
     response = MHD_create_response_from_data (strlen ("Thank you!"),
                                        "Thank you!",
                                        MHD_NO, MHD_NO);
+    add_cors_headers(response);
     MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
     MHD_destroy_response (response);
     return MHD_YES;
@@ -1459,6 +1490,7 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
                                                   32 * 1024,
                                                   &server_send_callback, s,
                                                   NULL);
+    add_cors_headers(response);
     MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
     MHD_destroy_response (response);
     return MHD_YES;
@@ -1473,9 +1505,8 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
                        s, sc,
                        GNUNET_i2s (&s->target),
                        http_common_plugin_address_to_string (NULL,
-                                                             plugin->protocol,
-                                                             s->addr,
-                                                             s->addrlen));
+                           plugin->protocol, s->address->address,
+                           s->address->address_length));
       sc->connected = GNUNET_YES;
       return MHD_YES;
     }
@@ -1487,14 +1518,14 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
                        s, sc,
                        GNUNET_i2s (&s->target),
                        http_common_plugin_address_to_string (NULL,
-                                                             plugin->protocol,
-                                                             s->addr,
-                                                             s->addrlen));
+                           plugin->protocol, s->address->address,
+                           s->address->address_length));
       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);
+      add_cors_headers(response);
       MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
       MHD_destroy_response (response);
       return MHD_YES;
@@ -1507,9 +1538,8 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
                        s, sc,
                        GNUNET_i2s (&s->target),
                        http_common_plugin_address_to_string (NULL,
-                                                             plugin->protocol,
-                                                             s->addr,
-                                                             s->addrlen),
+                           plugin->protocol, s->address->address,
+                           s->address->address_length),
                        *upload_data_size);
       struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
 
@@ -1585,12 +1615,12 @@ server_disconnect_cb (void *cls, struct MHD_Connection *connection,
                      "Peer `%s' connection  %p, GET on address `%s' disconnected\n",
                      GNUNET_i2s (&s->target), s->server_send,
                      http_common_plugin_address_to_string (NULL,
-                                                           plugin->protocol,
-                                                           s->addr, s->addrlen));
+                         plugin->protocol, s->address->address,
+                         s->address->address_length));
     s->server_send = NULL;
-    if (NULL != (s->server_recv))
+    if (!(sc->options & OPTION_LONG_POLL) && NULL != (s->server_recv))
     {
-      s->server_recv->disconnect = GNUNET_YES;
+      s->disconnect = GNUNET_YES;
       GNUNET_assert (NULL != s->server_recv->mhd_conn);
 #if MHD_VERSION >= 0x00090E00
       MHD_set_connection_option (s->server_recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
@@ -1605,20 +1635,9 @@ server_disconnect_cb (void *cls, struct MHD_Connection *connection,
                      "Peer `%s' connection %p PUT on address `%s' disconnected\n",
                      GNUNET_i2s (&s->target), s->server_recv,
                      http_common_plugin_address_to_string (NULL,
-                                                           plugin->protocol,
-                                                           s->addr, s->addrlen));
+                         plugin->protocol, s->address->address,
+                         s->address->address_length));
     s->server_recv = NULL;
-    /* Do not terminate session when PUT disconnects
-    if (NULL != (s->server_send))
-    {
-        s->server_send->disconnect = GNUNET_YES;
-      GNUNET_assert (NULL != s->server_send->mhd_conn);
-#if MHD_VERSION >= 0x00090E00
-      MHD_set_connection_option (s->server_send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
-                                 1);
-#endif
-      server_reschedule (plugin, s->server_send->mhd_daemon, GNUNET_NO);
-    }*/
     if (s->msg_tk != NULL)
     {
       GNUNET_SERVER_mst_destroy (s->msg_tk);
@@ -1629,20 +1648,20 @@ server_disconnect_cb (void *cls, struct MHD_Connection *connection,
   GNUNET_free (sc);
   plugin->cur_connections--;
 
-  if ((s->server_send == NULL) && (s->server_recv == NULL))
+  if (s->disconnect && (s->server_send == NULL) && (s->server_recv == NULL))
   {
     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                      "Peer `%s' on address `%s' disconnected\n",
                      GNUNET_i2s (&s->target),
                      http_common_plugin_address_to_string (NULL,
-                                                           plugin->protocol,
-                                                           s->addr, s->addrlen));
+                         plugin->protocol, s->address->address,
+                         s->address->address_length));
 
     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);
+        plugin->env->session_end (plugin->env->cls, s->address, s);
     }
     server_delete_session (plugin, s);
   }
@@ -1951,7 +1970,8 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin)
                 "No usable TLS certificate found, creating certificate\n");
     errno = 0;
     cert_creation =
-        GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL,
+        GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+                                 NULL, NULL, NULL,
                                  "gnunet-transport-certificate-creation",
                                  "gnunet-transport-certificate-creation",
                                  key_file, cert_file, NULL);
@@ -2205,9 +2225,10 @@ server_add_address (void *cls, int add_remove, const struct sockaddr *addr,
                  socklen_t addrlen)
 {
   struct HTTP_Server_Plugin *plugin = cls;
+  struct GNUNET_HELLO_Address *address;
   struct HttpAddressWrapper *w = NULL;
 
-  w = GNUNET_malloc (sizeof (struct HttpAddressWrapper));
+  w = GNUNET_new (struct HttpAddressWrapper);
   w->address = http_common_address_from_socket (plugin->protocol, addr, addrlen);
   if (NULL == w->address)
   {
@@ -2222,11 +2243,17 @@ server_add_address (void *cls, int add_remove, const struct sockaddr *addr,
                    http_common_plugin_address_to_string (NULL,
                                                          plugin->protocol,
                                                          w->address, w->addrlen));
+  /* modify our published address list */
 #if BUILD_HTTPS
-  plugin->env->notify_address (plugin->env->cls, add_remove, w->address, w->addrlen, "https_client");
+  address = GNUNET_HELLO_address_allocate (plugin->env->my_identity,
+      "https_client", w->address, w->addrlen, GNUNET_HELLO_ADDRESS_INFO_NONE);
 #else
-  plugin->env->notify_address (plugin->env->cls, add_remove, w->address, w->addrlen, "http_client");
+  address = GNUNET_HELLO_address_allocate (plugin->env->my_identity,
+      "http_client", w->address, w->addrlen, GNUNET_HELLO_ADDRESS_INFO_NONE);
 #endif
+
+  plugin->env->notify_address (plugin->env->cls, add_remove, address);
+  GNUNET_HELLO_address_free (address);
 }
 
 
@@ -2243,6 +2270,7 @@ server_remove_address (void *cls, int add_remove, const struct sockaddr *addr,
                     socklen_t addrlen)
 {
   struct HTTP_Server_Plugin *plugin = cls;
+  struct GNUNET_HELLO_Address *address;
   struct HttpAddressWrapper *w = plugin->addr_head;
   size_t saddr_len;
   void * saddr = http_common_address_from_socket (plugin->protocol, addr, addrlen);
@@ -2266,12 +2294,20 @@ server_remove_address (void *cls, int add_remove, const struct sockaddr *addr,
                    http_common_plugin_address_to_string (NULL,
                                                          plugin->protocol,
                                                          w->address, w->addrlen));
+
+
   GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w);
+
+  /* modify our published address list */
 #if BUILD_HTTPS
-  plugin->env->notify_address (plugin->env->cls, add_remove, w->address, w->addrlen, "https_client");
+  address = GNUNET_HELLO_address_allocate (plugin->env->my_identity,
+      "https_client", w->address, w->addrlen, GNUNET_HELLO_ADDRESS_INFO_NONE);
 #else
-  plugin->env->notify_address (plugin->env->cls, add_remove, w->address, w->addrlen, "http_client");
+  address = GNUNET_HELLO_address_allocate (plugin->env->my_identity,
+      "http_client", w->address, w->addrlen, GNUNET_HELLO_ADDRESS_INFO_NONE);
 #endif
+  plugin->env->notify_address (plugin->env->cls, add_remove, address);
+  GNUNET_HELLO_address_free (address);
   GNUNET_free (w->address);
   GNUNET_free (w);
 }
@@ -2648,6 +2684,8 @@ static void
 server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct HTTP_Server_Plugin *plugin = cls;
+  struct HttpAddress *ext_addr;
+  size_t ext_addr_len;
   unsigned int urlen;
   char *url;
 
@@ -2658,26 +2696,30 @@ server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskCo
   GNUNET_asprintf(&url, "%s://%s", plugin->protocol, plugin->external_hostname);
 
   urlen = strlen (url) + 1;
-  plugin->ext_addr = GNUNET_malloc (sizeof (struct HttpAddress) + urlen);
-  plugin->ext_addr->options = htonl(plugin->options);
-  plugin->ext_addr->urlen = htonl (urlen);
-  plugin->ext_addr_len = sizeof (struct HttpAddress) + urlen;
-  memcpy (&plugin->ext_addr[1], url, urlen);
+  ext_addr = GNUNET_malloc (sizeof (struct HttpAddress) + urlen);
+  ext_addr->options = htonl(plugin->options);
+  ext_addr->urlen = htonl (urlen);
+  ext_addr_len = sizeof (struct HttpAddress) + urlen;
+  memcpy (&ext_addr[1], url, urlen);
   GNUNET_free (url);
+
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                    "Notifying transport about external hostname address `%s'\n", plugin->external_hostname);
 
 #if BUILD_HTTPS
   if (GNUNET_YES == plugin->verify_external_hostname)
-    GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name,
-                     "Enabling SSL verification for external hostname address `%s'\n", plugin->external_hostname);
-  plugin->env->notify_address (plugin->env->cls, GNUNET_YES,
-                               plugin->ext_addr, plugin->ext_addr_len,
-                               "https_client");
+  GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name,
+      "Enabling SSL verification for external hostname address `%s'\n",
+      plugin->external_hostname);
+  plugin->ext_addr = GNUNET_HELLO_address_allocate (plugin->env->my_identity,
+      "https_client", ext_addr, ext_addr_len, GNUNET_HELLO_ADDRESS_INFO_NONE );
+  plugin->env->notify_address (plugin->env->cls, GNUNET_YES, plugin->ext_addr);
+  GNUNET_free (ext_addr);
 #else
-  plugin->env->notify_address (plugin->env->cls, GNUNET_YES,
-                               plugin->ext_addr, plugin->ext_addr_len,
-                               "http_client");
+  plugin->ext_addr = GNUNET_HELLO_address_allocate (plugin->env->my_identity,
+      "http_client", ext_addr, ext_addr_len, GNUNET_HELLO_ADDRESS_INFO_NONE );
+  plugin->env->notify_address (plugin->env->cls, GNUNET_YES, plugin->ext_addr);
+  GNUNET_free (ext_addr);
 #endif
 }
 
@@ -2757,7 +2799,7 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin)
     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                      "Binding %s plugin to specific IPv4 address: `%s'\n",
                      plugin->protocol, bind4_address);
-    plugin->server_addr_v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
+    plugin->server_addr_v4 = GNUNET_new (struct sockaddr_in);
     if (1 != inet_pton (AF_INET, bind4_address,
                         &plugin->server_addr_v4->sin_addr))
     {
@@ -2788,7 +2830,7 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin)
     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                      "Binding %s plugin to specific IPv6 address: `%s'\n",
                      plugin->protocol, bind6_address);
-    plugin->server_addr_v6 = GNUNET_malloc (sizeof (struct sockaddr_in6));
+    plugin->server_addr_v6 = GNUNET_new (struct sockaddr_in6);
     if (1 !=
         inet_pton (AF_INET6, bind6_address, &plugin->server_addr_v6->sin6_addr))
     {
@@ -3001,21 +3043,19 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
                      "Notifying transport to remove address `%s'\n",
                      http_common_plugin_address_to_string (NULL,
                                                            plugin->protocol,
-                                                           plugin->ext_addr,
-                                                           plugin->ext_addr_len));
+                                                           plugin->ext_addr->address,
+                                                           plugin->ext_addr->address_length));
 #if BUILD_HTTPS
     plugin->env->notify_address (plugin->env->cls,
                                  GNUNET_NO,
-                                 plugin->ext_addr,
-                                 plugin->ext_addr_len,
-                                 "https_client");
+                                 plugin->ext_addr);
 #else
   plugin->env->notify_address (plugin->env->cls,
                                GNUNET_NO,
-                               plugin->ext_addr,
-                               plugin->ext_addr_len,
-                               "http_client");
+                               plugin->ext_addr);
 #endif
+    GNUNET_HELLO_address_free (plugin->ext_addr);
+    plugin->ext_addr = NULL;
   }
 
   /* Stop to report addresses to transport service */
@@ -3032,7 +3072,7 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
     {
       /* Notify transport immediately that this session is invalid */
       pos->session_ended = GNUNET_YES;
-      plugin->env->session_end (plugin->env->cls, &pos->target, pos);
+      plugin->env->session_end (plugin->env->cls, pos->address, pos);
     }
     server_delete_session (plugin, pos);
   }
@@ -3042,6 +3082,7 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
   GNUNET_free_non_null (plugin->ext_addr);
   GNUNET_free_non_null (plugin->server_addr_v4);
   GNUNET_free_non_null (plugin->server_addr_v6);
+  regfree(&plugin->url_regex);
 
   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                    _("Shutdown for plugin `%s' complete\n"),
@@ -3069,7 +3110,7 @@ http_plugin_address_to_string (void *cls,
 /**
  * Function obtain the network type for a session
  *
- * @param cls closure ('struct Plugin*')
+ * @param cls closure ('struct HTTP_Server_Plugin*')
  * @param session the session
  * @return the network type in HBO or GNUNET_SYSERR
  */
@@ -3121,7 +3162,7 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
   api->string_to_address = &http_common_plugin_string_to_address;
   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
   api->get_network = &http_server_get_network;
-
+  api->update_session_timeout = &http_server_plugin_update_session_timeout;
 #if BUILD_HTTPS
   plugin->name = "transport-https_server";
   plugin->protocol = "https";
@@ -3130,6 +3171,14 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
   plugin->protocol = "http";
 #endif
 
+  /* Compile URL regex */
+  if (regcomp(&plugin->url_regex, URL_REGEX, REG_EXTENDED)) {
+    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
+                     _("Unable to compile URL regex\n"));
+    LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
+    return NULL;
+  }
+
   /* Configure plugin */
   if (GNUNET_SYSERR == server_configure_plugin (plugin))
   {