Index: server/config.c =================================================================== --- server/config.c (.../2.2.3) (revision 493482) +++ server/config.c (.../2.2.4) (revision 493482) @@ -326,6 +326,7 @@ const char *p; int result; const char *old_handler = r->handler; + const char *ignore; /* * The new insert_filter stage makes the most sense here. We only use @@ -376,6 +377,22 @@ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "handler \"%s\" not found for: %s", r->handler, r->filename); } + if ((result != OK) && (result != DONE) && (result != DECLINED) + && !ap_is_HTTP_VALID_RESPONSE(result)) { + /* If a module is deliberately returning something else + * (request_rec in non-HTTP or proprietary extension?) + * let it set a note to allow it explicitly. + * Otherwise, a return code that is neither reserved nor HTTP + * is a bug, as in PR#31759. + */ + ignore = apr_table_get(r->notes, "HTTP_IGNORE_RANGE"); + if (!ignore) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Handler for %s returned invalid result code %d", + r->handler, result); + result = HTTP_INTERNAL_SERVER_ERROR; + } + } return result == DECLINED ? HTTP_INTERNAL_SERVER_ERROR : result; } Index: server/mpm/winnt/service.c =================================================================== --- server/mpm/winnt/service.c (.../2.2.3) (revision 493482) +++ server/mpm/winnt/service.c (.../2.2.4) (revision 493482) @@ -436,7 +436,7 @@ /* Time to fix up the description, upon each successful restart */ - full_description = ap_get_server_version(); + full_description = ap_get_server_description(); if ((osver.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osver.dwMajorVersion > 4) Index: server/mpm/winnt/mpm_winnt.c =================================================================== --- server/mpm/winnt/mpm_winnt.c (.../2.2.3) (revision 493482) +++ server/mpm/winnt/mpm_winnt.c (.../2.2.4) (revision 493482) @@ -1668,7 +1668,7 @@ /* A real-honest to goodness parent */ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "%s configured -- resuming normal operations", - ap_get_server_version()); + ap_get_server_description()); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "Server built: %s", ap_get_server_built()); Index: server/mpm/winnt/mpm_winnt.h =================================================================== --- server/mpm/winnt/mpm_winnt.h (.../2.2.3) (revision 493482) +++ server/mpm/winnt/mpm_winnt.h (.../2.2.4) (revision 493482) @@ -32,7 +32,7 @@ #define SERVICE_APACHE_RESTART 128 #ifndef AP_DEFAULT_SERVICE_NAME -#define AP_DEFAULT_SERVICE_NAME "Apache2" +#define AP_DEFAULT_SERVICE_NAME "Apache2.2" #endif #define SERVICECONFIG9X "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices" Index: server/mpm/mpmt_os2/mpmt_os2.c =================================================================== --- server/mpm/mpmt_os2/mpmt_os2.c (.../2.2.3) (revision 493482) +++ server/mpm/mpmt_os2/mpmt_os2.c (.../2.2.4) (revision 493482) @@ -207,7 +207,7 @@ int listener_num, num_listeners, slot; ULONG rc; - printf("%s \n", ap_get_server_version()); + printf("%s \n", ap_get_server_description()); set_signals(); if (ap_setup_listeners(ap_server_conf) < 1) { @@ -270,7 +270,7 @@ ap_scoreboard_image->global->restart_time = apr_time_now(); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "%s configured -- resuming normal operations", - ap_get_server_version()); + ap_get_server_description()); ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, "Server built: %s", ap_get_server_built()); #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH Index: server/mpm/netware/mpm_netware.c =================================================================== --- server/mpm/netware/mpm_netware.c (.../2.2.3) (revision 493482) +++ server/mpm/netware/mpm_netware.c (.../2.2.4) (revision 493482) @@ -723,7 +723,7 @@ request_count = 0; ClearScreen (getscreenhandle()); - printf("%s \n", ap_get_server_version()); + printf("%s \n", ap_get_server_description()); for (i=0;iserver_signature == srv_sig_withmail) { return apr_pstrcat(r->pool, prefix, "
", - ap_get_server_version(), + ap_get_server_banner(), " Server at server->server_admin) ? "" : "mailto:", ap_escape_html(r->pool, r->server->server_admin), @@ -2671,7 +2671,7 @@ "
\n", NULL); } - return apr_pstrcat(r->pool, prefix, "
", ap_get_server_version(), + return apr_pstrcat(r->pool, prefix, "
", ap_get_server_banner(), " Server at ", ap_escape_html(r->pool, ap_get_server_name(r)), " Port ", sport, @@ -2699,8 +2699,9 @@ * string. */ -static char *server_version = NULL; -static int version_locked = 0; +static char *server_banner = NULL; +static int banner_locked = 0; +static char *server_description = NULL; enum server_token_type { SrvTk_MAJOR, /* eg: Apache/2 */ @@ -2712,11 +2713,12 @@ }; static enum server_token_type ap_server_tokens = SrvTk_FULL; -static apr_status_t reset_version(void *dummy) +static apr_status_t reset_banner(void *dummy) { - version_locked = 0; + banner_locked = 0; ap_server_tokens = SrvTk_FULL; - server_version = NULL; + server_banner = NULL; + server_description = NULL; return APR_SUCCESS; } @@ -2728,40 +2730,56 @@ version->add_string = AP_SERVER_ADD_STRING; } +AP_DECLARE(const char *) ap_get_server_description(void) +{ + return server_description ? server_description : + AP_SERVER_BASEVERSION " (" PLATFORM ")"; +} + +AP_DECLARE(const char *) ap_get_server_banner(void) +{ + return server_banner ? server_banner : AP_SERVER_BASEVERSION; +} + +/* ap_get_server_version() is deprecated. ap_get_server_banner() + * provides the same semantics. + */ AP_DECLARE(const char *) ap_get_server_version(void) { - return (server_version ? server_version : AP_SERVER_BASEVERSION); + return ap_get_server_banner(); } AP_DECLARE(void) ap_add_version_component(apr_pool_t *pconf, const char *component) { - if (! version_locked) { + if (! banner_locked) { /* * If the version string is null, register our cleanup to reset the * pointer on pool destruction. We also know that, if NULL, * we are adding the original SERVER_BASEVERSION string. */ - if (server_version == NULL) { - apr_pool_cleanup_register(pconf, NULL, reset_version, + if (server_banner == NULL) { + apr_pool_cleanup_register(pconf, NULL, reset_banner, apr_pool_cleanup_null); - server_version = apr_pstrdup(pconf, component); + server_banner = apr_pstrdup(pconf, component); } else { /* * Tack the given component identifier to the end of * the existing string. */ - server_version = apr_pstrcat(pconf, server_version, " ", - component, NULL); + server_banner = apr_pstrcat(pconf, server_banner, " ", + component, NULL); } } + server_description = apr_pstrcat(pconf, server_description, " ", + component, NULL); } /* - * This routine adds the real server base identity to the version string, + * This routine adds the real server base identity to the banner string, * and then locks out changes until the next reconfig. */ -static void ap_set_version(apr_pool_t *pconf) +static void set_banner(apr_pool_t *pconf) { if (ap_server_tokens == SrvTk_PRODUCT_ONLY) { ap_add_version_component(pconf, AP_SERVER_BASEPRODUCT); @@ -2780,12 +2798,13 @@ } /* - * Lock the server_version string if we're not displaying + * Lock the server_banner string if we're not displaying * the full set of tokens */ if (ap_server_tokens != SrvTk_FULL) { - version_locked++; + banner_locked++; } + server_description = AP_SERVER_BASEVERSION " (" PLATFORM ")"; } static const char *set_serv_tokens(cmd_parms *cmd, void *dummy, @@ -3756,7 +3775,7 @@ logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out); ident_lookup = APR_RETRIEVE_OPTIONAL_FN(ap_ident_lookup); - ap_set_version(pconf); + set_banner(pconf); ap_setup_make_content_type(pconf); return OK; } Index: server/listen.c =================================================================== --- server/listen.c (.../2.2.3) (revision 493482) +++ server/listen.c (.../2.2.4) (revision 493482) @@ -361,6 +361,9 @@ int num_open; const char *userdata_key = "ap_open_listeners"; void *data; +#if AP_NONBLOCK_WHEN_MULTI_LISTEN + int use_nonblock; +#endif /* Don't allocate a default listener. If we need to listen to a * port, then the user needs to have a Listen directive in their @@ -415,6 +418,9 @@ /* Remove the current listener from the list */ previous->next = lr->next; + lr = previous; /* maintain current value of previous after + * post-loop expression is evaluated + */ continue; } #endif @@ -473,16 +479,15 @@ * is already forgotten about by the time we call accept, we won't * be hung until another connection arrives on that port */ - if (ap_listeners && ap_listeners->next) { - for (lr = ap_listeners; lr; lr = lr->next) { - apr_status_t status; + use_nonblock = (ap_listeners && ap_listeners->next); + for (lr = ap_listeners; lr; lr = lr->next) { + apr_status_t status; - status = apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, 1); - if (status != APR_SUCCESS) { - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool, - "unable to make listening socket non-blocking"); - return -1; - } + status = apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, use_nonblock); + if (status != APR_SUCCESS) { + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool, + "unable to control socket non-blocking status"); + return -1; } } #endif /* AP_NONBLOCK_WHEN_MULTI_LISTEN */ Index: server/util_script.c =================================================================== --- server/util_script.c (.../2.2.3) (revision 493482) +++ server/util_script.c (.../2.2.4) (revision 493482) @@ -223,7 +223,7 @@ #endif apr_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r)); - apr_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version()); + apr_table_addn(e, "SERVER_SOFTWARE", ap_get_server_banner()); apr_table_addn(e, "SERVER_NAME", ap_escape_html(r->pool, ap_get_server_name(r))); apr_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */ Index: server/mpm_common.c =================================================================== --- server/mpm_common.c (.../2.2.3) (revision 493482) +++ server/mpm_common.c (.../2.2.4) (revision 493482) @@ -635,7 +635,7 @@ * requests in their logs. */ srequest = apr_pstrcat(p, "GET / HTTP/1.0\r\nUser-Agent: ", - ap_get_server_version(), + ap_get_server_banner(), " (internal dummy connection)\r\n\r\n", NULL); /* Since some operating systems support buffering of data or entire Index: server/log.c =================================================================== --- server/log.c (.../2.2.3) (revision 493482) +++ server/log.c (.../2.2.4) (revision 493482) @@ -225,8 +225,13 @@ "%s", description); } +/* Create a child process running PROGNAME with a pipe connected to + * the childs stdin. The write-end of the pipe will be placed in + * *FPIN on successful return. If dummy_stderr is non-zero, the + * stderr for the child will be the same as the stdout of the parent. + * Otherwise the child will inherit the stderr from the parent. */ static int log_child(apr_pool_t *p, const char *progname, - apr_file_t **fpin) + apr_file_t **fpin, int dummy_stderr) { /* Child process code for 'ErrorLog "|..."'; * may want a common framework for this, since I expect it will @@ -235,6 +240,7 @@ apr_status_t rc; apr_procattr_t *procattr; apr_proc_t *procnew; + apr_file_t *errfile; if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS) && ((rc = apr_procattr_cmdtype_set(procattr, @@ -244,7 +250,12 @@ APR_NO_PIPE, APR_NO_PIPE)) == APR_SUCCESS) && ((rc = apr_procattr_error_check_set(procattr, 1)) == APR_SUCCESS) - && ((rc = apr_procattr_child_errfn_set(procattr, log_child_errfn)) == APR_SUCCESS)) { + && ((rc = apr_procattr_child_errfn_set(procattr, log_child_errfn)) == APR_SUCCESS) + && (!dummy_stderr + || ((rc = apr_file_open_stdout(&errfile, p)) == APR_SUCCESS + && (rc = apr_procattr_child_err_set(procattr, + errfile, + errfile)) == APR_SUCCESS))) { char **args; const char *pname; @@ -261,12 +272,21 @@ * close_handle_in_child() */ } + + /* apr_procattr_child_err_set dups errfile twice: close the + * remaining "parent-side" copy (apr_proc_create closes the + * other). */ + if (dummy_stderr) { + apr_file_close(procnew->err); + } } return rc; } -static int open_error_log(server_rec *s, apr_pool_t *p) +/* Open the error log for the given server_rec. If IS_MAIN is + * non-zero, s is the main server. */ +static int open_error_log(server_rec *s, int is_main, apr_pool_t *p) { const char *fname; int rc; @@ -274,8 +294,11 @@ if (*s->error_fname == '|') { apr_file_t *dummy = NULL; - /* This starts a new process... */ - rc = log_child (p, s->error_fname + 1, &dummy); + /* Spawn a new child logger. If this is the main server_rec, + * the new child must use a dummy stderr since the current + * stderr might be a pipe to the old logger. Otherwise, the + * child inherits the parents stderr. */ + rc = log_child(p, s->error_fname + 1, &dummy, is_main); if (rc != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL, "Couldn't start ErrorLog process"); @@ -338,7 +361,7 @@ apr_pool_cleanup_register(p, NULL, clear_handle_list, apr_pool_cleanup_null); - if (open_error_log(s_main, p) != OK) { + if (open_error_log(s_main, 1, p) != OK) { return DONE; } @@ -377,7 +400,7 @@ } if (q == virt) { - if (open_error_log(virt, p) != OK) { + if (open_error_log(virt, 0, p) != OK) { return DONE; } } @@ -955,7 +978,7 @@ apr_file_t *dummy = NULL; int rc; - rc = log_child(p, program, &dummy); + rc = log_child(p, program, &dummy, 0); if (rc != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL, "Couldn't start piped log process"); Index: server/main.c =================================================================== --- server/main.c (.../2.2.3) (revision 493482) +++ server/main.c (.../2.2.4) (revision 493482) @@ -88,7 +88,7 @@ static void show_compile_settings(void) { - printf("Server version: %s\n", ap_get_server_version()); + printf("Server version: %s\n", ap_get_server_description()); printf("Server built: %s\n", ap_get_server_built()); printf("Server's Module Magic Number: %u:%u\n", MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR); @@ -549,7 +549,7 @@ break; case 'v': - printf("Server version: %s\n", ap_get_server_version()); + printf("Server version: %s\n", ap_get_server_description()); printf("Server built: %s\n", ap_get_server_built()); destroy_and_exit_process(process, 0); Property changes on: server ___________________________________________________________________ Name: svn:ignore - Makefile .deps .libs *.lo *.la test_char.h gen_test_char gen_test_char.exe export_files exports.c export_vars.h Debug Release ApacheCoreOS2.def httpd.exp *.plg *.dep *.mak BuildLog.htm *.stc *.stt *.sto *.vcproj buildmarked.c + Makefile .deps .libs *.lo *.la test_char.h gen_test_char gen_test_char.exe gen_test_char.exe.manifest export_files exports.c export_vars.h Debug Release ApacheCoreOS2.def httpd.exp *.plg *.dep *.mak BuildLog.htm *.stc *.stt *.sto *.vcproj buildmarked.c Index: modules/http/http_filters.c =================================================================== --- modules/http/http_filters.c (.../2.2.3) (revision 493482) +++ modules/http/http_filters.c (.../2.2.4) (revision 493482) @@ -737,7 +737,7 @@ } } else { - form_header_field(&h, "Server", ap_get_server_version()); + form_header_field(&h, "Server", ap_get_server_banner()); } /* unset so we don't send them again */ Index: modules/ldap/util_ldap.c =================================================================== --- modules/ldap/util_ldap.c (.../2.2.3) (revision 493482) +++ modules/ldap/util_ldap.c (.../2.2.4) (revision 493482) @@ -198,18 +198,10 @@ return APR_SUCCESS; } - -/* - * Connect to the LDAP server and binds. Does not connect if already - * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound. - * - * Returns LDAP_SUCCESS on success; and an error code on failure - */ -static int uldap_connection_open(request_rec *r, - util_ldap_connection_t *ldc) +static int uldap_connection_init(request_rec *r, + util_ldap_connection_t *ldc ) { int rc = 0; - int failures = 0; int version = LDAP_VERSION3; apr_ldap_err_t *result = NULL; struct timeval timeOut = {10,0}; /* 10 second connection timeout */ @@ -217,129 +209,144 @@ (util_ldap_state_t *)ap_get_module_config(r->server->module_config, &ldap_module); - /* sanity check for NULL */ - if (!ldc) { - return -1; - } + /* Since the host will include a port if the default port is not used, + * always specify the default ports for the port parameter. This will + * allow a host string that contains multiple hosts the ability to mix + * some hosts with ports and some without. All hosts which do not + * specify a port will use the default port. + */ + apr_ldap_init(ldc->pool, &(ldc->ldap), + ldc->host, + APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT, + APR_LDAP_NONE, + &(result)); - /* If the connection is already bound, return - */ - if (ldc->bound) - { - ldc->reason = "LDAP: connection open successful (already bound)"; - return LDAP_SUCCESS; + + if (result != NULL && result->rc) { + ldc->reason = result->reason; } - /* create the ldap session handle - */ if (NULL == ldc->ldap) { - /* Since the host will include a port if the default port is not used, - * always specify the default ports for the port parameter. This will - * allow a host string that contains multiple hosts the ability to mix - * some hosts with ports and some without. All hosts which do not - * specify a port will use the default port. - */ - apr_ldap_init(ldc->pool, &(ldc->ldap), - ldc->host, - APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT, - APR_LDAP_NONE, - &(result)); + ldc->bound = 0; + if (NULL == ldc->reason) { + ldc->reason = "LDAP: ldap initialization failed"; + } + else { + ldc->reason = result->reason; + } + return(result->rc); + } + /* always default to LDAP V3 */ + ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version); - if (result != NULL && result->rc) { + /* set client certificates */ + if (!apr_is_empty_array(ldc->client_certs)) { + apr_ldap_set_option(ldc->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT, + ldc->client_certs, &(result)); + if (LDAP_SUCCESS != result->rc) { + uldap_connection_unbind( ldc ); ldc->reason = result->reason; + return(result->rc); } + } - if (NULL == ldc->ldap) - { - ldc->bound = 0; - if (NULL == ldc->reason) { - ldc->reason = "LDAP: ldap initialization failed"; - } - else { - ldc->reason = result->reason; - } + /* switch on SSL/TLS */ + if (APR_LDAP_NONE != ldc->secure) { + apr_ldap_set_option(ldc->pool, ldc->ldap, + APR_LDAP_OPT_TLS, &ldc->secure, &(result)); + if (LDAP_SUCCESS != result->rc) { + uldap_connection_unbind( ldc ); + ldc->reason = result->reason; return(result->rc); } + } - /* always default to LDAP V3 */ - ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version); + /* Set the alias dereferencing option */ + ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref)); - /* set client certificates */ - if (!apr_is_empty_array(ldc->client_certs)) { - apr_ldap_set_option(ldc->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT, - ldc->client_certs, &(result)); - if (LDAP_SUCCESS != result->rc) { - ldap_unbind_s(ldc->ldap); - ldc->ldap = NULL; - ldc->bound = 0; - ldc->reason = result->reason; - return(result->rc); - } - } - - /* switch on SSL/TLS */ - if (APR_LDAP_NONE != ldc->secure) { - apr_ldap_set_option(ldc->pool, ldc->ldap, - APR_LDAP_OPT_TLS, &ldc->secure, &(result)); - if (LDAP_SUCCESS != result->rc) { - ldap_unbind_s(ldc->ldap); - ldc->ldap = NULL; - ldc->bound = 0; - ldc->reason = result->reason; - return(result->rc); - } - } - - /* Set the alias dereferencing option */ - ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref)); - /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */ #ifdef APR_LDAP_OPT_VERIFY_CERT - apr_ldap_set_option(ldc->pool, ldc->ldap, - APR_LDAP_OPT_VERIFY_CERT, &(st->verify_svr_cert), &(result)); + apr_ldap_set_option(ldc->pool, ldc->ldap, + APR_LDAP_OPT_VERIFY_CERT, &(st->verify_svr_cert), &(result)); #else #if defined(LDAPSSL_VERIFY_SERVER) - if (st->verify_svr_cert) { - result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER); - } - else { - result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE); - } + if (st->verify_svr_cert) { + result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER); + } + else { + result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE); + } #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT) - /* This is not a per-connection setting so just pass NULL for the - Ldap connection handle */ - if (st->verify_svr_cert) { - int i = LDAP_OPT_X_TLS_DEMAND; - result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i); - } - else { - int i = LDAP_OPT_X_TLS_NEVER; - result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i); - } + /* This is not a per-connection setting so just pass NULL for the + Ldap connection handle */ + if (st->verify_svr_cert) { + int i = LDAP_OPT_X_TLS_DEMAND; + result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i); + } + else { + int i = LDAP_OPT_X_TLS_NEVER; + result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i); + } #endif #endif #ifdef LDAP_OPT_NETWORK_TIMEOUT - if (st->connectionTimeout > 0) { - timeOut.tv_sec = st->connectionTimeout; - } + if (st->connectionTimeout > 0) { + timeOut.tv_sec = st->connectionTimeout; + } - if (st->connectionTimeout >= 0) { - rc = apr_ldap_set_option(ldc->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT, - (void *)&timeOut, &(result)); - if (APR_SUCCESS != rc) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, - "LDAP: Could not set the connection timeout"); - } + if (st->connectionTimeout >= 0) { + rc = apr_ldap_set_option(ldc->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT, + (void *)&timeOut, &(result)); + if (APR_SUCCESS != rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "LDAP: Could not set the connection timeout"); } + } #endif + return(rc); +} +/* + * Connect to the LDAP server and binds. Does not connect if already + * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound. + * + * Returns LDAP_SUCCESS on success; and an error code on failure + */ +static int uldap_connection_open(request_rec *r, + util_ldap_connection_t *ldc) +{ + int rc = 0; + int failures = 0; + + /* sanity check for NULL */ + if (!ldc) { + return -1; } + /* If the connection is already bound, return + */ + if (ldc->bound) + { + ldc->reason = "LDAP: connection open successful (already bound)"; + return LDAP_SUCCESS; + } + /* create the ldap session handle + */ + if (NULL == ldc->ldap) + { + rc = uldap_connection_init( r, ldc ); + if (LDAP_SUCCESS != rc) + { + return rc; + } + } + + /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is * returned. Break out of the loop on Success or any other error. * @@ -355,16 +362,22 @@ (char *)ldc->bindpw); if (LDAP_SERVER_DOWN != rc) { break; - } + } else if (failures == 5) { + /* attempt to init the connection once again */ + uldap_connection_unbind( ldc ); + rc = uldap_connection_init( r, ldc ); + if (LDAP_SUCCESS != rc) + { + break; + } + } } /* free the handle if there was an error */ if (LDAP_SUCCESS != rc) { - ldap_unbind_s(ldc->ldap); - ldc->ldap = NULL; - ldc->bound = 0; + uldap_connection_unbind(ldc); ldc->reason = "LDAP: ldap_simple_bind_s() failed"; } else { Index: modules/metadata/mod_headers.c =================================================================== --- modules/metadata/mod_headers.c (.../2.2.3) (revision 493482) +++ modules/metadata/mod_headers.c (.../2.2.4) (revision 493482) @@ -89,7 +89,8 @@ hdr_set = 's', /* set (replace old value) */ hdr_append = 'm', /* append (merge into any old value) */ hdr_unset = 'u', /* unset header */ - hdr_echo = 'e' /* echo headers from request to response */ + hdr_echo = 'e', /* echo headers from request to response */ + hdr_edit = 'r' /* change value by regexp */ } hdr_actions; /* @@ -119,6 +120,7 @@ apr_array_header_t *ta; /* Array of format_tag structs */ ap_regex_t *regex; const char *condition_var; + const char *subs; } header_entry; /* echo_do is used for Header echo to iterate through the request headers*/ @@ -348,6 +350,7 @@ /* No string to parse with unset and echo commands */ if (hdr->action == hdr_unset || + hdr->action == hdr_edit || hdr->action == hdr_echo) { return NULL; } @@ -368,7 +371,8 @@ const char *action, const char *hdr, const char *value, - const char* envclause) + const char *subs, + const char *envclause) { headers_conf *dirconf = indirconf; const char *condition_var = NULL; @@ -392,10 +396,29 @@ new->action = hdr_unset; else if (!strcasecmp(action, "echo")) new->action = hdr_echo; + else if (!strcasecmp(action, "edit")) + new->action = hdr_edit; else - return "first argument must be 'add', 'set', 'append', 'unset' or " - "'echo'."; + return "first argument must be 'add', 'set', 'append', 'unset', " + "'echo' or 'edit'."; + if (new->action == hdr_edit) { + if (subs == NULL) { + return "Header edit requires a match and a substitution"; + } + new->regex = ap_pregcomp(cmd->pool, value, AP_REG_EXTENDED); + if (new->regex == NULL) { + return "Header edit regex could not be compiled"; + } + new->subs = subs; + } + else { + /* there's no subs, so envclause is really that argument */ + if (envclause != NULL) { + return "Too many arguments to directive"; + } + envclause = subs; + } if (new->action == hdr_unset) { if (value) { if (envclause) { @@ -465,6 +488,7 @@ const char *hdr; const char *val; const char *envclause; + const char *subs; action = ap_getword_conf(cmd->pool, &args); if (cmd->info == &hdr_out) { @@ -478,6 +502,7 @@ } hdr = ap_getword_conf(cmd->pool, &args); val = *args ? ap_getword_conf(cmd->pool, &args) : NULL; + subs = *args ? ap_getword_conf(cmd->pool, &args) : NULL; envclause = *args ? ap_getword_conf(cmd->pool, &args) : NULL; if (*args) { @@ -485,7 +510,7 @@ " has too many arguments", NULL); } - return header_inout_cmd(cmd, indirconf, action, hdr, val, envclause); + return header_inout_cmd(cmd, indirconf, action, hdr, val, subs, envclause); } /* @@ -512,6 +537,26 @@ } return str ? str : ""; } +static const char *process_regexp(header_entry *hdr, const char *value, + apr_pool_t *pool) +{ + unsigned int nmatch = 10; + ap_regmatch_t pmatch[10]; + const char *subs; + char *ret; + int diffsz; + if (ap_regexec(hdr->regex, value, nmatch, pmatch, 0)) { + /* no match, nothing to do */ + return value; + } + subs = ap_pregsub(pool, hdr->subs, value, nmatch, pmatch); + diffsz = strlen(subs) - (pmatch[0].rm_eo - pmatch[0].rm_so); + ret = apr_palloc(pool, strlen(value) + 1 + diffsz); + memcpy(ret, value, pmatch[0].rm_so); + strcpy(ret + pmatch[0].rm_so, subs); + strcat(ret, value + pmatch[0].rm_eo); + return ret; +} static int echo_header(echo_do *v, const char *key, const char *val) { @@ -528,7 +573,9 @@ static void do_headers_fixup(request_rec *r, apr_table_t *headers, apr_array_header_t *fixup, int early) { + echo_do v; int i; + const char *val; for (i = 0; i < fixup->nelts; ++i) { header_entry *hdr = &((header_entry *) (fixup->elts))[i]; @@ -568,15 +615,19 @@ apr_table_unset(headers, hdr->header); break; case hdr_echo: - { - echo_do v; v.r = r; v.hdr = hdr; apr_table_do((int (*) (void *, const char *, const char *)) echo_header, (void *) &v, r->headers_in, NULL); break; + case hdr_edit: + val = apr_table_get(headers, hdr->header); + if (val != NULL) { + apr_table_setn(headers, hdr->header, + process_regexp(hdr, val, r->pool)); + } + break; } - } } } Index: modules/metadata/mod_mime_magic.c =================================================================== --- modules/metadata/mod_mime_magic.c (.../2.2.3) (revision 493482) +++ modules/metadata/mod_mime_magic.c (.../2.2.4) (revision 493482) @@ -935,7 +935,7 @@ return -1; } if ((result = apr_file_open(&f, fname, APR_READ | APR_BUFFERED, - APR_OS_DEFAULT, p) != APR_SUCCESS)) { + APR_OS_DEFAULT, p)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, result, s, MODNAME ": can't read magic file %s", fname); return -1; Index: modules/proxy/mod_proxy_balancer.c =================================================================== --- modules/proxy/mod_proxy_balancer.c (.../2.2.3) (revision 493482) +++ modules/proxy/mod_proxy_balancer.c (.../2.2.4) (revision 493482) @@ -93,6 +93,7 @@ /* Set to the original configuration */ workers[i].s->lbstatus = workers[i].s->lbfactor = (workers[i].lbfactor ? workers[i].lbfactor : 1); + workers[i].s->lbset = workers[i].lbset; } /* Set default number of attempts to the number of * workers. @@ -121,9 +122,7 @@ ++path; if (strlen(path)) { char *q; - path = apr_pstrdup(pool, path); - if ((q = strchr(path, '?'))) - *q = '\0'; + path = apr_strtok(apr_pstrdup(pool, path), "?&", &q); return path; } } @@ -169,15 +168,65 @@ /* Find the worker that has the 'route' defined */ static proxy_worker *find_route_worker(proxy_balancer *balancer, - const char *route) + const char *route, request_rec *r) { int i; - proxy_worker *worker = (proxy_worker *)balancer->workers->elts; - for (i = 0; i < balancer->workers->nelts; i++) { - if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) { - return worker; + int checking_standby; + int checked_standby; + + proxy_worker *worker; + + checking_standby = checked_standby = 0; + while (!checked_standby) { + worker = (proxy_worker *)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) ) + continue; + if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) { + if (worker && PROXY_WORKER_IS_USABLE(worker)) { + return worker; + } else { + /* + * If the worker is in error state run + * retry on that worker. It will be marked as + * operational if the retry timeout is elapsed. + * The worker might still be unusable, but we try + * anyway. + */ + ap_proxy_retry_worker("BALANCER", worker, r->server); + if (PROXY_WORKER_IS_USABLE(worker)) { + return worker; + } else { + /* + * We have a worker that is unusable. + * It can be in error or disabled, but in case + * it has a redirection set use that redirection worker. + * This enables to safely remove the member from the + * balancer. Of course you will need some kind of + * session replication between those two remote. + */ + if (*worker->s->redirect) { + proxy_worker *rworker = NULL; + rworker = find_route_worker(balancer, worker->s->redirect, r); + /* Check if the redirect worker is usable */ + if (rworker && !PROXY_WORKER_IS_USABLE(rworker)) { + /* + * If the worker is in error state run + * retry on that worker. It will be marked as + * operational if the retry timeout is elapsed. + * The worker might still be unusable, but we try + * anyway. + */ + ap_proxy_retry_worker("BALANCER", rworker, r->server); + } + if (rworker && PROXY_WORKER_IS_USABLE(rworker)) + return rworker; + } + } + } + } } - worker++; + checked_standby = checking_standby++; } return NULL; } @@ -210,20 +259,16 @@ /* We have a route in path or in cookie * Find the worker that has this route defined. */ - worker = find_route_worker(balancer, *route); - if (worker && !PROXY_WORKER_IS_USABLE(worker)) { - /* We have a worker that is unusable. - * It can be in error or disabled, but in case - * it has a redirection set use that redirection worker. - * This enables to safely remove the member from the - * balancer. Of course you will need a some kind of - * session replication between those two remote. + worker = find_route_worker(balancer, *route, r); + if (worker && strcmp(*route, worker->s->route)) { + /* + * Notice that the route of the worker chosen is different from + * the route supplied by the client. */ - if (*worker->s->redirect) - worker = find_route_worker(balancer, worker->s->redirect); - /* Check if the redirect worker is usable */ - if (worker && !PROXY_WORKER_IS_USABLE(worker)) - worker = NULL; + apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1"); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: BALANCER: Route changed from %s to %s", + *route, worker->s->route); } return worker; } @@ -235,18 +280,28 @@ request_rec *r) { proxy_worker *candidate = NULL; + apr_status_t rv; - if (PROXY_THREAD_LOCK(balancer) != APR_SUCCESS) + if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "proxy: BALANCER: (%s). Lock failed for find_best_worker()", balancer->name); return NULL; + } candidate = (*balancer->lbmethod->finder)(balancer, r); + if (candidate) + candidate->s->elected++; + /* PROXY_THREAD_UNLOCK(balancer); return NULL; */ - PROXY_THREAD_UNLOCK(balancer); + if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "proxy: BALANCER: (%s). Unlock failed for find_best_worker()", balancer->name); + } if (candidate == NULL) { /* All the workers are in error state or disabled. @@ -334,7 +389,8 @@ */ if ((rv = PROXY_THREAD_LOCK(*balancer)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, - "proxy: BALANCER: lock"); + "proxy: BALANCER: (%s). Lock failed for pre_request", + (*balancer)->name); return DECLINED; } if (runtime) { @@ -349,6 +405,9 @@ for (i = 0; i < (*balancer)->workers->nelts; i++) { /* Take into calculation only the workers that are * not in error state or not disabled. + * + * TODO: Abstract the below, since this is dependent + * on the LB implementation */ if (PROXY_WORKER_IS_USABLE(workers)) { workers->s->lbstatus += workers->s->lbfactor; @@ -365,11 +424,19 @@ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "proxy: BALANCER: (%s). All workers are in error state for route (%s)", (*balancer)->name, route); - PROXY_THREAD_UNLOCK(*balancer); + if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "proxy: BALANCER: (%s). Unlock failed for pre_request", + (*balancer)->name); + } return HTTP_SERVICE_UNAVAILABLE; } - PROXY_THREAD_UNLOCK(*balancer); + if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "proxy: BALANCER: (%s). Unlock failed for pre_request", + (*balancer)->name); + } if (!*worker) { runtime = find_best_worker(*balancer, r); if (!runtime) { @@ -379,9 +446,28 @@ return HTTP_SERVICE_UNAVAILABLE; } + if ((*balancer)->sticky && runtime) { + /* + * This balancer has sticky sessions and the client either has not + * supplied any routing information or all workers for this route + * including possible redirect and hotstandby workers are in error + * state, but we have found another working worker for this + * balancer where we can send the request. Thus notice that we have + * changed the route to the backend. + */ + apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1"); + } *worker = runtime; } + /* Add balancer/worker info to env. */ + apr_table_setn(r->subprocess_env, + "BALANCER_NAME", (*balancer)->name); + apr_table_setn(r->subprocess_env, + "BALANCER_WORKER_NAME", (*worker)->name); + apr_table_setn(r->subprocess_env, + "BALANCER_WORKER_ROUTE", (*worker)->s->route); + /* Rewrite the url from 'balancer://url' * to the 'worker_scheme://worker_hostname[:worker_port]/url' * This replaces the balancers fictional name with the @@ -392,6 +478,12 @@ if (route) { apr_table_setn(r->notes, "session-sticky", (*balancer)->sticky); apr_table_setn(r->notes, "session-route", route); + + /* Add session info to env. */ + apr_table_setn(r->subprocess_env, + "BALANCER_SESSION_STICKY", (*balancer)->sticky); + apr_table_setn(r->subprocess_env, + "BALANCER_SESSION_ROUTE", route); } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: BALANCER (%s) worker (%s) rewritten to %s", @@ -409,7 +501,8 @@ if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, - "proxy: BALANCER: lock"); + "proxy: BALANCER: (%s). Lock failed for post_request", + balancer->name); return HTTP_INTERNAL_SERVER_ERROR; } /* TODO: calculate the bytes transferred @@ -420,7 +513,11 @@ * track on that. */ - PROXY_THREAD_UNLOCK(balancer); + if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "proxy: BALANCER: (%s). Unlock failed for post_request", + balancer->name); + } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy_balancer_post_request for (%s)", balancer->name); @@ -564,6 +661,12 @@ else if (!strcasecmp(val, "Enable")) wsel->s->status &= ~PROXY_WORKER_DISABLED; } + if ((val = apr_table_get(params, "ls"))) { + int ival = atoi(val); + if (ival >= 0 && ival <= 99) { + wsel->s->lbset = ival; + } + } } if (apr_table_get(params, "xml")) { @@ -602,7 +705,7 @@ ap_rputs("

