/** * Ask the client to call us once the specified number of bytes * are free in the transmission buffer. May call the notify * method immediately if enough space is available. * * @param client connection to the service * @param size number of bytes to send * @param timeout after how long should we give up (and call * notify with buf NULL and size 0)? * @param auto_retry if the connection to the service dies, should we * automatically re-connect and retry (within the timeout period) * or should we immediately fail in this case? Pass GNUNET_YES * if the caller does not care about temporary connection errors, * for example because the protocol is stateless * @param notify function to call * @param notify_cls closure for notify * @return NULL if our buffer will never hold size bytes, * a handle if the notify callback was queued (can be used to cancel) */ struct GNUNET_CLIENT_TransmitHandle * GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *client, size_t size, struct GNUNET_TIME_Relative timeout, int auto_retry, GNUNET_CONNECTION_TransmitReadyNotify notify, void *notify_cls) { struct GNUNET_CLIENT_TransmitHandle *th; if (NULL != client->th) { /* If this breaks, you most likley called this function twice without waiting * for completion or canceling the request */ GNUNET_break (0); return NULL; } th = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_TransmitHandle)); th->client = client; th->size = size; th->timeout = GNUNET_TIME_relative_to_absolute (timeout); /* always auto-retry on first message to service */ th->auto_retry = (GNUNET_YES == client->first_message) ? GNUNET_YES : auto_retry; client->first_message = GNUNET_NO; th->notify = notify; th->notify_cls = notify_cls; th->attempts_left = MAX_ATTEMPTS; client->th = th; if (NULL == client->connection) { th->reconnect_task = GNUNET_SCHEDULER_add_delayed (client->back_off, &client_delayed_retry, th); } else { th->th = GNUNET_CONNECTION_notify_transmit_ready (client->connection, size, timeout, &client_notify, th); if (NULL == th->th) { GNUNET_break (0); GNUNET_free (th); client->th = NULL; return NULL; } } return th; }
static void task_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { ls = open_listen_socket (); lsock = GNUNET_CONNECTION_create_from_existing (ls); GNUNET_assert (lsock != NULL); csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT); GNUNET_assert (csock != NULL); GNUNET_assert (NULL != GNUNET_CONNECTION_notify_transmit_ready (csock, 1024, GNUNET_TIME_UNIT_SECONDS, &send_kilo, cls)); }
static void task (void *cls) { ls = open_listen_socket (); lsock = GNUNET_CONNECTION_create_from_existing (ls); GNUNET_assert (lsock != NULL); csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT); GNUNET_assert (csock != NULL); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test asks for write notification\n"); GNUNET_assert (NULL != GNUNET_CONNECTION_notify_transmit_ready (csock, 12, GNUNET_TIME_UNIT_SECONDS, &make_hello, NULL)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test prepares to accept\n"); GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, ls, &run_accept, cls); }
/** * Register SOCKS5 handshake sender * * @param ih handshake * @return non-NULL if the notify callback was queued, * NULL if we are already going to notify someone else (busy) */ struct GNUNET_CONNECTION_TransmitHandle * register_sender (struct GNUNET_SOCKS_Handshake *ih) { struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_MINUTES; GNUNET_assert (SOCKS5_step_done > ih->step); GNUNET_assert (ih->step >= 0); if (0 == ih->step) timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 3); unsigned char * b = ih->outstep[ih->step]; unsigned char * e = ih->outstep[ih->step+1]; GNUNET_assert (ih->outbuf <= b && b < e && e < &ih->outbuf[1024]); ih->th = GNUNET_CONNECTION_notify_transmit_ready (ih->socks5_connection, e - b, timeout, &transmit_ready, ih); return ih->th; }
/** * This task is run if we should re-try connection to the * service after a while. * * @param cls our "struct GNUNET_CLIENT_TransmitHandle" of the request * @param tc unused */ static void client_delayed_retry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_CLIENT_TransmitHandle *th = cls; struct GNUNET_TIME_Relative delay; th->reconnect_task = GNUNET_SCHEDULER_NO_TASK; th->client->connection = do_connect (th->client->service_name, th->client->cfg, th->client->attempts++); th->client->first_message = GNUNET_YES; if (NULL == th->client->connection) { /* could happen if we're out of sockets */ delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (th->timeout), th->client->back_off); th->client->back_off = GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply (th->client->back_off, 2), GNUNET_TIME_UNIT_SECONDS); LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmission failed %u times, trying again in %s.\n", MAX_ATTEMPTS - th->attempts_left, GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES)); th->reconnect_task = GNUNET_SCHEDULER_add_delayed (delay, &client_delayed_retry, th); return; } th->th = GNUNET_CONNECTION_notify_transmit_ready (th->client->connection, th->size, GNUNET_TIME_absolute_get_remaining (th->timeout), &client_notify, th); if (NULL == th->th) { GNUNET_break (0); th->client->th = NULL; th->notify (th->notify_cls, 0, NULL); GNUNET_free (th); return; } }
static size_t send_kilo (void *cls, size_t size, void *buf) { int *ok = cls; if (size == 0) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got the desired timeout!\n"); GNUNET_assert (buf == NULL); *ok = 0; GNUNET_CONNECTION_destroy (lsock); GNUNET_CONNECTION_destroy (csock); return 0; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending kilo to fill buffer.\n"); GNUNET_assert (size >= 1024); memset (buf, 42, 1024); GNUNET_assert (NULL != GNUNET_CONNECTION_notify_transmit_ready (csock, 1024, GNUNET_TIME_UNIT_SECONDS, &send_kilo, cls)); return 1024; }