diff --git a/mongoose.c b/mongoose.c
index 743a4099e61d13899b9cfb042366af91c77399e6..ec207e6f30c026c477f6728e1e065b1b82b85708 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -662,6 +662,42 @@ void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) {
   memset((char *) ctx, 0, sizeof(*ctx));
 }
 
+/*
+ * Stringify binary data. Output buffer size must be 2 * size_of_input + 1
+ * because each byte of input takes 2 bytes in string representation
+ * plus 1 byte for the terminating \0 character.
+ */
+void cs_to_hex(char *to, const unsigned char *p, size_t len) {
+  static const char *hex = "0123456789abcdef";
+
+  for (; len--; p++) {
+    *to++ = hex[p[0] >> 4];
+    *to++ = hex[p[0] & 0x0f];
+  }
+  *to = '\0';
+}
+
+char *cs_md5(char buf[33], ...) {
+  unsigned char hash[16];
+  const unsigned char *p;
+  va_list ap;
+  MD5_CTX ctx;
+
+  MD5_Init(&ctx);
+
+  va_start(ap, buf);
+  while ((p = va_arg(ap, const unsigned char *) ) != NULL) {
+    size_t len = va_arg(ap, size_t);
+    MD5_Update(&ctx, p, len);
+  }
+  va_end(ap);
+
+  MD5_Final(hash, &ctx);
+  cs_to_hex(buf, hash, sizeof(hash));
+
+  return buf;
+}
+
 #endif /* EXCLUDE_COMMON */
 #ifdef NS_MODULE_LINES
 #line 1 "src/../../common/base64.c"
