Check that you are not present in trail twice
[oweals/gnunet.git] / src / transport / plugin_transport_http_client.c
index 68b25afe59ffd32c162d6bcb602106d15923b150..615639a6e077402e211fe44ede0cbd7bb8a55c1f 100644 (file)
@@ -37,7 +37,7 @@
 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_client_done
 #endif
 
-#define VERBOSE_CURL GNUNET_YES
+#define VERBOSE_CURL GNUNET_NO
 
 #define PUT_DISCONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
 
  */
 struct HTTP_Client_Plugin;
 
+/**
+ * State of a HTTP PUT request
+ */
+enum HTTP_PUT_REQUEST_STATE
+{
+  /**
+   *  Just created, not yet connected
+   */
+  H_NOT_CONNECTED,
+
+  /**
+   *  Connected
+   */
+  H_CONNECTED,
+
+  /**
+   *  Paused, nothing to send
+   */
+  H_PAUSED,
+
+  /**
+   * Temporary disconnect in progress due to inactivity
+   */
+  H_TMP_DISCONNECTING,
+
+  /**
+   * Send request while temporary disconnect, reconnect
+   */
+  H_TMP_RECONNECT_REQUIRED,
+
+  /**
+   * Temporarily disconnected
+   */
+  H_TMP_DISCONNECTED,
+
+  /**
+   * Disconnected
+   */
+  H_DISCONNECTED
+};
 
 /**
  *  Message to send using http
@@ -106,17 +146,22 @@ struct HTTP_Message
 
 
 /**
- * Session handle for connections.
+ * Session handle for HTTP(S) connections.
  */
 struct Session;
 
 
 /**
- * A connection handle
+ * A request handle
  *
  */
-struct ConnectionHandle
+struct RequestHandle
 {
+  /**
+   * Current state of this request
+   */
+  enum HTTP_PUT_REQUEST_STATE state;
+
   /**
    * The curl easy handle
    */
@@ -134,12 +179,6 @@ struct ConnectionHandle
  */
 struct Session
 {
-  /**
-   * To whom are we talking to (set to our identity
-   * if we are still waiting for the welcome message)
-   */
-  struct GNUNET_PeerIdentity target;
-
   /**
    * The URL to connect to
    */
@@ -155,25 +194,15 @@ struct Session
    */
   struct HTTP_Client_Plugin *plugin;
 
-  /**
-   * Client send handle
-   */
-  void *client_put;
-
   /**
    * Handle for the HTTP PUT request.
    */
-  struct ConnectionHandle put;
+  struct RequestHandle put;
 
   /**
    * Handle for the HTTP GET request.
    */
-  struct ConnectionHandle get;
-
-  /**
-   * Client receive handle
-   */
-  void *client_get;
+  struct RequestHandle get;
 
   /**
    * next pointer for double linked list
@@ -236,26 +265,6 @@ struct Session
    * ATS network type in NBO
    */
   uint32_t ats_address_network_type;
-
-  /**
-   * Is the client PUT handle currently paused?
-   */
-  int put_paused;
-
-  /**
-   * Is the client PUT handle disconnect in progress?
-   */
-  int put_tmp_disconnecting;
-
-  /**
-   * Is the client PUT handle temporarily disconnected?
-   */
-  int put_tmp_disconnected;
-
-  /**
-   * We received data to send while disconnecting, reconnect immediately
-   */
-  int put_reconnect_required;
 };
 
 
@@ -344,15 +353,15 @@ struct HTTP_Client_Plugin
 
   /**
    * Maximum number of sockets the plugin can use
-   * Each http inbound /outbound connections are two connections
+   * Each http connections are two requests
    */
-  unsigned int max_connections;
+  unsigned int max_requests;
 
   /**
    * Current number of sockets the plugin can use
-   * Each http inbound /outbound connections are two connections
+   * Each http connections are two requests
    */
-  unsigned int cur_connections;
+  unsigned int cur_requests;
 
   /**
    * Last used unique HTTP connection tag
@@ -369,9 +378,21 @@ struct HTTP_Client_Plugin
    */
   uint16_t use_ipv4;
 
+  /**
+   * Should we emulate an XHR client for testing?
+   */
+  int emulate_xhr;
 };
 
-
+/**
+ * Disconnect a session
+ *
+ * @param cls the `struct HTTP_Client_Plugin *`
+ * @param s session
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+http_client_plugin_session_disconnect (void *cls, struct Session *s);
 
 /**
  * If a session monitor is attached, notify it about the new
@@ -415,6 +436,7 @@ client_delete_session (struct Session *s)
   struct HTTP_Client_Plugin *plugin = s->plugin;
   struct HTTP_Message *pos;
   struct HTTP_Message *next;
+  CURLMcode mret;
 
   if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task)
   {
@@ -427,11 +449,52 @@ client_delete_session (struct Session *s)
     GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
     s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
   }
+  if (GNUNET_SCHEDULER_NO_TASK != s->recv_wakeup_task)
+  {
+    GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
+    s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   GNUNET_assert (GNUNET_OK ==
                  GNUNET_CONTAINER_multipeermap_remove (plugin->sessions,
-                                                       &s->target,
+                                                       &s->address->peer,
                                                        s));
+  if (NULL != s->put.easyhandle)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Session %p/request %p: disconnecting PUT request to peer `%s'\n",
+         s,
+         s->put.easyhandle,
+         GNUNET_i2s (&s->address->peer));
 
+    /* remove curl handle from multi handle */
+    mret = curl_multi_remove_handle (plugin->curl_multi_handle,
+                                     s->put.easyhandle);
+    GNUNET_break (CURLM_OK == mret);
+    curl_easy_cleanup (s->put.easyhandle);
+    GNUNET_assert (plugin->cur_requests > 0);
+    plugin->cur_requests--;
+    s->put.easyhandle = NULL;
+  }
+  if (NULL != s->get.easyhandle)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Session %p/request %p: disconnecting GET request to peer `%s'\n",
+         s, s->get.easyhandle,
+         GNUNET_i2s (&s->address->peer));
+    /* remove curl handle from multi handle */
+    mret = curl_multi_remove_handle (plugin->curl_multi_handle,
+                                     s->get.easyhandle);
+    GNUNET_break (CURLM_OK == mret);
+    curl_easy_cleanup (s->get.easyhandle);
+    GNUNET_assert (plugin->cur_requests > 0);
+    plugin->cur_requests--;
+    s->get.easyhandle = NULL;
+  }
+
+  GNUNET_STATISTICS_set (plugin->env->stats,
+                         HTTP_STAT_STR_CONNECTIONS,
+                         plugin->cur_requests,
+                         GNUNET_NO);
   next = s->msg_head;
   while (NULL != (pos = next))
   {
@@ -445,7 +508,7 @@ client_delete_session (struct Session *s)
     s->bytes_in_queue -= pos->size;
     if (NULL != pos->transmit_cont)
       pos->transmit_cont (pos->transmit_cont_cls,
-                          &s->target,
+                          &s->address->peer,
                           GNUNET_SYSERR,
                           pos->size,
                           pos->pos + s->overhead);
@@ -526,7 +589,7 @@ client_schedule (struct HTTP_Client_Plugin *plugin,
   mret = curl_multi_fdset (plugin->curl_multi_handle, &rs, &ws, &es, &max);
   if (mret != CURLM_OK)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
+    LOG (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
                 "curl_multi_fdset", __FILE__, __LINE__,
                 curl_multi_strerror (mret));
     return GNUNET_SYSERR;
@@ -541,7 +604,7 @@ client_schedule (struct HTTP_Client_Plugin *plugin,
 
   if (mret != CURLM_OK)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+    LOG (GNUNET_ERROR_TYPE_ERROR,
                 _("%s failed at %s:%d: `%s'\n"),
                 "curl_multi_timeout", __FILE__, __LINE__,
                 curl_multi_strerror (mret));
