|
@@ -2892,6 +2892,110 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+static char *get_fname(const char *path)
|
|
|
|
|
+{
|
|
|
|
|
+ char *p=strrchr(path, '/');
|
|
|
|
|
+ if(p) {
|
|
|
|
|
+ return p+1;
|
|
|
|
|
+ }
|
|
|
|
|
+ return (char*)path;
|
|
|
|
|
+}
|
|
|
|
|
+void mg_http_serve_file2(struct mg_connection *c, struct mg_http_message *hm,
|
|
|
|
|
+ const char *path,
|
|
|
|
|
+ const struct mg_http_serve_opts *opts) {
|
|
|
|
|
+ char etag[64], tmp[MG_PATH_MAX];
|
|
|
|
|
+ struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
|
|
|
|
+ struct mg_fd *fd = NULL;
|
|
|
|
|
+ size_t size = 0;
|
|
|
|
|
+ time_t mtime = 0;
|
|
|
|
|
+ struct mg_str *inm = NULL;
|
|
|
|
|
+ struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types);
|
|
|
|
|
+ bool gzip = false;
|
|
|
|
|
+
|
|
|
|
|
+ if (path != NULL) {
|
|
|
|
|
+ // If a browser sends us "Accept-Encoding: gzip", try to open .gz first
|
|
|
|
|
+ struct mg_str *ae = mg_http_get_header(hm, "Accept-Encoding");
|
|
|
|
|
+ if (ae != NULL) {
|
|
|
|
|
+ char *ae_ = mg_mprintf("%.*s", ae->len, ae->buf);
|
|
|
|
|
+ if (ae_ != NULL && strstr(ae_, "gzip") != NULL) {
|
|
|
|
|
+ mg_snprintf(tmp, sizeof(tmp), "%s.gz", path);
|
|
|
|
|
+ fd = mg_fs_open(fs, tmp, MG_FS_READ);
|
|
|
|
|
+ if (fd != NULL) gzip = true, path = tmp;
|
|
|
|
|
+ }
|
|
|
|
|
+ free(ae_);
|
|
|
|
|
+ }
|
|
|
|
|
+ // No luck opening .gz? Open what we've told to open
|
|
|
|
|
+ if (fd == NULL) fd = mg_fs_open(fs, path, MG_FS_READ);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Failed to open, and page404 is configured? Open it, then
|
|
|
|
|
+ if (fd == NULL && opts->page404 != NULL) {
|
|
|
|
|
+ fd = mg_fs_open(fs, opts->page404, MG_FS_READ);
|
|
|
|
|
+ path = opts->page404;
|
|
|
|
|
+ mime = guess_content_type(mg_str(path), opts->mime_types);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (fd == NULL || fs->st(path, &size, &mtime) == 0) {
|
|
|
|
|
+ mg_http_reply(c, 404, opts->extra_headers, "Not found\n");
|
|
|
|
|
+ mg_fs_close(fd);
|
|
|
|
|
+ // NOTE: mg_http_etag() call should go first!
|
|
|
|
|
+ } else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
|
|
|
|
|
+ (inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
|
|
|
|
|
+ mg_strcasecmp(*inm, mg_str(etag)) == 0) {
|
|
|
|
|
+ mg_fs_close(fd);
|
|
|
|
|
+ mg_http_reply(c, 304, opts->extra_headers, "");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ int n, status = 200;
|
|
|
|
|
+ char range[100];
|
|
|
|
|
+ size_t r1 = 0, r2 = 0, cl = size;
|
|
|
|
|
+
|
|
|
|
|
+ // Handle Range header
|
|
|
|
|
+ struct mg_str *rh = mg_http_get_header(hm, "Range");
|
|
|
|
|
+ range[0] = '\0';
|
|
|
|
|
+ if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0) {
|
|
|
|
|
+ // If range is specified like "400-", set second limit to content len
|
|
|
|
|
+ if (n == 1) r2 = cl - 1;
|
|
|
|
|
+ if (r1 > r2 || r2 >= cl) {
|
|
|
|
|
+ status = 416;
|
|
|
|
|
+ cl = 0;
|
|
|
|
|
+ mg_snprintf(range, sizeof(range), "Content-Range: bytes */%lld\r\n",
|
|
|
|
|
+ (int64_t) size);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ status = 206;
|
|
|
|
|
+ cl = r2 - r1 + 1;
|
|
|
|
|
+ mg_snprintf(range, sizeof(range),
|
|
|
|
|
+ "Content-Range: bytes %llu-%llu/%llu\r\n", (uint64_t) r1,
|
|
|
|
|
+ (uint64_t) (r1 + cl - 1), (uint64_t) size);
|
|
|
|
|
+ fs->sk(fd->fd, r1);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ mg_printf(c,
|
|
|
|
|
+ "HTTP/1.1 %d %s\r\n"
|
|
|
|
|
+ "Content-Type: %.*s\r\n"
|
|
|
|
|
+ "Content-Disposition: attachment;filename=%s\r\n"
|
|
|
|
|
+ "Etag: %s\r\n"
|
|
|
|
|
+ "Content-Length: %llu\r\n"
|
|
|
|
|
+ "%s%s%s\r\n",
|
|
|
|
|
+ status, mg_http_status_code_str(status), (int) mime.len, mime.buf,
|
|
|
|
|
+ get_fname(path),
|
|
|
|
|
+ etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "",
|
|
|
|
|
+ range, opts->extra_headers ? opts->extra_headers : "");
|
|
|
|
|
+ if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0) {
|
|
|
|
|
+ c->is_draining = 1;
|
|
|
|
|
+ c->is_resp = 0;
|
|
|
|
|
+ mg_fs_close(fd);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Track to-be-sent content length at the end of c->data, aligned
|
|
|
|
|
+ size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) /
|
|
|
|
|
+ sizeof(size_t) * sizeof(size_t)];
|
|
|
|
|
+ c->pfn = static_cb;
|
|
|
|
|
+ c->pfn_data = fd;
|
|
|
|
|
+ *clp = cl;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
struct printdirentrydata {
|
|
struct printdirentrydata {
|
|
|
struct mg_connection *c;
|
|
struct mg_connection *c;
|
|
|
struct mg_http_message *hm;
|
|
struct mg_http_message *hm;
|