@@ -3481,7 +3517,7 @@ static int get_request_len(const char *s, int buf_len) {
 static const char *parse_http_headers(const char *s, const char *end, int len,
                                       struct http_message *req) {
   int i;
-  for (i = 0; i < (int) ARRAY_SIZE(req->header_names); i++) {
+  for (i = 0; i < (int) ARRAY_SIZE(req->header_names) - 1; i++) {
     struct mg_str *k = &req->header_names[i], *v = &req->header_values[i];
 
     s = mg_skip(s, end, ": ", k);
@@ -3573,7 +3609,7 @@ int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req) {
 struct mg_str *mg_get_http_header(struct http_message *hm, const char *name) {
   size_t i, len = strlen(name);
 
-  for (i = 0; i < ARRAY_SIZE(hm->header_names); i++) {
+  for (i = 0; hm->header_names[i].len > 0; i++) {
     struct mg_str *h = &hm->header_names[i], *v = &hm->header_values[i];
     if (h->p != NULL && h->len == len && !mg_ncasecmp(h->p, name, len))
       return v;
@@ -4619,9 +4655,10 @@ void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...) {
 int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
                          size_t buf_size) {
   int ch = ' ', ch1 = ',', len = 0, n = strlen(var_name);
-  const char *p, *end = hdr->p + hdr->len, *s = NULL;
+  const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL;
 
   if (buf != NULL && buf_size > 0) buf[0] = '\0';
+  if (hdr == NULL) return 0;
 
   /* Find where variable starts */
   for (s = hdr->p; s != NULL && s + n < end; s++) {
@@ -4663,66 +4700,6 @@ static int is_file_hidden(const char *path,
 }
 
 #ifndef MG_DISABLE_HTTP_DIGEST_AUTH
-static FILE *open_auth_file(const char *path, int is_directory,
-                            const struct mg_serve_http_opts *opts) {
-  char buf[MAX_PATH_SIZE];
-  const char *p;
-  FILE *fp = NULL;
-
-  if (opts->global_auth_file != NULL) {
-    fp = fopen(opts->global_auth_file, "r");
-  } else if (is_directory && opts->per_directory_auth_file) {
-    snprintf(buf, sizeof(buf), "%s%c%s", path, DIRSEP,
-             opts->per_directory_auth_file);
-    fp = fopen(buf, "r");
-  } else if (opts->per_directory_auth_file) {
-    if ((p = strrchr(path, '/')) == NULL && (p = strrchr(path, '\\')) == NULL) {
-      p = path;
-    }
-    snprintf(buf, sizeof(buf), "%.*s/%s", (int) (p - path), path,
-             opts->per_directory_auth_file);
-    fp = fopen(buf, "r");
-  }
-
-  return fp;
-}
-
-/*
- * Stringify binary data. Output buffer size must be 2 * size_of_input + 1
- * because each byte of input takes 2 bytes in string representation
- * plus 1 byte for the terminating \0 character.
- */
-static void bin2str(char *to, const unsigned char *p, size_t len) {
-  static const char *hex = "0123456789abcdef";
-
-  for (; len--; p++) {
-    *to++ = hex[p[0] >> 4];
-    *to++ = hex[p[0] & 0x0f];
-  }
-  *to = '\0';
-}
-
-static char *mg_md5(char *buf, ...) {
-  unsigned char hash[16];
-  const unsigned char *p;
-  va_list ap;
-  MD5_CTX ctx;
-
-  MD5_Init(&ctx);
-
-  va_start(ap, buf);
-  while ((p = va_arg(ap, const unsigned char *) ) != NULL) {
-    size_t len = va_arg(ap, size_t);
-    MD5_Update(&ctx, p, len);
-  }
-  va_end(ap);
-
-  MD5_Final(hash, &ctx);
-  bin2str(buf, hash, sizeof(hash));
-
-  return buf;
-}
-
 static void mkmd5resp(const char *method, size_t method_len, const char *uri,
                       size_t uri_len, const char *ha1, size_t ha1_len,
                       const char *nonce, size_t nonce_len, const char *nc,
@@ -4732,8 +4709,8 @@ static void mkmd5resp(const char *method, size_t method_len, const char *uri,
   static const size_t one = 1;
   char ha2[33];
 
-  mg_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
-  mg_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
+  cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
+  cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
          nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
          colon, one, ha2, sizeof(ha2) - 1, NULL);
 }
@@ -4747,7 +4724,7 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
   char ha1[33], resp[33], cnonce[40];
 
   snprintf(cnonce, sizeof(cnonce), "%x", (unsigned int) time(NULL));
-  mg_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain,
+  cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain,
          (size_t) strlen(auth_domain), colon, one, passwd,
          (size_t) strlen(passwd), NULL);
   mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1,
@@ -4820,26 +4797,47 @@ static int mg_http_check_digest_auth(struct http_message *hm,
 }
 
 static int is_authorized(struct http_message *hm, const char *path,
-                         int is_directory, struct mg_serve_http_opts *opts) {
+                         int is_directory, const char *domain,
+                         const char *passwords_file, int is_global_pass_file) {
+  char buf[MAX_PATH_SIZE];
+  const char *p;
   FILE *fp;
   int authorized = 1;
 
-  if (opts->auth_domain != NULL && (opts->per_directory_auth_file != NULL ||
-                                    opts->global_auth_file != NULL) &&
-      (fp = open_auth_file(path, is_directory, opts)) != NULL) {
-    authorized = mg_http_check_digest_auth(hm, opts->auth_domain, fp);
-    fclose(fp);
+  if (domain != NULL && passwords_file != NULL) {
+    if (is_global_pass_file) {
+      fp = fopen(passwords_file, "r");
+    } else if (is_directory) {
+      snprintf(buf, sizeof(buf), "%s%c%s", path, DIRSEP, passwords_file);
+      fp = fopen(buf, "r");
+    } else {
+      if ((p = strrchr(path, '/')) == NULL &&
+          (p = strrchr(path, '\\')) == NULL) {
+        p = path;
+      }
+      snprintf(buf, sizeof(buf), "%.*s/%s", (int) (p - path), path,
+               passwords_file);
+      fp = fopen(buf, "r");
+    }
+
+    if (fp != NULL) {
+      authorized = mg_http_check_digest_auth(hm, domain, fp);
+      fclose(fp);
+    }
   }
 
   return authorized;
 }
 #else
 static int is_authorized(struct http_message *hm, const char *path,
-                         int is_directory, struct mg_serve_http_opts *opts) {
+                         int is_directory, const char *domain,
+                         const char *passwords_file, int is_global_pass_file) {
   (void) hm;
   (void) path;
   (void) is_directory;
-  (void) opts;
+  (void) domain;
+  (void) passwords_file;
+  (void) is_global_pass_file;
   return 1;
 }
 #endif
@@ -5691,6 +5689,16 @@ static void handle_cgi(struct mg_connection *nc, const char *prog,
 }
 #endif
 
+static void mg_send_digest_auth_request(struct mg_connection *c,
+                                        const char *domain) {
+  mg_printf(c,
+            "HTTP/1.1 401 Unauthorized\r\n"
+            "WWW-Authenticate: Digest qop=\"auth\", "
+            "realm=\"%s\", nonce=\"%lu\"\r\n"
+            "Content-Length: 0\r\n\r\n",
+            domain, (unsigned long) time(NULL));
+}
+
 void mg_send_http_file(struct mg_connection *nc, char *path,
                        size_t path_buf_len, struct http_message *hm,
                        struct mg_serve_http_opts *opts) {
@@ -5706,13 +5714,11 @@ void mg_send_http_file(struct mg_connection *nc, char *path,
     nc->flags |= MG_F_CLOSE_IMMEDIATELY;
   } else if (is_dav && opts->dav_document_root == NULL) {
     send_http_error(nc, 501, NULL);
-  } else if (!is_authorized(hm, path, is_directory, opts)) {
-    mg_printf(nc,
-              "HTTP/1.1 401 Unauthorized\r\n"
-              "WWW-Authenticate: Digest qop=\"auth\", "
-              "realm=\"%s\", nonce=\"%lu\"\r\n"
-              "Content-Length: 0\r\n\r\n",
-              opts->auth_domain, (unsigned long) time(NULL));
+  } else if (!is_authorized(hm, path, is_directory, opts->auth_domain,
+                            opts->global_auth_file, 1) ||
+             !is_authorized(hm, path, is_directory, opts->auth_domain,
+                            opts->per_directory_auth_file, 0)) {
+    mg_send_digest_auth_request(nc, opts->auth_domain);
   } else if ((stat_result != 0 || is_file_hidden(path, opts)) && !is_dav) {
     mg_printf(nc, "%s", "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n");
   } else if (is_directory && path[strlen(path) - 1] != '/' && !is_dav) {
@@ -5723,6 +5729,13 @@ void mg_send_http_file(struct mg_connection *nc, char *path,
 #ifndef MG_DISABLE_DAV
   } else if (!mg_vcmp(&hm->method, "PROPFIND")) {
     handle_propfind(nc, path, &st, hm, opts);
+#ifndef MG_DISABLE_DAV_AUTH
+  } else if (is_dav &&
+             (opts->dav_auth_file == NULL ||
+              !is_authorized(hm, path, is_directory, opts->auth_domain,
+                             opts->dav_auth_file, 1))) {
+    mg_send_digest_auth_request(nc, opts->auth_domain);
+#endif
   } else if (!mg_vcmp(&hm->method, "MKCOL")) {
     handle_mkcol(nc, path, hm);
   } else if (!mg_vcmp(&hm->method, "DELETE")) {
@@ -5756,6 +5769,7 @@ void mg_send_http_file(struct mg_connection *nc, char *path,
 void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
                    struct mg_serve_http_opts opts) {
   char path[MG_MAX_PATH];
+  struct mg_str *hdr;
   uri_to_path(hm, path, sizeof(path), &opts);
   if (opts.per_directory_auth_file == NULL) {
     opts.per_directory_auth_file = ".htpasswd";
@@ -5773,6 +5787,15 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
     opts.index_files = "index.html,index.htm,index.shtml,index.cgi,index.php";
   }
   mg_send_http_file(nc, path, sizeof(path), hm, &opts);
+
+  /* Close connection for non-keep-alive requests */
+  if (mg_vcmp(&hm->proto, "HTTP/1.1") != 0 ||
+      ((hdr = mg_get_http_header(hm, "Connection")) != NULL &&
+       mg_vcmp(hdr, "keep-alive") != 0)) {
+#if 0
+    nc->flags |= MG_F_SEND_AND_CLOSE;
+#endif
+  }
 }
 
 #endif /* MG_DISABLE_FILESYSTEM */
diff --git a/mongoose.h b/mongoose.h
index 3c63288452f4a121b031979e08f71520b4a59c1c..43d6079672250aacc0780f97b3de40d3a78fc88a 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -404,6 +404,22 @@ void MD5_Init(MD5_CTX *c);
 void MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len);
 void MD5_Final(unsigned char *md, MD5_CTX *c);
 
+/*
+ * Return stringified MD5 hash for NULL terminated list of strings.
+ * Example:
+ *
+ *    char buf[33];
+ *    cs_md5(buf, "foo", "bar", NULL);
+ */
+char *cs_md5(char buf[33], ...);
+
+/*
+ * Stringify binary data. Output buffer size must be 2 * size_of_input + 1
+ * because each byte of input takes 2 bytes in string representation
+ * plus 1 byte for the terminating \0 character.
+ */
+void cs_to_hex(char *to, const unsigned char *p, size_t len);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
@@ -1259,10 +1275,11 @@ extern "C" {
 struct http_message {
   struct mg_str message; /* Whole message: request line + headers + body */
 
-  struct mg_str proto; /* "HTTP/1.1" -- for both request and response */
   /* HTTP Request line (or HTTP response line) */
   struct mg_str method; /* "GET" */
   struct mg_str uri;    /* "/my_file.html" */
+  struct mg_str proto;  /* "HTTP/1.1" -- for both request and response */
+
   /* For responses, code and response status message are set */
   int resp_code;
   struct mg_str resp_status_msg;
@@ -1561,6 +1578,9 @@ struct mg_serve_http_opts {
   /* List of index files. Default is "" */
   const char *index_files;
 
+  /* Path to a HTTP requests log file. Leave as NULL to disable access log. */
+  const char *access_log_file;
+
   /*
    * Leave as NULL to disable authentication.
    * To enable directory protection with authentication, set this to ".htpasswd"
@@ -1614,6 +1634,9 @@ struct mg_serve_http_opts {
   /* DAV document root. If NULL, DAV requests are going to fail. */
   const char *dav_document_root;
 
+  /* DAV passwords file. If NULL, DAV requests are going to fail. */
+  const char *dav_auth_file;
+
   /* Glob pattern for the files to hide. */
   const char *hidden_file_pattern;