Exemple #1
0
static handler_t cgi_response_headers(server *srv, connection *con, struct http_response_opts_t *opts) {
    /* response headers just completed */
    handler_ctx *hctx = (handler_ctx *)opts->pdata;

    if (con->response.htags & HTTP_HEADER_UPGRADE) {
        if (hctx->conf.upgrade && con->http_status == 101) {
            /* 101 Switching Protocols; transition to transparent proxy */
            http_response_upgrade_read_body_unknown(srv, con);
        }
        else {
            con->response.htags &= ~HTTP_HEADER_UPGRADE;
          #if 0
            /* preserve prior questionable behavior; likely broken behavior
             * anyway if backend thinks connection is being upgraded but client
             * does not receive Connection: upgrade */
            http_header_response_unset(con, HTTP_HEADER_UPGRADE,
                                       CONST_STR_LEN("Upgrade"));
          #endif
        }
    }

    if (hctx->conf.upgrade && !(con->response.htags & HTTP_HEADER_UPGRADE)) {
        chunkqueue *cq = con->request_content_queue;
        hctx->conf.upgrade = 0;
        if (cq->bytes_out == (off_t)con->request.content_length) {
            cgi_connection_close_fdtocgi(srv, hctx); /*(closes hctx->fdtocgi)*/
        }
    }

    return HANDLER_GO_ON;
}
Exemple #2
0
static void cgi_connection_close(server *srv, handler_ctx *hctx) {
	plugin_data *p = hctx->plugin_data;
	connection *con = hctx->remote_conn;

	/* the connection to the browser went away, but we still have a connection
	 * to the CGI script
	 *
	 * close cgi-connection
	 */

	if (hctx->fd != -1) {
		/* close connection to the cgi-script */
		fdevent_fdnode_event_del(srv->ev, hctx->fdn);
		/*fdevent_unregister(srv->ev, hctx->fd);*//*(handled below)*/
		fdevent_sched_close(srv->ev, hctx->fd, 0);
		hctx->fdn = NULL;
	}

	if (hctx->fdtocgi != -1) {
		cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
	}

	if (hctx->pid > 0) {
		cgi_pid_kill(p, hctx->pid);
	}

	con->plugin_ctx[p->id] = NULL;

	cgi_handler_ctx_free(hctx);

	/* finish response (if not already con->file_started, con->file_finished) */
	if (con->mode == p->id) {
		http_response_backend_done(srv, con);
	}
}
Exemple #3
0
static handler_t cgi_handle_fdevent_send (server *srv, void *ctx, int revents) {
	handler_ctx *hctx = ctx;
	connection  *con  = hctx->remote_conn;

	/*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/
	joblist_append(srv, con);

	if (revents & FDEVENT_OUT) {
		if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) {
			cgi_connection_close(srv, hctx);
			return HANDLER_ERROR;
		}
		/* more request body to be sent to CGI */
	}

	if (revents & FDEVENT_HUP) {
		/* skip sending remaining data to CGI */
		if (con->request.content_length) {
			chunkqueue *cq = con->request_content_queue;
			chunkqueue_mark_written(cq, chunkqueue_length(cq));
			if (cq->bytes_in != (off_t)con->request.content_length) {
				con->keep_alive = 0;
			}
		}

		cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
	} else if (revents & FDEVENT_ERR) {
		/* kill all connections to the cgi process */
#if 1
		log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
#endif
		cgi_connection_close(srv, hctx);
		return HANDLER_ERROR;
	}

	return HANDLER_FINISHED;
}
Exemple #4
0
static int cgi_write_request(server *srv, handler_ctx *hctx, int fd) {
	connection *con = hctx->remote_conn;
	chunkqueue *cq = con->request_content_queue;
	chunk *c;

	/* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
	 * solution: if this is still a problem on windows, then substitute
	 * socketpair() for pipe() and closesocket() for close() on windows.
	 */

	for (c = cq->first; c; c = cq->first) {
		ssize_t r = -1;

		switch(c->type) {
		case FILE_CHUNK:
			r = cgi_write_file_chunk_mmap(srv, con, fd, cq);
			break;

		case MEM_CHUNK:
			if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) {
				switch(errno) {
				case EAGAIN:
				case EINTR:
					/* ignore and try again */
					r = 0;
					break;
				case EPIPE:
				case ECONNRESET:
					/* connection closed */
					r = -2;
					break;
				default:
					/* fatal error */
					log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno));
					r = -1;
					break;
				}
			} else if (r > 0) {
				chunkqueue_mark_written(cq, r);
			}
			break;
		}

		if (0 == r) break; /*(might block)*/

		switch (r) {
		case -1:
			/* fatal error */
			return -1;
		case -2:
			/* connection reset */
			log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI");
			/* skip all remaining data */
			chunkqueue_mark_written(cq, chunkqueue_length(cq));
			break;
		default:
			break;
		}
	}

	if (cq->bytes_out == (off_t)con->request.content_length) {
		/* sent all request body input */
		/* close connection to the cgi-script */
		if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/
			--srv->cur_fds;
			if (close(fd)) {
				log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", fd, strerror(errno));
			}
		} else {
			cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
		}
	} else {
		off_t cqlen = cq->bytes_in - cq->bytes_out;
		if (cq->bytes_in != con->request.content_length && cqlen < 65536 - 16384) {
			/*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
			if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
				con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
				con->is_readable = 1; /* trigger optimistic read from client */
			}
		}
		if (-1 == hctx->fdtocgi) { /*(not registered yet)*/
			hctx->fdtocgi = fd;
			hctx->fde_ndx_tocgi = -1;
			fdevent_register(srv->ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx);
		}
		if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/
			if ((fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT)) {
				fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, 0);
			}
		} else {
			/* more request body remains to be sent to CGI so register for fdevents */
			fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, FDEVENT_OUT);
		}
	}

	return 0;
}
Exemple #5
0
static void cgi_connection_close(server *srv, handler_ctx *hctx) {
	int status;
	pid_t pid;
	plugin_data *p = hctx->plugin_data;
	connection *con = hctx->remote_conn;

#ifndef __WIN32

	/* the connection to the browser went away, but we still have a connection
	 * to the CGI script
	 *
	 * close cgi-connection
	 */

	if (hctx->fd != -1) {
		/* close connection to the cgi-script */
		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
		fdevent_unregister(srv->ev, hctx->fd);
		fdevent_sched_close(srv->ev, hctx->fd, 0);
	}

	if (hctx->fdtocgi != -1) {
		cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
	}

	pid = hctx->pid;

	con->plugin_ctx[p->id] = NULL;

	cgi_handler_ctx_free(hctx);

	/* if waitpid hasn't been called by response.c yet, do it here */
	if (pid) {
		/* check if the CGI-script is already gone */
		switch(waitpid(pid, &status, WNOHANG)) {
		case 0:
			/* not finished yet */
#if 0
			log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
#endif
			break;
		case -1:
			/* */
			if (errno == EINTR) break;

			/*
			 * errno == ECHILD happens if _subrequest catches the process-status before
			 * we have read the response of the cgi process
			 *
			 * -> catch status
			 * -> WAIT_FOR_EVENT
			 * -> read response
			 * -> we get here with waitpid == ECHILD
			 *
			 */
			if (errno != ECHILD) {
				log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
			}
			/* anyway: don't wait for it anymore */
			pid = 0;
			break;
		default:
			if (WIFEXITED(status)) {
#if 0
				log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
#endif
			} else {
				log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
			}

			pid = 0;
			break;
		}

		if (pid) {
			kill(pid, SIGTERM);

			/* cgi-script is still alive, queue the PID for removal */
			cgi_pid_add(srv, p, pid);
		}
	}
#endif

	/* finish response (if not already con->file_started, con->file_finished) */
	if (con->mode == p->id) {
		http_response_backend_done(srv, con);
	}
}