Esempio n. 1
0
static int cgi_demux_response(server *srv, handler_ctx *hctx) {
	plugin_data *p    = hctx->plugin_data;
	connection  *con  = hctx->remote_conn;

	while(1) {
		int n;

		buffer_prepare_copy(hctx->response, 1024);
		if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
			if (errno == EAGAIN || errno == EINTR) {
				/* would block, wait for signal */
				return FDEVENT_HANDLED_NOT_FINISHED;
			}
			/* error */
			log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
			return FDEVENT_HANDLED_ERROR;
		}

		if (n == 0) {
			/* read finished */

			con->file_finished = 1;

			/* send final chunk */
			http_chunk_append_mem(srv, con, NULL, 0);
			joblist_append(srv, con);

			return FDEVENT_HANDLED_FINISHED;
		}

		hctx->response->ptr[n] = '\0';
		hctx->response->used = n+1;

		/* split header from body */

		if (con->file_started == 0) {
			char *c;
			int in_header = 0;
			int header_end = 0;
			int cp, eol = EOL_UNSET;
			size_t used = 0;

			buffer_append_string_buffer(hctx->response_header, hctx->response);

			/* nph (non-parsed headers) */
			if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) in_header = 1;

			/* search for the \r\n\r\n or \n\n in the string */
			for (c = hctx->response_header->ptr, cp = 0, used = hctx->response_header->used - 1; used; c++, cp++, used--) {
				if (*c == ':') in_header = 1;
				else if (*c == '\n') {
					if (in_header == 0) {
						/* got a response without a response header */

						c = NULL;
						header_end = 1;
						break;
					}

					if (eol == EOL_UNSET) eol = EOL_N;

					if (*(c+1) == '\n') {
						header_end = 1;
						break;
					}

				} else if (used > 1 && *c == '\r' && *(c+1) == '\n') {
					if (in_header == 0) {
						/* got a response without a response header */

						c = NULL;
						header_end = 1;
						break;
					}

					if (eol == EOL_UNSET) eol = EOL_RN;

					if (used > 3 &&
					    *(c+2) == '\r' &&
					    *(c+3) == '\n') {
						header_end = 1;
						break;
					}

					/* skip the \n */
					c++;
					cp++;
					used--;
				}
			}

			if (header_end) {
				if (c == NULL) {
					/* no header, but a body */

					if (con->request.http_version == HTTP_VERSION_1_1) {
						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
					}

					http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
					joblist_append(srv, con);
				} else {
					size_t hlen = c - hctx->response_header->ptr + (eol == EOL_RN ? 4 : 2);
					size_t blen = hctx->response_header->used - hlen - 1;

					/* a small hack: terminate after at the second \r */
					hctx->response_header->used = hlen + 1 - (eol == EOL_RN ? 2 : 1);
					hctx->response_header->ptr[hlen - (eol == EOL_RN ? 2 : 1)] = '\0';

					/* parse the response header */
					cgi_response_parse(srv, con, p, hctx->response_header, eol);

					/* enable chunked-transfer-encoding */
					if (con->request.http_version == HTTP_VERSION_1_1 &&
					    !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
					}

					if ((hctx->response->used != hlen) && blen > 0) {
						http_chunk_append_mem(srv, con, c + (eol == EOL_RN ? 4: 2), blen + 1);
						joblist_append(srv, con);
					}
				}

				con->file_started = 1;
			}
		} else {
			http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used);
			joblist_append(srv, con);
		}

#if 0
		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
