* University of Illinois, Urbana-Champaign.
*/
-#define CORE_PRIVATE
-#include "ap_config.h"
+#include "apr.h"
#include "apr_strings.h"
#include "apr_lib.h"
+#include "apr_fnmatch.h"
+#include "apr_thread_proc.h" /* for RLIMIT stuff */
+
+#if APR_HAVE_SYS_UIO_H
+#include <sys/uio.h> /* for iovec */
+#endif
+
+#define CORE_PRIVATE
+#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "rfc1413.h"
#include "util_md5.h"
-#include "apr_fnmatch.h"
#include "http_connection.h"
#include "ap_buckets.h"
#include "util_filter.h"
#include "util_ebcdic.h"
#include "mpm.h"
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
+
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <strings.h>
#endif
-/* Make sure we don't write less than 4096 bytes at any one time.
- */
-#define MIN_SIZE_TO_WRITE 9000
-
-/* Allow Apache to use ap_mmap */
-#ifdef AP_USE_MMAP_FILES
-#include "apr_mmap.h"
-
-/* mmap support for static files based on ideas from John Heidemann's
- * patch against 1.0.5. See
- * <http://www.isi.edu/~johnh/SOFTWARE/APACHE/index.html>.
- */
-
-/* Files have to be at least this big before they're mmap()d. This is to deal
- * with systems where the expense of doing an mmap() and an munmap() outweighs
- * the benefit for small files. It shouldn't be set lower than 1.
- */
-#ifndef MMAP_THRESHOLD
-# ifdef SUNOS4
-# define MMAP_THRESHOLD (8*1024)
-# else
-# define MMAP_THRESHOLD 1
-# endif /* SUNOS4 */
-#endif /* MMAP_THRESHOLD */
-#ifndef MMAP_LIMIT
-#define MMAP_LIMIT (4*1024*1024)
-#endif
-#endif /* AP_USE_MMAP_FILES */
-
/* LimitXMLRequestBody handling */
#define AP_LIMIT_UNSET ((long) -1)
#define AP_DEFAULT_LIMIT_XML_BODY ((size_t)1000000)
/* Code from Harald Hanche-Olsen <hanche@imf.unit.no> */
static apr_inline void do_double_reverse (conn_rec *conn)
{
- struct hostent *hptr;
+ apr_sockaddr_t *sa;
+ apr_status_t rv;
if (conn->double_reverse) {
/* already done */
conn->double_reverse = -1;
return;
}
- hptr = gethostbyname(conn->remote_host);
- if (hptr) {
- char **haddr;
-
- for (haddr = hptr->h_addr_list; *haddr; haddr++) {
- if (((struct in_addr *)(*haddr))->s_addr
- == conn->remote_addr.sin_addr.s_addr) {
- conn->double_reverse = 1;
- return;
- }
- }
+ rv = apr_getaddrinfo(&sa, conn->remote_host, APR_UNSPEC, 0, 0, conn->pool);
+ if (rv == APR_SUCCESS) {
+ while (sa) {
+ if (sa->ipaddr_len == conn->remote_addr->ipaddr_len &&
+ !memcmp(sa->ipaddr_ptr, conn->remote_addr->ipaddr_ptr,
+ sa->ipaddr_len)) {
+ conn->double_reverse = 1;
+ return;
+ }
+ sa = sa->next;
+ }
}
conn->double_reverse = -1;
}
&& conn->remote_host == NULL
&& (type == REMOTE_DOUBLE_REV
|| hostname_lookups != HOSTNAME_LOOKUP_OFF)) {
- if (apr_get_remote_hostname(&conn->remote_host, conn->client_socket)
- == APR_SUCCESS){
+ apr_sockaddr_t *remote_addr;
+
+ apr_get_sockaddr(&remote_addr, APR_REMOTE, conn->client_socket);
+ if (apr_getnameinfo(&conn->remote_host, remote_addr, 0) == APR_SUCCESS) {
ap_str_tolower(conn->remote_host);
if (hostname_lookups == HOSTNAME_LOOKUP_DOUBLE) {
* port of the actual socket.
*
* The DNS option to UseCanonicalName causes this routine to do a
- * reverse lookup on the local IP address of the connectiona and use
+ * reverse lookup on the local IP address of the connection and use
* that for the ServerName. This makes its value more reliable while
* at the same time allowing Demon's magic virtual hosting to work.
* The assumption is that DNS lookups are sufficiently quick...
}
if (d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
if (conn->local_host == NULL) {
- struct in_addr *iaddr;
- struct hostent *hptr;
- iaddr = &(conn->local_addr.sin_addr);
- hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr),
- AF_INET);
- if (hptr != NULL) {
- conn->local_host = apr_pstrdup(conn->pool,
- (void *)hptr->h_name);
- ap_str_tolower(conn->local_host);
- }
- else {
- conn->local_host = apr_pstrdup(conn->pool,
- r->server->server_hostname);
- }
- }
+ apr_sockaddr_t *local_addr;
+
+ apr_get_sockaddr(&local_addr, APR_LOCAL, conn->client_socket);
+ if (apr_getnameinfo(&conn->local_host, local_addr, 0) != APR_SUCCESS)
+ conn->local_host = apr_pstrdup(conn->pool, r->server->server_hostname);
+ else {
+ ap_str_tolower(conn->local_host);
+ }
+ }
return conn->local_host;
}
/* default */
return r->server->server_hostname;
}
-AP_DECLARE(unsigned) ap_get_server_port(const request_rec *r)
+AP_DECLARE(apr_port_t) ap_get_server_port(const request_rec *r)
{
- unsigned port;
+ apr_port_t port;
core_dir_config *d =
(core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
if (d->use_canonical_name == USE_CANONICAL_NAME_OFF
|| d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
- if (r->hostname)
- apr_get_local_port(&port, r->connection->client_socket);
+ if (r->hostname) {
+ apr_sockaddr_t *localsa;
+
+ apr_get_sockaddr(&localsa, APR_LOCAL, r->connection->client_socket);
+ apr_get_port(&port, localsa);
+ }
}
/* default */
return port;
}
arg = ap_os_canonical_filename(cmd->pool, arg);
- if (/* TODO: ap_configtestonly && ap_docrootcheck && */ !ap_is_directory(arg)) {
+ if (/* TODO: ap_configtestonly && ap_docrootcheck && */ !ap_is_directory(cmd->pool, arg)) {
if (cmd->server->is_virtual) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, cmd->pool,
"Warning: DocumentRoot [%s] does not exist",
arg);
}
arg = ap_os_canonical_filename(cmd->pool, arg);
- if (!ap_is_directory(arg)) {
+ if (!ap_is_directory(cmd->pool, arg)) {
return "ServerRoot must be a valid directory";
}
ap_server_root = arg;
}
#endif
-static apr_status_t writev_it_all(apr_socket_t *s, struct iovec *vec, int nvec,
- apr_ssize_t len, apr_ssize_t *nbytes)
+static apr_status_t writev_it_all(apr_socket_t *s,
+ struct iovec *vec, int nvec,
+ apr_size_t len, apr_size_t *nbytes)
{
apr_size_t bytes_written = 0;
apr_status_t rv;
- apr_ssize_t n = len;
- apr_ssize_t i = 0;
+ apr_size_t n = len;
+ apr_size_t i = 0;
*nbytes = 0;
return APR_SUCCESS;
}
+
+/* sendfile_it_all()
+ * send the entire file using sendfile()
+ * handle partial writes
+ * return only when all bytes have been sent or an error is encountered.
+ */
+
+#if APR_HAS_SENDFILE
+static apr_status_t sendfile_it_all(conn_rec *c,
+ apr_file_t *fd,
+ apr_hdtr_t *hdtr,
+ apr_off_t file_offset,
+ apr_size_t file_bytes_left,
+ apr_size_t total_bytes_left,
+ apr_int32_t flags)
+{
+ apr_status_t rv;
+ apr_int32_t timeout = 0;
+
+ AP_DEBUG_ASSERT((apr_getsocketopt(c->client_socket, APR_SO_TIMEOUT,
+ &timeout) == APR_SUCCESS) &&
+ timeout > 0); /* socket must be in timeout mode */
+ do {
+ apr_size_t tmplen = file_bytes_left;
+
+ rv = apr_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
+ flags);
+ total_bytes_left -= tmplen;
+ if (!total_bytes_left || rv != APR_SUCCESS) {
+ return rv; /* normal case & error exit */
+ }
+
+ AP_DEBUG_ASSERT(total_bytes_left > 0 && tmplen > 0);
+
+ /* partial write, oooh noooo...
+ * Skip over any header data which was written
+ */
+ while (tmplen && hdtr->numheaders) {
+ if (tmplen >= hdtr->headers[0].iov_len) {
+ tmplen -= hdtr->headers[0].iov_len;
+ --hdtr->numheaders;
+ ++hdtr->headers;
+ }
+ else {
+ char *iov_base = (char *)hdtr->headers[0].iov_base;
+
+ hdtr->headers[0].iov_len -= tmplen;
+ iov_base += tmplen;
+ hdtr->headers[0].iov_base = iov_base;
+ tmplen = 0;
+ }
+ }
+
+ /* Skip over any file data which was written */
+
+ if (tmplen <= file_bytes_left) {
+ file_offset += tmplen;
+ file_bytes_left -= tmplen;
+ continue;
+ }
+ tmplen -= file_bytes_left;
+ file_bytes_left = 0;
+ file_offset = 0;
+
+ /* Skip over any trailer data which was written */
+
+ while (tmplen && hdtr->numtrailers) {
+ if (tmplen >= hdtr->trailers[0].iov_len) {
+ tmplen -= hdtr->trailers[0].iov_len;
+ --hdtr->numtrailers;
+ ++hdtr->trailers;
+ }
+ else {
+ char *iov_base = (char *)hdtr->trailers[0].iov_base;
+
+ hdtr->trailers[0].iov_len -= tmplen;
+ iov_base += tmplen;
+ hdtr->trailers[0].iov_base = iov_base;
+ tmplen = 0;
+ }
+ }
+ } while (1);
+}
+#endif
+
/*
* send_the_file()
* Sends the contents of file fd along with header/trailer bytes, if any,
*/
static apr_status_t send_the_file(conn_rec *c, apr_file_t *fd,
apr_hdtr_t *hdtr, apr_off_t offset,
- apr_ssize_t length, apr_ssize_t *nbytes)
+ apr_size_t length, apr_size_t *nbytes)
{
apr_status_t rv = APR_SUCCESS;
apr_int32_t togo; /* Remaining number of bytes in the file to send */
- apr_ssize_t sendlen = 0;
- apr_ssize_t bytes_sent;
+ apr_size_t sendlen = 0;
+ apr_size_t bytes_sent;
apr_int32_t i;
apr_off_t o; /* Track the file offset for partial writes */
char buffer[8192];
static int default_handler(request_rec *r)
{
- core_dir_config *d =
- (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
- int rangestatus, errstatus;
+ ap_bucket_brigade *bb;
+ ap_bucket *e;
+ core_dir_config *d;
+ int errstatus;
apr_file_t *fd = NULL;
apr_status_t status;
-#ifdef AP_USE_MMAP_FILES
- apr_mmap_t *mm = NULL;
-#endif
/* XXX if/when somebody writes a content-md5 filter we either need to
* remove this support or coordinate when to use the filter vs.
* when to use this code
* support fairly closely (unlike 1.3, we don't handle computing md5
* when the charset is translated).
*/
- int bld_content_md5 =
- (d->content_md5 & 1) && r->output_filters->frec->ftype != AP_FTYPE_CONTENT;
+ int bld_content_md5;
+
+ /*
+ * The old way of doing handlers meant that this handler would
+ * match literally anything - this way will require handler to
+ * have a / in the middle, which probably captures the original
+ * intent, but may cause problems at first - Ben 7th Jan 01
+ */
+ if(strcmp(r->handler,"default-handler")
+ && ap_strcmp_match(r->handler,"*/*"))
+ return DECLINED;
+
+ d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ bld_content_md5 = (d->content_md5 & 1)
+ && r->output_filters->frec->ftype != AP_FTYPE_CONTENT;
ap_allow_methods(r, MERGE_ALLOW, "GET", "OPTIONS", "POST", NULL);
return HTTP_METHOD_NOT_ALLOWED;
}
- if ((status = apr_open(&fd, r->filename, APR_READ | APR_BINARY, 0, r->pool)) != APR_SUCCESS) {
+ if ((status = apr_open(&fd, r->filename, APR_READ | APR_BINARY, 0, r->connection->pool)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
"file permissions deny server access: %s", r->filename);
return HTTP_FORBIDDEN;
ap_set_last_modified(r);
ap_set_etag(r);
apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
- if (((errstatus = ap_meets_conditions(r)) != OK)
- || (errstatus = ap_set_content_length(r, r->finfo.size))) {
+ ap_set_content_length(r, r->finfo.size);
+ if ((errstatus = ap_meets_conditions(r)) != OK) {
apr_close(fd);
return errstatus;
}
-#ifdef AP_USE_MMAP_FILES
- if ((r->finfo.size >= MMAP_THRESHOLD)
- && (r->finfo.size < MMAP_LIMIT)
- && (!r->header_only || bld_content_md5)) {
- /* we need to protect ourselves in case we die while we've got the
- * file mmapped */
- apr_status_t status;
- if ((status = apr_mmap_create(&mm, fd, 0, r->finfo.size, r->connection->pool)) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_CRIT, status, r,
- "default_handler: mmap failed: %s", r->filename);
- mm = NULL;
- }
- }
- else {
- mm = NULL;
+ if (bld_content_md5) {
+ apr_table_setn(r->headers_out, "Content-MD5",
+ ap_md5digest(r->pool, fd));
}
- if (mm == NULL) {
-#endif
+ bb = ap_brigade_create(r->pool);
+ e = ap_bucket_create_file(fd, 0, r->finfo.size);
- if (bld_content_md5) {
- apr_table_setn(r->headers_out, "Content-MD5",
- ap_md5digest(r->pool, fd));
- }
+ AP_BRIGADE_INSERT_HEAD(bb, e);
+ e = ap_bucket_create_eos();
+ AP_BRIGADE_INSERT_TAIL(bb, e);
- rangestatus = ap_set_byterange(r);
-
- ap_send_http_header(r);
-
- if (!r->header_only) {
- apr_size_t length = r->finfo.size;
- apr_off_t offset = 0;
- apr_size_t nbytes = 0;
-
- if (!rangestatus) {
- ap_send_fd(fd, r, offset, length, &nbytes);
- }
- else {
- while (ap_each_byterange(r, &offset, &length)) {
- if ((status = ap_send_fd(fd, r, offset, length, &nbytes)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
- "error byteserving file: %s", r->filename);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- }
- }
- }
-
-#ifdef AP_USE_MMAP_FILES
- }
- else {
- unsigned char *addr;
- apr_mmap_offset((void**)&addr, mm ,0);
-
- if (bld_content_md5) {
- apr_md5_ctx_t context;
-
- apr_MD5Init(&context);
- apr_MD5Update(&context, addr, (unsigned int)r->finfo.size);
- apr_table_setn(r->headers_out, "Content-MD5",
- ap_md5contextTo64(r->pool, &context));
- }
-
- rangestatus = ap_set_byterange(r);
- ap_send_http_header(r);
-
- if (!r->header_only) {
- if (!rangestatus) {
- ap_send_mmap(mm, r, 0, r->finfo.size);
- }
- else {
- apr_off_t offset;
- apr_size_t length;
- while (ap_each_byterange(r, &offset, &length)) {
- ap_send_mmap(mm, r, offset, length);
- }
- }
- }
- }
-#endif
-
- apr_close(fd);
- return OK;
+ return ap_pass_brigade(r->output_filters, bb);
}
+
/*
* coalesce_filter()
* This is a simple filter to coalesce many small buckets into one large
typedef struct COALESCE_FILTER_CTX {
char *buf; /* Start of buffer */
char *cur; /* Pointer to next location to write */
- apr_ssize_t cnt; /* Number of bytes put in buf */
- apr_ssize_t avail; /* Number of bytes available in the buf */
+ apr_size_t cnt; /* Number of bytes put in buf */
+ apr_size_t avail; /* Number of bytes available in the buf */
} coalesce_filter_ctx_t;
-#define FILTER_BUFF_SIZE 8192
#define MIN_BUCKET_SIZE 200
static apr_status_t coalesce_filter(ap_filter_t *f, ap_bucket_brigade *b)
{
if (ctx == NULL) {
f->ctx = ctx = apr_pcalloc(p, sizeof(coalesce_filter_ctx_t));
- ctx->avail = FILTER_BUFF_SIZE;
+ ctx->avail = AP_MIN_BYTES_TO_WRITE;
}
if (ctx->cnt) {
}
else {
const char *str;
- apr_ssize_t n;
+ apr_size_t n;
rv = ap_bucket_read(e, &str, &n, AP_BLOCK_READ);
if (rv != APR_SUCCESS) {
/* XXX: log error */
if ((n < MIN_BUCKET_SIZE) && (n < ctx->avail)) {
/* Coalesce this bucket into the buffer */
if (ctx->buf == NULL) {
- ctx->buf = apr_palloc(p, FILTER_BUFF_SIZE);
+ ctx->buf = apr_palloc(p, AP_MIN_BYTES_TO_WRITE);
ctx->cur = ctx->buf;
ctx->cnt = 0;
}
}
if (pass_the_brigade) {
- /* Insert ctx->buf into the correct spotin the brigade */
+ /* Insert ctx->buf into the correct spot in the brigade */
+ e = ap_bucket_create_pool(ctx->buf, ctx->cnt, p);
if (insert_first) {
- e = ap_bucket_create_pool(ctx->buf, ctx->cnt, p);
AP_BRIGADE_INSERT_HEAD(b, e);
}
else if (insert_before) {
- e = ap_bucket_create_pool(ctx->buf, ctx->cnt, p);
AP_BUCKET_INSERT_BEFORE(e, insert_before);
AP_BUCKET_REMOVE(insert_before);
ap_bucket_destroy(insert_before);
if (ctx) {
ctx->cur = ctx->buf;
ctx->cnt = 0;
- ctx->avail = FILTER_BUFF_SIZE;
+ ctx->avail = AP_MIN_BYTES_TO_WRITE;
}
}
else {
else if (e->length == -1) {
/* unknown amount of data (e.g. a pipe) */
const char *data;
- apr_ssize_t len;
+ apr_size_t len;
rv = ap_bucket_read(e, &data, &len, AP_BLOCK_READ);
if (rv != APR_SUCCESS) {
{
apr_status_t rv;
ap_bucket_brigade *more = NULL;
- apr_ssize_t bytes_sent = 0, nbytes;
+ apr_size_t bytes_sent = 0, nbytes;
ap_bucket *e;
conn_rec *c = f->c;
core_output_filter_ctx_t *ctx = f->ctx;
- apr_ssize_t nvec = 0;
- apr_ssize_t nvec_trailers= 0;
+ apr_size_t nvec = 0;
+ apr_size_t nvec_trailers= 0;
struct iovec vec[MAX_IOVEC_TO_WRITE];
struct iovec vec_trailers[MAX_IOVEC_TO_WRITE];
apr_file_t *fd = NULL;
- apr_ssize_t flen = 0;
+ apr_size_t flen = 0;
apr_off_t foffset = 0;
if (ctx == NULL) {
}
else {
const char *str;
- apr_ssize_t n;
+ apr_size_t n;
rv = ap_bucket_read(e, &str, &n, AP_BLOCK_READ);
if (n) {
nbytes += n;
/* Completed iterating over the brigades, now determine if we want to
* buffer the brigade or send the brigade out on the network
*/
- if (!fd && (!more) && (nbytes < MIN_SIZE_TO_WRITE) && !AP_BUCKET_IS_FLUSH(e) || (AP_BUCKET_IS_EOS(e) && c->keepalive)) {
+ if ((!fd && (!more) && (nbytes < AP_MIN_BYTES_TO_WRITE) && !AP_BUCKET_IS_FLUSH(e))
+ || (AP_BUCKET_IS_EOS(e) && c->keepalive)) {
/* NEVER save an EOS in here. If we are saving a brigade with an
* EOS bucket, then we are doing keepalive connections, and we want
/* Prepare the socket to be reused */
flags |= APR_SENDFILE_DISCONNECT_SOCKET;
}
- nbytes = flen;
- rv = apr_sendfile(c->client_socket,
- fd, /* The file to send */
- &hdtr, /* Header and trailer iovecs */
- &foffset, /* Offset in file to begin sending from */
- &nbytes,
- flags);
- bytes_sent = nbytes;
+ rv = sendfile_it_all(c, /* the connection */
+ fd, /* the file to send */
+ &hdtr, /* header and trailer iovecs */
+ foffset, /* offset in the file to begin
+ sending from */
+ flen, /* length of file */
+ nbytes + flen, /* total length including
+ headers */
+ flags); /* apr_sendfile flags */
/* If apr_sendfile() returns APR_ENOTIMPL, call send_the_file() to
* loop on apr_read/apr_send to send the file. Our Windows binary
return APR_SUCCESS;
}
-static const handler_rec core_handlers[] = {
-{ "*/*", default_handler },
-{ "default-handler", default_handler },
-{ NULL, NULL }
-};
-
static void core_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
{
ap_init_bucket_types(pconf);
ap_hook_http_method(core_method,NULL,NULL,AP_HOOK_REALLY_LAST);
ap_hook_default_port(core_port,NULL,NULL,AP_HOOK_REALLY_LAST);
ap_hook_open_logs(core_open_logs,NULL,NULL,AP_HOOK_MIDDLE);
+ ap_hook_handler(default_handler,NULL,NULL,AP_HOOK_REALLY_LAST);
/* FIXME: I suspect we can eliminate the need for these - Ben */
ap_hook_type_checker(do_nothing,NULL,NULL,AP_HOOK_REALLY_LAST);
ap_hook_access_checker(do_nothing,NULL,NULL,AP_HOOK_REALLY_LAST);
* filters
*/
ap_hook_insert_filter(core_insert_filter, NULL, NULL, AP_HOOK_MIDDLE);
+
ap_register_input_filter("HTTP_IN", ap_http_filter, AP_FTYPE_CONNECTION);
ap_register_input_filter("DECHUNK", ap_dechunk_filter, AP_FTYPE_TRANSCODE);
ap_register_input_filter("CORE_IN", core_input_filter, AP_FTYPE_NETWORK);
- ap_register_output_filter("HTTP_HEADER", ap_http_header_filter, AP_FTYPE_HTTP_HEADER);
+ ap_register_output_filter("HTTP_HEADER", ap_http_header_filter,
+ AP_FTYPE_HTTP_HEADER);
ap_register_output_filter("CONTENT_LENGTH", ap_content_length_filter,
AP_FTYPE_HTTP_HEADER);
+ ap_register_output_filter("BYTERANGE", ap_byterange_filter,
+ AP_FTYPE_HTTP_HEADER);
ap_register_output_filter("CORE", core_output_filter, AP_FTYPE_NETWORK);
ap_register_output_filter("SUBREQ_CORE", ap_sub_req_output_filter,
AP_FTYPE_CONTENT);
ap_register_output_filter("CHUNK", chunk_filter, AP_FTYPE_TRANSCODE);
- ap_register_output_filter("COALESCE", coalesce_filter, AP_FTYPE_CONNECTION);
+ ap_register_output_filter("COALESCE", coalesce_filter, AP_FTYPE_CONTENT);
}
AP_DECLARE_DATA module core_module = {
create_core_server_config, /* create per-server config structure */
merge_core_server_configs, /* merge per-server config structures */
core_cmds, /* command apr_table_t */
- core_handlers, /* handlers */
register_hooks /* register hooks */
};