Exemple #1
0
int read_header(request * req)
{
    int bytes;
    unsigned char *check, *buffer;

    check = req->client_stream + req->parse_pos;
    buffer = req->client_stream;
    bytes = req->client_stream_pos;

#ifdef VERY_FASCIST_LOGGING
    if (check < (buffer + bytes)) {
        buffer[bytes] = '\0';
        log_error_time();
        fprintf(stderr, "%s:%d - Parsing headers (\"%s\")\n",
                __FILE__, __LINE__, check);
    }
#endif
    while (check < (buffer + bytes)) {
        /* check for illegal characters here
         * Anything except CR, LF, and US-ASCII - control is legal
         * We accept tab but don't do anything special with it.
         */
        if (*check != '\r' && *check != '\n' && *check != '\t' &&
            (*check < 32 || *check > 127)) {
            log_error_doc(req);
            fprintf(stderr, "Illegal character (%d) in stream.\n", (unsigned int) *check);
            send_r_bad_request(req);
            return 0;
        }
        switch (req->status) {
        case READ_HEADER:
            if (*check == '\r') {
                req->status = ONE_CR;
                req->header_end = check;
            } else if (*check == '\n') {
                req->status = ONE_LF;
                req->header_end = check;
            }
            break;

        case ONE_CR:
            if (*check == '\n')
                req->status = ONE_LF;
            else if (*check != '\r')
                req->status = READ_HEADER;
            break;

        case ONE_LF:
            /* if here, we've found the end (for sure) of a header */
            if (*check == '\r') /* could be end o headers */
                req->status = TWO_CR;
            else if (*check == '\n')
                req->status = BODY_READ;
            else
                req->status = READ_HEADER;
            break;

        case TWO_CR:
            if (*check == '\n')
                req->status = BODY_READ;
            else if (*check != '\r')
                req->status = READ_HEADER;
            break;

        default:
            break;
        }

#ifdef VERY_FASCIST_LOGGING
        log_error_time();
        fprintf(stderr, "status, check: %d, %d\n", req->status, *check);
#endif

        req->parse_pos++;       /* update parse position */
        check++;

        if (req->status == ONE_LF) {
            *req->header_end = '\0';

            if (req->header_end - req->header_line >= MAX_HEADER_LENGTH) {
                log_error_doc(req);
                fprintf(stderr, "Header too long at %d bytes: \"%s\"\n",
                        req->header_end - req->header_line,
                        req->header_line);
                send_r_bad_request(req);
                return 0;
            }

            /* terminate string that begins at req->header_line */

            if (req->logline) {
                if (process_option_line(req) == 0) {
                    /* errors already logged */
                    return 0;
                }
            } else {
                if (process_logline(req) == 0)
                    /* errors already logged */
                    return 0;
                if (req->http_version == HTTP09)
                    return process_header_end(req);
            }
            /* set header_line to point to beginning of new header */
            req->header_line = check;
        } else if (req->status == BODY_READ) {
#ifdef VERY_FASCIST_LOGGING
            int retval;
            log_error_time();
            fprintf(stderr, "%s:%d -- got to body read.\n",
                    __FILE__, __LINE__);
            retval = process_header_end(req);
#else
            int retval = process_header_end(req);
#endif
            /* process_header_end inits non-POST cgi's */

            if (retval && req->method == M_POST) {
                /* for body_{read,write}, set header_line to start of data,
                   and header_end to end of data */
                req->header_line = check;
                req->header_end =
                    req->client_stream + req->client_stream_pos;

                req->status = BODY_WRITE;
                /* so write it */
                /* have to write first, or read will be confused
                 * because of the special case where the
                 * filesize is less than we have already read.
                 */

                /*

                   As quoted from RFC1945:

                   A valid Content-Length is required on all HTTP/1.0 POST requests. An
                   HTTP/1.0 server should respond with a 400 (bad request) message if it
                   cannot determine the length of the request message's content.

                 */

                if (req->content_length) {
                    int content_length;

                    content_length = boa_atoi(req->content_length);
                    /* Is a content-length of 0 legal? */
                    if (content_length < 0) {
                        log_error_doc(req);
                        fprintf(stderr,
                                "Invalid Content-Length [%s] on POST!\n",
                                req->content_length);
                        send_r_bad_request(req);
                        return 0;
                    }
                    if (single_post_limit
                        && content_length > single_post_limit) {
                        log_error_doc(req);
                        fprintf(stderr,
                                "Content-Length [%d] > SinglePostLimit [%d] on POST!\n",
                                content_length, single_post_limit);
                        send_r_bad_request(req);
                        return 0;
                    }
                    req->filesize = content_length;
                    req->filepos = 0;
                    if (req->header_end - req->header_line > req->filesize) {
                        req->header_end = req->header_line + req->filesize;
                    }
                } else {
                    log_error_doc(req);
                    fprintf(stderr, "Unknown Content-Length POST!\n");
                    send_r_bad_request(req);
                    return 0;
                }
            }                   /* either process_header_end failed or req->method != POST */
            return retval;      /* 0 - close it done, 1 - keep on ready */
        }                       /* req->status == BODY_READ */
    }                           /* done processing available buffer */

#ifdef VERY_FASCIST_LOGGING
    log_error_time();
    fprintf(stderr, "%s:%d - Done processing buffer.  Status: %d\n",
            __FILE__, __LINE__, req->status);
#endif

    if (req->status < BODY_READ) {
        /* only reached if request is split across more than one packet */
        unsigned int buf_bytes_left;

        buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos;
        if (buf_bytes_left < 1 || buf_bytes_left > CLIENT_STREAM_SIZE) {
            log_error_doc(req);
            fputs("No space left in client stream buffer, closing\n",
                  stderr);
            req->response_status = 400; 
            req->status = DEAD;
            return 0;
        }

        bytes =
            read(req->fd, buffer + req->client_stream_pos, buf_bytes_left);

        if (bytes < 0) {
            if (errno == EINTR)
                return 1;
            else if (errno == EAGAIN || errno == EWOULDBLOCK) /* request blocked */
                return -1;
            log_error_doc(req);
            perror("header read"); /* don't need to save errno because log_error_doc does */
            req->response_status = 400; 
            return 0;
        } else if (bytes == 0) {
#ifndef QUIET_DISCONNECT
            log_error_doc(req);
            fputs("client unexpectedly closed connection.\n", stderr);
#endif
            req->response_status = 400;
            return 0;
        }

        /* bytes is positive */
        req->client_stream_pos += bytes;

#ifdef FASCIST_LOGGING1
        log_error_time();
        req->client_stream[req->client_stream_pos] = '\0';
        fprintf(stderr, "%s:%d -- We read %d bytes: \"%s\"\n",
                __FILE__, __LINE__, bytes,
#ifdef VERY_FASCIST_LOGGING2
                req->client_stream + req->client_stream_pos - bytes);
#else
                "");
#endif
#endif

        return 1;
    }
