コード例 #1
0
ファイル: subreq.c プロジェクト: dimarik/ugh
static
void ugh_subreq_wcb_connect(EV_P_ ev_io *w, int tev)
{
	ugh_subreq_t *r = aux_memberof(ugh_subreq_t, wev_connect, w);

	if (EV_READ & tev)
	{
		int optval = 0;
		socklen_t optlen = sizeof(optval);

		if (0 > getsockopt(w->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen))
		{
			optval = errno;
		}

		ugh_subreq_del(r, UGH_UPSTREAM_FT_ERROR, optval);
		return;
	}

	ev_io_stop(loop, &r->wev_connect);
	ev_timer_stop(loop, &r->wev_timeout_connect);

	r->connection_time = ev_now(loop) - r->response_time;

	ev_io_start(loop, &r->wev_send);
}
コード例 #2
0
ファイル: subreq.c プロジェクト: deepinit-arek/ugh
static
void ugh_subreq_wcb_send(EV_P_ ev_io *w, int tev)
{
	int rc;

	ugh_subreq_t *r = aux_memberof(ugh_subreq_t, wev_send, w);

	/* errno = 0; */

	rc = aux_unix_send(w->fd, r->b_send.data + r->b_send.rpos, r->b_send.wpos - r->b_send.rpos);
	log_debug("subreq send: %d: %.*s", rc, (int) (r->b_send.wpos - r->b_send.rpos), r->b_send.data + r->b_send.rpos);

	if (0 > rc)
	{
		log_warn("send error %.*s%s%.*s (%d: %s)", (int) r->u.uri.size, r->u.uri.data, r->u.args.size ? "?" : "", (int) r->u.args.size, r->u.args.data, errno, aux_strerror(errno));
		ugh_subreq_del(r, UGH_UPSTREAM_FT_ERROR);
		return;
	}

	/* log_debug("send %d bytes", rc); */

	if (0 == rc)
	{
		return;
	}

	if (UGH_TIMEOUT_ONCE == r->timeout_type)
	{
		ev_timer_again(loop, &r->wev_timeout);
	}

	r->b_send.rpos += rc;

	if (r->b_send.rpos == r->b_send.wpos)
	{
		ev_io_stop(loop, &r->wev_send);
		ev_io_start(loop, &r->wev_recv);
		return;
	}
}
コード例 #3
0
ファイル: subreq.c プロジェクト: deepinit-arek/ugh
static
void ugh_subreq_wcb_connect(EV_P_ ev_io *w, int tev)
{
	ugh_subreq_t *r = aux_memberof(ugh_subreq_t, wev_connect, w);

	if (EV_READ & tev)
	{
		int optval = 0;
		socklen_t optlen = sizeof(optval);

		if (0 > getsockopt(w->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen))
		{
			optval = errno;
		}

		log_warn("conn error %.*s%s%.*s (%d: %s)", (int) r->u.uri.size, r->u.uri.data, r->u.args.size ? "?" : "", (int) r->u.args.size, r->u.args.data, optval, aux_strerror(optval));
		ugh_subreq_del(r, UGH_UPSTREAM_FT_ERROR);
		return;
	}

	ev_io_stop(loop, &r->wev_connect);
	ev_io_start(loop, &r->wev_send);
}
コード例 #4
0
ファイル: subreq.c プロジェクト: dimarik/ugh
static
void ugh_subreq_wcb_timeout_connect(EV_P_ ev_timer *w, int tev)
{
	ugh_subreq_t *r = aux_memberof(ugh_subreq_t, wev_timeout_connect, w);
	ugh_subreq_del(r, UGH_UPSTREAM_FT_TIMEOUT_CONNECT, 0);
}
コード例 #5
0
ファイル: subreq.c プロジェクト: dimarik/ugh
static
int ugh_subreq_connect(void *data, in_addr_t addr)
{
	ugh_subreq_t *r = (ugh_subreq_t *) data;

	/* XXX this is needed temporarily, so if we del this subrequest due to
	 * resov error, we will not close any valid file descriptor inside
	 * ugh_subreq_del routine
	 */
	r->wev_recv.fd = -1;

	/* start calculating response_time from the first try */
	if (r->response_time == 0)
	{
		r->response_time = ev_now(loop);
	}

	/* reset connection_time */
	r->connection_time = 0;

	if (INADDR_NONE == addr)
	{
		ugh_subreq_del(r, UGH_UPSTREAM_FT_ERROR, ENXIO);
		return -1;
	}

	r->addr.sin_addr.s_addr = addr;

	log_debug("ugh_subreq_connect(%s:%u)", inet_ntoa(r->addr.sin_addr), ntohs(r->addr.sin_port));

	int sd, rc;

	if (0 > (sd = socket(AF_INET, SOCK_STREAM, 0)))
	{
		ugh_subreq_del(r, UGH_UPSTREAM_FT_ERROR, errno);
		return -1;
	}

	if (0 > (rc = aux_set_nonblk(sd, 1)))
	{
		close(sd);
		ugh_subreq_del(r, UGH_UPSTREAM_FT_ERROR, errno);
		return -1;
	}

	rc = connect(sd, (struct sockaddr *) &r->addr, sizeof(r->addr));

	if (0 > rc && EINPROGRESS != errno)
	{
		close(sd);
		ugh_subreq_del(r, UGH_UPSTREAM_FT_ERROR, errno);
		return -1;
	}

	/* errno = 0; */

	ev_io_init(&r->wev_recv, ugh_subreq_wcb_recv, sd, EV_READ);
	ev_io_init(&r->wev_send, ugh_subreq_wcb_send, sd, EV_WRITE);
	ev_io_init(&r->wev_connect, ugh_subreq_wcb_connect, sd, EV_READ | EV_WRITE);

	if (UGH_TIMEOUT_FULL == r->timeout_type)
	{
		ev_tstamp new_timeout = r->timeout - (ev_now(loop) - r->response_time);

		if (new_timeout != r->timeout)
		{
			/* XXX this is just temporarily on info level */
			log_info("updating timeout from %f to %f (%.*s:%.*s%.*s%s%.*s, addr=%s:%u)"
				, r->timeout
				, new_timeout
				, (int) r->u.host.size, r->u.host.data
				, (int) r->u.port.size, r->u.port.data
				, (int) r->u.uri.size, r->u.uri.data
				, r->u.args.size ? "?" : ""
				, (int) r->u.args.size, r->u.args.data
				, inet_ntoa(r->addr.sin_addr)
				, ntohs(r->addr.sin_port)
			);
		}

		if (new_timeout < 0)
		{
			ugh_subreq_del(r, UGH_UPSTREAM_FT_TIMEOUT, 0);
			return -1;
		}

		ev_timer_init(&r->wev_timeout, ugh_subreq_wcb_timeout, 0, new_timeout);
	}
	else
	{
		ev_timer_init(&r->wev_timeout, ugh_subreq_wcb_timeout, 0, r->timeout);
	}

	ev_timer_init(&r->wev_timeout_connect, ugh_subreq_wcb_timeout_connect, 0, r->timeout_connect);

	ev_timer_again(loop, &r->wev_timeout);
	ev_timer_again(loop, &r->wev_timeout_connect);

	ev_io_start(loop, &r->wev_connect);

	return 0;
}
コード例 #6
0
ファイル: subreq.c プロジェクト: dimarik/ugh
static
void ugh_subreq_wcb_recv(EV_P_ ev_io *w, int tev)
{
	ugh_subreq_t *r = aux_memberof(ugh_subreq_t, wev_recv, w);

	/* errno = 0; */

	int nb = aux_unix_recv(w->fd, r->buf_recv.data, r->buf_recv.size);
	log_debug("subreq recv: %d: %.*s", nb, nb, r->buf_recv.data);

	if (0 == nb)
	{
		if (r->content_length != UGH_RESPONSE_CLOSE_AFTER_BODY)
		{
			/*
			 * NOTE: recv(2) will never fail with EPIPE, so I'm using it here
			 * to get meaningful error message in ugh_subreq_del'
			 */
			log_warn("upstream prematurely closed connection");
			ugh_subreq_del(r, UGH_UPSTREAM_FT_ERROR, EPIPE);
		}
		else
		{
			ugh_subreq_del(r, UGH_UPSTREAM_FT_OFF, 0);
		}

		return;
	}

	if (0 > nb)
	{
		if (EAGAIN == errno)
		{
			if (UGH_TIMEOUT_ONCE == r->timeout_type)
			{
				ev_timer_again(loop, &r->wev_timeout);
			}

			return;
		}

		ugh_subreq_del(r, UGH_UPSTREAM_FT_ERROR, errno);
		return;
	}

	r->buf_recv.data += nb;
	r->buf_recv.size -= nb;

	if (UGH_TIMEOUT_ONCE == r->timeout_type)
	{
		ev_timer_again(loop, &r->wev_timeout);
	}

	if (NULL == r->body.data)
	{
		int status = ugh_parser_subreq(r, r->buf_recv.data - nb, nb);

		if (UGH_AGAIN == status)
		{
			return;
		}

		if (UGH_ERROR == status)
		{
			ugh_subreq_del(r, UGH_UPSTREAM_FT_INVALID_HEADER, 0);
			return;
		}

		ugh_header_t *hdr_content_length = ugh_subreq_header_get_nt(r, "Content-Length");

		if (0 != hdr_content_length->value.size)
		{
			r->content_length = atoi(hdr_content_length->value.data);

			if (r->content_length > r->buf_recv.size + (r->buf_recv.data - r->request_end))
			{
				r->body.data = aux_pool_nalloc(r->c->pool, r->content_length);
				r->body.size = r->buf_recv.data - r->request_end;

				memcpy(r->body.data, r->request_end, r->body.size);

				r->buf_recv.data = r->body.data + r->body.size;
				r->buf_recv.size = r->content_length - r->body.size;
			}
			else
			{
				r->body.data = r->request_end;
				r->body.size = r->buf_recv.data - r->request_end;
			}
		}
		else
		{
			ugh_header_t *hdr_transfer_encoding = ugh_subreq_header_get_nt(r, "Transfer-Encoding");

			if (7 == hdr_transfer_encoding->value.size && 0 == strncmp(hdr_transfer_encoding->value.data, "chunked", 7))
			{
				r->content_length = UGH_RESPONSE_CHUNKED;

				r->chunk_body_size = UGH_CHUNKS_BUF;
				r->body.data = aux_pool_nalloc(r->c->pool, r->chunk_body_size);
				r->body.size = 0;

				char *next_chunk = r->request_end;

				for (;;)
				{
					status = ugh_parser_chunks(r, next_chunk, r->buf_recv.data - next_chunk);

					if (UGH_AGAIN == status)
					{
						r->buf_recv.size += r->buf_recv.data - r->request_end;
						r->buf_recv.data = r->request_end;
						r->chunk_start = 0;
						return;
					}

					if (UGH_ERROR == status)
					{
						ugh_subreq_del(r, UGH_UPSTREAM_FT_INVALID_HEADER, 0);
						return;
					}

					if (0 == r->chunk_size)
					{
						ugh_subreq_del(r, UGH_UPSTREAM_FT_OFF, 0);
						return;
					}

					size_t recv_len = r->buf_recv.data - r->chunk_start;

					if (r->chunk_size > recv_len)
					{
						ugh_subreq_copy_chunk(r, r->chunk_start, recv_len);

						r->chunk_size -= recv_len;

						r->buf_recv.size += r->buf_recv.data - r->request_end;
						r->buf_recv.data = r->request_end;

						r->chunk_start = r->buf_recv.data;

						break;
					}
					else
					{
						ugh_subreq_copy_chunk(r, r->chunk_start, r->chunk_size);

						next_chunk = r->chunk_start + r->chunk_size;

						r->chunk_size = 0;
						r->chunk_start = 0;
					}
				}
			}
			else /* http/1.0 close after body response */
			{
				r->content_length = UGH_RESPONSE_CLOSE_AFTER_BODY;

				r->body.data = r->request_end;
				r->body.size = r->buf_recv.data - r->request_end;

				if (r->buf_recv.size == 0)
				{
					char *old_body = r->body.data;

					r->body.data = aux_pool_nalloc(r->c->pool, r->body.size * 2);

					memcpy(r->body.data, old_body, r->body.size);

					r->buf_recv.data = r->body.data + r->body.size;
					r->buf_recv.size = r->body.size;
				}
			}
		}
	}
	else if (r->content_length == UGH_RESPONSE_CHUNKED)
	{
		for (;;)
		{
			char *next_chunk = r->buf_recv.data - nb;

			if (r->chunk_start)
			{
				size_t recv_len = r->buf_recv.data - r->chunk_start;

				if (r->chunk_size > recv_len)
				{
					ugh_subreq_copy_chunk(r, r->chunk_start, recv_len);

					r->chunk_size -= recv_len;

					r->buf_recv.data -= nb;
					r->buf_recv.size += nb;

					r->chunk_start = r->buf_recv.data;

					break;
				}
				else
				{
					ugh_subreq_copy_chunk(r, r->chunk_start, r->chunk_size);

					next_chunk = r->chunk_start + r->chunk_size;

					r->chunk_size = 0;
				}
			}

			int status = ugh_parser_chunks(r, next_chunk, r->buf_recv.data - next_chunk);

			if (UGH_AGAIN == status)
			{
				r->buf_recv.data -= nb;
				r->buf_recv.size += nb;
				r->chunk_start = 0;
				return;
			}

			if (UGH_ERROR == status)
			{
				ugh_subreq_del(r, UGH_UPSTREAM_FT_INVALID_HEADER, 0);
				return;
			}

			if (0 == r->chunk_size)
			{
				ugh_subreq_del(r, UGH_UPSTREAM_FT_OFF, 0);
			}
		}
	}
	else if (r->content_length == UGH_RESPONSE_CLOSE_AFTER_BODY)
	{
		r->body.size += nb;

		if (r->buf_recv.size == 0)
		{
			char *old_body = r->body.data;

			r->body.data = aux_pool_nalloc(r->c->pool, r->body.size * 2);

			memcpy(r->body.data, old_body, r->body.size);

			r->buf_recv.data = r->body.data + r->body.size;
			r->buf_recv.size = r->body.size;
		}
	}
	else
	{
		r->body.size += nb;
	}

	if (r->body.size == r->content_length)
	{
		uint32_t ft_type = UGH_UPSTREAM_FT_OFF;

		switch (r->status)
		{
		case 400: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 401: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 402: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 403: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 404: ft_type = UGH_UPSTREAM_FT_HTTP_404; break;
		case 405: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 406: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 407: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 408: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 409: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 410: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 411: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 412: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 413: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 414: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 415: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 416: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 417: ft_type = UGH_UPSTREAM_FT_HTTP_4XX; break;
		case 500: ft_type = UGH_UPSTREAM_FT_HTTP_500; break;
		case 501: ft_type = UGH_UPSTREAM_FT_HTTP_5XX; break;
		case 502: ft_type = UGH_UPSTREAM_FT_HTTP_502; break;
		case 503: ft_type = UGH_UPSTREAM_FT_HTTP_503; break;
		case 504: ft_type = UGH_UPSTREAM_FT_HTTP_504; break;
		case 505: ft_type = UGH_UPSTREAM_FT_HTTP_5XX; break;
		case 506: ft_type = UGH_UPSTREAM_FT_HTTP_5XX; break;
		case 507: ft_type = UGH_UPSTREAM_FT_HTTP_5XX; break;
		}

		ugh_subreq_del(r, ft_type, 0);
	}
}