@@ -553,6 +616,7 @@ client_schedule (struct HTTP_Client_Plugin *plugin,
   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
 
+  /* Schedule task to run when select is ready to read or write */
   plugin->client_perform_task =
       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
                                    timeout, grs, gws,
@@ -562,7 +626,7 @@ client_schedule (struct HTTP_Client_Plugin *plugin,
   return GNUNET_OK;
 }
 
-
+#if VERBOSE_CURL
 /**
  * Loggging function
  *
@@ -580,7 +644,7 @@ client_log (CURL *curl,
             size_t size,
             void *cls)
 {
-  struct ConnectionHandle *ch = cls;
+  struct RequestHandle *ch = cls;
   const char *ttype = "UNSPECIFIED";
   char text[size + 2];
 
@@ -617,16 +681,26 @@ client_log (CURL *curl,
     text[size + 1] = '\0';
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Connection %p %s: %s",
+       "Request %p %s: %s",
        ch->easyhandle,
        ttype,
        text);
   return 0;
 }
+#endif
+
+/**
+ * Connect GET request
+ *
+ * @param s the session to connect
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
+ */
+static int
+client_connect_get (struct Session *s);
 
 
 /**
- * Connect a HTTP put connection
+ * Connect a HTTP put request
  *
  * @param s the session to connect
  * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for success
@@ -677,9 +751,9 @@ http_client_plugin_send (void *cls,
   char *stat_txt;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Session %p/connection %p: Sending message with %u to peer `%s' \n",
-       s, s->client_put,
-       msgbuf_size, GNUNET_i2s (&s->target));
+       "Session %p/request %p: Sending message with %u to peer `%s' \n",
+       s, s->put.easyhandle,
+       msgbuf_size, GNUNET_i2s (&s->address->peer));
 
   /* create new message and schedule */
   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
@@ -705,42 +779,42 @@ http_client_plugin_send (void *cls,
   notify_session_monitor (plugin,
                           s,
                           GNUNET_TRANSPORT_SS_UP);
-  if (GNUNET_YES == s->put_tmp_disconnecting)
+  if (H_TMP_DISCONNECTING == s->put.state)
   {
-    /* PUT connection is currently getting disconnected */
-    s->put_reconnect_required = GNUNET_YES;
+    /* PUT request is currently getting disconnected */
+    s->put.state = H_TMP_RECONNECT_REQUIRED;
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Session %p/connection %jp: currently disconnecting, reconnecting immediately\n",
+         "Session %p/request %p: currently disconnecting, reconnecting immediately\n",
          s,
-         s->client_put);
+         s->put.easyhandle);
     return msgbuf_size;
   }
-  if (GNUNET_YES == s->put_paused)
+  if (H_PAUSED == s->put.state)
   {
-    /* PUT connection was paused, unpause */
+    /* PUT request was paused, unpause */
     GNUNET_assert (s->put_disconnect_task != GNUNET_SCHEDULER_NO_TASK);
     GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
     s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Session %p/connection %p: unpausing connection\n",
-         s, s->client_put);
-    s->put_paused = GNUNET_NO;
-    if (NULL != s->client_put)
-      curl_easy_pause (s->client_put, CURLPAUSE_CONT);
+         "Session %p/request %p: unpausing request\n",
+         s, s->put.easyhandle);
+    s->put.state = H_CONNECTED;
+    if (NULL != s->put.easyhandle)
+      curl_easy_pause (s->put.easyhandle, CURLPAUSE_CONT);
   }
-  else if (GNUNET_YES == s->put_tmp_disconnected)
+  else if (H_TMP_DISCONNECTED == s->put.state)
   {
-    /* PUT connection was disconnected, reconnect */
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Session %p: Reconnecting PUT connection\n",
-         s);
-    s->put_tmp_disconnected = GNUNET_NO;
-    GNUNET_break (NULL == s->client_put);
+    /* PUT request was disconnected, reconnect */
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Session %p: Reconnecting PUT request\n", s);
+    GNUNET_break (NULL == s->put.easyhandle);
     if (GNUNET_SYSERR == client_connect_put (s))
+    {
+      /* Could not reconnect */
+      http_client_plugin_session_disconnect (plugin, s);
       return GNUNET_SYSERR;
+    }
   }
-  client_schedule (s->plugin,
-                   GNUNET_YES);
+  client_schedule (s->plugin, GNUNET_YES);
   return msgbuf_size;
 }
 
