static void
client_put_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct Session *s = cls;
  s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
                   "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;
  curl_easy_pause (s->client_put, CURLPAUSE_CONT);
  client_schedule (s->plugin, GNUNET_YES);
}
int
client_send (struct Session *s, struct HTTP_Message *msg)
{
  GNUNET_assert (s != NULL);
  GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg);

  if (GNUNET_YES != exist_session(p, s))
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }
  if (s->client_put_paused == GNUNET_YES)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
                     "Client: %p was suspended, unpausing\n", s->client_put);
    s->client_put_paused = GNUNET_NO;
    curl_easy_pause (s->client_put, CURLPAUSE_CONT);
  }
  client_schedule (s->plugin, GNUNET_YES);

  return GNUNET_OK;
}
int
client_disconnect (struct Session *s)
{
  int res = GNUNET_OK;
  CURLMcode mret;
  struct Plugin *plugin = s->plugin;
  struct HTTP_Message *msg;
  struct HTTP_Message *t;

  if (GNUNET_YES != exist_session(plugin, s))
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }

  if (s->client_put != NULL)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                     "Client: %p Deleting outbound PUT session to peer `%s'\n",
                     s->client_put, GNUNET_i2s (&s->target));

    mret = curl_multi_remove_handle (plugin->client_mh, s->client_put);
    if (mret != CURLM_OK)
    {
      curl_easy_cleanup (s->client_put);
      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 (s->client_get != NULL)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                     "Client: %p Deleting outbound GET session to peer `%s'\n",
                     s->client_get, GNUNET_i2s (&s->target));

    mret = curl_multi_remove_handle (plugin->client_mh, s->client_get);
    if (mret != CURLM_OK)
    {
      curl_easy_cleanup (s->client_get);
      res = GNUNET_SYSERR;
      GNUNET_break (0);
    }
    curl_easy_cleanup (s->client_get);
    s->client_get = NULL;
  }

  msg = s->msg_head;
  while (msg != NULL)
  {
    t = msg->next;
    if (NULL != msg->transmit_cont)
      msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
    GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
    GNUNET_free (msg);
    msg = t;
  }

  plugin->cur_connections -= 2;

  GNUNET_assert (plugin->outbound_sessions > 0);
  plugin->outbound_sessions --;
  GNUNET_STATISTICS_set (plugin->env->stats,
      "# HTTP outbound sessions",
      plugin->outbound_sessions,
      GNUNET_NO);

  /* Re-schedule since handles have changed */
  if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
  {
    GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
    plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
  }

  client_schedule (plugin, GNUNET_YES);

  return res;
}
/**
 * Task performing curl operations
 *
 * @param cls plugin as closure
 * @param tc gnunet scheduler task context
 */
static void
client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct Plugin *plugin = cls;
  int running;
  CURLMcode mret;

  GNUNET_assert (cls != NULL);

  plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
    return;

  do
  {
    running = 0;
    mret = curl_multi_perform (plugin->client_mh, &running);

    CURLMsg *msg;
    int msgs_left;

    while ((msg = curl_multi_info_read (plugin->client_mh, &msgs_left)))
    {
      CURL *easy_h = msg->easy_handle;
      struct Session *s = NULL;
      char *d = (char *) s;


      //GNUNET_assert (easy_h != NULL);
      if (easy_h == NULL)
      {
        GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                         "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 (CURLE_OK ==
                     curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
      s = (struct Session *) d;

      if (GNUNET_YES != exist_session(plugin, s))
      {
        GNUNET_break (0);
        return;
      }

      GNUNET_assert (s != NULL);

      if (msg->msg == CURLMSG_DONE)
      {
        GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                         "Client: %p connection to '%s'  %s ended with reason %i: `%s'\n",
                         msg->easy_handle, GNUNET_i2s (&s->target),
                         http_plugin_address_to_string (NULL, s->addr,
                                                        s->addrlen),
                         msg->data.result,
                         curl_easy_strerror (msg->data.result));

        client_disconnect (s);
        GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 
			 plugin->name,
			 "Notifying about ended session to peer `%s' `%s'\n", 
			 GNUNET_i2s (&s->target), 
			 http_plugin_address_to_string (plugin, s->addr, s->addrlen));
        notify_session_end (plugin, &s->target, s);
      }
    }
  }
  while (mret == CURLM_CALL_MULTI_PERFORM);
  client_schedule (plugin, GNUNET_NO);
}
/**
 * Disconnect a session
 *
 * @param s session
 * @return GNUNET_OK on success, GNUNET_SYSERR on error
 */
