static void _client_react(struct gridd_client_s *client) { EXTRA_ASSERT(client != NULL); EXTRA_ASSERT(client->abstract.vtable == &VTABLE_CLIENT); if (!client) return; GError *err = NULL; client->tv_step = oio_ext_monotonic_time (); retry: if (!(err = _client_manage_event(client))) { if (client->step == REP_READING_SIZE && client->reply && client->reply->len >= 4) goto retry; } else { _client_reset_request(client); _client_reset_reply(client); _client_reset_cnx(client); client->error = err; client->step = STATUS_FAILED; } }
static void _client_free(struct gridd_client_s *client) { EXTRA_ASSERT(client != NULL); EXTRA_ASSERT(client->abstract.vtable == &VTABLE_CLIENT); _client_reset_reply(client); _client_reset_request(client); _client_reset_cnx(client); _client_reset_target(client); _client_reset_error(client); if (client->reply) g_byte_array_free(client->reply, TRUE); client->fd = -1; SLICE_FREE (struct gridd_client_s, client); }
static GError* _client_request(struct gridd_client_s *client, GByteArray *req, gpointer ctx, client_on_reply cb) { EXTRA_ASSERT(client != NULL); EXTRA_ASSERT(client->abstract.vtable == &VTABLE_CLIENT); if ( NULL == req) return NEWERROR(CODE_INTERNAL_ERROR, "Invalid parameter"); switch (client->step) { case NONE: case CONNECTING: case CONNECTED: if (client->request != NULL) return NEWERROR(500, "Request already pending"); /* ok */ break; case REQ_SENDING: case REP_READING_SIZE: case REP_READING_DATA: return NEWERROR(500, "Request not terminated"); case STATUS_OK: case STATUS_FAILED: /* ok */ if (client->fd >= 0) client->step = REQ_SENDING; else client->step = CONNECTING; break; } /* if any, reset the last reply */ _client_reset_reply(client); _client_reset_request(client); _client_reset_error(client); /* Now set the new request components */ client->ctx = ctx; client->on_reply = cb; client->request = g_byte_array_ref(req); return NULL; }
static GError* _client_set_fd(struct gridd_client_s *client, int fd) { EXTRA_ASSERT(client != NULL); EXTRA_ASSERT(client->abstract.vtable == &VTABLE_CLIENT); if (fd >= 0) { switch (client->step) { case NONE: /* ok */ break; case CONNECTING: if (client->request != NULL) return NEWERROR(500, "Request pending"); /* PASSTHROUGH */ case CONNECTED: /* ok */ break; case REQ_SENDING: case REP_READING_SIZE: case REP_READING_DATA: return NEWERROR(500, "Request pending"); case STATUS_OK: case STATUS_FAILED: /* ok */ break; } } /* reset any connection and request */ _client_reset_reply(client); _client_reset_request(client); _client_reset_target(client); /* XXX do not call _client_reset_cnx(), or close the connexion. * It is the responsibility of the caller to manage this, because it * explicitely breaks the pending socket management. */ client->fd = fd; /* CONNECTING instead of CONNECTED helps coping with not yet * completely connected sockets */ client->step = (client->fd >= 0) ? CONNECTING : NONE; return NULL; }
static void _client_free(struct gridd_client_s *client) { EXTRA_ASSERT(client != NULL); EXTRA_ASSERT(client->abstract.vtable == &VTABLE_CLIENT); _client_reset_reply(client); _client_reset_request(client); _client_reset_cnx(client); _client_reset_target(client); _client_reset_error(client); if (client->reply) g_byte_array_free(client->reply, TRUE); if (client->past_url) g_string_free(client->past_url, TRUE); memset(client, 0, sizeof(*client)); client->fd = -1; g_free(client); }
static GError* _client_connect_addr(struct gridd_client_s *client, const struct addr_info_s *ai) { EXTRA_ASSERT(client != NULL); EXTRA_ASSERT(client->abstract.vtable == &VTABLE_CLIENT); if (NULL == ai || !ai->port) return NEWERROR(CODE_INTERNAL_ERROR, "Invalid parameter"); g_assert(client != NULL); _client_reset_cnx(client); _client_reset_target(client); _client_reset_reply(client); grid_addrinfo_to_string(ai, client->orig_url, URL_MAXLEN); memcpy(client->url, client->orig_url, URL_MAXLEN); client->step = NONE; return NULL; }
static GError* _client_connect_url(struct gridd_client_s *client, const gchar *url) { EXTRA_ASSERT(client != NULL); EXTRA_ASSERT(client->abstract.vtable == &VTABLE_CLIENT); if (NULL == url || !url[0]) return NEWERROR(CODE_INTERNAL_ERROR, "Bad address"); if (!metautils_url_valid_for_connect(url)) return NEWERROR(400, "Bad address [%s]", url); g_assert(client != NULL); _client_reset_cnx(client); _client_reset_target(client); _client_reset_reply(client); g_strlcpy(client->orig_url, url, URL_MAXLEN); memcpy(client->url, client->orig_url, URL_MAXLEN); client->step = NONE; return NULL; }
static GError * _client_manage_event_in_buffer(struct gridd_client_s *client, guint8 *d, gsize ds) { ssize_t rc; switch (client->step) { case CONNECTING: g_assert(client->fd >= 0); client->step = client->request ? REQ_SENDING : CONNECTED; return NULL; case REQ_SENDING: client->step = REQ_SENDING; g_get_current_time(&(client->tv_step)); if (!client->request) return NULL; _client_reset_reply(client); /* Continue to send the request */ rc = metautils_syscall_write(client->fd, client->request->data + client->sent_bytes, client->request->len - client->sent_bytes); if (rc < 0) return (errno == EINTR || errno == EAGAIN) ? NULL : NEWERROR(errno, "write error (%s)", strerror(errno)); if (rc > 0) client->sent_bytes += rc; if (client->sent_bytes < client->request->len) return NULL; client->step = REP_READING_SIZE; case REP_READING_SIZE: client->step = REP_READING_SIZE; g_get_current_time(&(client->tv_step)); if (!client->reply) client->reply = g_byte_array_new(); if (client->reply->len < 4) { /* Continue reading the size */ rc = metautils_syscall_read(client->fd, d, ds); if (rc < 0) return (errno == EINTR || errno == EAGAIN) ? NULL : NEWERROR(errno, "read error (%s)", strerror(errno)); if (rc > 0) g_byte_array_append(client->reply, d, rc); if (client->reply->len < 4) { if (!rc) return NEWERROR(errno, "EOF!"); return NULL; } } client->size = l4v_get_size(client->reply->data); case REP_READING_DATA: client->step = REP_READING_DATA; g_get_current_time(&(client->tv_step)); rc = 0; if (client->reply->len < client->size+4) { rc = metautils_syscall_read(client->fd, d, ds); if (rc < 0) return (errno == EINTR || errno == EAGAIN) ? NULL : NEWERROR(errno, "read error (%s)", strerror(errno)); if (rc > 0) g_byte_array_append(client->reply, d, rc); } if (client->reply->len >= client->size+4) { GError *err = _client_manage_reply_data(client); if (err) { client->step = STATUS_FAILED; return err; } else { if (client->step != CONNECTING && client->step != STATUS_FAILED && client->step != STATUS_OK) { client->reply = g_byte_array_remove_range(client->reply, 0, client->size+4); client->step = REP_READING_SIZE; client->size = 0; } } } else if (!rc) return NEWERROR(errno, "EOF!"); return NULL; default: g_assert_not_reached(); return NEWERROR(0, "Invalid state"); } g_assert_not_reached(); return NEWERROR(0, "BUG unreachable code"); }
static GError * _client_manage_reply(struct gridd_client_s *client, MESSAGE reply) { GError *err; gint status = 0; gchar *message = NULL; if (!metaXClient_reply_simple(reply, &status, &message, NULL)) return NEWERROR(500, "Invalid reply"); switch (status / 100) { case 0: err = NEWERROR(status, "net error: %s", message); g_free(message); metautils_pclose(&(client->fd)); client->step = STATUS_FAILED; return err; case 1: /* Informational reply : */ g_get_current_time(&(client->tv_step)); client->step = REP_READING_SIZE; g_free(message); return NULL; case 2: g_get_current_time(&(client->tv_step)); client->step = (status==200) ? STATUS_OK : REP_READING_SIZE; if (client->step == STATUS_OK) { if (!client->keepalive) metautils_pclose(&(client->fd)); } g_free(message); if (client->on_reply) { if (!client->on_reply(client->ctx, reply)) return NEWERROR(500, "Handler error"); } return NULL; case 3: /* redirection */ if (status == CODE_REDIRECT) { /* Reset the context */ _client_reset_reply(client); _client_reset_cnx(client); client->sent_bytes = 0; if ((++ client->nb_redirects) > 7) { g_free(message); return NEWERROR(CODE_TOOMANY_REDIRECT, "Too many redirections"); } /* Save the current URL to avoid looping, and check * for a potential loop */ g_string_append_c(client->past_url, '|'); g_string_append(client->past_url, client->url); if (_client_looped(client, message)) { g_free(message); return NEWERROR(CODE_LOOP_REDIRECT, "Looping on redirections"); } /* Replace the URL */ memset(client->url, 0, sizeof(client->url)); g_strlcpy(client->url, message, sizeof(client->url)-1); if (NULL != (err = _client_connect(client))) { g_free(message); g_prefix_error(&err, "Redirection error: Connect error: "); return err; } g_free(message); return NULL; } /* FALLTHROUGH */ default: /* all other are considered errors */ err = NEWERROR(status, "Request error: %s", message); g_free(message); if (!client->keepalive) _client_reset_cnx(client); _client_reset_reply(client); return err; } }
static GError * _client_manage_reply(struct gridd_client_s *client, MESSAGE reply) { GError *err; guint status = 0; gchar *message = NULL; if (NULL != (err = metaXClient_reply_simple(reply, &status, &message))) { g_prefix_error (&err, "reply: "); return err; } if (CODE_IS_NETWORK_ERROR(status)) { err = NEWERROR(status, "net error: %s", message); g_free(message); metautils_pclose(&(client->fd)); client->step = STATUS_FAILED; return err; } if (status == CODE_TEMPORARY) { g_get_current_time(&(client->tv_step)); client->step = REP_READING_SIZE; g_free(message); return NULL; } if (CODE_IS_OK(status)) { g_get_current_time(&(client->tv_step)); client->step = (status==CODE_FINAL_OK) ? STATUS_OK : REP_READING_SIZE; if (client->step == STATUS_OK) { if (!client->keepalive) metautils_pclose(&(client->fd)); } g_free(message); if (client->on_reply) { if (!client->on_reply(client->ctx, reply)) return NEWERROR(CODE_INTERNAL_ERROR, "Handler error"); } return NULL; } if (status == CODE_REDIRECT) { /* Reset the context */ _client_reset_reply(client); _client_reset_cnx(client); client->sent_bytes = 0; if ((++ client->nb_redirects) > 7) { g_free(message); return NEWERROR(CODE_TOOMANY_REDIRECT, "Too many redirections"); } /* Save the current URL to avoid looping, and check * for a potential loop */ g_string_append_c(client->past_url, '|'); g_string_append(client->past_url, client->url); if (_client_looped(client, message)) { g_free(message); return NEWERROR(CODE_LOOP_REDIRECT, "Looping on redirections"); } /* Replace the URL */ memset(client->url, 0, sizeof(client->url)); g_strlcpy(client->url, message, sizeof(client->url)-1); if (NULL != (err = _client_connect(client))) { g_free(message); g_prefix_error(&err, "Redirection error: Connect error: "); return err; } g_free(message); return NULL; } /* all other are considered errors */ err = NEWERROR(status, "Request error: %s", message); g_free(message); if (!client->keepalive) _client_reset_cnx(client); _client_reset_reply(client); return err; }
static GError * _client_manage_event_in_buffer(struct gridd_client_s *client, guint8 *d, gsize ds) { guint32 s32; ssize_t rc; switch (client->step) { case CONNECTING: EXTRA_ASSERT(client->fd >= 0); EXTRA_ASSERT(client->request != NULL); client->step = REQ_SENDING; return NULL; case REQ_SENDING: client->step = REQ_SENDING; if (!client->request) return NULL; _client_reset_reply(client); /* Continue to send the request */ rc = metautils_syscall_write(client->fd, client->request->data + client->sent_bytes, client->request->len - client->sent_bytes); if (rc < 0) return (errno == EINTR || errno == EAGAIN) ? NULL : NEWERROR(errno, "write error (%s)", strerror(errno)); if (rc > 0) client->sent_bytes += rc; if (client->sent_bytes < client->request->len) return NULL; client->step = REP_READING_SIZE; case REP_READING_SIZE: client->step = REP_READING_SIZE; if (!client->reply) client->reply = g_byte_array_new(); if (client->reply->len < 4) { /* Continue reading the size */ rc = metautils_syscall_read(client->fd, d, (4 - client->reply->len)); if (rc < 0) return (errno == EINTR || errno == EAGAIN) ? NULL : NEWERROR(errno, "read error (%s)", strerror(errno)); if (rc > 0) g_byte_array_append(client->reply, d, rc); if (client->reply->len < 4) { if (!rc) return NEWERROR(errno, "EOF!"); return NULL; } } EXTRA_ASSERT (client->reply->len == 4); s32 = *((guint32*)(client->reply->data)); client->size = g_ntohl(s32); case REP_READING_DATA: client->step = REP_READING_DATA; rc = 0; EXTRA_ASSERT (client->reply->len <= client->size + 4); if (client->reply->len < client->size + 4) { gsize remaiming = client->size + 4 - client->reply->len; gsize dmax = ds; if (dmax > remaiming) dmax = remaiming; rc = metautils_syscall_read(client->fd, d, dmax); if (rc < 0) return (errno == EINTR || errno == EAGAIN) ? NULL : NEWERROR(errno, "read error (%s)", strerror(errno)); if (rc > 0) g_byte_array_append(client->reply, d, rc); } EXTRA_ASSERT (client->reply->len <= client->size + 4); if (client->reply->len == client->size + 4) { GError *err = _client_manage_reply_data(client); if (err) { client->step = STATUS_FAILED; return err; } else { if (client->step != CONNECTING && client->step != STATUS_FAILED && client->step != STATUS_OK) { client->reply = g_byte_array_set_size(client->reply, 0); client->step = REP_READING_SIZE; client->size = 0; } } } else if (!rc) return NEWERROR(errno, "EOF!"); return NULL; default: g_assert_not_reached(); return NEWERROR(0, "Invalid state"); } g_assert_not_reached(); return NEWERROR(0, "BUG unreachable code"); }
static GError * _client_manage_reply(struct gridd_client_s *client, MESSAGE reply) { GError *err; guint status = 0; gchar *message = NULL; if (NULL != (err = metaXClient_reply_simple(reply, &status, &message))) { g_prefix_error (&err, "reply: "); return err; } STRING_STACKIFY(message); if (CODE_IS_NETWORK_ERROR(status)) { err = NEWERROR(status, "net error: %s", message); metautils_pclose(&(client->fd)); client->step = STATUS_FAILED; return err; } if (status == CODE_TEMPORARY) { client->step = REP_READING_SIZE; return NULL; } if (CODE_IS_OK(status)) { client->step = (status==CODE_FINAL_OK) ? STATUS_OK : REP_READING_SIZE; if (client->step == STATUS_OK) { if (!client->keepalive) metautils_pclose(&(client->fd)); } if (client->on_reply) { if (!client->on_reply(client->ctx, reply)) return NEWERROR(CODE_INTERNAL_ERROR, "Handler error"); } return NULL; } if (status == CODE_REDIRECT && !client->forbid_redirect) { /* Reset the context */ _client_reset_reply(client); _client_reset_cnx(client); client->sent_bytes = 0; if ((++ client->nb_redirects) > 3) return NEWERROR(CODE_TOOMANY_REDIRECT, "Too many redirections"); /* Replace the URL */ g_strlcpy(client->url, message, URL_MAXLEN); if (NULL != (err = _client_connect(client))) g_prefix_error(&err, "Redirection error: Connect error: "); return err; } /* all other are considered errors */ if (status != CODE_REDIRECT) err = NEWERROR(status, "Request error: %s", message); else err = NEWERROR(status, "%s", message); if (!client->keepalive) _client_reset_cnx(client); _client_reset_reply(client); return err; }