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