William A. Rowe, Jr. wrote:
Joe Orton wrote:

Dies how, what error, what is the server left doing? This needs further diagnosis; truss the server, loglevel debug output, etc. Does it work on the trunk?

Ok - I added the same disallow-empty brigade logic that's in the core
ap_rgetline_core() - just to prove up/prove down that this is a problem.

This modified mod_echo.c is attached - which also does some scoreboard
tracking (and proves up mod_status.c is wrong, we -can- and should
display the request, etc; we really need two modes, one SERVER_BUSY_READ
and SERVER_BUSY_READBODY - after the request headers were processed in
the http protocol handler).

The resulting error is;

[Tue Oct 04 09:18:51 2005] [info] ProtocolEcho: Error - read empty brigade from 10.0.8.13!

Here's a snip...

poll(0xFC407BE0, 2, -1)         (sleeping...)
lwp_sema_wait(0xFE807E30)       (sleeping...)
door_return(0x00000000, 0, 0x00000000, 0) (sleeping...)
poll(0xFC407BE0, 2, -1)                         = 1
accept(5, 0x0017CC84, 0x0017CCA4, 1)            = 10
fcntl(9, F_SETLKW, 0xFF2DD2E8)                  = 0
lwp_sema_post(0xFE807E30)                       = 0
lwp_sema_wait(0xFE807E30)                       = 0
lwp_mutex_wakeup(0xFF0C5558)                    = 0
lwp_mutex_lock(0xFF0C5558)                      = 0
lwp_sema_post(0xFE705E30)                       = 0
lwp_sema_wait(0xFE705E30)                       = 0
lwp_mutex_wakeup(0xFF0C5558)                    = 0
lwp_mutex_lock(0xFF0C5558)                      = 0
getsockname(10, 0x0017CC3C, 0x0017CC5C, 1)      = 0
read(10, 0x00180BD8, 8000)                      Err#11 EAGAIN
write(6, " [ T u e   O c t   0 4  ".., 91)      = 91
shutdown(10, 1, 1)                              = 0
fcntl(9, F_SETLKW, 0xFF2DD2C4)                  = 0
poll(0xFE7057A0, 1, 2000)                       = 1
read(10, 0xFE7059A8, 512)                       = 0
close(10)                                       = 0
lwp_cond_wait(0xFF0C5548, 0xFF0C5558, 0xFF0BEDB0) (sleeping...)

As you can see, read returns EAGAIN, and nothing in the core GETLINE
mode handles the manditory retry on APR_BLOCK mode.

Apparently on linux, we set up the sockopt differently...