#endif
	}

	return FDEVENT_HANDLED_NOT_FINISHED;
}
Esempio n. 2
0
static int cgi_demux_response(server *srv, handler_ctx *hctx) {
	plugin_data *p    = hctx->plugin_data;
	connection  *con  = hctx->remote_conn;

	while(1) {
		int n;
		int toread;

#if defined(__WIN32)
		buffer_string_prepare_copy(hctx->response, 4 * 1024);
#else
		if (ioctl(con->fd, FIONREAD, &toread) || toread <= 4*1024) {
			buffer_string_prepare_copy(hctx->response, 4 * 1024);
		} else {
			if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
			buffer_string_prepare_copy(hctx->response, toread);
		}
#endif

		if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
			if (errno == EAGAIN || errno == EINTR) {
				/* would block, wait for signal */
				fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
				return FDEVENT_HANDLED_NOT_FINISHED;
			}
			/* error */
			log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
			return FDEVENT_HANDLED_ERROR;
		}

		if (n == 0) {
			/* read finished */
			return FDEVENT_HANDLED_FINISHED;
		}

		buffer_commit(hctx->response, n);

		/* split header from body */

		if (con->file_started == 0) {
			int is_header = 0;
			int is_header_end = 0;
			size_t last_eol = 0;
			size_t i, header_len;

			buffer_append_string_buffer(hctx->response_header, hctx->response);

			/**
			 * we have to handle a few cases:
			 *
			 * nph:
			 * 
			 *   HTTP/1.0 200 Ok\n
			 *   Header: Value\n
			 *   \n
			 *
			 * CGI:
			 *   Header: Value\n
			 *   Status: 200\n
			 *   \n
			 *
			 * and different mixes of \n and \r\n combinations
			 * 
			 * Some users also forget about CGI and just send a response and hope 
			 * we handle it. No headers, no header-content seperator
			 * 
			 */
			
			/* nph (non-parsed headers) */
			if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;

			header_len = buffer_string_length(hctx->response_header);
			for (i = 0; !is_header_end && i < header_len; i++) {
				char c = hctx->response_header->ptr[i];

				switch (c) {
				case ':':
					/* we found a colon
					 *
					 * looks like we have a normal header 
					 */
					is_header = 1;
					break;
				case '\n':
					/* EOL */
					if (is_header == 0) {
						/* we got a EOL but we don't seem to got a HTTP header */

						is_header_end = 1;

						break;
					}

					/**
					 * check if we saw a \n(\r)?\n sequence 
					 */
					if (last_eol > 0 && 
					    ((i - last_eol == 1) || 
					     (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
						is_header_end = 1;
						break;
					}

					last_eol = i;

					break;
				}
			}

			if (is_header_end) {
				if (!is_header) {
					/* no header, but a body */
					if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) {
						return FDEVENT_HANDLED_ERROR;
					}
				} else {
					const char *bstart;
					size_t blen;

					/* the body starts after the EOL */
					bstart = hctx->response_header->ptr + i;
					blen = header_len - i;

					/**
					 * i still points to the char after the terminating EOL EOL
					 *
					 * put it on the last \n again
					 */
					i--;

					/* string the last \r?\n */
					if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
						i--;
					}

					buffer_string_set_length(hctx->response_header, i);

					/* parse the response header */
					cgi_response_parse(srv, con, p, hctx->response_header);

					if (con->http_status >= 300 && con->http_status < 400) {
						/*(con->parsed_response & HTTP_LOCATION)*/
						data_string *ds;
						if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Location"))
						    && ds->value->ptr[0] == '/') {
							if (++con->loops_per_request > 5) {
								log_error_write(srv, __FILE__, __LINE__, "sb", "too many internal loops while processing request:", con->request.orig_uri);
								con->http_status = 500; /* Internal Server Error */
								con->mode = DIRECT;
								return FDEVENT_HANDLED_FINISHED;
							}

							buffer_copy_buffer(con->request.uri, ds->value);

							if (con->request.content_length) {
								if (con->request.content_length != con->request_content_queue->bytes_in) {
									con->keep_alive = 0;
								}
								con->request.content_length = 0;
								chunkqueue_reset(con->request_content_queue);
							}

							if (con->http_status != 307 && con->http_status != 308) {
								/* Note: request body (if any) sent to initial dynamic handler
								 * and is not available to the internal redirect */
								con->request.http_method = HTTP_METHOD_GET;
							}

							connection_response_reset(srv, con); /*(includes con->http_status = 0)*/

							con->mode = DIRECT;
							return FDEVENT_HANDLED_COMEBACK;
						}
					}

					if (hctx->conf.xsendfile_allow) {
						data_string *ds;
						if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile"))) {
							http_response_xsendfile(srv, con, ds->value, hctx->conf.xsendfile_docroot);
							return FDEVENT_HANDLED_FINISHED;
						}
					}

					if (blen > 0) {
						if (0 != http_chunk_append_mem(srv, con, bstart, blen)) {
							return FDEVENT_HANDLED_ERROR;
						}
					}
				}

				con->file_started = 1;
			} else {
				/*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/
				if (header_len > MAX_HTTP_REQUEST_HEADER) {
					log_error_write(srv, __FILE__, __LINE__, "sb", "response headers too large for", con->uri.path);
					con->http_status = 502; /* Bad Gateway */
					con->mode = DIRECT;
					return FDEVENT_HANDLED_FINISHED;
				}
			}
		} else {
			if (0 != http_chunk_append_buffer(srv, con, hctx->response)) {
				return FDEVENT_HANDLED_ERROR;
			}
			if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
			    && chunkqueue_length(con->write_queue) > 65536 - 4096) {
				if (!con->is_writable) {
					/*(defer removal of FDEVENT_IN interest since
					 * connection_state_machine() might be able to send data
					 * immediately, unless !con->is_writable, where
					 * connection_state_machine() might not loop back to call
					 * mod_cgi_handle_subrequest())*/
					fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
				}
				break;
			}
		}

