+static int send_check_connections (struct Plugin *plugin, struct Session *ps)
+{
+ CURLMcode mret;
+ struct HTTP_Message * msg;
+
+ struct GNUNET_TIME_Relative timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
+
+ if (ps->direction == OUTBOUND)
+ {
+ /* RECV DIRECTION */
+ /* Check if session is connected to receive data, otherwise connect to peer */
+ if (ps->recv_connected == GNUNET_NO)
+ {
+ int fresh = GNUNET_NO;
+ if (ps->recv_endpoint == NULL)
+ {
+ fresh = GNUNET_YES;
+ ps->recv_endpoint = curl_easy_init();
+ }
+#if DEBUG_CURL
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_VERBOSE, 1L);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_DEBUGFUNCTION , &curl_logger);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_DEBUGDATA , ps->recv_endpoint);
+#endif
+#if BUILD_HTTPS
+ curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_SSL_VERIFYHOST, 0);
+#endif
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_URL, ps->url);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEHEADER, ps);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_READDATA, ps);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEDATA, ps);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_TIMEOUT, (long) timeout.rel_value);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_PRIVATE, ps);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_BUFFERSIZE, 2*GNUNET_SERVER_MAX_MESSAGE_SIZE);
+#if CURL_TCP_NODELAY
+ curl_easy_setopt(ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
+#endif
+
+ if (fresh==GNUNET_YES)
+ {
+ mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint);
+ if (mret != CURLM_OK)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Connection: %X: %s failed at %s:%d: `%s'\n"),
+ ps,
+ "curl_multi_add_handle", __FILE__, __LINE__,
+ curl_multi_strerror (mret));
+ return GNUNET_SYSERR;
+ }
+ }
+ if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel(plugin->http_curl_task);
+ plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ plugin->http_curl_task = GNUNET_SCHEDULER_add_now (&curl_perform, plugin);
+ }
+
+ /* waiting for receive direction */
+ if (ps->recv_connected==GNUNET_NO)
+ return GNUNET_NO;
+
+ /* SEND DIRECTION */
+ /* Check if session is connected to send data, otherwise connect to peer */
+ if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint!= NULL))
+ {
+ if (ps->send_active == GNUNET_YES)
+ {
+#if DEBUG_CONNECTIONS
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound active, enqueueing message\n",ps);
+#endif
+ return GNUNET_YES;
+ }
+ if (ps->send_active == GNUNET_NO)
+ {
+#if DEBUG_CONNECTIONS
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",ps);
+#endif
+ if (CURLE_OK == curl_easy_pause(ps->send_endpoint,CURLPAUSE_CONT))
+ {
+ ps->send_active=GNUNET_YES;
+ if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel(plugin->http_curl_task);
+ plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ plugin->http_curl_task = GNUNET_SCHEDULER_add_now (&curl_perform, plugin);
+ return GNUNET_YES;
+ }
+ else
+ return GNUNET_SYSERR;
+ }
+ }
+ /* not connected, initiate connection */
+ if (ps->send_connected==GNUNET_NO)
+ {
+ int fresh = GNUNET_NO;
+ if (NULL == ps->send_endpoint)
+ {
+ ps->send_endpoint = curl_easy_init();
+ fresh = GNUNET_YES;
+ }
+ GNUNET_assert (ps->send_endpoint != NULL);
+ GNUNET_assert (NULL != ps->pending_msgs_tail);
+#if DEBUG_CONNECTIONS
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound not connected, initiating connection\n",ps);
+#endif
+ ps->send_active = GNUNET_NO;
+ msg = ps->pending_msgs_tail;
+
+#if DEBUG_CURL
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_VERBOSE, 1L);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_DEBUGFUNCTION , &curl_logger);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_DEBUGDATA , ps->send_endpoint);
+#endif
+#if BUILD_HTTPS
+ curl_easy_setopt (ps->send_endpoint, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_SSL_VERIFYHOST, 0);
+#endif
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_URL, ps->url);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_PUT, 1L);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_HEADERFUNCTION, &curl_put_header_cb);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEHEADER, ps);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_TIMEOUT, (long) timeout.rel_value);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_PRIVATE, ps);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_BUFFERSIZE, 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
+#if CURL_TCP_NODELAY
+ curl_easy_setopt(ps->send_endpoint, CURLOPT_TCP_NODELAY, 1);
+#endif
+
+ if (fresh==GNUNET_YES)
+ {
+ mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint);
+ if (mret != CURLM_OK)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Connection: %X: %s failed at %s:%d: `%s'\n"),
+ ps,
+ "curl_multi_add_handle", __FILE__, __LINE__,
+ curl_multi_strerror (mret));
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+ if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel(plugin->http_curl_task);
+ plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ plugin->http_curl_task = GNUNET_SCHEDULER_add_now (&curl_perform, plugin);
+ return GNUNET_YES;
+ }
+ if (ps->direction == INBOUND)
+ {
+ GNUNET_assert (NULL != ps->pending_msgs_tail);
+ if ((ps->recv_connected==GNUNET_YES) && (ps->send_connected==GNUNET_YES) &&
+ (ps->recv_force_disconnect==GNUNET_NO) && (ps->recv_force_disconnect==GNUNET_NO))
+ return GNUNET_YES;
+ }
+ return GNUNET_SYSERR;
+}
+
+/**
+ * select best session to transmit data to peer
+ *
+ * @param pc peer context of target peer
+ * @param addr address of target peer
+ * @param addrlen address length
+ * @param force_address does transport service enforce address?
+ * @param session session passed by transport service
+ * @return selected session
+ *
+ */
+static struct Session * send_select_session (struct HTTP_PeerContext *pc, const void * addr, size_t addrlen, int force_address, struct Session * session)
+{
+ struct Session * tmp = NULL;
+ int addr_given = GNUNET_NO;
+
+ if ((addr!=NULL) && (addrlen>0))
+ addr_given = GNUNET_YES;
+
+ if (force_address == GNUNET_YES)
+ {
+ /* check session given as argument */
+ if ((session != NULL) && (addr_given == GNUNET_YES))
+ {
+ if (0 == memcmp(session->addr, addr, addrlen))
+ {
+ /* connection can not be used, since it is disconnected */
+ if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO))
+ {
+#if DEBUG_SESSION_SELECTION
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send to forced address \n", session);
+#endif
+ return session;
+ }
+ }
+ }
+ /* check last session used */
+ if ((pc->last_session != NULL)&& (addr_given == GNUNET_YES))
+ {
+ if (0 == memcmp(pc->last_session->addr, addr, addrlen))
+ {
+ /* connection can not be used, since it is disconnected */
+ if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO))
+ {
+#if DEBUG_SESSION_SELECTION
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session used to send to forced address \n", pc->last_session);
+#endif
+ return pc->last_session;
+ }
+ }
+ }
+ /* find session in existing sessions */
+ tmp = pc->head;
+ while ((tmp!=NULL) && (addr_given == GNUNET_YES))
+ {
+
+ if (0 == memcmp(tmp->addr, addr, addrlen))
+ {
+ /* connection can not be used, since it is disconnected */
+ if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO))
+ {
+#if DEBUG_SESSION_SELECTION
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to forced address \n", session);
+#endif
+ return session;
+ }
+
+ }
+ tmp=tmp->next;
+ }
+ /* no session to use */
+ return NULL;
+ }
+ if ((force_address == GNUNET_NO) || (force_address == GNUNET_SYSERR))
+ {
+ /* check session given as argument */
+ if (session != NULL)
+ {
+ /* connection can not be used, since it is disconnected */
+ if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO))
+ {
+#if DEBUG_SESSION_SELECTION
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send not-forced address \n", session);
+#endif
+ return session;
+ }
+
+ }
+ /* check last session used */
+ if (pc->last_session != NULL)
+ {
+ /* connection can not be used, since it is disconnected */
+ if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO))
+ {
+#if DEBUG_SESSION_SELECTION
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session to send to not-forced address \n", pc->last_session);
+#endif
+ return pc->last_session;
+ }
+ }
+ /* find session in existing sessions */
+ tmp = pc->head;
+ while (tmp!=NULL)
+ {
+ /* connection can not be used, since it is disconnected */
+ if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO))
+ {
+#if DEBUG_SESSION_SELECTION
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to not-forced address \n", tmp);
+#endif
+ return tmp;
+ }
+ tmp=tmp->next;
+ }
+ return NULL;
+ }
+ return NULL;
+}
+
+/**
+ * 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 target who should receive this message
+ * @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 session which session must be used (or NULL for "any")
+ * @param addr the address to use (can be NULL if the plugin
+ * is "on its own" (i.e. re-use existing TCP connection))
+ * @param addrlen length of the address in bytes
+ * @param force_address GNUNET_YES if the plugin MUST use the given address,
+ * GNUNET_NO means the plugin may use any other address and
+ * GNUNET_SYSERR means that only reliable existing
+ * bi-directional connections should be used (regardless
+ * of address)
+ * @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_plugin_send (void *cls,