@@ -757,85 +831,9 @@ http_client_plugin_session_disconnect (void *cls,
                                        struct Session *s)
 {
   struct HTTP_Client_Plugin *plugin = cls;
-  struct HTTP_Message *msg;
-  struct HTTP_Message *t;
-  int res = GNUNET_OK;
-  CURLMcode mret;
 
-  if (NULL != s->client_put)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Session %p/connection %p: disconnecting PUT connection to peer `%s'\n",
-         s,
-         s->client_put,
-         GNUNET_i2s (&s->target));
-
-    /* remove curl handle from multi handle */
-    mret = curl_multi_remove_handle (plugin->curl_multi_handle,
-                                     s->client_put);
-    if (mret != CURLM_OK)
-    {
-      /* clean up easy handle, handle is now invalid and free'd */
-      res = GNUNET_SYSERR;
-      GNUNET_break (0);
-    }
-    curl_easy_cleanup (s->client_put);
-    s->client_put = NULL;
-  }
-
-
-  if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
-  {
-    GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
-    s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
-  }
-
-  if (NULL != s->client_get)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Session %p/connection %p: disconnecting GET connection to peer `%s'\n",
-         s, s->client_get,
-         GNUNET_i2s (&s->target));
-    /* remove curl handle from multi handle */
-    mret = curl_multi_remove_handle (plugin->curl_multi_handle, s->client_get);
-    if (mret != CURLM_OK)
-    {
-      /* clean up easy handle, handle is now invalid and free'd */
-      res = GNUNET_SYSERR;
-      GNUNET_break (0);
-    }
-    curl_easy_cleanup (s->client_get);
-    s->client_get = NULL;
-  }
-
-  msg = s->msg_head;
-  while (NULL != msg)
-  {
-    t = msg->next;
-    if (NULL != msg->transmit_cont)
-      msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR,
-                          msg->size, msg->pos + s->overhead);
-    s->overhead = 0;
-    GNUNET_CONTAINER_DLL_remove (s->msg_head,
-                                 s->msg_tail,
-                                 msg);
-    GNUNET_assert (0 < s->msgs_in_queue);
-    s->msgs_in_queue--;
-    GNUNET_assert (msg->size <= s->bytes_in_queue);
-    s->bytes_in_queue -= msg->size;
-    GNUNET_free (msg);
-    msg = t;
-  }
-
-  GNUNET_assert (plugin->cur_connections >= 2);
-  plugin->cur_connections -= 2;
-  GNUNET_STATISTICS_set (plugin->env->stats,
-                         HTTP_STAT_STR_CONNECTIONS,
-                         plugin->cur_connections,
-                         GNUNET_NO);
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Session %p: notifying transport about ending session\n",s);
-
   plugin->env->session_end (plugin->env->cls, s->address, s);
   client_delete_session (s);
 
@@ -847,7 +845,7 @@ http_client_plugin_session_disconnect (void *cls,
   }
   client_schedule (plugin, GNUNET_YES);
 
-  return res;
+  return GNUNET_OK;
 }
 
 