Exemple #2
0
int read_header(request * req)
{
    int bytes, buf_bytes_left;
    char *check, *buffer;

    check = req->client_stream + req->parse_pos;
    buffer = req->client_stream;
    bytes = req->client_stream_pos;

#ifdef VERY_FASCIST_LOGGING
    if (check < (buffer + bytes)) {
        buffer[bytes] = '\0';
        log_error_time();
        fprintf(stderr, "%s:%d - Parsing headers (\"%s\")\n",
                __FILE__, __LINE__, check);
    }
#endif
    while (check < (buffer + bytes)) {
        switch (req->status) {
        case READ_HEADER:
            if (*check == '\r') {
                req->status = ONE_CR;
                req->header_end = check;
            } else if (*check == '\n') {
                req->status = ONE_LF;
                req->header_end = check;
            }
            break;

        case ONE_CR:
            if (*check == '\n')
                req->status = ONE_LF;
            else if (*check != '\r')
                req->status = READ_HEADER;
            break;

        case ONE_LF:
            /* if here, we've found the end (for sure) of a header */
            if (*check == '\r') /* could be end of headers */
                req->status = TWO_CR;
            else if (*check == '\n')
                req->status = BODY_READ;
            else
                req->status = READ_HEADER;
            break;

        case TWO_CR:
            if (*check == '\n')
                req->status = BODY_READ;
            else if (*check != '\r')
                req->status = READ_HEADER;
            break;

        default:
            break;
        }

#ifdef VERY_FASCIST_LOGGING
        log_error_time();
        fprintf(stderr, "status, check: %d, %d\n", req->status, *check);
#endif

        req->parse_pos++;       /* update parse position */
        check++;

        if (req->status == ONE_LF) {
            *req->header_end = '\0';

            /* terminate string that begins at req->header_line */
			
	     /* If has parsed request line before, then parse the request header */
            if (req->logline) {
                if (process_option_line(req) == 0) {
                    return 0;
                }
            } else {/* Else, parse the request line */
                if (process_logline(req) == 0)
                    return 0;
                if (req->simple) /* If request is in HTTP/0.9, return process result directly */
                    return process_header_end(req);
            }
            /* set header_line to point to beginning of new header */
            req->header_line = check;
        } else if (req->status == BODY_READ) {
#ifdef VERY_FASCIST_LOGGING
            int retval;
            log_error_time();
            fprintf(stderr, "%s:%d -- got to body read.\n",
                    __FILE__, __LINE__);
            retval = process_header_end(req);
#else
            int retval = process_header_end(req);
#endif
            /* process_header_end inits non-POST cgi's */

            if (retval && req->method == M_POST) {
                /* for body_{read,write}, set header_line to start of data,
                   and header_end to end of data */
                req->header_line = check;
                req->header_end =
                    req->client_stream + req->client_stream_pos;
/*  在process_header_end() 成功返回后,若请求方式为POST,则将请求状态从
	BODY_READ 变为BODY_WRITE*/
                req->status = BODY_WRITE;
                /* so write it */
                /* have to write first, or read will be confused
                 * because of the special case where the
                 * filesize is less than we have already read.
                 */

                /*

                   As quoted from RFC1945:

                   A valid Content-Length is required on all HTTP/1.0 POST requests. An
                   HTTP/1.0 server should respond with a 400 (bad request) message if it
                   cannot determine the length of the request message's content.

                 */

                if (req->content_length) {
                    int content_length;

                    content_length = boa_atoi(req->content_length);
                    /* Is a content-length of 0 legal? */
                    if (content_length <= 0) {
                        log_error_time();
                        fprintf(stderr, "Invalid Content-Length [%s] on POST!\n",
                                req->content_length);
                        send_r_bad_request(req);
                        return 0;
                    }
			/* POST 一次最多传1M 的内容 */
                    if (single_post_limit && content_length > single_post_limit) {
                        log_error_time();
                        fprintf(stderr, "Content-Length [%d] > SinglePostLimit [%d] on POST!\n",
                                content_length, single_post_limit);
                        send_r_bad_request(req);
                        return 0;
                    }
			/* 将文件大小置为content_length */
			/* Set content_length to filesize */
                    req->filesize = content_length;
                    req->filepos = 0;
                    if (req->header_end - req->header_line > req->filesize) {
                        req->header_end = req->header_line + req->filesize;
                    }
                } else {
                    log_error_time();
                    fprintf(stderr, "Unknown Content-Length POST!\n");
                    send_r_bad_request(req);
                    return 0;
                }
            }                   /* either process_header_end failed or req->method != POST */
            return retval;      /* 0 - close it done, 1 - keep on ready */
        }                       /* req->status == BODY_READ */
    }                           /* done processing available buffer */

#ifdef VERY_FASCIST_LOGGING
    log_error_time();
    fprintf(stderr, "%s:%d - Done processing buffer.  Status: %d\n",
            __FILE__, __LINE__, req->status);
#endif
/* 当请求的状态为READ_HEADER, ONE_CR, ONE_LF, TWO_CR的时候,将客户fd 中的内容读进
	buffer 并更新req->client_stream_pos */
    if (req->status < BODY_READ) {
        /* only reached if request is split across more than one packet */

        buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos;
        if (buf_bytes_left < 1) {
            log_error_time();
            fputs("buffer overrun - read.c, read_header - closing\n",
                  stderr);
            req->status = DEAD;
            return 0;
        }

        bytes = read(req->fd, buffer + req->client_stream_pos, buf_bytes_left);

        if (bytes < 0) {
            if (errno == EINTR)
                return 1;
            if (errno == EAGAIN || errno == EWOULDBLOCK) /* request blocked */
                return -1;
            /*
               else if (errno == EBADF || errno == EPIPE) {

               req->status = DEAD;
               return 0;
               */
            log_error_doc(req);
            perror("header read");            /* don't need to save errno because log_error_doc does */
            return 0;
        } else if (bytes == 0) {
            /*
               log_error_time();
               fputs("unexpected end of headers\n", stderr);
             */
            return 0;
        }

        /* bytes is positive */
        req->client_stream_pos += bytes;

#ifdef FASCIST_LOGGING1
        log_error_time();
        req->client_stream[req->client_stream_pos] = '\0';
        fprintf(stderr, "%s:%d -- We read %d bytes: \"%s\"\n",
                __FILE__, __LINE__, bytes,
#ifdef VERY_FASCIST_LOGGING2
                req->client_stream + req->client_stream_pos - bytes);
#else
                "");
#endif
#endif

        return 1;
    }