static int
client_disconnect (struct Session *s)
{
  struct HTTP_Client_Plugin *plugin = s->plugin;
  struct HTTP_Message *msg;
  struct HTTP_Message *t;
  int res = GNUNET_OK;
  CURLMcode mret;

  if (GNUNET_YES != client_exist_session (plugin, s))
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }

  if (s->client_put != NULL)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                     "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 (s->client_get != NULL)
  {
      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                       "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 (msg != NULL)
  {
    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_free (msg);
    msg = t;
  }

  GNUNET_assert (plugin->cur_connections >= 2);
  plugin->cur_connections -= 2;
  GNUNET_STATISTICS_set (plugin->env->stats,
      "# HTTP client sessions",
      plugin->cur_connections,
      GNUNET_NO);

  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                   "Session %p: notifying transport about ending session\n",s);

  plugin->env->session_end (plugin->env->cls, &s->target, s);
  client_delete_session (s);

  /* Re-schedule since handles have changed */
  if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
  {
    GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
    plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
  }
  client_schedule (plugin, GNUNET_YES);

  return res;
}
/**
 * Function that can be used by the transport service to transmit
 * a message using the plugin.   Note that in the case of a
 * peer disconnecting, the continuation MUST be called
 * prior to the disconnect notification itself.  This function
 * will be called with this peer's HELLO message to initiate
 * a fresh connection to another peer.
 *
 * @param cls closure
 * @param s which session must be used
 * @param msgbuf the message to transmit
 * @param msgbuf_size number of bytes in 'msgbuf'
 * @param priority how important is the message (most plugins will
 *                 ignore message priority and just FIFO)
 * @param to how long to wait at most for the transmission (does not
 *                require plugins to discard the message after the timeout,
 *                just advisory for the desired delay; most plugins will ignore
 *                this as well)
 * @param cont continuation to call once the message has
 *        been transmitted (or if the transport is ready
 *        for the next transmission call; or if the
 *        peer disconnected...); can be NULL
 * @param cont_cls closure for cont
 * @return number of bytes used (on the physical network, with overheads);
 *         -1 on hard errors (i.e. address invalid); 0 is a legal value
 *         and does NOT mean that the message was not transmitted (DV)
 */
static ssize_t
http_client_plugin_send (void *cls,
                         struct Session *s,
                         const char *msgbuf, size_t msgbuf_size,
                         unsigned int priority,
                         struct GNUNET_TIME_Relative to,
                         GNUNET_TRANSPORT_TransmitContinuation cont,
                         void *cont_cls)
{
  struct HTTP_Client_Plugin *plugin = cls;
  struct HTTP_Message *msg;
  char *stat_txt;

  GNUNET_assert (plugin != NULL);
  GNUNET_assert (s != NULL);

  /* lookup if session is really existing */
  if (GNUNET_YES != client_exist_session (plugin, s))
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }

  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
                   "Session %p/connection %p: Sending message with %u to peer `%s' \n",
                   s, s->client_put,
                   msgbuf_size, GNUNET_i2s (&s->target));

  /* create new message and schedule */
  msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
  msg->next = NULL;
  msg->size = msgbuf_size;
  msg->pos = 0;
  msg->buf = (char *) &msg[1];
  msg->transmit_cont = cont;
  msg->transmit_cont_cls = cont_cls;
  memcpy (msg->buf, msgbuf, msgbuf_size);
  GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg);

  GNUNET_asprintf (&stat_txt, "# bytes currently in %s_client buffers", plugin->protocol);
  GNUNET_STATISTICS_update (plugin->env->stats,
                            stat_txt, msgbuf_size, GNUNET_NO);

  GNUNET_free (stat_txt);

  if (GNUNET_YES == s->put_tmp_disconnecting)
  {
    /* PUT connection is currently getting disconnected */
    s->put_reconnect_required = GNUNET_YES;
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
                     "Session %p/connection %jp: currently disconnecting, reconnecting immediately\n",
                     s, s->client_put);
    return msgbuf_size;
  }
  else if (GNUNET_YES == s->put_paused)
  {
    /* PUT connection 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;
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
                     "Session %p/connection %p: unpausing connection\n",
                     s, s->client_put);
    s->put_paused = GNUNET_NO;
    curl_easy_pause (s->client_put, CURLPAUSE_CONT);
  }
  else if (GNUNET_YES == s->put_tmp_disconnected)
  {
    /* PUT connection was disconnected, reconnect */
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
                     "Session %p: Reconnecting PUT connection\n",
                     s);
    s->put_tmp_disconnected = GNUNET_NO;
    GNUNET_break (s->client_put == NULL);
    if (GNUNET_SYSERR == client_connect_put (s))
    {
      return GNUNET_SYSERR;
    }
  }

  client_schedule (s->plugin, GNUNET_YES);
  client_reschedule_session_timeout (s);
  return msgbuf_size;
}
/**
 * Task performing curl operations
 *
 * @param cls plugin as closure
 * @param tc gnunet scheduler task context
 */
static void
client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct HTTP_Client_Plugin *plugin = cls;
  int running;
  long http_statuscode;
  CURLMcode mret;

  GNUNET_assert (cls != NULL);

  plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
    return;

  do
  {
    running = 0;
    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)))
    {
      CURL *easy_h = msg->easy_handle;
      struct Session *s = NULL;
      char *d = (char *) s;

      if (easy_h == NULL)
      {
        GNUNET_break (0);
        GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                         "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 (CURLE_OK ==
                     curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
      s = (struct Session *) d;

      if (GNUNET_YES != client_exist_session(plugin, s))
      {
        GNUNET_break (0);
        return;
      }

      GNUNET_assert (s != NULL);
      if (msg->msg == CURLMSG_DONE)
      {
        curl_easy_getinfo (easy_h, CURLINFO_RESPONSE_CODE, &http_statuscode);
        if (easy_h == s->client_put)
        {
            if  ((0 != msg->data.result) || (http_statuscode != 200))
            {
                GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                  "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
              GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                "Session %p/connection %p: PUT connection to `%s' ended normal\n",
                s, msg->easy_handle, GNUNET_i2s (&s->target));
            if (s->client_get == NULL)
            {
              /* Disconnect other transmission direction and tell transport */
            }
            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);
                }
            }
        }
        if (easy_h == s->client_get)
        {
            if  ((0 != msg->data.result) || (http_statuscode != 200))
            {
              GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                "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
              GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                "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;
            client_disconnect (s);
        }
      }
    }
  }
  while (mret == CURLM_CALL_MULTI_PERFORM);
  client_schedule (plugin, GNUNET_NO);
}