@@ -904,10 +902,8 @@ http_client_plugin_peer_disconnect (void *cls,
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Transport tells me to disconnect `%s'\n",
        GNUNET_i2s (target));
-  GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions,
-                                              target,
-                                              &destroy_session_cb,
-                                              plugin);
+  GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions, target,
+      &destroy_session_cb, plugin);
 }
 
 
@@ -979,7 +975,7 @@ client_lookup_session (struct HTTP_Client_Plugin *plugin,
 /**
  * When we have nothing to transmit, we pause the HTTP PUT
  * after a while (so that gnurl stops asking).  This task
- * is the delayed task that actually pauses the PUT.
+ * is the delayed task that actually disconnects the PUT.
  *
  * @param cls the `struct Session *` with the put
  * @param tc scheduler context
@@ -992,12 +988,11 @@ client_put_disconnect (void *cls,
 
   s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Session %p/connection %p: will be disconnected due to no activity\n",
-       s, s->client_put);
-  s->put_paused = GNUNET_NO;
-  s->put_tmp_disconnecting = GNUNET_YES;
-  if (NULL != s->client_put)
-    curl_easy_pause (s->client_put, CURLPAUSE_CONT);
+       "Session %p/request %p: will be disconnected due to no activity\n",
+       s, s->put.easyhandle);
+  s->put.state = H_TMP_DISCONNECTING;
+  if (NULL != s->put.easyhandle)
+    curl_easy_pause (s->put.easyhandle, CURLPAUSE_CONT);
   client_schedule (s->plugin, GNUNET_YES);
 }
 
@@ -1010,7 +1005,7 @@ client_put_disconnect (void *cls,
  * @param size size of an individual element
  * @param nmemb count of elements that can be written to the buffer
  * @param cls our `struct Session`
- * @return bytes written to stream, returning 0 will terminate connection!
+ * @return bytes written to stream, returning 0 will terminate request!
  */
 static size_t
 client_send_cb (void *stream,
@@ -1024,24 +1019,32 @@ client_send_cb (void *stream,
   size_t len;
   char *stat_txt;
 
-  if (GNUNET_YES == s->put_tmp_disconnecting)
+  if (H_TMP_DISCONNECTING == s->put.state)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Session %p/connection %p: disconnect due to inactivity\n",
-         s,
-         s->client_put);
+         "Session %p/request %p: disconnect due to inactivity\n",
+         s, s->put.easyhandle);
     return 0;
   }
 
   if (NULL == msg)
   {
+    if (GNUNET_YES == plugin->emulate_xhr)
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Session %p/request %p: PUT request finished\n",
+           s, s->put.easyhandle);
+      s->put.state = H_TMP_DISCONNECTING;
+      return 0;
+    }
+
+    /* We have nothing to send, so pause PUT request */
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Session %p/connection %p: nothing to send, suspending\n",
-         s, s->client_put);
+         "Session %p/request %p: nothing to send, suspending\n",
+         s, s->put.easyhandle);
     s->put_disconnect_task = GNUNET_SCHEDULER_add_delayed (PUT_DISCONNECT_TIMEOUT,
-                                                           &client_put_disconnect,
-                                                           s);
-    s->put_paused = GNUNET_YES;
+        &client_put_disconnect, s);
+    s->put.state = H_PAUSED;
     return CURL_READFUNC_PAUSE;
   }
   /* data to send */
@@ -1054,8 +1057,8 @@ client_send_cb (void *stream,
   if (msg->pos == msg->size)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Session %p/connection %p: sent message with %u bytes sent, removing message from queue\n",
-         s, s->client_put, msg->size, msg->pos);
+         "Session %p/request %p: sent message with %u bytes sent, removing message from queue\n",
+         s, s->put.easyhandle, msg->size, msg->pos);
     /* Calling transmit continuation  */
     GNUNET_CONTAINER_DLL_remove (s->msg_head,
                                  s->msg_tail,
@@ -1066,7 +1069,7 @@ client_send_cb (void *stream,
     s->bytes_in_queue -= msg->size;
     if (NULL != msg->transmit_cont)
       msg->transmit_cont (msg->transmit_cont_cls,
-                          &s->target,
+                          &s->address->peer,
                           GNUNET_OK,
                           msg->size,
                           msg->size + s->overhead);
@@ -1112,22 +1115,20 @@ client_wake_up (void *cls,
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Session %p/connection %p: Waking up GET handle\n",
-       s,
-       s->client_get);
-  if (GNUNET_YES == s->put_paused)
+       "Session %p/request %p: Waking up GET handle\n",
+       s, s->get.easyhandle);
+  if (H_PAUSED == s->put.state)
   {
-    /* PUT connection was paused, unpause */
+    /* PUT request was paused, unpause */
     GNUNET_assert (s->put_disconnect_task != GNUNET_SCHEDULER_NO_TASK);
     GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
     s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
-    s->put_paused = GNUNET_NO;
-    if (NULL != s->client_put)
-      curl_easy_pause (s->client_put, CURLPAUSE_CONT);
+    s->put.state = H_CONNECTED;
+    if (NULL != s->put.easyhandle)
+      curl_easy_pause (s->put.easyhandle, CURLPAUSE_CONT);
   }
-  if (NULL != s->client_get)
-    curl_easy_pause (s->client_get,
-                     CURLPAUSE_CONT);
+  if (NULL != s->get.easyhandle)
+    curl_easy_pause (s->get.easyhandle, CURLPAUSE_CONT);
 }
 
 
@@ -1177,7 +1178,7 @@ client_receive_mst_cb (void *cls,
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Client: peer `%s' address `%s' next read delayed for %s\n",
-         GNUNET_i2s (&s->target),
+         GNUNET_i2s (&s->address->peer),
          http_common_plugin_address_to_string (s->plugin->protocol,
                                                s->address->address,
                                                s->address->address_length),
@@ -1190,7 +1191,7 @@ client_receive_mst_cb (void *cls,
 
 
 /**
- * Callback method used with libcurl when data for a PUT connection are
+ * Callback method used with libcurl when data for a PUT request are
  * received.  We do not expect data here, so we just discard it.
  *
  * @param stream pointer where to write data
@@ -1210,7 +1211,7 @@ client_receive_put (void *stream,
 
 
 /**
- * Callback method used with libcurl when data for a GET connection are
+ * Callback method used with libcurl when data for a GET request are
  * received. Forward to MST
  *
  * @param stream pointer where to write data
@@ -1230,9 +1231,9 @@ client_receive (void *stream,
   size_t len = size * nmemb;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Session %p / connection %p: Received %u bytes from peer `%s'\n",
-       s, s->client_get,
-       len, GNUNET_i2s (&s->target));
+       "Session %p / request %p: Received %u bytes from peer `%s'\n",
+       s, s->get.easyhandle,
+       len, GNUNET_i2s (&s->address->peer));
   now = GNUNET_TIME_absolute_get ();
   if (now.abs_value_us < s->next_receive.abs_value_us)
   {
@@ -1241,9 +1242,9 @@ client_receive (void *stream,
       = GNUNET_TIME_absolute_get_difference (now, s->next_receive);
 
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Session %p / connection %p: No inbound bandwidth available! Next read was delayed for %s\n",
+         "Session %p / request %p: No inbound bandwidth available! Next read was delayed for %s\n",
          s,
-         s->client_get,
+         s->get.easyhandle,
          GNUNET_STRINGS_relative_time_to_string (delta,
                                                  GNUNET_YES));
     if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
@@ -1284,114 +1285,142 @@ client_run (void *cls,
   int running;
   long http_statuscode;
   CURLMcode mret;
+  CURLMsg *msg;
+  int put_request; /* GNUNET_YES if easy handle is put, GNUNET_NO for get */
+  int msgs_left;
 
   plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
 
+  /* While data are available or timeouts occured */
   do
   {
     running = 0;
+    /* Perform operations for all handles */
     mret = curl_multi_perform (plugin->curl_multi_handle, &running);
 
-    CURLMsg *msg;
-    int msgs_left;
-
-    while ((msg = curl_multi_info_read (plugin->curl_multi_handle, &msgs_left)))
+    /* Get additional information for all handles */
+    while (NULL != (msg = curl_multi_info_read (plugin->curl_multi_handle, &msgs_left)))
     {
       CURL *easy_h = msg->easy_handle;
       struct Session *s = NULL;
-      char *d = (char *) s;
+      char *d = NULL; /* curl requires 'd' to be a 'char *' */
 
-      if (NULL == easy_h)
-      {
-        GNUNET_break (0);
-        LOG (GNUNET_ERROR_TYPE_DEBUG,
-             "Client: connection to ended with reason %i: `%s', %i handles running\n",
-             msg->data.result,
-             curl_easy_strerror (msg->data.result), running);
-        continue;
-      }
+      GNUNET_assert (NULL != easy_h);
 
-      GNUNET_assert (CURLE_OK ==
-                     curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
+      /* Obtain session from easy handle */
+      GNUNET_assert (CURLE_OK == curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
       s = (struct Session *) d;
-      GNUNET_assert (s != NULL);
-      if (msg->msg == CURLMSG_DONE)
+      GNUNET_assert (NULL != s);
+
+      if (msg->msg != CURLMSG_DONE)
+        continue; /* This should not happen */
+
+      /* Get HTTP response code */
+      GNUNET_break (CURLE_OK == curl_easy_getinfo (easy_h,
+          CURLINFO_RESPONSE_CODE, &http_statuscode));
+
+      if (easy_h == s->put.easyhandle)
+        put_request = GNUNET_YES;
+      else
+        put_request = GNUNET_NO;
+
+      /* Log status of terminated request */
+      if  ((0 != msg->data.result) || (http_statuscode != 200))
+        LOG (GNUNET_ERROR_TYPE_DEBUG,
+             "Session %p/request %p: %s request to `%s' ended with status %i reason %i: `%s'\n",
+             s, msg->easy_handle,
+             (GNUNET_YES == put_request) ? "PUT" : "GET",
+             GNUNET_i2s (&s->address->peer),
+             http_statuscode,
+             msg->data.result,
+             curl_easy_strerror (msg->data.result));
+      else
+        LOG (GNUNET_ERROR_TYPE_DEBUG,
+             "Session %p/request %p: %s request to `%s' ended normal\n",
+             s, msg->easy_handle,
+             (GNUNET_YES == put_request) ? "PUT" : "GET",
+             GNUNET_i2s (&s->address->peer));
+
+      /* Remove easy handle from multi handle */
+      curl_multi_remove_handle (plugin->curl_multi_handle, easy_h);
+
+      /* Clean up easy handle */
+      curl_easy_cleanup (easy_h);
+
+      /* Remove information */
+      GNUNET_assert (plugin->cur_requests > 0);
+      plugin->cur_requests--;
+      LOG  (GNUNET_ERROR_TYPE_INFO,
+          "%s request to %s done, number of requests decreased to %u\n",
+          (GNUNET_YES == put_request) ? "PUT" : "GET",
+          s->url,
+          plugin->cur_requests);
+
+      if (GNUNET_YES == put_request)
       {
-        GNUNET_break (CURLE_OK == curl_easy_getinfo (easy_h,
-            CURLINFO_RESPONSE_CODE, &http_statuscode));
-        if (easy_h == s->client_put)
-        {
-            if  ((0 != msg->data.result) || (http_statuscode != 200))
-            {
-              LOG (GNUNET_ERROR_TYPE_DEBUG,
-                   "Session %p/connection %p: PUT connection to `%s' ended with status %i reason %i: `%s'\n",
-                   s, msg->easy_handle,
-                   GNUNET_i2s (&s->target),
-                   http_statuscode,
-                   msg->data.result,
-                   curl_easy_strerror (msg->data.result));
-            }
-            else
-              LOG (GNUNET_ERROR_TYPE_DEBUG,
-                   "Session %p/connection %p: PUT connection to `%s' ended normal\n",
-                   s, msg->easy_handle,
-                   GNUNET_i2s (&s->target));
-            if (NULL == s->client_get)
+        /* Clean up a PUT request */
+        s->put.easyhandle = NULL;
+        s->put.s = NULL;
+
+        switch (s->put.state) {
+          case H_NOT_CONNECTED:
+          case H_DISCONNECTED:
+          case H_TMP_DISCONNECTED:
+            /* This must not happen */
+            GNUNET_break (0);
+            break;
+          case H_TMP_RECONNECT_REQUIRED:
+            /* Transport called send while disconnect in progess, reconnect */
+            if (GNUNET_SYSERR == client_connect_put (s))
             {
-              /* Disconnect other transmission direction and tell transport */
-              /* FIXME? */
-            }
-            curl_multi_remove_handle (plugin->curl_multi_handle, easy_h);
-            curl_easy_cleanup (easy_h);
-            s->put_tmp_disconnecting = GNUNET_NO;
-            s->put_tmp_disconnected = GNUNET_YES;
-            s->client_put = NULL;
-            s->put.easyhandle = NULL;
-            s->put.s = NULL;
-
-            /*
-             * Handling a rare case:
-             * plugin_send was called during temporary put disconnect,
-             * reconnect required after connection was disconnected
-             */
-            if (GNUNET_YES == s->put_reconnect_required)
-            {
-              s->put_reconnect_required = GNUNET_NO;
-              if (GNUNET_SYSERR == client_connect_put (s))
-              {
-                GNUNET_break (s->client_put == NULL);
-                GNUNET_break (s->put_tmp_disconnected == GNUNET_NO);
-              }
+              /* Reconnect failed, disconnect session */
+              http_client_plugin_session_disconnect (plugin, s);
             }
+            break;
+          case H_TMP_DISCONNECTING:
+            /* PUT gets temporarily disconnected */
+            s->put.state = H_TMP_DISCONNECTED;
+            break;
+          case H_PAUSED:
+          case H_CONNECTED:
+            /* PUT gets permanently disconnected */
+            s->put.state = H_DISCONNECTED;
+            http_client_plugin_session_disconnect (plugin, s);
+            break;
+          default:
+            GNUNET_break (0);
+            break;
         }
-        if (easy_h == s->client_get)
+      }
+      else if (GNUNET_NO == put_request)
+      {
+        /* Clean up a GET request */
+        s->get.easyhandle = NULL;
+        s->get.s = NULL;
+
+        /* If we are emulating an XHR client we need to make another GET
+         * request.
+         */
+        if (GNUNET_YES == plugin->emulate_xhr)
         {
-            if  ((0 != msg->data.result) || (http_statuscode != 200))
-            {
-              LOG (GNUNET_ERROR_TYPE_DEBUG,
-                   "Session %p/connection %p: GET connection to `%s' ended with status %i reason %i: `%s'\n",
-                   s,
-                   msg->easy_handle,
-                   GNUNET_i2s (&s->target),
-                   http_statuscode,
-                   msg->data.result,
-                   curl_easy_strerror (msg->data.result));
-
-            }
-            else
-              LOG (GNUNET_ERROR_TYPE_DEBUG,
-                   "Session %p/connection %p: GET connection to `%s' ended normal\n",
-                   s,
-                   msg->easy_handle,
-                   GNUNET_i2s (&s->target));
-            /* Disconnect other transmission direction and tell transport */
-            s->get.easyhandle = NULL;
-            s->get.s = NULL;
+          if (GNUNET_SYSERR == client_connect_get (s))
             http_client_plugin_session_disconnect (plugin, s);
         }
+        else
+        {
+          /* GET request was terminated, so disconnect session */
+          http_client_plugin_session_disconnect (plugin, s);
+        }
       }
+      else
+        GNUNET_break (0); /* Must not happen */
+
+      GNUNET_STATISTICS_set (plugin->env->stats,
+                             HTTP_STAT_STR_CONNECTIONS,
+                             plugin->cur_requests,
+                             GNUNET_NO);
     }
   }
   while (mret == CURLM_CALL_MULTI_PERFORM);
@@ -1400,7 +1429,7 @@ client_run (void *cls,
 
 
 /**
- * Connect GET connection for a session
+ * Connect GET request for a session
  *
  * @param s the session to connect
  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
@@ -1410,95 +1439,107 @@ client_connect_get (struct Session *s)
 {
   CURLMcode mret;
 
-  /* create get connection */
-  s->client_get = curl_easy_init ();
+  /* create get request */
+  s->get.easyhandle = curl_easy_init ();
   s->get.s = s;
-  s->get.easyhandle = s->client_get;
 #if VERBOSE_CURL
-  curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L);
-  curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log);
-  curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, &s->get);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_VERBOSE, 1L);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_DEBUGFUNCTION, &client_log);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_DEBUGDATA, &s->get);
 #endif
 #if BUILD_HTTPS
