You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Bill Stoddard <st...@raleigh.ibm.com> on 2000/02/04 20:58:56 UTC

[PATCH] mod_mmap_static hacking (caching file handles on Windows NT)

It would help to include the patch :-) Here it is...

Index: mod_mmap_static.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/modules/experimental/mod_mmap_static.c,v
retrieving revision 1.6
diff -u -r1.6 mod_mmap_static.c
--- mod_mmap_static.c 2000/01/04 19:00:56 1.6
+++ mod_mmap_static.c 2000/02/04 19:34:03
@@ -123,13 +123,18 @@
 #include "apr_mmap.h"

 module MODULE_VAR_EXPORT mmap_static_module;
+static ap_context_t *context;
+static int once_through = 0;

 typedef struct {
+#ifdef WIN32
+    ap_file_t *file;
+#else
     ap_mmap_t *mm;
+#endif
     char *filename;
-    struct stat finfo;
+    ap_finfo_t finfo;
 } a_file;
-ap_context_t *context;

 typedef struct {
     ap_array_header_t *files;
@@ -145,7 +150,36 @@
     sconf->inode_sorted = NULL;
     return sconf;
 }
+static void pre_config(ap_context_t *pconf, ap_context_t *plog,
ap_context_t *ptemp)
+{
+    context = pconf;
+}
+static ap_status_t open_file(ap_file_t **file, char* filename, int flg1,
int flg2,
+                             ap_context_t *context)
+{
+    ap_status_t rv;
+#ifndef WIN32
+    rv = ap_open(file, filename, flg1, flg2, context);
+#else
+    HANDLE hFile;
+    hFile = CreateFile(filename,          // pointer to name of the file
+                       GENERIC_READ,      // access (read-write) mode
+                       FILE_SHARE_READ,   // share mode
+                       NULL,              // pointer to security attributes
+                       OPEN_EXISTING,    // how to create
+                       FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN,
// file attributes
+                       NULL); // handle to file with attributes to copy
+    if (hFile != INVALID_HANDLE_VALUE)
+        rv = ap_put_os_file(file, &hFile, context);
+    else {
+        rv = GetLastError();
+        *file = NULL;
+    }

+#endif
+    return rv;
+}
+
 ap_status_t cleanup_mmap(void *sconfv)
 {
     a_server_config *sconf = sconfv;
@@ -155,9 +189,13 @@
     n = sconf->files->nelts;
     file = (a_file *)sconf->files->elts;
     while(n) {
-     ap_mmap_delete(file->mm);
-     ++file;
-     --n;
+#ifdef WIN32
+        ap_close(file->file);
+#else
+        ap_mmap_delete(file->mm);
+#endif
+        ++file;
+        --n;
     }
     return APR_SUCCESS;
 }
@@ -168,23 +206,33 @@
     a_file *new_file;
     a_file tmp;
     ap_file_t *fd = NULL;
+#ifndef WIN32
     caddr_t mm;
-
-    if (stat(filename, &tmp.finfo) == -1) {
+#endif
+    ap_status_t rc;
+    /* canonicalize the file name */
+    /* os_canonical... */
+    if (ap_stat(&tmp.finfo, filename, NULL) != APR_SUCCESS) {
  ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
      "mmap_static: unable to stat(%s), skipping", filename);
  return NULL;
     }
-    if ((tmp.finfo.st_mode & S_IFMT) != S_IFREG) {
+    if (tmp.finfo.filetype != APR_REG) {
  ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
      "mmap_static: %s isn't a regular file, skipping", filename);
  return NULL;
     }
-    if (ap_open(&fd, filename, APR_READ, APR_OS_DEFAULT, context) !=
APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
-     "mmap_static: unable to open(%s, O_RDONLY), skipping", filename);
+    /* Note: open_file should call ap_open for Unix and CreateFile for
Windows.
+     * The Windows file needs to be opened for async I/O to allow multiple
threads
+     * to serve it up at once.
+     */
+    rc = open_file(&fd, filename, APR_READ, APR_OS_DEFAULT, context);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
+                     "mmap_static: unable to open(%s, O_RDONLY), skipping",
filename);
  return NULL;
     }
+#ifndef WIN32
     if (ap_mmap_create(&tmp.mm, fd, 0, tmp.finfo.st_size, context) !=
APR_SUCCESS) {
  int save_errno = errno;
  ap_close(fd);
@@ -194,6 +242,9 @@
  return NULL;
     }
     ap_close(fd);
