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); }