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 */
};