-  curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
   {
     struct HttpAddress *ha;
+
     ha = (struct HttpAddress *) s->address->address;
 
     if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
         (ntohl (ha->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE))
     {
-      curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 1L);
-      curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 2L);
+      curl_easy_setopt (s->get.easyhandle, CURLOPT_SSL_VERIFYPEER, 1L);
+      curl_easy_setopt (s->get.easyhandle, CURLOPT_SSL_VERIFYHOST, 2L);
     }
     else
     {
-      curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0);
-      curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0);
+      curl_easy_setopt (s->get.easyhandle, CURLOPT_SSL_VERIFYPEER, 0);
+      curl_easy_setopt (s->get.easyhandle, CURLOPT_SSL_VERIFYHOST, 0);
     }
   }
-  curl_easy_setopt (s->client_get, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
-  curl_easy_setopt (s->client_get, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
 #else
-  curl_easy_setopt (s->client_get, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
-  curl_easy_setopt (s->client_get, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP);
 #endif
 
-  if (s->plugin->proxy_hostname != NULL)
+  if (NULL != s->plugin->proxy_hostname)
   {
-    curl_easy_setopt (s->client_get, CURLOPT_PROXY, s->plugin->proxy_hostname);
-    curl_easy_setopt (s->client_get, CURLOPT_PROXYTYPE, s->plugin->proxytype);
+    curl_easy_setopt (s->get.easyhandle, CURLOPT_PROXY, s->plugin->proxy_hostname);
+    curl_easy_setopt (s->get.easyhandle, CURLOPT_PROXYTYPE, s->plugin->proxytype);
     if (NULL != s->plugin->proxy_username)
-      curl_easy_setopt (s->client_get, CURLOPT_PROXYUSERNAME,
+      curl_easy_setopt (s->get.easyhandle, CURLOPT_PROXYUSERNAME,
           s->plugin->proxy_username);
     if (NULL != s->plugin->proxy_password)
-      curl_easy_setopt (s->client_get, CURLOPT_PROXYPASSWORD,
+      curl_easy_setopt (s->get.easyhandle, CURLOPT_PROXYPASSWORD,
           s->plugin->proxy_password);
     if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
-      curl_easy_setopt (s->client_get, CURLOPT_HTTPPROXYTUNNEL,
+      curl_easy_setopt (s->get.easyhandle, CURLOPT_HTTPPROXYTUNNEL,
           s->plugin->proxy_use_httpproxytunnel);
   }
 
-  curl_easy_setopt (s->client_get, CURLOPT_URL, s->url);
-  //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
-  //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps);
-  curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb);
-  curl_easy_setopt (s->client_get, CURLOPT_READDATA, s);
-  curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive);
-  curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s);
+  if (GNUNET_YES == s->plugin->emulate_xhr)
+  {
+    char *url;
+
+    GNUNET_asprintf(&url, "%s,1", s->url);
+    curl_easy_setopt (s->get.easyhandle, CURLOPT_URL, url);
+    GNUNET_free(url);
+  } else
+    curl_easy_setopt (s->get.easyhandle, CURLOPT_URL, s->url);
+  //curl_easy_setopt (s->get.easyhandle, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
+  //curl_easy_setopt (s->get.easyhandle, CURLOPT_WRITEHEADER, ps);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_READFUNCTION, client_send_cb);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_READDATA, s);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_WRITEFUNCTION, client_receive);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_WRITEDATA, s);
   /* No timeout by default, timeout done with session timeout */
-  curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT, 0);
-  curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s);
-  curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS,
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_TIMEOUT, 0);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_PRIVATE, s);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_CONNECTTIMEOUT_MS,
                     (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL));