Load Balancer Manager for ", r); ap_rvputs(r, ap_get_server_name(r), "

\n\n", NULL); ap_rvputs(r, "
Server Version: ", - ap_get_server_version(), "
\n", NULL); + ap_get_server_description(), "\n", NULL); ap_rvputs(r, "
Server Built: ", ap_get_server_built(), "\n
\n", NULL); balancer = (proxy_balancer *)conf->balancers->elts; @@ -626,12 +729,13 @@ ap_rputs("\n\n" "" "" - "" + "" + "" "\n", r); worker = (proxy_worker *)balancer->workers->elts; for (n = 0; n < balancer->workers->nelts; n++) { - + char fbuf[50]; ap_rvputs(r, "\n", NULL); ap_rvputs(r, "", worker->s->lbfactor); + ap_rprintf(r, "", r); + ap_rprintf(r, "\n", r); ++worker; @@ -662,20 +776,22 @@ ap_rvputs(r, "uri, "\">\n
", NULL); ap_rputs("
Worker URLRouteRouteRedirFactorStatusFactorSetStatusElectedToFrom
uri, "?b=", balancer->name + sizeof("balancer://") - 1, "&w=", ap_escape_uri(r->pool, worker->name), @@ -639,15 +743,25 @@ ap_rvputs(r, worker->name, "", worker->s->route, NULL); ap_rvputs(r, "", worker->s->redirect, NULL); - ap_rprintf(r, "%d", worker->s->lbfactor); + ap_rprintf(r, "%d%d", worker->s->lbset); if (worker->s->status & PROXY_WORKER_DISABLED) - ap_rputs("Dis", r); - else if (worker->s->status & PROXY_WORKER_IN_ERROR) - ap_rputs("Err", r); - else if (worker->s->status & PROXY_WORKER_INITIALIZED) + ap_rputs("Dis ", r); + if (worker->s->status & PROXY_WORKER_IN_ERROR) + ap_rputs("Err ", r); + if (worker->s->status & PROXY_WORKER_STOPPED) + ap_rputs("Stop ", r); + if (worker->s->status & PROXY_WORKER_HOT_STANDBY) + ap_rputs("Stby ", r); + if (PROXY_WORKER_IS_USABLE(worker)) ap_rputs("Ok", r); - else + if (!PROXY_WORKER_IS_INITIALIZED(worker)) ap_rputs("-", r); + ap_rputs("%" APR_SIZE_T_FMT "", worker->s->elected); + ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r); + ap_rputs("", r); + ap_rputs(apr_strfsize(worker->s->read, fbuf), r); ap_rputs("
\n", wsel->s->lbfactor); + ap_rprintf(r, "value=\"%d\">\n", wsel->s->lbfactor); + ap_rputs("\n", wsel->s->lbset); ap_rputs("\n", r); + ap_rputs("\">\n", r); ap_rputs("\n", r); + ap_rputs("\">\n", r); ap_rputs("\n", r); + ap_rputs(">\n", r); ap_rputs("\n", r); ap_rvputs(r, "
Load factor:
LB Set:
Route:route, NULL); - ap_rputs("\">
Route Redirect:redirect, NULL); - ap_rputs("\">
Status:Disabled: s->status & PROXY_WORKER_DISABLED) ap_rputs(" checked", r); ap_rputs("> | Enabled: s->status & PROXY_WORKER_DISABLED)) ap_rputs(" checked", r); - ap_rputs(">
\npool, wsel->name), "\">\n", NULL); @@ -796,39 +912,56 @@ { int i; int total_factor = 0; - proxy_worker *worker = (proxy_worker *)balancer->workers->elts; + proxy_worker *worker; proxy_worker *mycandidate = NULL; - - + int cur_lbset = 0; + int max_lbset = 0; + int checking_standby; + int checked_standby; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: Entering byrequests for BALANCER (%s)", balancer->name); /* First try to see if we have available candidate */ - for (i = 0; i < balancer->workers->nelts; i++) { - /* If the worker is in error state run - * retry on that worker. It will be marked as - * operational if the retry timeout is elapsed. - * The worker might still be unusable, but we try - * anyway. - */ - if (!PROXY_WORKER_IS_USABLE(worker)) - ap_proxy_retry_worker("BALANCER", worker, r->server); - /* Take into calculation only the workers that are - * not in error state or not disabled. - */ - if (PROXY_WORKER_IS_USABLE(worker)) { - worker->s->lbstatus += worker->s->lbfactor; - total_factor += worker->s->lbfactor; - if (!mycandidate || worker->s->lbstatus > mycandidate->s->lbstatus) - mycandidate = worker; + do { + checking_standby = checked_standby = 0; + while (!mycandidate && !checked_standby) { + worker = (proxy_worker *)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + if (!checking_standby) { /* first time through */ + if (worker->s->lbset > max_lbset) + max_lbset = worker->s->lbset; + } + if (worker->s->lbset > cur_lbset) + continue; + if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) ) + continue; + /* If the worker is in error state run + * retry on that worker. It will be marked as + * operational if the retry timeout is elapsed. + * The worker might still be unusable, but we try + * anyway. + */ + if (!PROXY_WORKER_IS_USABLE(worker)) + ap_proxy_retry_worker("BALANCER", worker, r->server); + /* Take into calculation only the workers that are + * not in error state or not disabled. + */ + if (PROXY_WORKER_IS_USABLE(worker)) { + worker->s->lbstatus += worker->s->lbfactor; + total_factor += worker->s->lbfactor; + if (!mycandidate || worker->s->lbstatus > mycandidate->s->lbstatus) + mycandidate = worker; + } + } + checked_standby = checking_standby++; } - worker++; - } + cur_lbset++; + } while (cur_lbset <= max_lbset && !mycandidate); if (mycandidate) { mycandidate->s->lbstatus -= total_factor; - mycandidate->s->elected++; } return mycandidate; @@ -857,41 +990,56 @@ int i; apr_off_t mytraffic = 0; apr_off_t curmin = 0; - proxy_worker *worker = (proxy_worker *)balancer->workers->elts; + proxy_worker *worker; proxy_worker *mycandidate = NULL; + int cur_lbset = 0; + int max_lbset = 0; + int checking_standby; + int checked_standby; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: Entering bytraffic for BALANCER (%s)", balancer->name); /* First try to see if we have available candidate */ - for (i = 0; i < balancer->workers->nelts; i++) { - /* If the worker is in error state run - * retry on that worker. It will be marked as - * operational if the retry timeout is elapsed. - * The worker might still be unusable, but we try - * anyway. - */ - if (!PROXY_WORKER_IS_USABLE(worker)) - ap_proxy_retry_worker("BALANCER", worker, r->server); - /* Take into calculation only the workers that are - * not in error state or not disabled. - */ - if (PROXY_WORKER_IS_USABLE(worker)) { - mytraffic = (worker->s->transferred/worker->s->lbfactor) + - (worker->s->read/worker->s->lbfactor); - if (!mycandidate || mytraffic < curmin) { - mycandidate = worker; - curmin = mytraffic; + do { + checking_standby = checked_standby = 0; + while (!mycandidate && !checked_standby) { + worker = (proxy_worker *)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + if (!checking_standby) { /* first time through */ + if (worker->s->lbset > max_lbset) + max_lbset = worker->s->lbset; + } + if (worker->s->lbset > cur_lbset) + continue; + if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) ) + continue; + /* If the worker is in error state run + * retry on that worker. It will be marked as + * operational if the retry timeout is elapsed. + * The worker might still be unusable, but we try + * anyway. + */ + if (!PROXY_WORKER_IS_USABLE(worker)) + ap_proxy_retry_worker("BALANCER", worker, r->server); + /* Take into calculation only the workers that are + * not in error state or not disabled. + */ + if (PROXY_WORKER_IS_USABLE(worker)) { + mytraffic = (worker->s->transferred/worker->s->lbfactor) + + (worker->s->read/worker->s->lbfactor); + if (!mycandidate || mytraffic < curmin) { + mycandidate = worker; + curmin = mytraffic; + } + } } + checked_standby = checking_standby++; } - worker++; - } + cur_lbset++; + } while (cur_lbset <= max_lbset && !mycandidate); - if (mycandidate) { - mycandidate->s->elected++; - } - return mycandidate; } Index: modules/proxy/ajp.h =================================================================== --- modules/proxy/ajp.h (.../2.2.3) (revision 493482) +++ modules/proxy/ajp.h (.../2.2.4) (revision 493482) @@ -471,6 +471,17 @@ apr_status_t ajp_parse_data(request_rec *r, ajp_msg_t *msg, apr_uint16_t *len, char **ptr); + +/** + * Handle the CPING/CPONG messages + * @param sock backend socket + * @param r current request + * @param timeout time window for receiving cpong reply + * @return APR_SUCCESS or error + */ +apr_status_t ajp_handle_cping_cpong(apr_socket_t *sock, + request_rec *r, + apr_interval_time_t timeout); /** @} */ #endif /* AJP_H */ Index: modules/proxy/mod_proxy_connect.c =================================================================== --- modules/proxy/mod_proxy_connect.c (.../2.2.3) (revision 493482) +++ modules/proxy/mod_proxy_connect.c (.../2.2.4) (revision 493482) @@ -224,7 +224,7 @@ "CONNECT %s HTTP/1.0" CRLF, r->uri); apr_socket_send(sock, buffer, &nbytes); nbytes = apr_snprintf(buffer, sizeof(buffer), - "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); + "Proxy-agent: %s" CRLF CRLF, ap_get_server_banner()); apr_socket_send(sock, buffer, &nbytes); } else { @@ -235,7 +235,7 @@ ap_xlate_proto_to_ascii(buffer, nbytes); apr_socket_send(client_socket, buffer, &nbytes); nbytes = apr_snprintf(buffer, sizeof(buffer), - "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); + "Proxy-agent: %s" CRLF CRLF, ap_get_server_banner()); ap_xlate_proto_to_ascii(buffer, nbytes); apr_socket_send(client_socket, buffer, &nbytes); #if 0 @@ -244,7 +244,7 @@ */ r->status = HTTP_OK; r->header_only = 1; - apr_table_set(r->headers_out, "Proxy-agent: %s", ap_get_server_version()); + apr_table_set(r->headers_out, "Proxy-agent: %s", ap_get_server_banner()); ap_rflush(r); #endif } Index: modules/proxy/mod_proxy_ajp.c =================================================================== --- modules/proxy/mod_proxy_ajp.c (.../2.2.3) (revision 493482) +++ modules/proxy/mod_proxy_ajp.c (.../2.2.4) (revision 493482) @@ -175,6 +175,8 @@ AJP13_MAX_SEND_BODY_SZ); if (status != APR_SUCCESS) { + /* We had a failure: Close connection to backend */ + conn->close++; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: ap_get_brigade failed"); apr_brigade_destroy(input_brigade); @@ -313,21 +315,30 @@ /* AJP13_SEND_BODY_CHUNK: piece of data */ status = ajp_parse_data(r, conn->data, &size, &buff); if (status == APR_SUCCESS) { - e = apr_bucket_transient_create(buff, size, - r->connection->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(output_brigade, e); - - if ( (conn->worker->flush_packets == flush_on) || - ( (conn->worker->flush_packets == flush_auto) && - (apr_poll(conn_poll, 1, &conn_poll_fd, - conn->worker->flush_wait) - == APR_TIMEUP) ) ) { + if (size == 0) { + /* AJP13_SEND_BODY_CHUNK with zero length + * is explicit flush message + */ e = apr_bucket_flush_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(output_brigade, e); } - apr_brigade_length(output_brigade, 0, &bb_len); - if (bb_len != -1) - conn->worker->s->read += bb_len; + else { + e = apr_bucket_transient_create(buff, size, + r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + + if ((conn->worker->flush_packets == flush_on) || + ((conn->worker->flush_packets == flush_auto) && + (apr_poll(conn_poll, 1, &conn_poll_fd, + conn->worker->flush_wait) + == APR_TIMEUP) ) ) { + e = apr_bucket_flush_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + } + apr_brigade_length(output_brigade, 0, &bb_len); + if (bb_len != -1) + conn->worker->s->read += bb_len; + } if (ap_pass_brigade(r->output_filters, output_brigade) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, @@ -530,6 +541,20 @@ goto cleanup; } + /* Handle CPING/CPONG */ + if (worker->ping_timeout_set) { + status = ajp_handle_cping_cpong(backend->sock, r, + worker->ping_timeout); + if (status != APR_SUCCESS) { + backend->close++; + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: AJP: cping/cpong failed to %pI (%s)", + worker->cp->addr, + worker->hostname); + status = HTTP_SERVICE_UNAVAILABLE; + goto cleanup; + } + } /* Step Three: Process the Request */ status = ap_proxy_ajp_request(p, r, backend, origin, dconf, uri, url, server_portstr); Index: modules/proxy/proxy_util.c =================================================================== --- modules/proxy/proxy_util.c (.../2.2.3) (revision 493482) +++ modules/proxy/proxy_util.c (.../2.2.4) (revision 493482) @@ -1605,7 +1605,7 @@ void *score = NULL; #endif - if (worker->s && (worker->s->status & PROXY_WORKER_INITIALIZED)) { + if (worker->s && PROXY_WORKER_IS_INITIALIZED(worker)) { /* The worker share is already initialized */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "proxy: worker %s already initialized", @@ -1639,7 +1639,7 @@ * recheck to see if we've already been here. Possible * if proxy is using scoreboard to hold shared stats */ - if (worker->s->status & PROXY_WORKER_INITIALIZED) { + if (PROXY_WORKER_IS_INITIALIZED(worker)) { /* The worker share is already initialized */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "proxy: worker %s already initialized", @@ -1840,6 +1840,7 @@ { int server_port; apr_status_t err = APR_SUCCESS; + apr_status_t uerr = APR_SUCCESS; /* * Break up the URL to determine the host to connect to @@ -1922,7 +1923,10 @@ conn->port, 0, worker->cp->pool); conn->addr = worker->cp->addr; - PROXY_THREAD_UNLOCK(worker); + if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, uerr, r->server, + "proxy: unlock"); + } } else conn->addr = worker->cp->addr; @@ -1969,7 +1973,8 @@ socket_status = apr_socket_recv(sock, test_buffer, &buffer_len); /* put back old timeout */ apr_socket_timeout_set(sock, current_timeout); - if (APR_STATUS_IS_EOF(socket_status)) + if (APR_STATUS_IS_EOF(socket_status) || + APR_STATUS_IS_ECONNRESET(socket_status)) return 0; else return 1; Index: modules/proxy/mod_proxy_http.c =================================================================== --- modules/proxy/mod_proxy_http.c (.../2.2.3) (revision 493482) +++ modules/proxy/mod_proxy_http.c (.../2.2.4) (revision 493482) @@ -1393,7 +1393,7 @@ * ProxyPassReverse/etc from here to ap_proxy_read_headers */ - if ((r->status == 401) && (conf->error_override != 0)) { + if ((r->status == 401) && (conf->error_override)) { const char *buf; const char *wa = "WWW-Authenticate"; if ((buf = apr_table_get(r->headers_out, wa))) { @@ -1452,7 +1452,7 @@ * if we are overriding the errors, we can't put the content * of the page into the brigade */ - if (conf->error_override == 0 || ap_is_HTTP_SUCCESS(r->status)) { + if (!conf->error_override || ap_is_HTTP_SUCCESS(r->status)) { /* read the body, pass it to the output filters */ apr_read_type_e mode = APR_NONBLOCK_READ; int finish = FALSE; Index: modules/proxy/mod_proxy.c =================================================================== --- modules/proxy/mod_proxy.c (.../2.2.3) (revision 493482) +++ modules/proxy/mod_proxy.c (.../2.2.4) (revision 493482) @@ -213,8 +213,14 @@ else worker->status &= ~PROXY_WORKER_IN_ERROR; } + else if (*v == 'H' || *v == 'h') { + if (mode) + worker->status |= PROXY_WORKER_HOT_STANDBY; + else + worker->status &= ~PROXY_WORKER_HOT_STANDBY; + } else { - return "Unknow status parameter option"; + return "Unknown status parameter option"; } } } @@ -238,6 +244,21 @@ else worker->flush_wait = ival * 1000; /* change to microseconds */ } + else if (!strcasecmp(key, "lbset")) { + ival = atoi(val); + if (ival < 0 || ival > 99) + return "lbset must be between 0 and 99"; + worker->lbset = ival; + } + else if (!strcasecmp(key, "ping")) { + /* Ping/Pong timeout in seconds. + */ + ival = atoi(val); + if (ival < 1) + return "Ping/Pong timeout must be at least one second"; + worker->ping_timeout = apr_time_from_sec(ival); + worker->ping_timeout_set = 1; + } else { return "unknown Worker parameter"; } @@ -1794,7 +1815,7 @@ ap_rputs("\n\n" "" "" - "" + "" "\n", r); worker = (proxy_worker *)balancer->workers->elts; @@ -1813,7 +1834,8 @@ ap_rvputs(r, "", worker->s->lbfactor); - ap_rprintf(r, "", worker->s->lbset); + ap_rprintf(r, "
SchHostStatRouteRedirFAccWrRdFSetAccWrRd
", worker->s->route, NULL); ap_rvputs(r, "", worker->s->redirect, NULL); ap_rprintf(r, "%d%d", (int)(worker->s->elected)); + ap_rprintf(r, "%d%" APR_SIZE_T_FMT "", worker->s->elected); ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r); ap_rputs("", r); ap_rputs(apr_strfsize(worker->s->read, fbuf), r); Index: modules/proxy/mod_proxy.h =================================================================== --- modules/proxy/mod_proxy.h (.../2.2.3) (revision 493482) +++ modules/proxy/mod_proxy.h (.../2.2.4) (revision 493482) @@ -219,7 +219,7 @@ apr_uint32_t flags; /* Conection flags */ int close; /* Close 'this' connection */ int close_on_recycle; /* Close the connection when returning to pool */ - proxy_worker *worker; /* Connection pool this connection belogns to */ + proxy_worker *worker; /* Connection pool this connection belongs to */ void *data; /* per scheme connection data */ #if APR_HAS_THREADS int inreslist; /* connection in apr_reslist? */ @@ -241,16 +241,27 @@ proxy_conn_rec *conn; /* Single connection for prefork mpm's */ }; -/* woker status flags */ +/* worker status flags */ #define PROXY_WORKER_INITIALIZED 0x0001 #define PROXY_WORKER_IGNORE_ERRORS 0x0002 #define PROXY_WORKER_IN_SHUTDOWN 0x0010 #define PROXY_WORKER_DISABLED 0x0020 #define PROXY_WORKER_STOPPED 0x0040 #define PROXY_WORKER_IN_ERROR 0x0080 +#define PROXY_WORKER_HOT_STANDBY 0x0100 -#define PROXY_WORKER_IS_USABLE(f) (!((f)->s->status & 0x00F0)) +#define PROXY_WORKER_NOT_USABLE_BITMAP ( PROXY_WORKER_IN_SHUTDOWN | \ +PROXY_WORKER_DISABLED | PROXY_WORKER_STOPPED | PROXY_WORKER_IN_ERROR ) +#define PROXY_WORKER_IS_INITIALIZED(f) ( (f)->s->status & \ + PROXY_WORKER_INITIALIZED ) + +#define PROXY_WORKER_IS_STANDBY(f) ( (f)->s->status & \ + PROXY_WORKER_HOT_STANDBY ) + +#define PROXY_WORKER_IS_USABLE(f) ( !((f)->s->status & \ + (PROXY_WORKER_NOT_USABLE_BITMAP)) && PROXY_WORKER_IS_INITIALIZED(f) ) + /* default worker retry timeout in seconds */ #define PROXY_WORKER_DEFAULT_RETRY 60 #define PROXY_WORKER_MAX_ROUTE_SIZ 63 @@ -268,6 +279,8 @@ char route[PROXY_WORKER_MAX_ROUTE_SIZ+1]; char redirect[PROXY_WORKER_MAX_ROUTE_SIZ+1]; void *context; /* general purpose storage */ + apr_size_t busy; /* busyness factor */ + int lbset; /* load balancer cluster set */ } proxy_worker_stat; /* Worker configuration */ @@ -288,29 +301,32 @@ apr_interval_time_t ttl; /* maximum amount of time in seconds a connection * may be available while exceeding the soft limit */ apr_interval_time_t timeout; /* connection timeout */ - char timeout_set; + char timeout_set; apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */ - char acquire_set; - apr_size_t recv_buffer_size; - char recv_buffer_size_set; - apr_size_t io_buffer_size; - char io_buffer_size_set; - char keepalive; - char keepalive_set; + char acquire_set; + apr_size_t recv_buffer_size; + char recv_buffer_size_set; + apr_size_t io_buffer_size; + char io_buffer_size_set; + char keepalive; + char keepalive_set; proxy_conn_pool *cp; /* Connection pool to use */ proxy_worker_stat *s; /* Shared data */ - void *opaque; /* per scheme worker data */ - int is_address_reusable; + void *opaque; /* per scheme worker data */ + int is_address_reusable; #if APR_HAS_THREADS apr_thread_mutex_t *mutex; /* Thread lock for updating address cache */ #endif - void *context; /* general purpose storage */ + void *context; /* general purpose storage */ enum { flush_off, flush_on, flush_auto } flush_packets; /* control AJP flushing */ - int flush_wait; /* poll wait time in microseconds if flush_auto */ + int flush_wait; /* poll wait time in microseconds if flush_auto */ + int lbset; /* load balancer cluster set */ + apr_interval_time_t ping_timeout; + char ping_timeout_set; }; /* Index: modules/proxy/mod_proxy_ftp.c =================================================================== --- modules/proxy/mod_proxy_ftp.c (.../2.2.3) (revision 493482) +++ modules/proxy/mod_proxy_ftp.c (.../2.2.4) (revision 493482) @@ -763,6 +763,7 @@ apr_status_t rv; conn_rec *origin, *data = NULL; apr_status_t err = APR_SUCCESS; + apr_status_t uerr = APR_SUCCESS; apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc); char *buf, *connectname; apr_port_t connectport; @@ -916,7 +917,10 @@ address_pool); if (worker->is_address_reusable && !worker->cp->addr) { worker->cp->addr = connect_addr; - PROXY_THREAD_UNLOCK(worker); + if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, uerr, r->server, + "proxy: FTP: unlock"); + } } /* * get all the possible IP addresses for the destname and loop through @@ -1662,7 +1666,7 @@ apr_rfc822_date(dates, r->request_time); apr_table_setn(r->headers_out, "Date", dates); - apr_table_setn(r->headers_out, "Server", ap_get_server_version()); + apr_table_setn(r->headers_out, "Server", ap_get_server_banner()); /* set content-type */ if (dirlisting) { Index: modules/proxy/ajp_utils.c =================================================================== --- modules/proxy/ajp_utils.c (.../2.2.3) (revision 0) +++ modules/proxy/ajp_utils.c (.../2.2.4) (revision 493482) @@ -0,0 +1,105 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 "ajp.h" + +/* + * Handle the CPING/CPONG + */ +apr_status_t ajp_handle_cping_cpong(apr_socket_t *sock, + request_rec *r, + apr_interval_time_t timeout) +{ + ajp_msg_t *msg; + apr_status_t rc, rv; + apr_interval_time_t org; + apr_byte_t result; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Into ajp_handle_cping_cpong"); + + rc = ajp_msg_create(r->pool, &msg); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_handle_cping_cpong: ajp_msg_create failed"); + return rc; + } + + rc = ajp_msg_serialize_cping(msg); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_handle_cping_cpong: ajp_marshal_into_msgb failed"); + return rc; + } + + rc = ajp_ilink_send(sock, msg); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_handle_cping_cpong: ajp_ilink_send failed"); + return rc; + } + + rc = apr_socket_timeout_get(sock, &org); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_handle_cping_cpong: apr_socket_timeout_get failed"); + return rc; + } + + /* Set CPING/CPONG response timeout */ + rc = apr_socket_timeout_set(sock, timeout); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_handle_cping_cpong: apr_socket_timeout_set failed"); + return rc; + } + ajp_msg_reuse(msg); + + /* Read CPONG reply */ + rv = ajp_ilink_receive(sock, msg); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_handle_cping_cpong: ajp_ilink_receive failed"); + goto cleanup; + } + + rv = ajp_msg_get_uint8(msg, &result); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_handle_cping_cpong: invalid CPONG message"); + goto cleanup; + } + if (result != CMD_AJP13_CPONG) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_handle_cping_cpong: awaited CPONG, received %d ", + result); + rv = APR_EGENERAL; + goto cleanup; + } + +cleanup: + /* Restore original socket timeout */ + rc = apr_socket_timeout_set(sock, org); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_handle_cping_cpong: apr_socket_timeout_set failed"); + return rc; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ajp_handle_cping_cpong: Done"); + return rv; +} Property changes on: modules/proxy/ajp_utils.c ___________________________________________________________________ Name: svn:eol-style + native Index: modules/aaa/mod_authnz_ldap.c =================================================================== --- modules/aaa/mod_authnz_ldap.c (.../2.2.3) (revision 493482) +++ modules/aaa/mod_authnz_ldap.c (.../2.2.4) (revision 493482) @@ -64,6 +64,7 @@ char *bindpw; /* Password to bind to server (can be NULL) */ int user_is_dn; /* If true, connection->user is DN instead of userid */ + char *remote_user_attribute; /* If set, connection->user is this attribute instead of userid */ int compare_dn_on_server; /* If true, will use server to do DN compare */ int have_ldap_url; /* Set if we have found an LDAP url */ @@ -303,6 +304,7 @@ sec->secure = -1; /*Initialize to unset*/ sec->user_is_dn = 0; + sec->remote_user_attribute = NULL; sec->compare_dn_on_server = 0; return sec; @@ -337,6 +339,7 @@ util_ldap_connection_t *ldc = NULL; int result = 0; + int remote_user_attribute_set = 0; const char *dn = NULL; authn_ldap_request_t *req = @@ -447,10 +450,28 @@ j++; } apr_table_setn(e, str, vals[i]); + + /* handle remote_user_attribute, if set */ + if (sec->remote_user_attribute && + !strcmp(sec->remote_user_attribute, sec->attributes[i])) { + r->user = (char *)apr_pstrdup(r->pool, vals[i]); + remote_user_attribute_set = 1; + } i++; } } + /* sanity check */ + if (sec->remote_user_attribute && !remote_user_attribute_set) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "[%" APR_PID_T_FMT "] auth_ldap authenticate: " + "REMOTE_USER was to be set with attribute '%s', " + "but this attribute was not requested for in the " + "LDAP query for the user. REMOTE_USER will fall " + "back to username or DN as appropriate.", getpid(), + sec->remote_user_attribute); + } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "] auth_ldap authenticate: accepting %s", getpid(), user); @@ -1041,6 +1062,13 @@ "DN of the remote user. By default, this is set to off, meaning that " "the REMOTE_USER variable will contain whatever value the remote user sent."), + AP_INIT_TAKE1("AuthLDAPRemoteUserAttribute", ap_set_string_slot, + (void *)APR_OFFSETOF(authn_ldap_config_t, + remote_user_attribute), OR_AUTHCFG, + "Override the user supplied username and place the " + "contents of this attribute in the REMOTE_USER " + "environment variable."), + AP_INIT_FLAG("AuthzLDAPAuthoritative", ap_set_flag_slot, (void *)APR_OFFSETOF(authn_ldap_config_t, auth_authoritative), OR_AUTHCFG, "Set to 'off' to allow access control to be passed along to lower modules if " Index: modules/debug/mod_dumpio.c =================================================================== --- modules/debug/mod_dumpio.c (.../2.2.3) (revision 493482) +++ modules/debug/mod_dumpio.c (.../2.2.4) (revision 493482) @@ -38,6 +38,7 @@ typedef struct dumpio_conf_t { int enable_input; int enable_output; + int loglevel; } dumpio_conf_t; /* @@ -47,8 +48,11 @@ static void dumpit(ap_filter_t *f, apr_bucket *b) { conn_rec *c = f->c; + dumpio_conf_t *ptr = + (dumpio_conf_t *) ap_get_module_config(c->base_server->module_config, + &dumpio_module); - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, + ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, "mod_dumpio: %s (%s-%s): %" APR_SIZE_T_FMT " bytes", f->frec->name, (APR_BUCKET_IS_METADATA(b)) ? "metadata" : "data", @@ -64,7 +68,7 @@ obuf = malloc(nbytes+1); /* use pool? */ memcpy(obuf, buf, nbytes); obuf[nbytes] = '\0'; - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, + ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, "mod_dumpio: %s (%s-%s): %s", f->frec->name, (APR_BUCKET_IS_METADATA(b)) ? "metadata" : "data", @@ -73,7 +77,7 @@ free(obuf); } } else { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, + ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, "mod_dumpio: %s (%s-%s): %s", f->frec->name, (APR_BUCKET_IS_METADATA(b)) ? "metadata" : "data", @@ -99,8 +103,11 @@ apr_bucket *b; apr_status_t ret; conn_rec *c = f->c; + dumpio_conf_t *ptr = + (dumpio_conf_t *) ap_get_module_config(c->base_server->module_config, + &dumpio_module); - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, + ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, "mod_dumpio: %s [%s-%s] %" APR_OFF_T_FMT " readbytes", f->frec->name, whichmode(mode), @@ -114,7 +121,7 @@ dumpit(f, b); } } else { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, + ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, "mod_dumpio: %s - %d", f->frec->name, ret) ; } @@ -125,8 +132,11 @@ { apr_bucket *b; conn_rec *c = f->c; + dumpio_conf_t *ptr = + (dumpio_conf_t *) ap_get_module_config(c->base_server->module_config, + &dumpio_module); - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "mod_dumpio: %s", f->frec->name) ; + ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, "mod_dumpio: %s", f->frec->name) ; for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { /* @@ -173,6 +183,7 @@ { dumpio_conf_t *ptr = apr_pcalloc(p, sizeof *ptr); ptr->enable_input = ptr->enable_output = 0; + ptr->loglevel = APLOG_DEBUG; return ptr; } @@ -196,11 +207,63 @@ return NULL; } +static const char *set_loglevel(cmd_parms *cmd, void *dummy, const char *arg) +{ + char *str; + dumpio_conf_t *ptr = + (dumpio_conf_t *) ap_get_module_config(cmd->server->module_config, + &dumpio_module); + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if ((str = ap_getword_conf(cmd->pool, &arg))) { + if (!strcasecmp(str, "emerg")) { + ptr->loglevel = APLOG_EMERG; + } + else if (!strcasecmp(str, "alert")) { + ptr->loglevel = APLOG_ALERT; + } + else if (!strcasecmp(str, "crit")) { + ptr->loglevel = APLOG_CRIT; + } + else if (!strcasecmp(str, "error")) { + ptr->loglevel = APLOG_ERR; + } + else if (!strcasecmp(str, "warn")) { + ptr->loglevel = APLOG_WARNING; + } + else if (!strcasecmp(str, "notice")) { + ptr->loglevel = APLOG_NOTICE; + } + else if (!strcasecmp(str, "info")) { + ptr->loglevel = APLOG_INFO; + } + else if (!strcasecmp(str, "debug")) { + ptr->loglevel = APLOG_DEBUG; + } + else { + return "DumpIOLogLevel requires level keyword: one of " + "emerg/alert/crit/error/warn/notice/info/debug"; + } + } + else { + return "DumpIOLogLevel requires level keyword"; + } + + return NULL; +} + static const command_rec dumpio_cmds[] = { AP_INIT_FLAG("DumpIOInput", dumpio_enable_input, NULL, RSRC_CONF, "Enable I/O Dump on Input Data"), AP_INIT_FLAG("DumpIOOutput", dumpio_enable_output, NULL, RSRC_CONF, "Enable I/O Dump on Output Data"), + AP_INIT_TAKE1("DumpIOLogLevel", set_loglevel, NULL, RSRC_CONF, + "Level at which DumpIO info is logged"), { NULL } }; Index: modules/experimental/mod_example.c =================================================================== --- modules/experimental/mod_example.c (.../2.2.3) (revision 493482) +++ modules/experimental/mod_example.c (.../2.2.4) (revision 493482) @@ -546,7 +546,7 @@ ap_rputs(" \n", r); ap_rputs("

\n", r); ap_rprintf(r, " Apache HTTP Server version: \"%s\"\n", - ap_get_server_version()); + ap_get_server_banner()); ap_rputs("
\n", r); ap_rprintf(r, " Server built: \"%s\"\n", ap_get_server_built()); ap_rputs("

\n", r);; Index: modules/ssl/ssl_engine_vars.c =================================================================== --- modules/ssl/ssl_engine_vars.c (.../2.2.3) (revision 493482) +++ modules/ssl/ssl_engine_vars.c (.../2.2.4) (revision 493482) @@ -192,7 +192,7 @@ if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12)) result = ssl_var_lookup_ssl_version(p, var+12); else if (strcEQ(var, "SERVER_SOFTWARE")) - result = ap_get_server_version(); + result = ap_get_server_banner(); else if (strcEQ(var, "API_VERSION")) { result = apr_itoa(p, MODULE_MAGIC_NUMBER); resdup = FALSE; Index: modules/cache/cache_util.c =================================================================== --- modules/cache/cache_util.c (.../2.2.3) (revision 493482) +++ modules/cache/cache_util.c (.../2.2.4) (revision 493482) @@ -65,6 +65,18 @@ } } + /* For HTTP caching purposes, an empty (NULL) path is equivalent to + * a single "/" path. RFCs 3986/2396 + */ + if (!url.path) { + if (*filter.path == '/' && pathlen == 1) { + return 1; + } + else { + return 0; + } + } + /* Url has met all of the filter conditions so far, determine * if the paths match. */ Index: modules/cache/mod_mem_cache.c =================================================================== --- modules/cache/mod_mem_cache.c (.../2.2.3) (revision 493482) +++ modules/cache/mod_mem_cache.c (.../2.2.4) (revision 493482) @@ -58,17 +58,11 @@ CACHE_TYPE_MMAP } cache_type_e; -typedef struct { - char* hdr; - char* val; -} cache_header_tbl_t; - typedef struct mem_cache_object { + apr_pool_t *pool; cache_type_e type; - apr_ssize_t num_header_out; - apr_ssize_t num_req_hdrs; - cache_header_tbl_t *header_out; - cache_header_tbl_t *req_hdrs; /* for Vary negotiation */ + apr_table_t *header_out; + apr_table_t *req_hdrs; /* for Vary negotiation */ apr_size_t m_len; void *m; apr_os_file_t fd; @@ -210,22 +204,9 @@ { mem_cache_object_t *mobj = obj->vobj; - /* TODO: - * We desperately need a more efficient way of allocating objects. We're - * making way too many malloc calls to create a fully populated - * cache object... - */ - - /* Cleanup the cache_object_t */ - if (obj->key) { - free((void*)obj->key); - } - - free(obj); - /* Cleanup the mem_cache_object_t */ if (mobj) { - if (mobj->type == CACHE_TYPE_HEAP && mobj->m) { + if (mobj->m) { free(mobj->m); } if (mobj->type == CACHE_TYPE_FILE && mobj->fd) { @@ -235,18 +216,9 @@ close(mobj->fd); #endif } - if (mobj->header_out) { - if (mobj->header_out[0].hdr) - free(mobj->header_out[0].hdr); - free(mobj->header_out); - } - if (mobj->req_hdrs) { - if (mobj->req_hdrs[0].hdr) - free(mobj->req_hdrs[0].hdr); - free(mobj->req_hdrs); - } - free(mobj); } + + apr_pool_destroy(mobj->pool); } static apr_status_t decrement_refcount(void *arg) { @@ -334,9 +306,10 @@ static int create_entity(cache_handle_t *h, cache_type_e type_e, request_rec *r, const char *key, apr_off_t len) { + apr_status_t rv; + apr_pool_t *pool; cache_object_t *obj, *tmp_obj; mem_cache_object_t *mobj; - apr_size_t key_len; if (len == -1) { /* Caching a streaming response. Assume the response is @@ -370,25 +343,21 @@ } } - /* Allocate and initialize cache_object_t */ - obj = calloc(1, sizeof(*obj)); - if (!obj) { + rv = apr_pool_create(&pool, NULL); + + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server, + "mem_cache: Failed to create memory pool."); return DECLINED; } - key_len = strlen(key) + 1; - obj->key = malloc(key_len); - if (!obj->key) { - cleanup_cache_object(obj); - return DECLINED; - } - memcpy((void*)obj->key, key, key_len); + /* Allocate and initialize cache_object_t */ + obj = apr_pcalloc(pool, sizeof(*obj)); + obj->key = apr_pstrdup(pool, key); + /* Allocate and init mem_cache_object_t */ - mobj = calloc(1, sizeof(*mobj)); - if (!mobj) { - cleanup_cache_object(obj); - return DECLINED; - } + mobj = apr_pcalloc(pool, sizeof(*mobj)); + mobj->pool = pool; /* Finish initing the cache object */ apr_atomic_set32(&obj->refcount, 1); @@ -539,64 +508,7 @@ return OK; } -static apr_status_t serialize_table(cache_header_tbl_t **obj, - apr_ssize_t *nelts, - apr_table_t *table) -{ - const apr_array_header_t *elts_arr = apr_table_elts(table); - apr_table_entry_t *elts = (apr_table_entry_t *) elts_arr->elts; - apr_ssize_t i; - apr_size_t len = 0; - apr_size_t idx = 0; - char *buf; - *nelts = elts_arr->nelts; - if (*nelts == 0 ) { - *obj=NULL; - return APR_SUCCESS; - } - *obj = malloc(sizeof(cache_header_tbl_t) * elts_arr->nelts); - if (NULL == *obj) { - return APR_ENOMEM; - } - for (i = 0; i < elts_arr->nelts; ++i) { - len += strlen(elts[i].key); - len += strlen(elts[i].val); - len += 2; /* Extra space for NULL string terminator for key and val */ - } - - /* Transfer the headers into a contiguous memory block */ - buf = malloc(len); - if (!buf) { - *obj = NULL; - return APR_ENOMEM; - } - - for (i = 0; i < *nelts; ++i) { - (*obj)[i].hdr = &buf[idx]; - len = strlen(elts[i].key) + 1; /* Include NULL terminator */ - memcpy(&buf[idx], elts[i].key, len); - idx+=len; - - (*obj)[i].val = &buf[idx]; - len = strlen(elts[i].val) + 1; - memcpy(&buf[idx], elts[i].val, len); - idx+=len; - } - return APR_SUCCESS; -} -static int unserialize_table( cache_header_tbl_t *ctbl, - int num_headers, - apr_table_t *t ) -{ - int i; - - for (i = 0; i < num_headers; ++i) { - apr_table_addn(t, ctbl[i].hdr, ctbl[i].val); - } - - return APR_SUCCESS; -} /* Define request processing hook handlers */ /* remove_url() * Notes: @@ -629,17 +541,12 @@ static apr_status_t recall_headers(cache_handle_t *h, request_rec *r) { - int rc; mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj; - h->req_hdrs = apr_table_make(r->pool, mobj->num_req_hdrs); - h->resp_hdrs = apr_table_make(r->pool, mobj->num_header_out); + h->req_hdrs = apr_table_copy(r->pool, mobj->req_hdrs); + h->resp_hdrs = apr_table_copy(r->pool, mobj->header_out); - rc = unserialize_table(mobj->req_hdrs, mobj->num_req_hdrs, h->req_hdrs); - rc = unserialize_table(mobj->header_out, mobj->num_header_out, - h->resp_hdrs); - - return rc; + return OK; } static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb) @@ -669,7 +576,6 @@ { cache_object_t *obj = h->cache_obj; mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj; - int rc; apr_table_t *headers_out; /* @@ -679,12 +585,7 @@ * - The original response headers (for returning with a cached response) * - The body of the message */ - rc = serialize_table(&mobj->req_hdrs, - &mobj->num_req_hdrs, - r->headers_in); - if (rc != APR_SUCCESS) { - return rc; - } + mobj->req_hdrs = apr_table_copy(mobj->pool, r->headers_in); /* Precompute how much storage we need to hold the headers */ headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out, @@ -698,13 +599,8 @@ } headers_out = apr_table_overlay(r->pool, headers_out, r->err_headers_out); + mobj->header_out = apr_table_copy(mobj->pool, headers_out); - rc = serialize_table(&mobj->header_out, &mobj->num_header_out, - headers_out); - if (rc != APR_SUCCESS) { - return rc; - } - /* Init the info struct */ obj->info.status = info->status; if (info->date) { @@ -812,13 +708,10 @@ /* Caching a streamed response. Reallocate a buffer of the * correct size and copy the streamed response into that * buffer */ - char *buf = malloc(obj->count); - if (!buf) { + mobj->m = realloc(mobj->m, obj->count); + if (!mobj->m) { return APR_ENOMEM; } - memcpy(buf, mobj->m, obj->count); - free(mobj->m); - mobj->m = buf; /* Now comes the crufty part... there is no way to tell the * cache that the size of the object has changed. We need Index: modules/cache/mod_cache.c =================================================================== --- modules/cache/mod_cache.c (.../2.2.3) (revision 493482) +++ modules/cache/mod_cache.c (.../2.2.4) (revision 493482) @@ -225,10 +225,12 @@ out = apr_brigade_create(r->pool, r->connection->bucket_alloc); rv = ap_pass_brigade(r->output_filters, out); if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, - "cache: error returned while trying to return %s " - "cached data", - cache->provider_name); + if (rv != AP_FILTER_ERROR) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "cache: error returned while trying to return %s " + "cached data", + cache->provider_name); + } return rv; } @@ -426,6 +428,11 @@ /* if a broken Expires header is present, don't cache it */ reason = apr_pstrcat(p, "Broken expires header: ", exps, NULL); } + else if (exp != APR_DATE_BAD && exp < r->request_time) + { + /* if a Expires header is in the past, don't cache it */ + reason = "Expires header already expired, not cacheable"; + } else if (r->args && exps == NULL) { /* if query string present but no expiration time, don't cache it * (RFC 2616/13.9) Index: modules/cache/mod_disk_cache.c =================================================================== --- modules/cache/mod_disk_cache.c (.../2.2.3) (revision 493482) +++ modules/cache/mod_disk_cache.c (.../2.2.4) (revision 493482) @@ -1006,7 +1006,7 @@ if (dobj->file_size > conf->maxfs) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "cache_disk: URL %s failed the size check " - "(%" APR_OFF_T_FMT ">%" APR_SIZE_T_FMT ")", + "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")", h->cache_obj->key, dobj->file_size, conf->maxfs); /* Remove the intermediate cache file and return non-APR_SUCCESS */ file_cache_errorcleanup(dobj, r); @@ -1030,7 +1030,7 @@ if (dobj->file_size < conf->minfs) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "cache_disk: URL %s failed the size check " - "(%" APR_OFF_T_FMT "<%" APR_SIZE_T_FMT ")", + "(%" APR_OFF_T_FMT " < %" APR_OFF_T_FMT ")", h->cache_obj->key, dobj->file_size, conf->minfs); /* Remove the intermediate cache file and return non-APR_SUCCESS */ file_cache_errorcleanup(dobj, r); @@ -1117,15 +1117,25 @@ { disk_cache_conf *conf = ap_get_module_config(parms->server->module_config, &disk_cache_module); - conf->minfs = atoi(arg); + + if (apr_strtoff(&conf->minfs, arg, NULL, 0) != APR_SUCCESS || + conf->minfs < 0) + { + return "CacheMinFileSize argument must be a non-negative integer representing the min size of a file to cache in bytes."; + } return NULL; } + static const char *set_cache_maxfs(cmd_parms *parms, void *in_struct_ptr, const char *arg) { disk_cache_conf *conf = ap_get_module_config(parms->server->module_config, &disk_cache_module); - conf->maxfs = atoi(arg); + if (apr_strtoff(&conf->maxfs, arg, NULL, 0) != APR_SUCCESS || + conf->maxfs < 0) + { + return "CacheMaxFileSize argument must be a non-negative integer representing the max size of a file to cache in bytes."; + } return NULL; } Index: modules/cache/mod_disk_cache.h =================================================================== --- modules/cache/mod_disk_cache.h (.../2.2.3) (revision 493482) +++ modules/cache/mod_disk_cache.h (.../2.2.4) (revision 493482) @@ -88,8 +88,8 @@ apr_size_t cache_root_len; int dirlevels; /* Number of levels of subdirectories */ int dirlength; /* Length of subdirectory names */ - apr_size_t minfs; /* minumum file size for cached files */ - apr_size_t maxfs; /* maximum file size for cached files */ + apr_off_t minfs; /* minimum file size for cached files */ + apr_off_t maxfs; /* maximum file size for cached files */ } disk_cache_conf; #endif /*MOD_DISK_CACHE_H*/ Index: modules/database/mod_dbd.c =================================================================== --- modules/database/mod_dbd.c (.../2.2.3) (revision 493482) +++ modules/database/mod_dbd.c (.../2.2.4) (revision 493482) @@ -25,6 +25,7 @@ #include "http_protocol.h" #include "http_config.h" #include "http_log.h" +#include "http_request.h" #include "apr_reslist.h" #include "apr_strings.h" #include "apr_dbd.h" @@ -147,12 +148,11 @@ const char *label) { dbd_prepared *prepared = apr_pcalloc(s->process->pool, sizeof(dbd_prepared)); + const char *key = apr_psprintf(s->process->pool, "%pp", s); prepared->label = label; prepared->query = query; - prepared->next = apr_hash_get(dbd_prepared_defns, s->server_hostname, - APR_HASH_KEY_STRING); - apr_hash_set(dbd_prepared_defns, s->server_hostname, APR_HASH_KEY_STRING, - prepared); + prepared->next = apr_hash_get(dbd_prepared_defns, key, APR_HASH_KEY_STRING); + apr_hash_set(dbd_prepared_defns, key, APR_HASH_KEY_STRING, prepared); } static const char *dbd_prepare(cmd_parms *cmd, void *cfg, const char *query, const char *label) @@ -361,6 +361,15 @@ svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module); apr_status_t rv; + /* dbd_setup in 2.2.3 and under was causing spurious error messages + * when dbd isn't configured. We can stop that with a quick check here + * together with a similar check in ap_dbd_open (where being + * unconfigured is a genuine error that must be reported). + */ + if (svr->name == no_dbdriver) { + return APR_SUCCESS; + } + if (!svr->persist) { return APR_SUCCESS; } @@ -442,6 +451,12 @@ apr_status_t rv = APR_SUCCESS; const char *errmsg; + /* If nothing is configured, we shouldn't be here */ + if (svr->name == no_dbdriver) { + ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool, "DBD: not configured"); + return NULL; + } + if (!svr->persist) { /* Return a once-only connection */ rv = dbd_construct(&rec, svr, s->process->pool); @@ -480,6 +495,12 @@ void *rec = NULL; svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module); + /* If nothing is configured, we shouldn't be here */ + if (svr->name == no_dbdriver) { + ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool, "DBD: not configured"); + return NULL; + } + if (!svr->persist) { /* Return a once-only connection */ rv = dbd_construct(&rec, svr, s->process->pool); @@ -525,7 +546,18 @@ DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_acquire(request_rec *r) { svr_cfg *svr; - dbd_pool_rec *req = ap_get_module_config(r->request_config, &dbd_module); + dbd_pool_rec *req; + + while (!ap_is_initial_req(r)) { + if (r->prev) { + r = r->prev; + } + else if (r->main) { + r = r->main; + } + } + + req = ap_get_module_config(r->request_config, &dbd_module); if (!req) { req = apr_palloc(r->pool, sizeof(dbd_pool_rec)); req->conn = ap_dbd_open(r->pool, r->server); @@ -572,7 +604,18 @@ DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_acquire(request_rec *r) { svr_cfg *svr; - ap_dbd_t *ret = ap_get_module_config(r->request_config, &dbd_module); + ap_dbd_t *ret; + + while (!ap_is_initial_req(r)) { + if (r->prev) { + r = r->prev; + } + else if (r->main) { + r = r->main; + } + } + + ret = ap_get_module_config(r->request_config, &dbd_module); if (!ret) { svr = ap_get_module_config(r->server->module_config, &dbd_module); ret = ap_dbd_open(r->pool, r->server); @@ -618,8 +661,9 @@ svr_cfg *svr; server_rec *sp; for (sp = s; sp; sp = sp->next) { + const char *key = apr_psprintf(s->process->pool, "%pp", s); svr = ap_get_module_config(sp->module_config, &dbd_module); - svr->prepared = apr_hash_get(dbd_prepared_defns, sp->server_hostname, + svr->prepared = apr_hash_get(dbd_prepared_defns, key, APR_HASH_KEY_STRING); } return OK; Index: modules/mappers/mod_rewrite.c =================================================================== --- modules/mappers/mod_rewrite.c (.../2.2.3) (revision 493482) +++ modules/mappers/mod_rewrite.c (.../2.2.4) (revision 493482) @@ -2003,7 +2003,7 @@ case 'S': if (!strcmp(var, "SERVER_SOFTWARE")) { - result = ap_get_server_version(); + result = ap_get_server_banner(); } break; } Index: modules/filters/mod_deflate.c =================================================================== --- modules/filters/mod_deflate.c (.../2.2.3) (revision 493482) +++ modules/filters/mod_deflate.c (.../2.2.4) (revision 493482) @@ -212,8 +212,88 @@ unsigned char *buffer; unsigned long crc; apr_bucket_brigade *bb, *proc_bb; + int (*libz_end_func)(z_streamp); + unsigned char *validation_buffer; + apr_size_t validation_buffer_length; } deflate_ctx; +/* Number of validation bytes (CRC and length) after the compressed data */ +#define VALIDATION_SIZE 8 +/* Do not update ctx->crc, see comment in flush_libz_buffer */ +#define NO_UPDATE_CRC 0 +/* Do update ctx->crc, see comment in flush_libz_buffer */ +#define UPDATE_CRC 1 + +static int flush_libz_buffer(deflate_ctx *ctx, deflate_filter_config *c, + struct apr_bucket_alloc_t *bucket_alloc, + int (*libz_func)(z_streamp, int), int flush, + int crc) +{ + int zRC = Z_OK; + int done = 0; + unsigned int deflate_len; + apr_bucket *b; + + for (;;) { + deflate_len = c->bufferSize - ctx->stream.avail_out; + + if (deflate_len != 0) { + /* + * Do we need to update ctx->crc? Usually this is the case for + * inflate action where we need to do a crc on the output, whereas + * in the deflate case we need to do a crc on the input + */ + if (crc) { + ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, + deflate_len); + } + b = apr_bucket_heap_create((char *)ctx->buffer, + deflate_len, NULL, + bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); + ctx->stream.next_out = ctx->buffer; + ctx->stream.avail_out = c->bufferSize; + } + + if (done) + break; + + zRC = libz_func(&ctx->stream, flush); + + /* + * We can ignore Z_BUF_ERROR because: + * When we call libz_func we can assume that + * + * - avail_in is zero (due to the surrounding code that calls + * flush_libz_buffer) + * - avail_out is non zero due to our actions some lines above + * + * So the only reason for Z_BUF_ERROR is that the internal libz + * buffers are now empty and thus we called libz_func one time + * too often. This does not hurt. It simply says that we are done. + */ + if (zRC == Z_BUF_ERROR) { + zRC = Z_OK; + break; + } + + done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END); + + if (zRC != Z_OK && zRC != Z_STREAM_END) + break; + } + return zRC; +} + +static apr_status_t deflate_ctx_cleanup(void *data) +{ + deflate_ctx *ctx = (deflate_ctx *)data; + + if (ctx) + ctx->libz_end_func(&ctx->stream); + return APR_SUCCESS; +} + static apr_status_t deflate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) { @@ -221,14 +301,16 @@ request_rec *r = f->r; deflate_ctx *ctx = f->ctx; int zRC; - deflate_filter_config *c = ap_get_module_config(r->server->module_config, - &deflate_module); + deflate_filter_config *c; /* Do nothing if asked to filter nothing. */ if (APR_BRIGADE_EMPTY(bb)) { - return APR_SUCCESS; + return ap_pass_brigade(f->next, bb); } + c = ap_get_module_config(r->server->module_config, + &deflate_module); + /* If we don't have a context, we need to ensure that it is okay to send * the deflated content. If we have a context, that means we've done * this before and we liked it. @@ -362,19 +444,32 @@ ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx)); ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); ctx->buffer = apr_palloc(r->pool, c->bufferSize); + ctx->libz_end_func = deflateEnd; zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED, c->windowSize, c->memlevel, Z_DEFAULT_STRATEGY); if (zRC != Z_OK) { - f->ctx = NULL; + deflateEnd(&ctx->stream); ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "unable to init Zlib: " "deflateInit2 returned %d: URL %s", zRC, r->uri); + /* + * Remove ourselves as it does not make sense to return: + * We are not able to init libz and pass data down the chain + * uncompressed. + */ + ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } + /* + * Register a cleanup function to ensure that we cleanup the internal + * libz resources. + */ + apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup, + apr_pool_cleanup_null); /* add immortal gzip header */ e = apr_bucket_immortal_create(gzip_header, sizeof gzip_header, @@ -400,49 +495,23 @@ const char *data; apr_bucket *b; apr_size_t len; - int done = 0; e = APR_BRIGADE_FIRST(bb); if (APR_BUCKET_IS_EOS(e)) { char *buf; - unsigned int deflate_len; ctx->stream.avail_in = 0; /* should be zero already anyway */ - for (;;) { - deflate_len = c->bufferSize - ctx->stream.avail_out; + /* flush the remaining data from the zlib buffers */ + flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, Z_FINISH, + NO_UPDATE_CRC); - if (deflate_len != 0) { - b = apr_bucket_heap_create((char *)ctx->buffer, - deflate_len, NULL, - f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->bb, b); - ctx->stream.next_out = ctx->buffer; - ctx->stream.avail_out = c->bufferSize; - } - - if (done) { - break; - } - - zRC = deflate(&ctx->stream, Z_FINISH); - - if (deflate_len == 0 && zRC == Z_BUF_ERROR) { - zRC = Z_OK; - } - - done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END); - - if (zRC != Z_OK && zRC != Z_STREAM_END) { - break; - } - } - - buf = apr_palloc(r->pool, 8); + buf = apr_palloc(r->pool, VALIDATION_SIZE); putLong((unsigned char *)&buf[0], ctx->crc); putLong((unsigned char *)&buf[4], ctx->stream.total_in); - b = apr_bucket_pool_create(buf, 8, r->pool, f->c->bucket_alloc); + b = apr_bucket_pool_create(buf, VALIDATION_SIZE, r->pool, + f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ctx->bb, b); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Zlib: Compressed %ld to %ld : URL %s", @@ -476,6 +545,8 @@ } deflateEnd(&ctx->stream); + /* No need for cleanup any longer */ + apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup); /* Remove EOS from the old list, and insert into the new. */ APR_BUCKET_REMOVE(e); @@ -488,28 +559,18 @@ } if (APR_BUCKET_IS_FLUSH(e)) { - apr_bucket *bkt; apr_status_t rv; - apr_bucket_delete(e); - - if (ctx->stream.avail_in > 0) { - zRC = deflate(&(ctx->stream), Z_SYNC_FLUSH); - if (zRC != Z_OK) { - return APR_EGENERAL; - } + /* flush the remaining data from the zlib buffers */ + zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, + Z_SYNC_FLUSH, NO_UPDATE_CRC); + if (zRC != Z_OK) { + return APR_EGENERAL; } - ctx->stream.next_out = ctx->buffer; - len = c->bufferSize - ctx->stream.avail_out; - - b = apr_bucket_heap_create((char *)ctx->buffer, len, - NULL, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->bb, b); - ctx->stream.avail_out = c->bufferSize; - - bkt = apr_bucket_flush_create(f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->bb, bkt); + /* Remove flush bucket from old brigade anf insert into the new. */ + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(ctx->bb, e); rv = ap_pass_brigade(f->next, ctx->bb); if (rv != APR_SUCCESS) { return rv; @@ -549,8 +610,9 @@ zRC = deflate(&(ctx->stream), Z_NO_FLUSH); - if (zRC != Z_OK) + if (zRC != Z_OK) { return APR_EGENERAL; + } } apr_bucket_delete(e); @@ -831,8 +893,8 @@ { int zlib_method; int zlib_flags; - int deflate_init = 1; - apr_bucket *bkt; + int inflate_init = 1; + apr_bucket *e; request_rec *r = f->r; deflate_ctx *ctx = f->ctx; int zRC; @@ -841,7 +903,7 @@ /* Do nothing if asked to filter nothing. */ if (APR_BRIGADE_EMPTY(bb)) { - return APR_SUCCESS; + return ap_pass_brigade(f->next, bb); } c = ap_get_module_config(r->server->module_config, &deflate_module); @@ -857,8 +919,9 @@ return ap_pass_brigade(f->next, bb); } - /* Let's see what our current Content-Encoding is. - * If gzip is present, don't gzip again. (We could, but let's not.) + /* + * Let's see what our current Content-Encoding is. + * Only inflate if gzip is present. */ encoding = apr_table_get(r->headers_out, "Content-Encoding"); if (encoding) { @@ -890,10 +953,12 @@ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); - ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc); + ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); ctx->buffer = apr_palloc(r->pool, c->bufferSize); + ctx->libz_end_func = inflateEnd; + ctx->validation_buffer = NULL; + ctx->validation_buffer_length = 0; - zRC = inflateInit2(&ctx->stream, c->windowSize); if (zRC != Z_OK) { @@ -903,60 +968,124 @@ "unable to init Zlib: " "inflateInit2 returned %d: URL %s", zRC, r->uri); + /* + * Remove ourselves as it does not make sense to return: + * We are not able to init libz and pass data down the chain + * compressed. + */ ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } - /* initialize deflate output buffer */ + /* + * Register a cleanup function to ensure that we cleanup the internal + * libz resources. + */ + apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup, + apr_pool_cleanup_null); + + apr_table_unset(r->headers_out, "Content-Length"); + + /* initialize inflate output buffer */ ctx->stream.next_out = ctx->buffer; ctx->stream.avail_out = c->bufferSize; - deflate_init = 0; + inflate_init = 0; } - for (bkt = APR_BRIGADE_FIRST(bb); - bkt != APR_BRIGADE_SENTINEL(bb); - bkt = APR_BUCKET_NEXT(bkt)) + while (!APR_BRIGADE_EMPTY(bb)) { const char *data; + apr_bucket *b; apr_size_t len; - /* If we actually see the EOS, that means we screwed up! */ - /* no it doesn't - not in a HEAD or 204/304 */ - if (APR_BUCKET_IS_EOS(bkt)) { - inflateEnd(&ctx->stream); - return ap_pass_brigade(f->next, bb); - } + e = APR_BRIGADE_FIRST(bb); - if (APR_BUCKET_IS_FLUSH(bkt)) { - apr_bucket *tmp_heap; - zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH); - if (zRC != Z_OK) { + if (APR_BUCKET_IS_EOS(e)) { + /* + * We are really done now. Ensure that we never return here, even + * if a second EOS bucket falls down the chain. Thus remove + * ourselves. + */ + ap_remove_output_filter(f); + /* should be zero already anyway */ + ctx->stream.avail_in = 0; + /* + * Flush the remaining data from the zlib buffers. It is correct + * to use Z_SYNC_FLUSH in this case and not Z_FINISH as in the + * deflate case. In the inflate case Z_FINISH requires to have a + * large enough output buffer to put ALL data in otherwise it + * fails, whereas in the deflate case you can empty a filled output + * buffer and call it again until no more output can be created. + */ + flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, Z_SYNC_FLUSH, + UPDATE_CRC); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "Zlib: Inflated %ld to %ld : URL %s", + ctx->stream.total_in, ctx->stream.total_out, r->uri); + + if (ctx->validation_buffer_length == VALIDATION_SIZE) { + unsigned long compCRC, compLen; + compCRC = getLong(ctx->validation_buffer); + if (ctx->crc != compCRC) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Zlib: Checksum of inflated stream invalid"); + return APR_EGENERAL; + } + ctx->validation_buffer += VALIDATION_SIZE / 2; + compLen = getLong(ctx->validation_buffer); + if (ctx->stream.total_out != compLen) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Zlib: Length of inflated stream invalid"); + return APR_EGENERAL; + } + } + else { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Inflate error %d on flush", zRC); - inflateEnd(&ctx->stream); + "Zlib: Validation bytes not present"); return APR_EGENERAL; } - ctx->stream.next_out = ctx->buffer; - len = c->bufferSize - ctx->stream.avail_out; + inflateEnd(&ctx->stream); + /* No need for cleanup any longer */ + apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup); - ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); - tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, - NULL, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); - ctx->stream.avail_out = c->bufferSize; + /* Remove EOS from the old list, and insert into the new. */ + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(ctx->bb, e); - /* Move everything to the returning brigade. */ - APR_BUCKET_REMOVE(bkt); - break; + /* + * Okay, we've seen the EOS. + * Time to pass it along down the chain. + */ + return ap_pass_brigade(f->next, ctx->bb); } + if (APR_BUCKET_IS_FLUSH(e)) { + apr_status_t rv; + + /* flush the remaining data from the zlib buffers */ + zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, inflate, + Z_SYNC_FLUSH, UPDATE_CRC); + if (zRC != Z_OK) { + return APR_EGENERAL; + } + + /* Remove flush bucket from old brigade anf insert into the new. */ + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(ctx->bb, e); + rv = ap_pass_brigade(f->next, ctx->bb); + if (rv != APR_SUCCESS) { + return rv; + } + continue; + } + /* read */ - apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ); + apr_bucket_read(e, &data, &len, APR_BLOCK_READ); /* first bucket contains zlib header */ - if (!deflate_init++) { + if (!inflate_init++) { if (len < 10) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Insufficient data for inflate"); @@ -1010,84 +1139,92 @@ ctx->stream.next_in = (unsigned char *)data; ctx->stream.avail_in = len; + if (ctx->validation_buffer) { + if (ctx->validation_buffer_length < VALIDATION_SIZE) { + apr_size_t copy_size; + + copy_size = VALIDATION_SIZE - ctx->validation_buffer_length; + if (copy_size > ctx->stream.avail_in) + copy_size = ctx->stream.avail_in; + memcpy(ctx->validation_buffer + ctx->validation_buffer_length, + ctx->stream.next_in, copy_size); + /* Saved copy_size bytes */ + ctx->stream.avail_in -= copy_size; + ctx->validation_buffer_length += copy_size; + } + if (ctx->stream.avail_in) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "Zlib: %d bytes of garbage at the end of " + "compressed stream.", ctx->stream.avail_in); + /* + * There is nothing worth consuming for zlib left, because it is + * either garbage data or the data has been copied to the + * validation buffer (processing validation data is no business + * for zlib). So set ctx->stream.avail_in to zero to indicate + * this to the following while loop. + */ + ctx->stream.avail_in = 0; + } + } + zRC = Z_OK; while (ctx->stream.avail_in != 0) { if (ctx->stream.avail_out == 0) { - apr_bucket *tmp_heap; + ctx->stream.next_out = ctx->buffer; len = c->bufferSize - ctx->stream.avail_out; ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); - tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, - NULL, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); + b = apr_bucket_heap_create((char *)ctx->buffer, len, + NULL, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); ctx->stream.avail_out = c->bufferSize; + /* Send what we have right now to the next filter. */ + rv = ap_pass_brigade(f->next, ctx->bb); + if (rv != APR_SUCCESS) { + return rv; + } } zRC = inflate(&ctx->stream, Z_NO_FLUSH); if (zRC == Z_STREAM_END) { + /* + * We have inflated all data. Now try to capture the + * validation bytes. We may not have them all available + * right now, but capture what is there. + */ + ctx->validation_buffer = apr_pcalloc(f->r->pool, + VALIDATION_SIZE); + if (ctx->stream.avail_in > VALIDATION_SIZE) { + ctx->validation_buffer_length = VALIDATION_SIZE; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "Zlib: %d bytes of garbage at the end of " + "compressed stream.", + ctx->stream.avail_in - VALIDATION_SIZE); + } else if (ctx->stream.avail_in > 0) { + ctx->validation_buffer_length = ctx->stream.avail_in; + } + if (ctx->validation_buffer_length) + memcpy(ctx->validation_buffer, ctx->stream.next_in, + ctx->validation_buffer_length); break; } if (zRC != Z_OK) { - inflateEnd(&ctx->stream); - return APR_EGENERAL; - } - } - if (zRC == Z_STREAM_END) { - apr_bucket *tmp_heap, *eos; - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "Zlib: Inflated %ld to %ld : URL %s", - ctx->stream.total_in, ctx->stream.total_out, - r->uri); - - len = c->bufferSize - ctx->stream.avail_out; - - ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); - tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, - NULL, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); - ctx->stream.avail_out = c->bufferSize; - - /* Is the remaining 8 bytes already in the avail stream? */ - if (ctx->stream.avail_in >= 8) { - unsigned long compCRC, compLen; - compCRC = getLong(ctx->stream.next_in); - if (ctx->crc != compCRC) { - inflateEnd(&ctx->stream); - return APR_EGENERAL; - } - ctx->stream.next_in += 4; - compLen = getLong(ctx->stream.next_in); - if (ctx->stream.total_out != compLen) { - inflateEnd(&ctx->stream); - return APR_EGENERAL; - } - } - else { - /* FIXME: We need to grab the 8 verification bytes - * from the wire! */ - inflateEnd(&ctx->stream); return APR_EGENERAL; } - - inflateEnd(&ctx->stream); - - eos = apr_bucket_eos_create(f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos); - break; } + apr_bucket_delete(e); } - rv = ap_pass_brigade(f->next, ctx->proc_bb); - apr_brigade_cleanup(ctx->proc_bb); - return rv ; + apr_brigade_cleanup(bb); + return APR_SUCCESS; } +#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH static void register_hooks(apr_pool_t *p) { ap_register_output_filter(deflateFilterName, deflate_out_filter, NULL, Index: modules/filters/mod_ext_filter.c =================================================================== --- modules/filters/mod_ext_filter.c (.../2.2.3) (revision 493482) +++ modules/filters/mod_ext_filter.c (.../2.2.4) (revision 493482) @@ -206,6 +206,7 @@ &ext_filter_module); const char *token; const char *name; + char *normalized_name; ef_filter_t *filter; name = ap_getword_white(cmd->pool, &args); @@ -213,7 +214,16 @@ return "Filter name not found"; } - if (apr_hash_get(conf->h, name, APR_HASH_KEY_STRING)) { + /* During request processing, we find information about the filter + * by looking up the filter name provided by core server in our + * hash table. But the core server has normalized the filter + * name by converting it to lower case. Thus, when adding the + * filter to our hash table we have to use lower case as well. + */ + normalized_name = apr_pstrdup(cmd->pool, name); + ap_str_tolower(normalized_name); + + if (apr_hash_get(conf->h, normalized_name, APR_HASH_KEY_STRING)) { return apr_psprintf(cmd->pool, "ExtFilter %s is already defined", name); } @@ -222,7 +232,7 @@ filter->name = name; filter->mode = OUTPUT_FILTER; filter->ftype = AP_FTYPE_RESOURCE; - apr_hash_set(conf->h, name, APR_HASH_KEY_STRING, filter); + apr_hash_set(conf->h, normalized_name, APR_HASH_KEY_STRING, filter); while (*args) { while (apr_isspace(*args)) { Index: modules/filters/mod_filter.c =================================================================== --- modules/filters/mod_filter.c (.../2.2.3) (revision 493482) +++ modules/filters/mod_filter.c (.../2.2.4) (revision 493482) @@ -638,7 +638,7 @@ } if ( (provider->dispatch == RESPONSE_HEADERS) - && !strcmp(str, "content-type")) { + && !strcasecmp(str, "content-type")) { provider->dispatch = CONTENT_TYPE; } provider->value = str; Index: modules/generators/mod_cgi.c =================================================================== --- modules/generators/mod_cgi.c (.../2.2.3) (revision 493482) +++ modules/generators/mod_cgi.c (.../2.2.4) (revision 493482) @@ -837,7 +837,9 @@ APR_BLOCK_READ, HUGE_STRING_LEN); if (rv != APR_SUCCESS) { - return rv; + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "Error reading request entity data"); + return HTTP_INTERNAL_SERVER_ERROR; } for (bucket = APR_BRIGADE_FIRST(bb); Index: modules/generators/mod_cgid.c =================================================================== --- modules/generators/mod_cgid.c (.../2.2.3) (revision 493482) +++ modules/generators/mod_cgid.c (.../2.2.4) (revision 493482) @@ -1387,7 +1387,9 @@ APR_BLOCK_READ, HUGE_STRING_LEN); if (rv != APR_SUCCESS) { - return rv; + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "Error reading request entity data"); + return HTTP_INTERNAL_SERVER_ERROR; } for (bucket = APR_BRIGADE_FIRST(bb); Index: modules/generators/mod_status.c =================================================================== --- modules/generators/mod_status.c (.../2.2.3) (revision 493482) +++ modules/generators/mod_status.c (.../2.2.4) (revision 493482) @@ -397,7 +397,7 @@ ap_rputs("

Apache Server Status for ", r); ap_rvputs(r, ap_get_server_name(r), "

\n\n", NULL); ap_rvputs(r, "
Server Version: ", - ap_get_server_version(), "
\n", NULL); + ap_get_server_description(), "\n", NULL); ap_rvputs(r, "
Server Built: ", ap_get_server_built(), "\n

\n", NULL); ap_rvputs(r, "
Current Time: ", Index: modules/generators/mod_info.c =================================================================== --- modules/generators/mod_info.c (.../2.2.3) (revision 493482) +++ modules/generators/mod_info.c (.../2.2.4) (revision 493482) @@ -346,7 +346,7 @@ ap_rprintf(r, "
Server Version: " "%s
\n", - ap_get_server_version()); + ap_get_server_description()); ap_rprintf(r, "
Server Built: " "%s
\n", Index: modules/arch/win32/mod_isapi.c =================================================================== --- modules/arch/win32/mod_isapi.c (.../2.2.3) (revision 493482) +++ modules/arch/win32/mod_isapi.c (.../2.2.4) (revision 493482) @@ -630,6 +630,8 @@ { int head_present = 1; int termarg; + int res; + int old_status; const char *termch; apr_size_t ate = 0; @@ -680,7 +682,7 @@ * or the http/x.x xxx message format */ if (toklen && apr_isdigit(*stattok)) { - statlen -= toklen; + statlen = toklen; stat = stattok; } } @@ -709,29 +711,71 @@ * * Parse them out, or die trying */ + old_status = cid->r->status; + if (stat) { - cid->r->status = ap_scan_script_header_err_strs(cid->r, NULL, - &termch, &termarg, stat, head, NULL); + res = ap_scan_script_header_err_strs(cid->r, NULL, &termch, &termarg, + stat, head, NULL); + } + else { + res = ap_scan_script_header_err_strs(cid->r, NULL, &termch, &termarg, + head, NULL); + } + + /* Set our status. */ + if (res) { + /* This is an immediate error result from the parser + */ + cid->r->status = res; + cid->r->status_line = ap_get_status_line(cid->r->status); cid->ecb->dwHttpStatusCode = cid->r->status; } + else if (cid->r->status) { + /* We have a status in r->status, so let's just use it. + * This is likely to be the Status: parsed above, and + * may also be a delayed error result from the parser. + * If it was filled in, status_line should also have + * been filled in. + */ + cid->ecb->dwHttpStatusCode = cid->r->status; + } + else if (cid->ecb->dwHttpStatusCode + && cid->ecb->dwHttpStatusCode != HTTP_OK) { + /* Now we fall back on dwHttpStatusCode if it appears + * ap_scan_script_header fell back on the default code. + * Any other results set dwHttpStatusCode to the decoded + * status value. + */ + cid->r->status = cid->ecb->dwHttpStatusCode; + cid->r->status_line = ap_get_status_line(cid->r->status); + } + else if (old_status) { + /* Well... either there is no dwHttpStatusCode or it's HTTP_OK. + * In any case, we don't have a good status to return yet... + * Perhaps the one we came in with will be better. Let's use it, + * if we were given one (note this is a pendantic case, it would + * normally be covered above unless the scan script code unset + * the r->status). Should there be a check here as to whether + * we are setting a valid response code? + */ + cid->r->status = old_status; + cid->r->status_line = ap_get_status_line(cid->r->status); + cid->ecb->dwHttpStatusCode = cid->r->status; + } else { - cid->r->status = ap_scan_script_header_err_strs(cid->r, NULL, - &termch, &termarg, head, NULL); - if (cid->ecb->dwHttpStatusCode && cid->r->status == HTTP_OK - && cid->ecb->dwHttpStatusCode != HTTP_OK) { - /* We tried every way to Sunday to get the status... - * so now we fall back on dwHttpStatusCode if it appears - * ap_scan_script_header fell back on the default code. - * Any other results set dwHttpStatusCode to the decoded - * status value. - */ - cid->r->status = cid->ecb->dwHttpStatusCode; - cid->r->status_line = ap_get_status_line(cid->r->status); - } - else { - cid->ecb->dwHttpStatusCode = cid->r->status; - } + /* None of dwHttpStatusCode, the parser's r->status nor the + * old value of r->status were helpful, and nothing was decoded + * from Status: string passed to us. Let's just say HTTP_OK + * and get the data out, this was the isapi dev's oversight. + */ + cid->r->status = HTTP_OK; + cid->r->status_line = ap_get_status_line(cid->r->status); + cid->ecb->dwHttpStatusCode = cid->r->status; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, cid->r, + "ISAPI: Could not determine HTTP response code; using %d", + cid->r->status); } + if (cid->r->status == HTTP_INTERNAL_SERVER_ERROR) { return -1; } @@ -771,7 +815,7 @@ char *buf_data = (char*)buf_ptr; apr_bucket_brigade *bb; apr_bucket *b; - apr_status_t rv; + apr_status_t rv = APR_SUCCESS; if (!cid->headers_set) { /* It appears that the foxisapi module and other clients @@ -797,10 +841,14 @@ APR_BRIGADE_INSERT_TAIL(bb, b); rv = ap_pass_brigade(r->output_filters, bb); cid->response_sent = 1; + if (rv != APR_SUCCESS) + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, + "ISAPI: WriteClient ap_pass_brigade " + "failed: %s", r->filename); } if ((flags & HSE_IO_ASYNC) && cid->completion) { - if (rv == OK) { + if (rv == APR_SUCCESS) { cid->completion(cid->ecb, cid->completion_arg, *size_arg, ERROR_SUCCESS); } @@ -809,7 +857,7 @@ *size_arg, ERROR_WRITE_FAULT); } } - return (rv == OK); + return (rv == APR_SUCCESS); } int APR_THREAD_FUNC ServerSupportFunction(isapi_cid *cid, @@ -822,6 +870,7 @@ conn_rec *c = r->connection; char *buf_data = (char*)buf_ptr; request_rec *subreq; + apr_status_t rv; switch (HSE_code) { case HSE_REQ_SEND_URL_REDIRECT_RESP: @@ -881,9 +930,19 @@ APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_flush_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); - ap_pass_brigade(cid->r->output_filters, bb); + rv = ap_pass_brigade(cid->r->output_filters, bb); cid->response_sent = 1; + if (rv != APR_SUCCESS) + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, + "ISAPI: ServerSupport function " + "HSE_REQ_SEND_RESPONSE_HEADER " + "ap_pass_brigade failed: %s", r->filename); + return (rv == APR_SUCCESS); } + /* Deliberately hold off sending 'just the headers' to begin to + * accumulate the body and speed up the overall response, or at + * least wait for the end the session. + */ return 1; } @@ -909,20 +968,33 @@ /* Map a URL to a filename */ char *file = (char *)buf_data; apr_uint32_t len; - subreq = ap_sub_req_lookup_uri(apr_pstrndup(r->pool, file, *buf_size), - r, NULL); + subreq = ap_sub_req_lookup_uri( + apr_pstrndup(cid->r->pool, file, *buf_size), r, NULL); - len = apr_cpystrn(file, subreq->filename, *buf_size) - file; + if (!subreq->filename) { + ap_destroy_sub_req(subreq); + return 0; + } + len = (apr_uint32_t)strlen(r->filename); - /* IIS puts a trailing slash on directories, Apache doesn't */ - if (subreq->finfo.filetype == APR_DIR) { - if (len < *buf_size - 1) { - file[len++] = '\\'; - file[len] = '\0'; - } - } - *buf_size = len; + if ((subreq->finfo.filetype == APR_DIR) + && (!subreq->path_info) + && (file[len - 1] != '/')) + file = apr_pstrcat(cid->r->pool, subreq->filename, "/", NULL); + else + file = apr_pstrcat(cid->r->pool, subreq->filename, + subreq->path_info, NULL); + + ap_destroy_sub_req(subreq); + +#ifdef WIN32 + /* We need to make this a real Windows path name */ + apr_filepath_merge(&file, "", file, APR_FILEPATH_NATIVE, r->pool); +#endif + + *buf_size = apr_cpystrn(buf_data, file, *buf_size) - buf_data; + return 1; } @@ -976,7 +1048,6 @@ HSE_TF_INFO *tf = (HSE_TF_INFO*)buf_data; apr_uint32_t sent = 0; apr_ssize_t ate = 0; - apr_status_t rv; apr_bucket_brigade *bb; apr_bucket *b; apr_file_t *fd; @@ -1050,28 +1121,7 @@ } sent += (apr_uint32_t)fsize; -#if APR_HAS_LARGE_FILES - if (r->finfo.size > AP_MAX_SENDFILE) { - /* APR_HAS_LARGE_FILES issue; must split into mutiple buckets, - * no greater than MAX(apr_size_t), and more granular than that - * in case the brigade code/filters attempt to read it directly. - */ - b = apr_bucket_file_create(fd, tf->Offset, AP_MAX_SENDFILE, - r->pool, c->bucket_alloc); - while (fsize > AP_MAX_SENDFILE) { - apr_bucket *bc; - apr_bucket_copy(b, &bc); - APR_BRIGADE_INSERT_TAIL(bb, bc); - b->start += AP_MAX_SENDFILE; - fsize -= AP_MAX_SENDFILE; - } - b->length = (apr_size_t)fsize; /* Resize just the last bucket */ - } - else -#endif - b = apr_bucket_file_create(fd, tf->Offset, (apr_size_t)fsize, - r->pool, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); + apr_brigade_insert_file(bb, fd, tf->Offset, fsize, r->pool); if (tf->pTail && tf->TailLength) { sent += tf->TailLength; @@ -1082,15 +1132,20 @@ b = apr_bucket_flush_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); - ap_pass_brigade(r->output_filters, bb); + rv = ap_pass_brigade(r->output_filters, bb); cid->response_sent = 1; + if (rv != APR_SUCCESS) + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, + "ISAPI: ServerSupport function " + "HSE_REQ_TRANSMIT_FILE " + "ap_pass_brigade failed: %s", r->filename); /* Use tf->pfnHseIO + tf->pContext, or if NULL, then use cid->fnIOComplete * pass pContect to the HseIO callback. */ if (tf->dwFlags & HSE_IO_ASYNC) { if (tf->pfnHseIO) { - if (rv == OK) { + if (rv == APR_SUCCESS) { tf->pfnHseIO(cid->ecb, tf->pContext, ERROR_SUCCESS, sent); } @@ -1100,7 +1155,7 @@ } } else if (cid->completion) { - if (rv == OK) { + if (rv == APR_SUCCESS) { cid->completion(cid->ecb, cid->completion_arg, sent, ERROR_SUCCESS); } @@ -1110,7 +1165,7 @@ } } } - return (rv == OK); + return (rv == APR_SUCCESS); } case HSE_REQ_REFRESH_ISAPI_ACL: @@ -1150,6 +1205,14 @@ } if ((*data_type & HSE_IO_ASYNC) && cid->completion) { + /* XXX: Many authors issue their next HSE_REQ_ASYNC_READ_CLIENT + * within the completion logic. An example is MS's own PSDK + * sample web/iis/extensions/io/ASyncRead. This potentially + * leads to stack exhaustion. To refactor, the notification + * logic needs to move to isapi_handler() - differentiating + * the cid->completed event with a new flag to indicate + * an async-notice versus the async request completed. + */ if (res >= 0) { cid->completion(cid->ecb, cid->completion_arg, read, ERROR_SUCCESS); @@ -1282,9 +1345,19 @@ APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_flush_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); - ap_pass_brigade(cid->r->output_filters, bb); + rv = ap_pass_brigade(cid->r->output_filters, bb); cid->response_sent = 1; + if (rv != APR_SUCCESS) + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, + "ISAPI: ServerSupport function " + "HSE_REQ_SEND_RESPONSE_HEADER_EX " + "ap_pass_brigade failed: %s", r->filename); + return (rv == APR_SUCCESS); } + /* Deliberately hold off sending 'just the headers' to begin to + * accumulate the body and speed up the overall response, or at + * least wait for the end the session. + */ return 1; } @@ -1380,7 +1453,7 @@ ap_add_common_vars(r); ap_add_cgi_vars(r); apr_table_setn(e, "UNMAPPED_REMOTE_USER", "REMOTE_USER"); - if ((val = apr_table_get(e, "HTTPS")) && strcmp(val, "on")) + if ((val = apr_table_get(e, "HTTPS")) && (strcmp(val, "on") == 0)) apr_table_setn(e, "SERVER_PORT_SECURE", "1"); else apr_table_setn(e, "SERVER_PORT_SECURE", "0"); @@ -1554,21 +1627,23 @@ case HSE_STATUS_ERROR: /* end response if we have yet to do so. */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), r, + "ISAPI: HSE_STATUS_ERROR result from " + "HttpExtensionProc(): %s", r->filename); r->status = HTTP_INTERNAL_SERVER_ERROR; break; default: - /* TODO: log unrecognized retval for debugging - */ - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "ISAPI: return code %d from HttpExtensionProc() " - "was not not recognized", rv); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), r, + "ISAPI: unrecognized result code %d " + "from HttpExtensionProc(): %s ", + rv, r->filename); r->status = HTTP_INTERNAL_SERVER_ERROR; break; } /* Flush the response now, including headers-only responses */ - if (cid->headers_set) { + if (cid->headers_set || cid->response_sent) { conn_rec *c = r->connection; apr_bucket_brigade *bb; apr_bucket *b; @@ -1577,12 +1652,16 @@ bb = apr_brigade_create(r->pool, c->bucket_alloc); b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); - b = apr_bucket_flush_create(c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); rv = ap_pass_brigade(r->output_filters, bb); cid->response_sent = 1; - return OK; /* NOT r->status or cid->r->status, even if it has changed. */ + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, + "ISAPI: ap_pass_brigade failed to " + "complete the response: %s ", r->filename); + } + + return OK; /* NOT r->status, even if it has changed. */ } /* As the client returned no error, and if we did not error out @@ -1592,8 +1671,8 @@ r->status = cid->ecb->dwHttpStatusCode; } - /* For all missing-response situations simply return the status. - * and let the core deal respond to the client. + /* For all missing-response situations simply return the status, + * and let the core respond to the client. */ return r->status; } Index: modules/arch/netware/mod_nw_ssl.c =================================================================== --- modules/arch/netware/mod_nw_ssl.c (.../2.2.3) (revision 493482) +++ modules/arch/netware/mod_nw_ssl.c (.../2.2.4) (revision 493482) @@ -1074,7 +1074,7 @@ /* XXX-Can't get specific SSL info from NetWare */ /*result = ssl_var_lookup_ssl_version(p, var+12);*/ else if (strcEQ(var, "SERVER_SOFTWARE")) - result = ap_get_server_version(); + result = ap_get_server_banner(); else if (strcEQ(var, "API_VERSION")) { result = apr_itoa(p, MODULE_MAGIC_NUMBER); resdup = FALSE; Index: modules/echo/mod_echo.c =================================================================== --- modules/echo/mod_echo.c (.../2.2.3) (revision 493482) +++ modules/echo/mod_echo.c (.../2.2.4) (revision 493482) @@ -63,9 +63,9 @@ bb = apr_brigade_create(c->pool, c->bucket_alloc); /* Get a single line of input from the client */ - if ((rv = ap_get_brigade(c->input_filters, bb, AP_MODE_GETLINE, - APR_BLOCK_READ, 0) != APR_SUCCESS || - APR_BRIGADE_EMPTY(bb))) { + if (((rv = ap_get_brigade(c->input_filters, bb, AP_MODE_GETLINE, + APR_BLOCK_READ, 0)) != APR_SUCCESS) || + APR_BRIGADE_EMPTY(bb)) { apr_brigade_destroy(bb); break; } Index: include/ap_release.h =================================================================== --- include/ap_release.h (.../2.2.3) (revision 493482) +++ include/ap_release.h (.../2.2.4) (revision 493482) @@ -45,7 +45,7 @@ #define AP_SERVER_MAJORVERSION_NUMBER 2 #define AP_SERVER_MINORVERSION_NUMBER 2 -#define AP_SERVER_PATCHLEVEL_NUMBER 3 +#define AP_SERVER_PATCHLEVEL_NUMBER 4 #define AP_SERVER_DEVBUILD_BOOLEAN 0 #if AP_SERVER_DEVBUILD_BOOLEAN Index: include/ap_mmn.h =================================================================== --- include/ap_mmn.h (.../2.2.3) (revision 493482) +++ include/ap_mmn.h (.../2.2.4) (revision 493482) @@ -111,6 +111,8 @@ * proxy_server (minor) * 20051115.2 (2.2.2) added inreslist member to proxy_conn_rec (minor) * 20051115.3 (2.2.3) Added server_scheme member to server_rec (minor) + * 20051115.4 (2.2.4) Added ap_get_server_banner() and + * ap_get_server_description() (minor) */ #define MODULE_MAGIC_COOKIE 0x41503232UL /* "AP22" */ @@ -118,7 +120,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20051115 #endif -#define MODULE_MAGIC_NUMBER_MINOR 3 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 4 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a Index: include/httpd.h =================================================================== --- include/httpd.h (.../2.2.3) (revision 493482) +++ include/httpd.h (.../2.2.4) (revision 493482) @@ -420,13 +420,33 @@ AP_DECLARE(void) ap_get_server_revision(ap_version_t *version); /** - * Get the server version string + * Get the server version string, as controlled by the ServerTokens directive * @return The server version string + * @deprecated @see ap_get_server_banner() and ap_get_server_description() */ AP_DECLARE(const char *) ap_get_server_version(void); /** - * Add a component to the version string + * Get the server banner in a form suitable for sending over the + * network, with the level of information controlled by the + * ServerTokens directive. + * @return The server banner + */ +AP_DECLARE(const char *) ap_get_server_banner(void); + +/** + * Get the server description in a form suitable for local displays, + * status reports, or logging. This includes the detailed server + * version and information about some modules. It is not affected + * by the ServerTokens directive. + * @return The server description + */ +AP_DECLARE(const char *) ap_get_server_description(void); + +/** + * Add a component to the server description and banner strings + * (The latter is returned by the deprecated function + * ap_get_server_version().) * @param pconf The pool to allocate the component from * @param component The string to add */ @@ -518,6 +538,8 @@ #define ap_is_HTTP_CLIENT_ERROR(x) (((x) >= 400)&&((x) < 500)) /** is the status code a server error */ #define ap_is_HTTP_SERVER_ERROR(x) (((x) >= 500)&&((x) < 600)) +/** is the status code a (potentially) valid response code? */ +#define ap_is_HTTP_VALID_RESPONSE(x) (((x) >= 100)&&((x) < 600)) /** should the status code drop the connection */ #define ap_status_drops_connection(x) \ Index: support/rotatelogs.c =================================================================== --- support/rotatelogs.c (.../2.2.3) (revision 493482) +++ support/rotatelogs.c (.../2.2.4) (revision 493482) @@ -54,7 +54,7 @@ #endif #define BUFSIZE 65536 -#define ERRMSGSZ 82 +#define ERRMSGSZ 128 #ifndef MAX_PATH #define MAX_PATH 1024 @@ -188,6 +188,7 @@ if (nLogFD == NULL) { int tLogStart; + apr_status_t rv; if (tRotation) { tLogStart = (now / tRotation) * tRotation; @@ -208,22 +209,28 @@ sprintf(buf2, "%s.%010d", szLogRoot, tLogStart); } tLogEnd = tLogStart + tRotation; - apr_file_open(&nLogFD, buf2, APR_READ | APR_WRITE | APR_CREATE | APR_APPEND, - APR_OS_DEFAULT, pool); - if (nLogFD == NULL) { + rv = apr_file_open(&nLogFD, buf2, APR_WRITE | APR_CREATE | APR_APPEND, + APR_OS_DEFAULT, pool); + if (rv != APR_SUCCESS) { + char error[120]; + + apr_strerror(rv, error, sizeof error); + /* Uh-oh. Failed to open the new log file. Try to clear * the previous log file, note the lost log entries, * and keep on truckin'. */ if (nLogFDprev == NULL) { - fprintf(stderr, "1 Previous file handle doesn't exists %s\n", buf2); + fprintf(stderr, "Could not open log file '%s' (%s)\n", buf2, error); exit(2); } else { nLogFD = nLogFDprev; - sprintf(errbuf, - "Resetting log file due to error opening " - "new log file. %10d messages lost.\n", - nMessCount); + /* Try to keep this error message constant length + * in case it occurs several times. */ + apr_snprintf(errbuf, sizeof errbuf, + "Resetting log file due to error opening " + "new log file, %10d messages lost: %-25.25s\n", + nMessCount, error); nWrite = strlen(errbuf); apr_file_trunc(nLogFD, 0); if (apr_file_write(nLogFD, errbuf, &nWrite) != APR_SUCCESS) {