Beispiel #1
0
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;
	}
}
Beispiel #2
0
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);
}
Beispiel #3
0
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;
}
Beispiel #4
0
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;
}
Beispiel #5
0
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);
}
Beispiel #6
0
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;
}
Beispiel #7
0
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;
}
Beispiel #8
0
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");
}
Beispiel #9
0
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;
	}
}
Beispiel #10
0
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;
}
Beispiel #11
0
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");
}
Beispiel #12
0
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;
}