-  curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE,
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_BUFFERSIZE,
                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
 #if CURL_TCP_NODELAY
   curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
 #endif
-  curl_easy_setopt (s->client_get, CURLOPT_FOLLOWLOCATION, 0);
+  curl_easy_setopt (s->get.easyhandle, CURLOPT_FOLLOWLOCATION, 0);
 
-  mret = curl_multi_add_handle (s->plugin->curl_multi_handle, s->client_get);
-  if (mret != CURLM_OK)
+  mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
+                                s->get.easyhandle);
+  if (CURLM_OK != mret)
   {
     LOG (GNUNET_ERROR_TYPE_ERROR,
          "Session %p : Failed to add GET handle to multihandle: `%s'\n",
          s,
          curl_multi_strerror (mret));
-    curl_easy_cleanup (s->client_get);
-    s->client_get = NULL;
+    curl_easy_cleanup (s->get.easyhandle);
+    s->get.easyhandle = NULL;
     s->get.s = NULL;
     s->get.easyhandle = NULL;
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-
+  s->plugin->cur_requests++;
+  LOG  (GNUNET_ERROR_TYPE_INFO,
+      "GET request `%s' established, number of requests increased to %u\n",
+      s->url, s->plugin->cur_requests);
   return GNUNET_OK;
 }
 
 
 /**
- * Connect a HTTP put connection
+ * Connect a HTTP put request
  *
  * @param s the session to connect
  * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for ok
@@ -1508,19 +1549,18 @@ client_connect_put (struct Session *s)
 {
   CURLMcode mret;
 
-  /* create put connection */
+  /* create put request */
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Session %p: Init PUT handle\n", s);
-  s->client_put = curl_easy_init ();
+  s->put.easyhandle = curl_easy_init ();
   s->put.s = s;
-  s->put.easyhandle = s->client_put;
 #if VERBOSE_CURL
-  curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L);
-  curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log);
-  curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, &s->put);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_VERBOSE, 1L);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_DEBUGFUNCTION, &client_log);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_DEBUGDATA, &s->put);
 #endif
 #if BUILD_HTTPS
-  curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
   {
     struct HttpAddress *ha;
     ha = (struct HttpAddress *) s->address->address;
@@ -1528,75 +1568,81 @@ client_connect_put (struct Session *s)
     if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
         (ntohl (ha->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE))
     {
-      curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 1L);
-      curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 2L);
+      curl_easy_setopt (s->put.easyhandle, CURLOPT_SSL_VERIFYPEER, 1L);
+      curl_easy_setopt (s->put.easyhandle, CURLOPT_SSL_VERIFYHOST, 2L);
     }
     else
     {
-      curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0);
-      curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0);
+      curl_easy_setopt (s->put.easyhandle, CURLOPT_SSL_VERIFYPEER, 0);
+      curl_easy_setopt (s->put.easyhandle, CURLOPT_SSL_VERIFYHOST, 0);
     }
   }