+#else
+    tmp.file = fd;
+#endif
     tmp.filename = ap_pstrdup(cmd->pool, filename);
     sconf = ap_get_module_config(cmd->server->module_config,
&mmap_static_module);
     new_file = ap_push_array(sconf->files);
@@ -205,6 +256,7 @@
     return NULL;
 }

+#ifdef WIN32
 static int file_compare(const void *av, const void *bv)
 {
     const a_file *a = av;
@@ -212,7 +264,7 @@

     return strcmp(a->filename, b->filename);
 }
-
+#else
 static int inode_compare(const void *av, const void *bv)
 {
     const a_file *a = *(a_file **)av;
@@ -225,9 +277,9 @@
     }
     return c;
 }
-
+#endif
 static void mmap_post_config(ap_context_t *p, ap_context_t *plog,
-          ap_context_t *ptemp, server_rec *s)
+                             ap_context_t *ptemp, server_rec *s)
 {
     a_server_config *sconf;
     ap_array_header_t *inodes;
@@ -243,13 +295,14 @@
     qsort(elts, nelts, sizeof(a_file), file_compare);

     /* build an index by inode as well, speeds up the search in the handler
*/
+#ifndef WIN32
     inodes = ap_make_array(p, nelts, sizeof(a_file *));
     sconf->inode_sorted = inodes;
     for (i = 0; i < nelts; ++i) {
  *(a_file **)ap_push_array(inodes) = &elts[i];
     }
     qsort(inodes->elts, nelts, sizeof(a_file *), inode_compare);
-
+#endif
     /* and make the virtualhosts share the same thing */
     for (s = s->next; s; s = s->next) {
  ap_set_module_config(s->module_config, &mmap_static_module, sconf);
@@ -259,7 +312,47 @@
 /* If it's one of ours, fill in r->finfo now to avoid extra stat()... this
is a
  * bit of a kludge, because we really want to run after core_translate
runs.
  */
-
+int core_translate_copy(request_rec *r)
+{
+    void *sconf = r->server->module_config;
+    core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+    if (r->proxyreq) {
+        return HTTP_FORBIDDEN;
+    }
+    if ((r->uri[0] != '/') && strcmp(r->uri, "*")) {
+        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                      "Invalid URI in request %s", r->the_request);
+        return BAD_REQUEST;
+    }
+
+    if (r->server->path
+        && !strncmp(r->uri, r->server->path, r->server->pathlen)
+        && (r->server->path[r->server->pathlen - 1] == '/'
+            || r->uri[r->server->pathlen] == '/'
+            || r->uri[r->server->pathlen] == '\0')) {
+        r->filename = ap_pstrcat(r->pool, conf->ap_document_root,
+                                 (r->uri + r->server->pathlen), NULL);
+    }
+    else {
+        /*
+         * Make sure that we do not mess up the translation by adding two
+         * /'s in a row.  This happens under windows when the document
+         * root ends with a /
+         */
+        if ((conf->ap_document_root[strlen(conf->ap_document_root)-1] ==
'/')
+            && (*(r->uri) == '/')) {
+            r->filename = ap_pstrcat(r->pool, conf->ap_document_root,
r->uri+1,
+                                     NULL);
+        }
+        else {
+            r->filename = ap_pstrcat(r->pool, conf->ap_document_root,
r->uri,
+                                     NULL);
+        }
+
+        return OK;
+    }
+}
 static int mmap_static_xlat(request_rec *r)
 {
     a_server_config *sconf;
@@ -273,10 +366,12 @@
     if (ap_is_empty_table(sconf->files))
  return DECLINED;

-/*    res = core_module.translate_handler(r);
+/*  res = core_module.translate_handler(r);  */
+    res = core_translate_copy(r);
     if (res == DECLINED || !r->filename) {
  return res;
-    }*/
+    }
+
     if (!r->filename)
         return DECLINED;
     tmp.filename = r->filename;
@@ -299,23 +394,38 @@
     a_file **pmatch;
     a_file *match;
     int rangestatus, errstatus;
+#ifdef WIN32
+    HANDLE hCurrentProcess;
+#endif

     /* we don't handle anything but GET */
     if (r->method_number != M_GET) return DECLINED;

     /* file doesn't exist, we won't be dealing with it */
-    if (r->finfo.st_mode == 0) return DECLINED;
+    if (r->finfo.protection == 0) return DECLINED;

     sconf = ap_get_module_config(r->server->module_config,
&mmap_static_module);
+#ifdef WIN32
+    tmp.filename = r->filename;
+#else
     tmp.finfo.st_dev = r->finfo.st_dev;
     tmp.finfo.st_ino = r->finfo.st_ino;
+#endif
     ptmp = &tmp;
+#ifdef WIN32
+    match = (a_file *)bsearch(ptmp, sconf->files->elts,
+ sconf->files->nelts, sizeof(a_file), file_compare);
+    if (match == NULL) {
+ return DECLINED;
+    }
+#else
     pmatch = (a_file **)bsearch(&ptmp, sconf->inode_sorted->elts,
  sconf->inode_sorted->nelts, sizeof(a_file *), inode_compare);
     if (pmatch == NULL) {
  return DECLINED;
     }
     match = *pmatch;
+#endif

     /* note that we would handle GET on this resource */
     r->allowed |= (1 << M_GET);
@@ -326,11 +436,11 @@
     if ((errstatus = ap_discard_request_body(r)) != OK)
         return errstatus;

-    ap_update_mtime(r, match->finfo.st_mtime);
+    ap_update_mtime(r, match->finfo.mtime);
     ap_set_last_modified(r);
     ap_set_etag(r);
     if (((errstatus = ap_meets_conditions(r)) != OK)
- || (errstatus = ap_set_content_length (r, match->finfo.st_size))) {
+ || (errstatus = ap_set_content_length (r, match->finfo.size))) {
      return errstatus;
     }

@@ -338,16 +448,53 @@
     ap_send_http_header(r);

     if (!r->header_only) {
+        long length = match->finfo.size;
+        ap_off_t offset = 0;
+#ifdef WIN32
+//        ap_bflush(r->connection->client->);
+        struct iovec iov;
+        ap_hdtr_t hdtr;
+        ap_hdtr_t *phdtr = &hdtr;
+        /* frob the client buffer */
+        iov.iov_base = r->connection->client->outbase;
+        iov.iov_len =  r->connection->client->outcnt;
+        r->connection->client->outcnt = 0;
+
+        /* initialize the ap_hdtr_t struct */
+        phdtr->headers = &iov;
+        phdtr->numheaders = 1;
+        phdtr->trailers = NULL;
+        phdtr->numtrailers = 0;
+
+ if (!rangestatus) {
+            iol_sendfile(r->connection->client->iol,
+                         match->file,
+                         phdtr,
+                         &offset,
+                         &length,
+                         0);
+ }
+ else {
+     while (ap_each_byterange(r, &offset, &length)) {
+                iol_sendfile(r->connection->client->iol,
+                             match->file,
+                             phdtr,
+                             &offset,
+                             &length,
+                             0);
+                phdtr = NULL;
+     }
+ }
+#else
  if (!rangestatus) {
      ap_send_mmap (match->mm, r, 0, match->finfo.st_size);
  }
  else {
-     long length;
-     ap_off_t offset;
      while (ap_each_byterange(r, &offset, &length)) {
   ap_send_mmap(match->mm, r, offset, length);
      }
  }
+#endif
     }
     return OK;
 }