Exemple #3
0
int read_header(request * req)
{
	int bytes, buf_bytes_left;
	char *check, *buffer;

	if (req->pipeline_start){
		buffer = req->client_stream;
		bytes = req->client_stream_pos = req->pipeline_start;
		req->pipeline_start = 0;
	} else { /* first from free_request */
		buffer = req->client_stream + req->client_stream_pos;
		buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos;
		if (buf_bytes_left < 0) {
#ifdef BOA_TIME_LOG
			log_error_time();
			fputs("buffer overrun - read.c, read_header - closing\n", stderr);
#endif
			return 0;
		}
		/*MN stuff to support ssl*/
#ifdef SERVER_SSL
		if(req->ssl == NULL){
#endif /*SERVER_SSL*/
			bytes = read(req->fd, buffer, buf_bytes_left);
#ifdef SERVER_SSL
		}
		else{
			char tmp;
			bytes = SSL_read(req->ssl, buffer, buf_bytes_left);
#if 0
			printf("SSL_read 1\n");
			if(bytes > 0){
				tmp = buffer[bytes-1];
				buffer[bytes-1] = '\0';
				printf("buffer-\n %s\n----------\n", buffer);
				buffer[bytes-1] = tmp;
			}
#endif /*0*/
		}
#endif /*SERVER_SSL*/

		if (bytes == -1) {
			if (errno == EINTR)
				return 1;
			if (errno == EAGAIN || errno == EWOULDBLOCK)	/* request blocked */
				return -1;
			else if (errno == EBADF || errno == EPIPE) {
				SQUASH_KA(req);		/* force close fd */
				return 0;
			} else {
#if 0
				boa_perror(req, "header read");
#endif
				return 0;
			}
		} else if (bytes == 0)
			return 0;
		req->client_stream_pos += bytes;
	}

	check = buffer;

	while (check < (buffer + bytes)) {
		switch (req->status) {
		case READ_HEADER:
			if (*check == '\r') {
				req->status = ONE_CR;
				req->header_end = check;
			} else if (*check == '\n') {
				req->status = ONE_LF;
				req->header_end = check;
			}
			break;

		case ONE_CR:
			if (*check == '\n')
				req->status = ONE_LF;
			else
				req->status = READ_HEADER;
			break;

		case ONE_LF:			/* if here, we've found the end (for sure) of a header */
			if (*check == '\r')	/* could be end o headers */
				req->status = TWO_CR;
			else if (*check == '\n')
				req->status = BODY_READ;
			else
				req->status = READ_HEADER;
			break;

		case TWO_CR:
			if (*check == '\n')
				req->status = BODY_READ;
			else
				req->status = READ_HEADER;
			break;

		default:
			break;
		}

		++check;

		if (req->status == ONE_LF) {
			*req->header_end = '\0';
			/* terminate string that begins at req->header_line */
			/* (or at req->data_mem, if we've never been here before */

			/* the following logic still needs work, esp. after req->simple */
			if (req->logline)
				process_option_line(req);
			else {
				if (process_logline(req) == 0)
					return 0;
				if (req->simple)
					return process_header_end(req);
			}
			req->header_line = check; /* start of unprocessed data */
		} else if (req->status == BODY_READ) {
			int retval = process_header_end(req);
			/* process_header_end inits non-POST cgi's */
			
			req->pipeline_start = (check - req->client_stream);

			if (retval && req->method == M_POST) {

				/* rest of non-header data is contained in
				   the area following check

				   check now points to data 
				 */

				if (req->content_length)
					req->filesize = atoi(req->content_length);
				else {
#ifdef BOA_TIME_LOG
					log_error_time();
					fprintf(stderr, "Unknown Content-Length POST\n");
#endif
				}
				
				/* buffer + bytes is 1 past the end of the data */
				req->filepos = (buffer + bytes) - check;

				/* copy the remainder into req->buffer, otherwise
				 * we don't have a full BUFFER_SIZE to play with,
				 * and buffer overruns occur */
				memcpy(req->buffer, check, req->filepos);
				req->header_line = req->buffer;
				req->header_end = req->buffer + req->filepos;				

				if (req->filepos >= req->filesize)
					req->cgi_status = CGI_CLOSE;	/* close after write */
			}
			return retval;		/* 0 - close it done, 1 - keep on ready */
		}
	}

	/* check for bogus requests */
	if (req->status == READ_HEADER && request_type(req) == M_INVALID) {
		syslog(LOG_ERR, "malformed request: \"%s\"\n", req->logline);
		send_r_bad_request(req);
		return 0;
	}

	/* only reached if request is split across more than one packet */
	return 1;
}
Exemple #4
0
int read_header(request * req)
{
	int bytes, buf_bytes_left;
	char *check, *buffer;
	char *pstr;
	char str_lang[6];
	char str_type[4] = {0};
	int	nIndex = 1;
	char ttc[16] = {0};

    check = req->client_stream + req->parse_pos;
    buffer = req->client_stream;
    bytes = req->client_stream_pos;

#ifdef VERY_FASCIST_LOGGING
    if (check < (buffer + bytes)) {
        buffer[bytes] = '\0';
        log_error_time();
        fprintf(stderr, "1) %s:%d - Parsing headers (\"%s\")\n",
                __FILE__, __LINE__, check);
    }
#endif

	tcapi_get("SysInfo_Entry", "TerritoryCode", ttc);

	/* Paul add 2013/3/7, for retrieve Language type from HTTP header */
	tcapi_get("LanguageSwitch_Entry", "Type", str_type);
	if(strlen(str_type))
		nIndex = atoi(str_type);

	if(nIndex == 0) /* Language never been set before, start checking */
	{
		tcapi_get("WebCurSet_Entry", "detected_lang_type", str_type);
		if(strlen(str_type))
			nIndex = atoi(str_type);

		if(nIndex == 0) { // Language never been detect before, start checking
			memset(str_lang, 0, sizeof(str_lang));
			if(pstr = strstr(check,"Accept-Language:"))
			{
				strncpy (str_lang, &check[pstr-check+17], 6);
				str_lang[5] = '\0';

				int i, len = strlen(str_lang);

				for(i=0; i<len; ++i)
				{
					if(isupper(str_lang[i])){
						str_lang[i] = tolower(str_lang[i]);
					}
				}

				//choose proper language type
				sprintf(str_type, "%d", getLangType(ttc, str_lang) );
				tcapi_set("WebCurSet_Entry", "detected_lang_type", str_type);

				initandparserfile();
			}
		}
	}

    while (check < (buffer + bytes)) {
        switch (req->status) {
        case READ_HEADER:
            if (*check == '\r') {
                req->status = ONE_CR;
                req->header_end = check;
            } else if (*check == '\n') {
                req->status = ONE_LF;
                req->header_end = check;
            }
            break;

        case ONE_CR:
            if (*check == '\n')
                req->status = ONE_LF;
            else if (*check != '\r')
                req->status = READ_HEADER;
            break;

        case ONE_LF:
            /* if here, we've found the end (for sure) of a header */
            if (*check == '\r') /* could be end o headers */
                req->status = TWO_CR;
            else if (*check == '\n')
                req->status = BODY_READ;
            else
                req->status = READ_HEADER;
            break;

        case TWO_CR:
            if (*check == '\n')
                req->status = BODY_READ;
            else if (*check != '\r')
                req->status = READ_HEADER;
            break;

        default:
            break;
        }
#if 0
#ifdef VERY_FASCIST_LOGGING
        log_error_time();
        fprintf(stderr, "status, check: %d, %d\n", req->status, *check);
#endif
#endif
        req->parse_pos++;       /* update parse position */
        check++;

        if (req->status == ONE_LF) {
            *req->header_end = '\0';

            /* terminate string that begins at req->header_line */

            if (req->logline) {
                if (process_option_line(req) == 0) {
                    return 0;
                }
            } else {
                if (process_logline(req) == 0)
                    return 0;
                if (req->simple)
                    return process_header_end(req);
            }
            /* set header_line to point to beginning of new header */
            req->header_line = check;
        } else if (req->status == BODY_READ) {
#ifdef VERY_FASCIST_LOGGING
            int retval;
            log_error_time();
            fprintf(stderr, "%s:%d -- got to body read.\n",
                    __FILE__, __LINE__);
            retval = process_header_end(req);
#else
            int retval = process_header_end(req);
#endif
            /* process_header_end inits non-POST cgi's */

            if (retval && req->method == M_POST) {
                /* for body_{read,write}, set header_line to start of data,
                   and header_end to end of data */
                req->header_line = check;
                req->header_end =
                    req->client_stream + req->client_stream_pos;

                req->status = BODY_WRITE;
                /* so write it */
                /* have to write first, or read will be confused
                 * because of the special case where the
                 * filesize is less than we have already read.
                 */

                /*

                   As quoted from RFC1945:

                   A valid Content-Length is required on all HTTP/1.0 POST requests. An
                   HTTP/1.0 server should respond with a 400 (bad request) message if it
                   cannot determine the length of the request message's content.

                 */

                if (req->content_length) {
                    int content_length;

                    content_length = boa_atoi(req->content_length);
#if 0	//Content-Length is required but don't limit to larger then zero. Sam, 2013/7/3
                    /* Is a content-length of 0 legal? */
                    if (content_length <= 0) {
                        log_error_time();
                        fprintf(stderr, "Invalid Content-Length [%s] on POST!\n",
                                req->content_length);
                        send_r_bad_request(req);
                        return 0;
                    }
#endif
                    if (single_post_limit && content_length > single_post_limit) {
                        log_error_time();
                        fprintf(stderr, "Content-Length [%d] > SinglePostLimit [%d] on POST!\n",
                                content_length, single_post_limit);
                        send_r_bad_request(req);
                        return 0;
                    }
                    req->filesize = content_length;
                    req->filepos = 0;
                    if (req->header_end - req->header_line > req->filesize) {
                        req->header_end = req->header_line + req->filesize;
                    }
                } else {
                    log_error_time();
                    fprintf(stderr, "Unknown Content-Length POST!\n");
                    send_r_bad_request(req);
                    return 0;
                }
            }                   /* either process_header_end failed or req->method != POST */
            return retval;      /* 0 - close it done, 1 - keep on ready */
        }                       /* req->status == BODY_READ */
    }                           /* done processing available buffer */

#ifdef VERY_FASCIST_LOGGING
    log_error_time();
    fprintf(stderr, "%s:%d - Done processing buffer.  Status: %d\n",
            __FILE__, __LINE__, req->status);
#endif

    if (req->status < BODY_READ) {
        /* only reached if request is split across more than one packet */

        buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos;
        if (buf_bytes_left < 1) {
            log_error_time();
            fputs("buffer overrun - read.c, read_header - closing\n",
                  stderr);
            req->status = DEAD;
            return 0;
        }

#if defined(TCSUPPORT_WEBSERVER_SSL)
	if(req->ssl == NULL)
#endif
	{
        bytes = read(req->fd, buffer + req->client_stream_pos, buf_bytes_left);
	}
#if defined(TCSUPPORT_WEBSERVER_SSL)
	else{
		bytes = boa_sslRead(req->ssl, buffer + req->client_stream_pos, buf_bytes_left);
	}
#endif 
        if (bytes < 0) {
            if (errno == EINTR)
                return 1;
            if (errno == EAGAIN || errno == EWOULDBLOCK) /* request blocked */
                return -1;
            /*
               else if (errno == EBADF || errno == EPIPE) {

               req->status = DEAD;
               return 0;
            */
               
#if 0		//lee 9-25
            log_error_doc(req);
            perror("header read");            /* don't need to save errno because log_error_doc does */
#endif
			return 0;
        } else if (bytes == 0) {
            /*
               log_error_time();
               fputs("unexpected end of headers\n", stderr);
             */
            return 0;
        }

        /* bytes is positive */
        req->client_stream_pos += bytes;

#ifdef FASCIST_LOGGING1
        log_error_time();
        req->client_stream[req->client_stream_pos] = '\0';
        fprintf(stderr, "%s:%d -- We read %d bytes: \"%s\"\n",
                __FILE__, __LINE__, bytes,
#ifdef VERY_FASCIST_LOGGING2
                req->client_stream + req->client_stream_pos - bytes);
#else
                "");
#endif
#endif

        return 1;
    }