static handler_t proxy_write_request(server *srv, handler_ctx *hctx) { data_proxy *host= hctx->host; connection *con = hctx->remote_conn; int ret; if (!host || (!host->host->used || !host->port)) return -1; switch(hctx->state) { case PROXY_STATE_CONNECT: /* wait for the connect() to finish */ /* connect failed ? */ if (-1 == hctx->fde_ndx) return HANDLER_ERROR; /* wait */ return HANDLER_WAIT_FOR_EVENT; break; case PROXY_STATE_INIT: #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) if (strstr(host->host->ptr,":")) { if (-1 == (hctx->fd = socket(AF_INET6, SOCK_STREAM, 0))) { log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); return HANDLER_ERROR; } } else #endif { if (-1 == (hctx->fd = socket(AF_INET, SOCK_STREAM, 0))) { log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); return HANDLER_ERROR; } } hctx->fde_ndx = -1; srv->cur_fds++; fdevent_register(srv->ev, hctx->fd, proxy_handle_fdevent, hctx); if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); return HANDLER_ERROR; } switch (proxy_establish_connection(srv, hctx)) { case 1: proxy_set_state(srv, hctx, PROXY_STATE_CONNECT); /* connection is in progress, wait for an event and call getsockopt() below */ fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); return HANDLER_WAIT_FOR_EVENT; case -1: /* if ECONNREFUSED choose another connection -> FIXME */ hctx->fde_ndx = -1; return HANDLER_ERROR; default: /* everything is ok, go on */ proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE); break; } /* fall through */ case PROXY_STATE_PREPARE_WRITE: proxy_create_env(srv, hctx); proxy_set_state(srv, hctx, PROXY_STATE_WRITE); /* fall through */ case PROXY_STATE_WRITE:; ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT); chunkqueue_remove_finished_chunks(hctx->wb); if (-1 == ret) { /* error on our side */ log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno); return HANDLER_ERROR; } else if (-2 == ret) { /* remote close */ log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed, remote connection close:", strerror(errno), errno); return HANDLER_ERROR; } if (hctx->wb->bytes_out == hctx->wb->bytes_in) { proxy_set_state(srv, hctx, PROXY_STATE_READ); fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); } else { fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); return HANDLER_WAIT_FOR_EVENT; } return HANDLER_WAIT_FOR_EVENT; case PROXY_STATE_READ: /* waiting for a response */ return HANDLER_WAIT_FOR_EVENT; default: log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state"); return HANDLER_ERROR; } return HANDLER_GO_ON; }
static handler_t proxy_write_request(server *srv, handler_ctx *hctx) { data_proxy *host= hctx->host; plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; int ret; if (!host || (!host->host->used || !host->port)) return -1; switch(hctx->state) { case PROXY_STATE_INIT: if (-1 == (hctx->fd = socket(AF_INET, SOCK_STREAM, 0))) { log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed: ", strerror(errno)); return HANDLER_ERROR; } hctx->fde_ndx = -1; srv->cur_fds++; fdevent_register(srv->ev, hctx->fd, proxy_handle_fdevent, hctx); if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); return HANDLER_ERROR; } /* fall through */ case PROXY_STATE_CONNECT: /* try to finish the connect() */ if (hctx->state == PROXY_STATE_INIT) { /* first round */ switch (proxy_establish_connection(srv, hctx)) { case 1: proxy_set_state(srv, hctx, PROXY_STATE_CONNECT); /* connection is in progress, wait for an event and call getsockopt() below */ fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); return HANDLER_WAIT_FOR_EVENT; case -1: /* if ECONNREFUSED choose another connection -> FIXME */ hctx->fde_ndx = -1; return HANDLER_ERROR; default: /* everything is ok, go on */ break; } } else { int socket_error; socklen_t socket_error_len = sizeof(socket_error); /* we don't need it anymore */ fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); /* try to finish the connect() */ if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { log_error_write(srv, __FILE__, __LINE__, "ss", "getsockopt failed:", strerror(errno)); return HANDLER_ERROR; } if (socket_error != 0) { log_error_write(srv, __FILE__, __LINE__, "ss", "establishing connection failed:", strerror(socket_error), "port:", hctx->host->port); return HANDLER_ERROR; } if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "s", "proxy - connect - delayed success"); } } proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE); /* fall through */ case PROXY_STATE_PREPARE_WRITE: proxy_create_env(srv, hctx); proxy_set_state(srv, hctx, PROXY_STATE_WRITE); /* fall through */ case PROXY_STATE_WRITE:; ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb); chunkqueue_remove_finished_chunks(hctx->wb); if (-1 == ret) { if (errno != EAGAIN && errno != EINTR) { log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), errno); return HANDLER_ERROR; } else { fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); return HANDLER_WAIT_FOR_EVENT; } } if (hctx->wb->bytes_out == hctx->wb->bytes_in) { proxy_set_state(srv, hctx, PROXY_STATE_READ); fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); } else { fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); return HANDLER_WAIT_FOR_EVENT; } return HANDLER_WAIT_FOR_EVENT; case PROXY_STATE_READ: /* waiting for a response */ return HANDLER_WAIT_FOR_EVENT; default: log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state"); return HANDLER_ERROR; } return HANDLER_GO_ON; }