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_handle_fdevent(server *srv, void *ctx, int revents) { handler_ctx *hctx = ctx; connection *con = hctx->remote_conn; plugin_data *p = hctx->plugin_data; if ((revents & FDEVENT_IN) && hctx->state == PROXY_STATE_READ) { if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sd", "proxy: fdevent-in", hctx->state); } switch (proxy_demux_response(srv, hctx)) { case 0: break; case 1: /* we are done */ proxy_connection_close(srv, hctx); joblist_append(srv, con); return HANDLER_FINISHED; case -1: if (con->file_started == 0) { /* nothing has been send out yet, send a 500 */ connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); con->http_status = 500; con->mode = DIRECT; } else { /* response might have been already started, kill the connection */ connection_set_state(srv, con, CON_STATE_ERROR); } joblist_append(srv, con); return HANDLER_FINISHED; } } if (revents & FDEVENT_OUT) { if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sd", "proxy: fdevent-out", hctx->state); } if (hctx->state == PROXY_STATE_CONNECT) { 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); hctx->fde_ndx = -1; /* 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)); joblist_append(srv, con); return HANDLER_FINISHED; } if (socket_error != 0) { log_error_write(srv, __FILE__, __LINE__, "ss", "establishing connection failed:", strerror(socket_error), "port:", hctx->host->port); joblist_append(srv, con); return HANDLER_FINISHED; } if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "s", "proxy - connect - delayed success"); } proxy_set_state(srv, hctx, PROXY_STATE_PREPARE_WRITE); } if (hctx->state == PROXY_STATE_PREPARE_WRITE || hctx->state == PROXY_STATE_WRITE) { /* we are allowed to send something out * * 1. after a just finished connect() call * 2. in a unfinished write() call (long POST request) */ return mod_proxy_handle_subrequest(srv, con, p); } else { log_error_write(srv, __FILE__, __LINE__, "sd", "proxy: out", hctx->state); } } /* perhaps this issue is already handled */ if (revents & FDEVENT_HUP) { if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sd", "proxy: fdevent-hup", hctx->state); } if (hctx->state == PROXY_STATE_CONNECT) { /* connect() -> EINPROGRESS -> HUP */ /** * what is proxy is doing if it can't reach the next hop ? * */ if (hctx->host) { hctx->host->is_disabled = 1; hctx->host->disable_ts = srv->cur_ts; log_error_write(srv, __FILE__, __LINE__, "sbdd", "proxy-server disabled:", hctx->host->host, hctx->host->port, hctx->fd); /* disable this server */ hctx->host->is_disabled = 1; hctx->host->disable_ts = srv->cur_ts; proxy_connection_close(srv, hctx); /* reset the enviroment and restart the sub-request */ buffer_reset(con->physical.path); con->mode = DIRECT; joblist_append(srv, con); } else { proxy_connection_close(srv, hctx); joblist_append(srv, con); con->mode = DIRECT; con->http_status = 503; } return HANDLER_FINISHED; } if (!con->file_finished) { http_chunk_append_mem(srv, con, NULL, 0); } con->file_finished = 1; proxy_connection_close(srv, hctx); joblist_append(srv, con); } else if (revents & FDEVENT_ERR) { /* kill all connections to the proxy process */ log_error_write(srv, __FILE__, __LINE__, "sd", "proxy-FDEVENT_ERR, but no HUP", revents); con->file_finished = 1; joblist_append(srv, con); proxy_connection_close(srv, hctx); } return HANDLER_FINISHED; }
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; }