-  curl_easy_setopt (s->client_put, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
-  curl_easy_setopt (s->client_put, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
 #else
-  curl_easy_setopt (s->client_put, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
-  curl_easy_setopt (s->client_put, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP);
 #endif
   if (s->plugin->proxy_hostname != NULL)
   {
-    curl_easy_setopt (s->client_put, CURLOPT_PROXY, s->plugin->proxy_hostname);
-    curl_easy_setopt (s->client_put, CURLOPT_PROXYTYPE, s->plugin->proxytype);
+    curl_easy_setopt (s->put.easyhandle, CURLOPT_PROXY, s->plugin->proxy_hostname);
+    curl_easy_setopt (s->put.easyhandle, CURLOPT_PROXYTYPE, s->plugin->proxytype);
     if (NULL != s->plugin->proxy_username)
-      curl_easy_setopt (s->client_put, CURLOPT_PROXYUSERNAME,
+      curl_easy_setopt (s->put.easyhandle, CURLOPT_PROXYUSERNAME,
           s->plugin->proxy_username);
     if (NULL != s->plugin->proxy_password)
-      curl_easy_setopt (s->client_put, CURLOPT_PROXYPASSWORD,
+      curl_easy_setopt (s->put.easyhandle, CURLOPT_PROXYPASSWORD,
           s->plugin->proxy_password);
     if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
-      curl_easy_setopt (s->client_put, CURLOPT_HTTPPROXYTUNNEL,
+      curl_easy_setopt (s->put.easyhandle, CURLOPT_HTTPPROXYTUNNEL,
           s->plugin->proxy_use_httpproxytunnel);
   }
 
-  curl_easy_setopt (s->client_put, CURLOPT_URL, s->url);
-  curl_easy_setopt (s->client_put, CURLOPT_UPLOAD, 1L);
-  //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &client_curl_header);
-  //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps);
-  curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb);
-  curl_easy_setopt (s->client_put, CURLOPT_READDATA, s);
-  curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive_put);
-  curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_URL, s->url);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_UPLOAD, 1L);
+  //curl_easy_setopt (s->put.easyhandle, CURLOPT_HEADERFUNCTION, &client_curl_header);
+  //curl_easy_setopt (s->put.easyhandle, CURLOPT_WRITEHEADER, ps);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_READFUNCTION, client_send_cb);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_READDATA, s);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_WRITEFUNCTION, client_receive_put);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_WRITEDATA, s);
   /* No timeout by default, timeout done with session timeout */
-  curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT, 0);
-  curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s);
-  curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS,
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_TIMEOUT, 0);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_PRIVATE, s);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_CONNECTTIMEOUT_MS,
                     (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL));
-  curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE,
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_BUFFERSIZE,
                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
 #if CURL_TCP_NODELAY
-  curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1);
+  curl_easy_setopt (s->put.easyhandle, CURLOPT_TCP_NODELAY, 1);
 #endif
-  mret = curl_multi_add_handle (s->plugin->curl_multi_handle, s->client_put);
-  if (mret != CURLM_OK)
+  mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
+                                s->put.easyhandle);
+  if (CURLM_OK != mret)
   {
     LOG (GNUNET_ERROR_TYPE_ERROR,
          "Session %p : Failed to add PUT handle to multihandle: `%s'\n",
-         s,
-         curl_multi_strerror (mret));
-    curl_easy_cleanup (s->client_put);
-    s->client_put = NULL;
+         s, curl_multi_strerror (mret));
+    curl_easy_cleanup (s->put.easyhandle);
+    s->put.easyhandle = NULL;
     s->put.easyhandle = NULL;
     s->put.s = NULL;
-    s->put_tmp_disconnected = GNUNET_YES;
+    s->put.state = H_DISCONNECTED;
     return GNUNET_SYSERR;
   }
-  s->put_tmp_disconnected = GNUNET_NO;
+  s->put.state = H_CONNECTED;
+  s->plugin->cur_requests++;
+
+  LOG  (GNUNET_ERROR_TYPE_INFO,
+      "PUT request `%s' established, number of requests increased to %u\n",
+      s->url, s->plugin->cur_requests);
+
   return GNUNET_OK;
 }
 
 
 /**
- * Connect both PUT and GET connection for a session
+ * Connect both PUT and GET request for a session
  *
  * @param s the session to connect
  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
@@ -1608,46 +1654,43 @@ client_connect (struct Session *s)
   int res = GNUNET_OK;
 
   /* create url */
-  if (NULL == http_common_plugin_address_to_string (plugin->protocol,
-                                                    s->address->address,
-                                                    s->address->address_length))
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Invalid address peer `%s'\n",
-         GNUNET_i2s (&s->target));
-    return GNUNET_SYSERR;
-  }
+  if (NULL == http_common_plugin_address_to_string(plugin->protocol,
+          s->address->address, s->address->address_length))
+    {
+      LOG(GNUNET_ERROR_TYPE_DEBUG, "Invalid address peer `%s'\n",
+          GNUNET_i2s(&s->address->peer));
+      return GNUNET_SYSERR;
+    }
 
-  GNUNET_asprintf (&s->url, "%s/%s;%u",
-                  http_common_plugin_address_to_url (NULL,
-                                                      s->address->address,
-                                                      s->address->address_length),
-                  GNUNET_i2s_full (plugin->env->my_identity),
-                  plugin->last_tag);
+  GNUNET_asprintf(&s->url, "%s/%s;%u",
+      http_common_plugin_address_to_url(NULL, s->address->address,
+          s->address->address_length),
+      GNUNET_i2s_full(plugin->env->my_identity), plugin->last_tag);
 
   plugin->last_tag++;
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Initiating outbound session peer `%s' using address `%s'\n",
-       GNUNET_i2s (&s->target), s->url);
+       GNUNET_i2s (&s->address->peer), s->url);
 
-  if ((GNUNET_SYSERR == client_connect_get (s)) ||
-      (GNUNET_SYSERR == client_connect_put (s)))
-  {
-    plugin->env->session_end (plugin->env->cls, s->address, s);
-    client_delete_session (s);
+  if (GNUNET_SYSERR == client_connect_get (s))
     return GNUNET_SYSERR;
+  /* If we are emulating an XHR client then delay sending a PUT request until
+   * there is something to send.
+   */
+  if (GNUNET_YES == plugin->emulate_xhr)
+  {
+    s->put.state = H_TMP_DISCONNECTED;
   }
+  else if (GNUNET_SYSERR == client_connect_put (s))
+    return GNUNET_SYSERR;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Session %p: connected with connections GET %p and PUT %p\n",
-       s,
-       s->client_get,
-       s->client_put);
+       "Session %p: connected with GET %p and PUT %p\n",
+       s, s->get.easyhandle, s->put.easyhandle);
   /* Perform connect */
