Beispiel #1
0
void block_request(request * req)
{
		//fprintf(stderr,"block_request\n");
    dequeue(&request_ready, req);
    enqueue(&request_block, req);

    if (req->buffer_end) {
        BOA_FD_SET(req->fd, &block_write_fdset);
    } else {
        switch (req->status) {
        case WRITE:
        case PIPE_WRITE:
        case DONE:
            BOA_FD_SET(req->fd, &block_write_fdset);
            break;
        case PIPE_READ:
            BOA_FD_SET(req->data_fd, &block_read_fdset);
            break;
        case BODY_WRITE:
            BOA_FD_SET(req->post_data_fd, &block_write_fdset);
            break;
        default:
            BOA_FD_SET(req->fd, &block_read_fdset);
            break;
        }
    }
}
Beispiel #2
0
void block_request(request * req)
{
    dequeue(&request_ready, req);
    enqueue(&request_block, req);

    if (req->buffer_end) {
        BOA_FD_SET(req, req->fd, BOA_WRITE);
    } else {
        switch (req->status) {
        case IOSHUFFLE:
#ifndef HAVE_SENDFILE
            if (req->buffer_end - req->buffer_start == 0) {
                BOA_FD_SET(req, req->data_fd, BOA_READ);
                break;
            }
#endif
        case WRITE:
        case PIPE_WRITE:
        case DONE:
            BOA_FD_SET(req, req->fd, BOA_WRITE);
            break;
        case PIPE_READ:
            BOA_FD_SET(req, req->data_fd, BOA_READ);
            break;
        case BODY_WRITE:
            BOA_FD_SET(req, req->post_data_fd, BOA_WRITE);
            break;
        default:
            BOA_FD_SET(req, req->fd, BOA_READ);
            break;
        }
    }
}
Beispiel #3
0
void select_loop(int server_s)
{
    FD_ZERO(&block_read_fdset);
    FD_ZERO(&block_write_fdset);
    /* set server_s and req_timeout */
    req_timeout.tv_sec = (ka_timeout ? ka_timeout : REQUEST_TIMEOUT);
    req_timeout.tv_usec = 0l;   /* reset timeout */

    /* preset max_fd */
    max_fd = -1;

    while (1) {
        if (sighup_flag)
            sighup_run();
        if (sigchld_flag)
            sigchld_run();
        if (sigalrm_flag)
            sigalrm_run();

        if (sigterm_flag) {
            if (sigterm_flag == 1)
                sigterm_stage1_run(server_s);
            if (sigterm_flag == 2 && !request_ready && !request_block) {
                sigterm_stage2_run();
            }
        }

        /* reset max_fd */
        max_fd = -1;

        if (request_block)
            /* move selected req's from request_block to request_ready */
            fdset_update();

        /* any blocked req's move from request_ready to request_block */
        process_requests(server_s);

        if (!sigterm_flag && total_connections < (max_connections - 10)) {
            BOA_FD_SET(server_s, &block_read_fdset); /* server always set */
#ifdef SERVER_SSL
            if (do_sock < 2)
                BOA_FD_SET(server_ssl, &block_read_fdset); /* server always set */
#endif
        }

        req_timeout.tv_sec = (request_ready ? 0 :
                              (ka_timeout ? ka_timeout : REQUEST_TIMEOUT));
        req_timeout.tv_usec = 0l;   /* reset timeout */

        if (select(max_fd + 1, &block_read_fdset,
                   &block_write_fdset, NULL,
                   (request_ready || request_block ? &req_timeout : NULL)) == -1) {
            /* what is the appropriate thing to do here on EBADF */
            if (errno == EINTR)
                continue;   /* while(1) */
            else if (errno != EBADF) {
                DIE("select");
            }
        }

        time(&current_time);
        if (FD_ISSET(server_s, &block_read_fdset))
            pending_requests = 1;
    }
}
Beispiel #4
0
static void fdset_update(void)
{
    request *current, *next;

    for(current = request_block;current;current = next) {
        time_t time_since = current_time - current->time_last;
        next = current->next;

        /* hmm, what if we are in "the middle" of a request and not
         * just waiting for a new one... perhaps check to see if anything
         * has been read via header position, etc... */
        if (current->kacount < ka_max && /* we *are* in a keepalive */
            (time_since >= ka_timeout) && /* ka timeout */
            !current->logline)  /* haven't read anything yet */
            current->status = DEAD; /* connection keepalive timed out */
        else if (time_since > REQUEST_TIMEOUT) {
            log_error_doc(current);
            fputs("connection timed out\n", stderr);
            current->status = DEAD;
        }
        if (current->buffer_end && current->status < DEAD) {
            if (FD_ISSET(current->fd, &block_write_fdset))
                ready_request(current);
            else {
                BOA_FD_SET(current->fd, &block_write_fdset);
            }
        } else {
            switch (current->status) {
            case WRITE:
            case PIPE_WRITE:
                if (FD_ISSET(current->fd, &block_write_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->fd, &block_write_fdset);
                }
                break;
            case BODY_WRITE:
                if (FD_ISSET(current->post_data_fd, &block_write_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->post_data_fd, &block_write_fdset);
                }
                break;
            case PIPE_READ:
                if (FD_ISSET(current->data_fd, &block_read_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->data_fd, &block_read_fdset);
                }
                break;
            case DONE:
                if (FD_ISSET(current->fd, &block_write_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->fd, &block_write_fdset);
                }
                break;
            case DEAD:
                ready_request(current);
                break;
            default:
                if (FD_ISSET(current->fd, &block_read_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->fd, &block_read_fdset);
                }
                break;
            }
        }
        current = next;
    }
}
Beispiel #5
0
void loop(int server_s)
{
    FD_ZERO(BOA_READ);
    FD_ZERO(BOA_WRITE);

    max_fd = -1;

    while (1) {
        /* handle signals here */
        if (sighup_flag)
            sighup_run();
        if (sigchld_flag)
            sigchld_run();
        if (sigalrm_flag)
            sigalrm_run();

        if (sigterm_flag) {
            /* sigterm_flag:
             * 1. caught, unprocessed.
             * 2. caught, stage 1 processed
             */
            if (sigterm_flag == 1) {
                sigterm_stage1_run();
                BOA_FD_CLR(req, server_s, BOA_READ);
                close(server_s);
                /* make sure the server isn't in the block list */
                server_s = -1;
            }
            if (sigterm_flag == 2 && !request_ready && !request_block) {
                sigterm_stage2_run(); /* terminal */
            }
        } else {
            if (total_connections > max_connections) {
                /* FIXME: for poll we don't subtract 20. why? */
                BOA_FD_CLR(req, server_s, BOA_READ);
            } else {
                BOA_FD_SET(req, server_s, BOA_READ); /* server always set */
            }
        }

        pending_requests = 0;
        /* max_fd is > 0 when something is blocked */

        if (max_fd) {
            struct timeval req_timeout; /* timeval for select */

            req_timeout.tv_sec = (request_ready ? 0 : default_timeout);
            req_timeout.tv_usec = 0l; /* reset timeout */

            if (select(max_fd + 1, BOA_READ,
                       BOA_WRITE, NULL,
                       (request_ready || request_block ?
                        &req_timeout : NULL)) == -1) {
                /* what is the appropriate thing to do here on EBADF */
                if (errno == EINTR)
                    continue;       /* while(1) */
                else if (errno != EBADF) {
                    DIE("select");
                }
            }
            /* FIXME: optimize for when select returns 0 (timeout).
             * Thus avoiding many operations in fdset_update
             * and others.
             */
            if (!sigterm_flag && FD_ISSET(server_s, BOA_READ)) {
                pending_requests = 1;
            }
            time(&current_time); /* for "new" requests if we've been in
            * select too long */
            /* if we skip this section (for example, if max_fd == 0),
             * then we aren't listening anyway, so we can't accept
             * new conns.  Don't worry about it.
             */
        }

        /* reset max_fd */
        max_fd = -1;

        if (request_block) {
            /* move selected req's from request_block to request_ready */
            fdset_update();
        }

        /* any blocked req's move from request_ready to request_block */
        if (pending_requests || request_ready) {
            process_requests(server_s);
        }
    }
}
Beispiel #6
0
static void free_request(request * req)
{
    int i;
    /* free_request should *never* get called by anything but
       process_requests */

    if (req->buffer_end && req->status < TIMED_OUT) {
        /*
         WARN("request sent to free_request before DONE.");
         */
        req->status = DONE;

        /* THIS IS THE SAME CODE EXECUTED BY THE 'DONE' SECTION
         * of process_requests. It must be exactly the same!
         */
        i = req_flush(req);
        /*
         * retval can be -2=error, -1=blocked, or bytes left
         */
        if (i == -2) {          /* error */
            req->status = DEAD;
        } else if (i > 0) {
            return;
        }
    }
    /* put request on the free list */
    dequeue(&request_ready, req); /* dequeue from ready or block list */

    /* set response status to 408 if the client has timed out */
    if (req->status == TIMED_OUT && req->response_status == 0)
        req->response_status = 408;

    /* always log */
    log_access(req);

    if (req->mmap_entry_var)
        release_mmap(req->mmap_entry_var);
    else if (req->data_mem)
        munmap(req->data_mem, req->filesize);

    if (req->data_fd) {
        close(req->data_fd);
        BOA_FD_CLR(req, req->data_fd, BOA_READ);
    }

    if (req->post_data_fd) {
        close(req->post_data_fd);
        BOA_FD_CLR(req, req->post_data_fd, BOA_WRITE);
    }

    if (req->response_status >= 400)
        status.errors++;

    for (i = COMMON_CGI_COUNT; i < req->cgi_env_index; ++i) {
        if (req->cgi_env[i]) {
            free(req->cgi_env[i]);
        } else {
            log_error_time();
            fprintf(stderr, "Warning: CGI Environment contains NULL value"
                    "(index %d of %d).\n", i, req->cgi_env_index);
        }
    }

    if (req->pathname)
        free(req->pathname);
    if (req->path_info)
        free(req->path_info);
    if (req->path_translated)
        free(req->path_translated);
    if (req->script_name)
        free(req->script_name);
    if (req->host)
        free(req->host);
    if (req->ranges)
        ranges_reset(req);

    if (req->status < TIMED_OUT && (req->keepalive == KA_ACTIVE) &&
        (req->response_status < 500 && req->response_status != 0) && req->kacount > 0) {
        sanitize_request(req, 0);

        --(req->kacount);

        status.requests++;
        enqueue(&request_block, req);
        BOA_FD_SET(req, req->fd, BOA_READ);
        BOA_FD_CLR(req, req->fd, BOA_WRITE);
        return;
    }

    /*
       While debugging some weird errors, Jon Nelson learned that
       some versions of Netscape Navigator break the
       HTTP specification.

       Some research on the issue brought up:

       http://www.apache.org/docs/misc/known_client_problems.html

       As quoted here:

       "
       Trailing CRLF on POSTs

       This is a legacy issue. The CERN webserver required POST
       data to have an extra CRLF following it. Thus many
       clients send an extra CRLF that is not included in the
       Content-Length of the request. Apache works around this
       problem by eating any empty lines which appear before a
       request.
       "

       Boa will (for now) hack around this stupid bug in Netscape
       (and Internet Exploder)
       by reading up to 32k after the connection is all but closed.
       This should eliminate any remaining spurious crlf sent
       by the client.

       Building bugs *into* software to be compatible is
       just plain wrong
     */

    if (req->method == M_POST) {
        char buf[32768];
        read(req->fd, buf, sizeof(buf));
    }
    close(req->fd);
    BOA_FD_CLR(req, req->fd, BOA_READ);
    BOA_FD_CLR(req, req->fd, BOA_WRITE);
    total_connections--;

    enqueue(&request_free, req);

    return;
}
Beispiel #7
0
void select_loop(int server_s)
{
    FD_ZERO(&block_read_fdset);
    FD_ZERO(&block_write_fdset);
    /* set server_s and req_timeout */
    req_timeout.tv_sec = (ka_timeout ? ka_timeout : REQUEST_TIMEOUT);
    req_timeout.tv_usec = 0l;   /* reset timeout */

    /* preset max_fd */
    max_fd = -1;

    while (1) {
        if (sighup_flag)
            sighup_run();

        if (sigchld_flag)
            sigchld_run();

        if (sigalrm_flag)
            sigalrm_run();

        if (sigterm_flag) {
            if (sigterm_flag == 1)
                sigterm_stage1_run(server_s);

            if (sigterm_flag == 2 /*&& !request_ready && !request_block*/) {
                sigterm_stage2_run();

            }
        }

        /* reset max_fd */
        max_fd = -1;

        if (request_block)
        {
            /* move selected req's from request_block to request_ready */
            //fprintf(stderr, "fdsetupdated\n");
            fdset_update();
        }

        /* any blocked req's move from request_ready to request_block */

        process_requests(server_s);

        if (!sigterm_flag && total_connections < (max_connections - 10)
#if  defined(TCSUPPORT_WEBSERVER_SSL)
                &&  http_mode != HTTPS_ONLY
#endif
           ) {
            BOA_FD_SET(server_s, &block_read_fdset);
        }

#if  defined(TCSUPPORT_WEBSERVER_SSL)
        if (!sigterm_flag  &&  total_connections < (max_connections - 10)  &&  http_mode != HTTP_ONLY)
        {
            BOA_FD_SET(server_ssl, &block_read_fdset);
        }
#endif

        req_timeout.tv_sec = (request_ready ? 0 :
                              (ka_timeout ? ka_timeout : REQUEST_TIMEOUT));

        req_timeout.tv_usec = 0l;   /* reset timeout */


        if (select(max_fd + 1, &block_read_fdset,
                   &block_write_fdset, NULL,
                   (request_ready || request_block ? &req_timeout : NULL)) == -1) {
            /* what is the appropriate thing to do here on EBADF */
            if (errno == EINTR)
            {
                continue;   /* while(1) */
            }
            else if (errno != EBADF) {
                DIE("select");
            }
        }

        time(&current_time);

        if (FD_ISSET(server_s, &block_read_fdset))
        {
            pending_requests = 1;
        }

#if  defined(TCSUPPORT_WEBSERVER_SSL)
        if (FD_ISSET(server_ssl, &block_read_fdset))
        {
            ssl_pending_requests = 1;
        }
#endif
    }
}
Beispiel #8
0
void loop(int server_s)
{
    FD_ZERO(BOA_READ);
    FD_ZERO(BOA_WRITE);

    max_fd = -1;

    while (1) {
        /* handle signals here */
        if (sighup_flag)
            sighup_run();
        if (sigchld_flag)
            sigchld_run();
        if (sigalrm_flag)
            sigalrm_run();

        if (sigterm_flag) {
            /* sigterm_flag:
             * 1. caught, unprocessed.
             * 2. caught, stage 1 processed
             */
            if (sigterm_flag == 1) {
                sigterm_stage1_run();
                BOA_FD_CLR(req, server_s, BOA_READ);
                close(server_s);
                /* make sure the server isn't in the block list */
                server_s = -1;
            }
            if (sigterm_flag == 2 && !request_ready && !request_block) {
                sigterm_stage2_run(); /* terminal */
            }
        } else {
            if (total_connections > max_connections) {
                /* FIXME: for poll we don't subtract 20. why? */
                BOA_FD_CLR(req, server_s, BOA_READ);
            } else {
                BOA_FD_SET(req, server_s, BOA_READ); /* server always set */
            }
        }
	if (isREBOOTASP == 1) {
			if(last_req_after_upgrade != req_after_upgrade){
					last_req_after_upgrade = req_after_upgrade;
			}
	}

        pending_requests = 0;
        /* max_fd is > 0 when something is blocked */

        if (max_fd) {
            struct timeval req_timeout; /* timeval for select */

            req_timeout.tv_sec = (request_ready ? 0 : default_timeout);
            req_timeout.tv_usec = 0l; /* reset timeout */

            if (select(max_fd + 1, BOA_READ,
                       BOA_WRITE, NULL,
                       (request_ready || request_block ?
                        &req_timeout : NULL)) == -1) {
                /* what is the appropriate thing to do here on EBADF */
                if (errno == EINTR) {
			//fprintf(stderr,"####%s:%d isFWUPGRADE=%d isREBOOTASP=%d###\n",  __FILE__, __LINE__ ,isFWUPGRADE , isREBOOTASP);
			//fprintf(stderr,"####%s:%d last_req_after_upgrade=%d req_after_upgrade=%d confirm_last_req=%d###\n",  __FILE__, __LINE__ ,last_req_after_upgrade , req_after_upgrade, confirm_last_req);
                	if (isFWUPGRADE !=0 && isREBOOTASP == 1 ) {
                		if (last_req_after_upgrade == req_after_upgrade)
              				confirm_last_req++;
                		if (confirm_last_req >3)
                			goto ToUpgrade;
                	} else if(isCFGUPGRADE ==2  && isREBOOTASP == 1 ) {
                		goto ToReboot;
                	}
                	else if (isFWUPGRADE ==0 && isREBOOTASP == 1) {
                		if (last_req_after_upgrade == req_after_upgrade)
              				confirm_last_req++;
                		if (confirm_last_req >3) {
                			isFWUPGRADE = 0;
                			isREBOOTASP = 0;
                			//isFAKEREBOOT = 0;
                			confirm_last_req=0;
              			}
                	}
                    continue;       /* while(1) */                
                }
                else if (errno != EBADF) {
                    DIE("select");
                }
            }
            /* FIXME: optimize for when select returns 0 (timeout).
             * Thus avoiding many operations in fdset_update
             * and others.
             */
            if (!sigterm_flag && FD_ISSET(server_s, BOA_READ)) {
                pending_requests = 1;
            }
            time(&current_time); /* for "new" requests if we've been in
            * select too long */
            /* if we skip this section (for example, if max_fd == 0),
             * then we aren't listening anyway, so we can't accept
             * new conns.  Don't worry about it.
             */
        }

        /* reset max_fd */
        max_fd = -1;

        if (request_block) {
            /* move selected req's from request_block to request_ready */
            fdset_update();
        }

        /* any blocked req's move from request_ready to request_block */
        if (pending_requests || request_ready) {
        	if (isFWUPGRADE !=0 && isREBOOTASP == 1 ){
        		req_after_upgrade++;
        	}else if(isFWUPGRADE ==0 && isREBOOTASP == 1){
        		req_after_upgrade++;
        	}
            process_requests(server_s);
            continue;
        }

ToUpgrade:
	 if (isFWUPGRADE !=0 && isREBOOTASP == 1 ) {
		char buffer[200];
		
		//fprintf(stderr,"\r\n [%s-%u] FirmwareUpgrade start",__FILE__,__LINE__);
		FirmwareUpgrade(firmware_data, firmware_len, 0, buffer);
		//fprintf(stderr,"\r\n [%s-%u] FirmwareUpgrade end",__FILE__,__LINE__);
		//system("echo 7 > /proc/gpio"); // disable system LED
		isFWUPGRADE=0;
		isREBOOTASP=0;
		//reboot_time = 5;
     		break;
     	}

ToReboot:
	 if(isCFGUPGRADE == 2 && isREBOOTASP ==1) {
	 	isCFGUPGRADE=0;
		isREBOOTASP=0;
		system("reboot");
		for(;;);
	 }
    }
}
Beispiel #9
0
static void fdset_update(void)
{
    request *current, *next;

    time(&current_time);
    for (current = request_block; current; current = next) {
        time_t time_since = current_time - current->time_last;
        next = current->next;

        /* hmm, what if we are in "the middle" of a request and not
         * just waiting for a new one... perhaps check to see if anything
         * has been read via header position, etc... */
        if (current->kacount < ka_max && /* we *are* in a keepalive */
            (time_since >= ka_timeout) && /* ka timeout */
            !current->logline) { /* haven't read anything yet */
            log_error_doc(current);
            fputs("connection timed out\n", stderr);
            current->status = TIMED_OUT; /* connection timed out */
        } else if (time_since > REQUEST_TIMEOUT) {
            log_error_doc(current);
            fputs("connection timed out\n", stderr);
            current->status = TIMED_OUT; /* connection timed out */
        }
        if (current->buffer_end && /* there is data to write */
            current->status < DONE) {
            if (FD_ISSET(current->fd, BOA_WRITE))
                ready_request(current);
            else {
                BOA_FD_SET(current, current->fd, BOA_WRITE);
            }
        } else {
            switch (current->status) {
            case IOSHUFFLE:
#ifndef HAVE_SENDFILE
                if (current->buffer_end - current->buffer_start == 0) {
                    if (FD_ISSET(current->data_fd, BOA_READ))
                        ready_request(current);
                    break;
                }
#endif
            case WRITE:
            case PIPE_WRITE:
                if (FD_ISSET(current->fd, BOA_WRITE))
                    ready_request(current);
                else {
                    BOA_FD_SET(current, current->fd, BOA_WRITE);
                }
                break;
            case BODY_WRITE:
// davidhsu ------------------------------
#ifndef NEW_POST
                if (FD_ISSET(current->post_data_fd, BOA_WRITE))
                    ready_request(current);
                else {
                    BOA_FD_SET(current, current->post_data_fd,
                               BOA_WRITE);
                }
#else
		ready_request(current);
#endif
//--------------------------------------				
                break;
            case PIPE_READ:
                if (FD_ISSET(current->data_fd, BOA_READ))
                    ready_request(current);
                else {
                    BOA_FD_SET(current, current->data_fd,
                               BOA_READ);
                }
                break;
            case DONE:
                if (FD_ISSET(current->fd, BOA_WRITE))
                    ready_request(current);
                else {
                    BOA_FD_SET(current, current->fd, BOA_WRITE);
                }
                break;
            case TIMED_OUT:
            case DEAD:
                ready_request(current);
                break;
            default:
                if (FD_ISSET(current->fd, BOA_READ))
                    ready_request(current);
                else {
                    BOA_FD_SET(current, current->fd, BOA_READ);
                }
                break;
            }
        }
        current = next;
    }
}
Beispiel #10
0
static void free_request(request ** list_head_addr, request * req)
{
    int i;
    /* free_request should *never* get called by anything but
       process_requests */

    if (req->buffer_end && req->status != DEAD) {
        req->status = DONE;
        return;
    }
    /* put request on the free list */
    dequeue(list_head_addr, req); /* dequeue from ready or block list */

    if (req->logline)           /* access log */
        log_access(req);

    if (req->mmap_entry_var)
        release_mmap(req->mmap_entry_var);
    else if (req->data_mem)
        munmap(req->data_mem, req->filesize);

    if (req->data_fd)
        close(req->data_fd);

    if (req->post_data_fd)
        close(req->post_data_fd);

    if (req->response_status >= 400)
        status.errors++;

    for (i = COMMON_CGI_COUNT; i < req->cgi_env_index; ++i) {
        if (req->cgi_env[i]) {
            free(req->cgi_env[i]);
        } else {
            log_error_time();
            fprintf(stderr, "Warning: CGI Environment contains NULL value" \
                    "(index %d of %d).\n", i, req->cgi_env_index);
        }
    }

    if (req->pathname)
        free(req->pathname);
    if (req->path_info)
        free(req->path_info);
    if (req->path_translated)
        free(req->path_translated);
    if (req->script_name)
        free(req->script_name);

    if ((req->keepalive == KA_ACTIVE) &&
            (req->response_status < 500) && req->kacount > 0) {
        int bytes_to_move;

        request *conn = new_request();
        if (!conn) {
            /* errors already reported */
            enqueue(&request_free, req);
            close(req->fd);
            total_connections--;
            return;
        }
        conn->fd = req->fd;
        conn->status = READ_HEADER;
        conn->header_line = conn->client_stream;
        conn->kacount = req->kacount - 1;

        /* close enough and we avoid a call to time(NULL) */
        conn->time_last = req->time_last;

        /* for log file and possible use by CGI programs */
        memcpy(conn->remote_ip_addr, req->remote_ip_addr, NI_MAXHOST);
        memcpy(conn->local_ip_addr, req->local_ip_addr, NI_MAXHOST);

        /* for possible use by CGI programs */
        conn->remote_port = req->remote_port;

        status.requests++;

        /* we haven't parsed beyond req->parse_pos, so... */
        bytes_to_move = req->client_stream_pos - req->parse_pos;

        if (bytes_to_move) {
            memcpy(conn->client_stream,
                    req->client_stream + req->parse_pos, bytes_to_move);
            conn->client_stream_pos = bytes_to_move;
        }
        enqueue(&request_block, conn);
        BOA_FD_SET(conn->fd, &block_read_fdset);

        enqueue(&request_free, req);
        return;
    }

    /*
       While debugging some weird errors, Jon Nelson learned that
       some versions of Netscape Navigator break the
       HTTP specification.

       Some research on the issue brought up:

http://www.apache.org/docs/misc/known_client_problems.html

As quoted here:

"
Trailing CRLF on POSTs

This is a legacy issue. The CERN webserver required POST
data to have an extra CRLF following it. Thus many
clients send an extra CRLF that is not included in the
Content-Length of the request. Apache works around this
problem by eating any empty lines which appear before a
request.
"

Boa will (for now) hack around this stupid bug in Netscape
(and Internet Exploder)
by reading up to 32k after the connection is all but closed.
This should eliminate any remaining spurious crlf sent
by the client.

Building bugs *into* software to be compatable is
just plain wrong
*/

    if (req->method == M_POST) {
        char buf[32768];
        read(req->fd, buf, 32768);
    }
    close(req->fd);
    total_connections--;

    enqueue(&request_free, req);

    return;
}