@@ -362,8 +509,9 @@
 static void register_hooks(void)
 {
     static const char* const aszPre[]={"http_core.c",NULL};
+    ap_hook_pre_config(pre_config,NULL,NULL,HOOK_MIDDLE);
     ap_hook_post_config(mmap_post_config, NULL, NULL, HOOK_MIDDLE);
-    ap_hook_translate_name(mmap_static_xlat, aszPre, NULL, HOOK_LAST);
+    ap_hook_translate_name(mmap_static_xlat, aszPre, NULL, HOOK_MIDDLE);
 };

 static const handler_rec mmap_static_handlers[] =




Re: [PATCH] mod_mmap_static hacking (caching file handles on Windows NT)

Posted by David Reid <ab...@dial.pipex.com>.
It was suggested a while back that we split the directory structure a little
to have specific modules for platforms.  Maybe we should do it now and copy
all modules into both?  It shouldn't take long for a few volunteers to strip
out non-platform code.

david
----- Original Message -----
From: "Bill Stoddard" <st...@raleigh.ibm.com>
To: <ne...@apache.org>
Sent: Friday, February 04, 2000 7:58 PM
Subject: [PATCH] mod_mmap_static hacking (caching file handles on Windows
NT)


> It would help to include the patch :-) Here it is...
>
> Index: mod_mmap_static.c
> ===================================================================
> RCS file:
/home/cvs/apache-2.0/src/modules/experimental/mod_mmap_static.c,v
> retrieving revision 1.6
> diff -u -r1.6 mod_mmap_static.c
> --- mod_mmap_static.c 2000/01/04 19:00:56 1.6
> +++ mod_mmap_static.c 2000/02/04 19:34:03
> @@ -123,13 +123,18 @@
>  #include "apr_mmap.h"
>
>  module MODULE_VAR_EXPORT mmap_static_module;
> +static ap_context_t *context;
> +static int once_through = 0;
>
>  typedef struct {
> +#ifdef WIN32
> +    ap_file_t *file;
> +#else
>      ap_mmap_t *mm;
> +#endif
>      char *filename;
> -    struct stat finfo;
> +    ap_finfo_t finfo;
>  } a_file;
> -ap_context_t *context;
>
>  typedef struct {
>      ap_array_header_t *files;
> @@ -145,7 +150,36 @@
>      sconf->inode_sorted = NULL;
>      return sconf;
>  }
> +static void pre_config(ap_context_t *pconf, ap_context_t *plog,
> ap_context_t *ptemp)
> +{
> +    context = pconf;
> +}
> +static ap_status_t open_file(ap_file_t **file, char* filename, int flg1,
> int flg2,
> +                             ap_context_t *context)
> +{
> +    ap_status_t rv;
> +#ifndef WIN32
> +    rv = ap_open(file, filename, flg1, flg2, context);
> +#else
> +    HANDLE hFile;
> +    hFile = CreateFile(filename,          // pointer to name of the file
> +                       GENERIC_READ,      // access (read-write) mode
> +                       FILE_SHARE_READ,   // share mode
> +                       NULL,              // pointer to security
attributes
> +                       OPEN_EXISTING,    // how to create
> +                       FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN,
> // file attributes
> +                       NULL); // handle to file with attributes to copy
> +    if (hFile != INVALID_HANDLE_VALUE)
> +        rv = ap_put_os_file(file, &hFile, context);
> +    else {
> +        rv = GetLastError();
> +        *file = NULL;
> +    }
>
> +#endif
> +    return rv;
> +}
> +
>  ap_status_t cleanup_mmap(void *sconfv)
>  {
>      a_server_config *sconf = sconfv;
> @@ -155,9 +189,13 @@
>      n = sconf->files->nelts;
>      file = (a_file *)sconf->files->elts;
>      while(n) {
> -     ap_mmap_delete(file->mm);
> -     ++file;
> -     --n;
> +#ifdef WIN32
> +        ap_close(file->file);
> +#else
> +        ap_mmap_delete(file->mm);
> +#endif
> +        ++file;
> +        --n;
>      }
>      return APR_SUCCESS;
>  }
> @@ -168,23 +206,33 @@
>      a_file *new_file;
>      a_file tmp;
>      ap_file_t *fd = NULL;
> +#ifndef WIN32
>      caddr_t mm;
> -
> -    if (stat(filename, &tmp.finfo) == -1) {
> +#endif
> +    ap_status_t rc;
> +    /* canonicalize the file name */
> +    /* os_canonical... */
> +    if (ap_stat(&tmp.finfo, filename, NULL) != APR_SUCCESS) {
>   ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
>       "mmap_static: unable to stat(%s), skipping", filename);
>   return NULL;
>      }
> -    if ((tmp.finfo.st_mode & S_IFMT) != S_IFREG) {
> +    if (tmp.finfo.filetype != APR_REG) {
>   ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
>       "mmap_static: %s isn't a regular file, skipping", filename);
>   return NULL;
>      }
> -    if (ap_open(&fd, filename, APR_READ, APR_OS_DEFAULT, context) !=
> APR_SUCCESS) {
> - ap_log_error(APLOG_MARK, APLOG_WARNING, errno, cmd->server,
> -     "mmap_static: unable to open(%s, O_RDONLY), skipping", filename);
> +    /* Note: open_file should call ap_open for Unix and CreateFile for
> Windows.
> +     * The Windows file needs to be opened for async I/O to allow
multiple
> threads
> +     * to serve it up at once.
> +     */
> +    rc = open_file(&fd, filename, APR_READ, APR_OS_DEFAULT, context);
> +    if (rc != APR_SUCCESS) {
> +        ap_log_error(APLOG_MARK, APLOG_WARNING, rc, cmd->server,
> +                     "mmap_static: unable to open(%s, O_RDONLY),
skipping",
> filename);
>   return NULL;
>      }
> +#ifndef WIN32
>      if (ap_mmap_create(&tmp.mm, fd, 0, tmp.finfo.st_size, context) !=
> APR_SUCCESS) {
>   int save_errno = errno;
>   ap_close(fd);
> @@ -194,6 +242,9 @@
>   return NULL;
>      }
>      ap_close(fd);
> +#else
> +    tmp.file = fd;
> +#endif
>      tmp.filename = ap_pstrdup(cmd->pool, filename);
>      sconf = ap_get_module_config(cmd->server->module_config,
> &mmap_static_module);
>      new_file = ap_push_array(sconf->files);
> @@ -205,6 +256,7 @@
>      return NULL;
>  }
>
> +#ifdef WIN32
>  static int file_compare(const void *av, const void *bv)
>  {
>      const a_file *a = av;
> @@ -212,7 +264,7 @@
>
>      return strcmp(a->filename, b->filename);
>  }
> -
> +#else
>  static int inode_compare(const void *av, const void *bv)
>  {
>      const a_file *a = *(a_file **)av;
> @@ -225,9 +277,9 @@
>      }
>      return c;
>  }
> -
> +#endif
>  static void mmap_post_config(ap_context_t *p, ap_context_t *plog,
> -          ap_context_t *ptemp, server_rec *s)
> +                             ap_context_t *ptemp, server_rec *s)
>  {
>      a_server_config *sconf;
>      ap_array_header_t *inodes;
> @@ -243,13 +295,14 @@
>      qsort(elts, nelts, sizeof(a_file), file_compare);
>
>      /* build an index by inode as well, speeds up the search in the
handler
> */
> +#ifndef WIN32
>      inodes = ap_make_array(p, nelts, sizeof(a_file *));
>      sconf->inode_sorted = inodes;
>      for (i = 0; i < nelts; ++i) {
>   *(a_file **)ap_push_array(inodes) = &elts[i];
>      }
>      qsort(inodes->elts, nelts, sizeof(a_file *), inode_compare);
> -
> +#endif
>      /* and make the virtualhosts share the same thing */
>      for (s = s->next; s; s = s->next) {
>   ap_set_module_config(s->module_config, &mmap_static_module, sconf);
> @@ -259,7 +312,47 @@
>  /* If it's one of ours, fill in r->finfo now to avoid extra stat()...
this
> is a
>   * bit of a kludge, because we really want to run after core_translate
> runs.
>   */
> -
> +int core_translate_copy(request_rec *r)
> +{
> +    void *sconf = r->server->module_config;
> +    core_server_config *conf = ap_get_module_config(sconf, &core_module);
> +
> +    if (r->proxyreq) {
> +        return HTTP_FORBIDDEN;
> +    }
> +    if ((r->uri[0] != '/') && strcmp(r->uri, "*")) {
> +        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
> +                      "Invalid URI in request %s", r->the_request);
> +        return BAD_REQUEST;
> +    }
> +
> +    if (r->server->path
> +        && !strncmp(r->uri, r->server->path, r->server->pathlen)
> +        && (r->server->path[r->server->pathlen - 1] == '/'
> +            || r->uri[r->server->pathlen] == '/'
> +            || r->uri[r->server->pathlen] == '\0')) {
> +        r->filename = ap_pstrcat(r->pool, conf->ap_document_root,
> +                                 (r->uri + r->server->pathlen), NULL);
> +    }
> +    else {
> +        /*
> +         * Make sure that we do not mess up the translation by adding two
> +         * /'s in a row.  This happens under windows when the document
> +         * root ends with a /
> +         */
> +        if ((conf->ap_document_root[strlen(conf->ap_document_root)-1] ==
> '/')
> +            && (*(r->uri) == '/')) {
> +            r->filename = ap_pstrcat(r->pool, conf->ap_document_root,
> r->uri+1,
> +                                     NULL);
> +        }
> +        else {
> +            r->filename = ap_pstrcat(r->pool, conf->ap_document_root,
> r->uri,
> +                                     NULL);
> +        }
> +
> +        return OK;
> +    }
> +}
>  static int mmap_static_xlat(request_rec *r)
>  {
>      a_server_config *sconf;
> @@ -273,10 +366,12 @@
>      if (ap_is_empty_table(sconf->files))
>   return DECLINED;
>
> -/*    res = core_module.translate_handler(r);
> +/*  res = core_module.translate_handler(r);  */
> +    res = core_translate_copy(r);
>      if (res == DECLINED || !r->filename) {
>   return res;
> -    }*/
> +    }
> +
>      if (!r->filename)
>          return DECLINED;
>      tmp.filename = r->filename;
> @@ -299,23 +394,38 @@
>      a_file **pmatch;
>      a_file *match;
>      int rangestatus, errstatus;
> +#ifdef WIN32
> +    HANDLE hCurrentProcess;
> +#endif
>
>      /* we don't handle anything but GET */
>      if (r->method_number != M_GET) return DECLINED;
>
>      /* file doesn't exist, we won't be dealing with it */
> -    if (r->finfo.st_mode == 0) return DECLINED;
> +    if (r->finfo.protection == 0) return DECLINED;
>
>      sconf = ap_get_module_config(r->server->module_config,
> &mmap_static_module);
> +#ifdef WIN32
> +    tmp.filename = r->filename;
> +#else
>      tmp.finfo.st_dev = r->finfo.st_dev;
>      tmp.finfo.st_ino = r->finfo.st_ino;
> +#endif
>      ptmp = &tmp;
> +#ifdef WIN32
> +    match = (a_file *)bsearch(ptmp, sconf->files->elts,
> + sconf->files->nelts, sizeof(a_file), file_compare);
> +    if (match == NULL) {
> + return DECLINED;
> +    }
> +#else
>      pmatch = (a_file **)bsearch(&ptmp, sconf->inode_sorted->elts,
>   sconf->inode_sorted->nelts, sizeof(a_file *), inode_compare);
>      if (pmatch == NULL) {
>   return DECLINED;
>      }
>      match = *pmatch;
> +#endif
>
>      /* note that we would handle GET on this resource */
>      r->allowed |= (1 << M_GET);
> @@ -326,11 +436,11 @@
>      if ((errstatus = ap_discard_request_body(r)) != OK)
>          return errstatus;
>
> -    ap_update_mtime(r, match->finfo.st_mtime);
> +    ap_update_mtime(r, match->finfo.mtime);
>      ap_set_last_modified(r);
>      ap_set_etag(r);
>      if (((errstatus = ap_meets_conditions(r)) != OK)
> - || (errstatus = ap_set_content_length (r, match->finfo.st_size))) {
> + || (errstatus = ap_set_content_length (r, match->finfo.size))) {
>       return errstatus;
>      }
>
> @@ -338,16 +448,53 @@
>      ap_send_http_header(r);
>
>      if (!r->header_only) {
> +        long length = match->finfo.size;
> +        ap_off_t offset = 0;
> +#ifdef WIN32
> +//        ap_bflush(r->connection->client->);
> +        struct iovec iov;
> +        ap_hdtr_t hdtr;
> +        ap_hdtr_t *phdtr = &hdtr;
> +        /* frob the client buffer */
> +        iov.iov_base = r->connection->client->outbase;
> +        iov.iov_len =  r->connection->client->outcnt;
> +        r->connection->client->outcnt = 0;
> +
> +        /* initialize the ap_hdtr_t struct */
> +        phdtr->headers = &iov;
> +        phdtr->numheaders = 1;
> +        phdtr->trailers = NULL;
> +        phdtr->numtrailers = 0;
> +
> + if (!rangestatus) {
> +            iol_sendfile(r->connection->client->iol,
> +                         match->file,
> +                         phdtr,
> +                         &offset,
> +                         &length,
> +                         0);
> + }
> + else {
> +     while (ap_each_byterange(r, &offset, &length)) {
> +                iol_sendfile(r->connection->client->iol,
> +                             match->file,
> +                             phdtr,
> +                             &offset,
> +                             &length,
> +                             0);
> +                phdtr = NULL;
> +     }
> + }
> +#else
>   if (!rangestatus) {
>       ap_send_mmap (match->mm, r, 0, match->finfo.st_size);
>   }
>   else {
> -     long length;
> -     ap_off_t offset;
>       while (ap_each_byterange(r, &offset, &length)) {
>    ap_send_mmap(match->mm, r, offset, length);
>       }
>   }
> +#endif
>      }
>      return OK;
>  }
> @@ -362,8 +509,9 @@
>  static void register_hooks(void)
>  {
>      static const char* const aszPre[]={"http_core.c",NULL};
> +    ap_hook_pre_config(pre_config,NULL,NULL,HOOK_MIDDLE);
>      ap_hook_post_config(mmap_post_config, NULL, NULL, HOOK_MIDDLE);
> -    ap_hook_translate_name(mmap_static_xlat, aszPre, NULL, HOOK_LAST);
> +    ap_hook_translate_name(mmap_static_xlat, aszPre, NULL, HOOK_MIDDLE);
>  };
>
>  static const handler_rec mmap_static_handlers[] =
>
>
>