[pid 18543] poll([{fd=4, events=POLLIN, revents=POLLIN}, {fd=3, events=POLLIN}], 2, -1) = 1 [pid 18543] accept(4, {sa_family=AF_INET6, sin6_port=htons(56559), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 8
[pid 18543] semop(163843, 0x89e752, 1)  = 0
[pid 18543] futex(0x912fb1c, FUTEX_WAKE, 1 <unfinished ...>
[pid 18540] <... futex resumed> )       = 0
[pid 18543] <... futex resumed> )       = 1
[pid 18540] futex(0x912fb18, FUTEX_WAIT, 2, NULL <unfinished ...>
[pid 18543] futex(0x912fb18, FUTEX_WAKE, 1 <unfinished ...>
[pid 18540] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 18543] <... futex resumed> )       = 0
[pid 18540] futex(0x912fb18, FUTEX_WAKE, 1 <unfinished ...>
[pid 18543] semop(163843, 0x89e74c, 1 <unfinished ...>
[pid 18540] <... futex resumed> )       = 0
[pid 18543] <... semop resumed> )       = 0
[pid 18543] poll( <unfinished ...>
[pid 18540] futex(0x912fae4, FUTEX_WAKE, 1) = 0
[pid 18540] getsockname(8, {sa_family=AF_INET6, sin6_port=htons(8007), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
[pid 18540] gettimeofday({1128449197, 353743}, NULL) = 0
[pid 18540] read(8, "test\r\n", 8000)   = 6
[pid 18540] writev(8, [{"test\r\n", 6}], 1) = 6

and thus, we have some data captured in the first call by mod_echo.

Notice the -big- difference.  On linux, we hit poll() before read().

On solaris, for some oddball reason, we don't poll, we don't loop.
Not pretty.

Bill
/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
 * applicable.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ap_config.h"
#include "ap_mmn.h"
#include "httpd.h"
#include "http_config.h"
#include "http_connection.h"
#include "http_core.h"
#include "http_log.h"

#include "apr_buckets.h"
#include "apr_strings.h"
#include "util_filter.h"
#include "scoreboard.h"

module AP_MODULE_DECLARE_DATA echo_module;

typedef struct {
    int bEnabled;
} EchoConfig;

static void *create_echo_server_config(apr_pool_t *p, server_rec *s)
{
    EchoConfig *pConfig = apr_pcalloc(p, sizeof *pConfig);

    pConfig->bEnabled = 0;

    return pConfig;
}

static const char *echo_on(cmd_parms *cmd, void *dummy, int arg)
{
    EchoConfig *pConfig = ap_get_module_config(cmd->server->module_config,
                                               &echo_module);
    pConfig->bEnabled = arg;

    return NULL;
}

struct ap_sb_handle_t {
    int child_num;
    int thread_num;
};

static int update_echo_child_status(ap_sb_handle_t *sbh, 
                                    int status, conn_rec *c)
{
    int old_status;
    worker_score *ws;

    if (sbh->child_num < 0) {
        return -1;
    }
    ws = ap_get_scoreboard_worker(sbh->child_num, sbh->thread_num);
    old_status = ws->status;
    ws->status = status;

    if (!ap_extended_status) {
        return old_status;
    }

    ws->last_used = apr_time_now();

    if (!c) {
        return old_status;
    }
    apr_cpystrn(ws->client, 
                ap_get_remote_host(c, c->base_server->lookup_defaults,
                                   REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
    apr_cpystrn(ws->request, "ECHO", sizeof(ws->request));
    apr_cpystrn(ws->vhost, c->base_server->server_hostname, sizeof(ws->vhost));
    return old_status;
}

static int process_echo_connection(conn_rec *c)
{
    apr_bucket_brigade *bb;
    apr_bucket *b;
    apr_status_t rv;
    EchoConfig *pConfig = ap_get_module_config(c->base_server->module_config,
                                               &echo_module);

    if (!pConfig->bEnabled) {
        return DECLINED;
    }

    ap_time_process_request(c->sbh, START_PREQUEST);    
    update_echo_child_status(c->sbh, SERVER_BUSY_READ, c);

    bb = apr_brigade_create(c->pool, c->bucket_alloc);

    for ( ; ; ) {
        /* Get a single line of input from the client */
        update_echo_child_status(c->sbh, SERVER_BUSY_READ, NULL);
        rv = ap_get_brigade(c->input_filters, bb, AP_MODE_GETLINE,
                            APR_BLOCK_READ, 0);

        if (rv == APR_EOF)
            break;

        if (rv != APR_SUCCESS) {
            ap_log_error(APLOG_MARK, APLOG_INFO, rv, c->base_server,
                         "ProtocolEcho: Failure reading from %s",
                         c->remote_ip);
            break; 
        }

        /* Something horribly wrong happened.  Someone didn't block! */
        if (APR_BRIGADE_EMPTY(bb)) {
            ap_log_error(APLOG_MARK, APLOG_INFO, rv, c->base_server,
                         "ProtocolEcho: Error - read empty brigade from %s!",
                         c->remote_ip);
            break;
        }

        /* Make sure the data is flushed to the client */
        b = apr_bucket_flush_create(c->bucket_alloc);
        update_echo_child_status(c->sbh, SERVER_BUSY_WRITE, NULL);
        APR_BRIGADE_INSERT_TAIL(bb, b);
        rv = ap_pass_brigade(c->output_filters, bb);
        if (rv != APR_SUCCESS) {
            ap_log_error(APLOG_MARK, APLOG_INFO, rv, c->base_server,
                         "ProtocolEcho: Failure reading from %s",
                         c->remote_ip);
            break; 
        }
        apr_brigade_cleanup(bb);
    }
    apr_brigade_destroy(bb);
    ap_time_process_request(c->sbh, STOP_PREQUEST);    
    update_echo_child_status(c->sbh, SERVER_CLOSING, NULL);
    return OK;
}

static const command_rec echo_cmds[] = 
{
    AP_INIT_FLAG("ProtocolEcho", echo_on, NULL, RSRC_CONF,
                 "Run an echo server on this host"),
    { NULL }
};

static void register_hooks(apr_pool_t *p)
{
    ap_hook_process_connection(process_echo_connection, NULL, NULL,
                               APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA echo_module = {
    STANDARD20_MODULE_STUFF,
    NULL,			/* create per-directory config structure */
    NULL,			/* merge per-directory config structures */
    create_echo_server_config,	/* create per-server config structure */
    NULL,			/* merge per-server config structures */
    echo_cmds,			/* command apr_table_t */
    register_hooks		/* register hooks */
};

Reply via email to