#if 0
		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
#endif
	}

	return FDEVENT_HANDLED_NOT_FINISHED;
}
static int cgi_demux_response(server *srv, handler_ctx *hctx) {
	plugin_data *p    = hctx->plugin_data;
	connection  *con  = hctx->remote_conn;

	while(1) {
		int n;
		int toread;

#if defined(__WIN32)
		buffer_prepare_copy(hctx->response, 4 * 1024);
#else
		if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
			buffer_prepare_copy(hctx->response, 4 * 1024);
		} else {
			if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
			buffer_prepare_copy(hctx->response, toread + 1);
		}
#endif

		if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
			if (errno == EAGAIN || errno == EINTR) {
				/* would block, wait for signal */
				return FDEVENT_HANDLED_NOT_FINISHED;
			}
			/* error */
			log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
			return FDEVENT_HANDLED_ERROR;
		}

		if (n == 0) {
			/* read finished */

			con->file_finished = 1;

			/* send final chunk */
			http_chunk_append_mem(srv, con, NULL, 0);
			joblist_append(srv, con);

			return FDEVENT_HANDLED_FINISHED;
		}

		hctx->response->ptr[n] = '\0';
		hctx->response->used = n+1;

		/* split header from body */

		if (con->file_started == 0) {
			int is_header = 0;
			int is_header_end = 0;
			size_t last_eol = 0;
			size_t i;

			buffer_append_string_buffer(hctx->response_header, hctx->response);

			/**
			 * we have to handle a few cases:
			 *
			 * nph:
			 * 
			 *   HTTP/1.0 200 Ok\n
			 *   Header: Value\n
			 *   \n
			 *
			 * CGI:
			 *   Header: Value\n
			 *   Status: 200\n
			 *   \n
			 *
			 * and different mixes of \n and \r\n combinations
			 * 
			 * Some users also forget about CGI and just send a response and hope 
			 * we handle it. No headers, no header-content seperator
			 * 
			 */
			
			/* nph (non-parsed headers) */
			if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
				
			for (i = 0; !is_header_end && i < hctx->response_header->used - 1; i++) {
				char c = hctx->response_header->ptr[i];

				switch (c) {
				case ':':
					/* we found a colon
					 *
					 * looks like we have a normal header 
					 */
					is_header = 1;
					break;
				case '\n':
					/* EOL */
					if (is_header == 0) {
						/* we got a EOL but we don't seem to got a HTTP header */

						is_header_end = 1;

						break;
					}

					/**
					 * check if we saw a \n(\r)?\n sequence 
					 */
					if (last_eol > 0 && 
					    ((i - last_eol == 1) || 
					     (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
						is_header_end = 1;
						break;
					}

					last_eol = i;

					break;
				}
			}

			if (is_header_end) {
				if (!is_header) {
					/* no header, but a body */

					if (con->request.http_version == HTTP_VERSION_1_1) {
						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
					}

					http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
					joblist_append(srv, con);
				} else {
					const char *bstart;
					size_t blen;
					
					/**
					 * i still points to the char after the terminating EOL EOL
					 *
					 * put it on the last \n again
					 */
					i--;
					
					/* the body starts after the EOL */
					bstart = hctx->response_header->ptr + (i + 1);
					blen = (hctx->response_header->used - 1) - (i + 1);
					
					/* string the last \r?\n */
					if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
						i--;
					}

					hctx->response_header->ptr[i] = '\0';
					hctx->response_header->used = i + 1; /* the string + \0 */
					
					/* parse the response header */
					cgi_response_parse(srv, con, p, hctx->response_header);

					/* enable chunked-transfer-encoding */
					if (con->request.http_version == HTTP_VERSION_1_1 &&
					    !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
					}

					if (blen > 0) {
						http_chunk_append_mem(srv, con, bstart, blen + 1);
						joblist_append(srv, con);
					}
				}

				con->file_started = 1;
			}
		} else {
			http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used);
			joblist_append(srv, con);
		}

#if 0
		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
#endif
	}

	return FDEVENT_HANDLED_NOT_FINISHED;
}