-  plugin->cur_connections += 2;
   GNUNET_STATISTICS_set (plugin->env->stats,
                          HTTP_STAT_STR_CONNECTIONS,
-                         plugin->cur_connections,
+                         plugin->cur_requests,
                          GNUNET_NO);
   /* Re-schedule since handles have changed */
   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
@@ -1655,6 +1698,8 @@ client_connect (struct Session *s)
     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
   }
+
+  /* Schedule task to run immediately */
   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin);
   return res;
 }
@@ -1739,12 +1784,13 @@ http_client_plugin_get_session (void *cls,
   if (NULL != s)
     return s;
 
-  if (plugin->max_connections <= plugin->cur_connections)
+  /* create a new session */
+  if (plugin->max_requests <= plugin->cur_requests)
   {
     LOG (GNUNET_ERROR_TYPE_WARNING,
-         "Maximum number of connections (%u) reached: "
+         "Maximum number of requests (%u) reached: "
          "cannot connect to peer `%s'\n",
-         plugin->max_connections,
+         plugin->max_requests,
          GNUNET_i2s (&address->peer));
     return NULL;
   }
@@ -1781,13 +1827,11 @@ http_client_plugin_get_session (void *cls,
   }
 
   s = GNUNET_new (struct Session);
-  s->target = address->peer;
   s->plugin = plugin;
   s->address = GNUNET_HELLO_address_copy (address);
   s->ats_address_network_type = ats.value;
-  s->put_paused = GNUNET_NO;
-  s->put_tmp_disconnecting = GNUNET_NO;
-  s->put_tmp_disconnected = GNUNET_NO;
+
+  s->put.state = H_NOT_CONNECTED;
   s->timeout = GNUNET_TIME_relative_to_absolute (HTTP_CLIENT_SESSION_TIMEOUT);
   s->timeout_task =  GNUNET_SCHEDULER_add_delayed (HTTP_CLIENT_SESSION_TIMEOUT,
                                                    &client_session_timeout,
@@ -1798,11 +1842,11 @@ http_client_plugin_get_session (void *cls,
        http_common_plugin_address_to_string (plugin->protocol,
                                              s->address->address,
                                              s->address->address_length),
-       GNUNET_i2s (&s->target));
+       GNUNET_i2s (&s->address->peer));
 
   /* add new session */
   (void) GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
-                                            &s->target,
+                                            &s->address->peer,
                                             s,
                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
   /* initiate new connection */
@@ -1811,15 +1855,12 @@ http_client_plugin_get_session (void *cls,
     LOG (GNUNET_ERROR_TYPE_ERROR,
          "Cannot connect to peer `%s' address `%s''\n",
          http_common_plugin_address_to_string (plugin->protocol,
-                                               s->address->address,
-                                               s->address->address_length),
-         GNUNET_i2s (&s->target));
+             s->address->address, s->address->address_length),
+             GNUNET_i2s (&s->address->peer));
     client_delete_session (s);
     return NULL;
   }
-  notify_session_monitor (plugin,
-                          s,
-                          GNUNET_TRANSPORT_SS_UP); /* or handshake? */
+  notify_session_monitor (plugin, s, GNUNET_TRANSPORT_SS_UP); /* or handshake? */
   return s;
 }
 
@@ -1926,7 +1967,7 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
 static int
 client_configure_plugin (struct HTTP_Client_Plugin *plugin)
 {
-  unsigned long long max_connections;
+  unsigned long long max_requests;
   char *proxy_type;
 
 
@@ -1935,13 +1976,13 @@ client_configure_plugin (struct HTTP_Client_Plugin *plugin)
       GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
                                              plugin->name,
                                              "MAX_CONNECTIONS",
-                                             &max_connections))
-    max_connections = 128;
-  plugin->max_connections = max_connections;
+                                             &max_requests))
+    max_requests = 128;
+  plugin->max_requests = max_requests;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       _("Maximum number of connections is %u\n"),
-       plugin->max_connections);
+       _("Maximum number of requests is %u\n"),
+       plugin->max_requests);
 
   /* Read proxy configuration */
   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
@@ -2025,23 +2066,23 @@ client_configure_plugin (struct HTTP_Client_Plugin *plugin)
 
     GNUNET_free_non_null (proxy_type);
   }
+
+  /* Should we emulate an XHR client for testing? */
+  plugin->emulate_xhr
+    = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
+                                            plugin->name,
+                                            "EMULATE_XHR");
   return GNUNET_OK;
 }
 
 
 /**
- * Function called by the pretty printer for the resolved address for
- * each human-readable address obtained.  The callback can be called
- * several times. The last invocation must be with a @a address of
- * NULL and a @a res of #GNUNET_OK.  Thus, to indicate conversion
- * errors, the callback might be called first with @a address NULL and
- * @a res being #GNUNET_SYSERR.  In that case, there must still be a
- * subsequent call later with @a address NULL and @a res #GNUNET_OK.
+ * Function to convert an address to a human-readable string.
  *
  * @param cls closure
- * @param address one of the names for the host, NULL on last callback
- * @param res #GNUNET_OK if conversion was successful, #GNUNET_SYSERR on failure,
- *      #GNUNET_OK on last callback
+ * @param addr address to convert
+ * @param addrlen address length
+ * @return res string if conversion was successful, NULL otherwise
  */
 static const char *
 http_client_plugin_address_to_string (void *cls,
@@ -2079,7 +2120,7 @@ http_client_plugin_update_session_timeout (void *cls,
  *
  * @param cls closure
  * @param peer which peer was the session for
- * @param session which session is being updated
+ * @param s which session is being updated
  * @param delay new delay to use for receiving
  */
 static void
@@ -2096,10 +2137,8 @@ http_client_plugin_update_inbound_delay (void *cls,
   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
   {
     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
-    s->recv_wakeup_task
-      = GNUNET_SCHEDULER_add_delayed (delay,
-                                      &client_wake_up,
-                                      s);
+    s->recv_wakeup_task = GNUNET_SCHEDULER_add_delayed (delay,
+        &client_wake_up, s);
   }
 }