|
@@ -420,8 +420,11 @@ struct file {
|
|
int64_t size;
|
|
int64_t size;
|
|
FILE *fp;
|
|
FILE *fp;
|
|
const char *membuf; // Non-NULL if file data is in memory
|
|
const char *membuf; // Non-NULL if file data is in memory
|
|
|
|
+ // set to 1 if the content is gzipped
|
|
|
|
+ // in which case we need a content-encoding: gzip header
|
|
|
|
+ int gzipped;
|
|
};
|
|
};
|
|
-#define STRUCT_FILE_INITIALIZER {0, 0, 0, NULL, NULL}
|
|
|
|
|
|
+#define STRUCT_FILE_INITIALIZER {0, 0, 0, NULL, NULL, 0}
|
|
|
|
|
|
// Describes listening socket, or socket which was accept()-ed by the master
|
|
// Describes listening socket, or socket which was accept()-ed by the master
|
|
// thread and queued for future handling by the worker thread.
|
|
// thread and queued for future handling by the worker thread.
|
|
@@ -1772,6 +1775,8 @@ static void convert_uri_to_file_name(struct mg_connection *conn, char *buf,
|
|
const char *rewrite, *uri = conn->request_info.uri;
|
|
const char *rewrite, *uri = conn->request_info.uri;
|
|
char *p;
|
|
char *p;
|
|
int match_len;
|
|
int match_len;
|
|
|
|
+ char gz_path[PATH_MAX];
|
|
|
|
+ char const* accept_encoding;
|
|
|
|
|
|
// Using buf_len - 1 because memmove() for PATH_INFO may shift part
|
|
// Using buf_len - 1 because memmove() for PATH_INFO may shift part
|
|
// of the path one byte on the right.
|
|
// of the path one byte on the right.
|
|
@@ -1787,26 +1792,42 @@ static void convert_uri_to_file_name(struct mg_connection *conn, char *buf,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if (!mg_stat(conn, buf, filep)) {
|
|
|
|
- // Support PATH_INFO for CGI scripts.
|
|
|
|
- for (p = buf + strlen(buf); p > buf + 1; p--) {
|
|
|
|
- if (*p == '/') {
|
|
|
|
- *p = '\0';
|
|
|
|
- if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
|
|
|
|
- strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
|
|
|
|
- mg_stat(conn, buf, filep)) {
|
|
|
|
- // Shift PATH_INFO block one character right, e.g.
|
|
|
|
- // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
|
|
|
|
- // conn->path_info is pointing to the local variable "path" declared
|
|
|
|
- // in handle_request(), so PATH_INFO is not valid after
|
|
|
|
- // handle_request returns.
|
|
|
|
- conn->path_info = p + 1;
|
|
|
|
- memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0
|
|
|
|
- p[1] = '/';
|
|
|
|
- break;
|
|
|
|
- } else {
|
|
|
|
- *p = '/';
|
|
|
|
- }
|
|
|
|
|
|
+ if (mg_stat(conn, buf, filep)) return;
|
|
|
|
+
|
|
|
|
+ // if we can't find the actual file, look for the file
|
|
|
|
+ // with the same name but a .gz extension. If we find it,
|
|
|
|
+ // use that and set the gzipped flag in the file struct
|
|
|
|
+ // to indicate that the response need to have the content-
|
|
|
|
+ // encoding: gzip header
|
|
|
|
+ // we can only do this if the browser declares support
|
|
|
|
+ if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
|
|
|
|
+ if (strstr(accept_encoding,"gzip") != NULL) {
|
|
|
|
+ snprintf(gz_path, sizeof(gz_path), "%s.gz", buf);
|
|
|
|
+ if (mg_stat(conn, gz_path, filep)) {
|
|
|
|
+ filep->gzipped = 1;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Support PATH_INFO for CGI scripts.
|
|
|
|
+ for (p = buf + strlen(buf); p > buf + 1; p--) {
|
|
|
|
+ if (*p == '/') {
|
|
|
|
+ *p = '\0';
|
|
|
|
+ if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
|
|
|
|
+ strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
|
|
|
|
+ mg_stat(conn, buf, filep)) {
|
|
|
|
+ // Shift PATH_INFO block one character right, e.g.
|
|
|
|
+ // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
|
|
|
|
+ // conn->path_info is pointing to the local variable "path" declared
|
|
|
|
+ // in handle_request(), so PATH_INFO is not valid after
|
|
|
|
+ // handle_request returns.
|
|
|
|
+ conn->path_info = p + 1;
|
|
|
|
+ memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0
|
|
|
|
+ p[1] = '/';
|
|
|
|
+ break;
|
|
|
|
+ } else {
|
|
|
|
+ *p = '/';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -2828,17 +2849,29 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
|
|
int64_t cl, r1, r2;
|
|
int64_t cl, r1, r2;
|
|
struct vec mime_vec;
|
|
struct vec mime_vec;
|
|
int n;
|
|
int n;
|
|
|
|
+ char gz_path[PATH_MAX];
|
|
|
|
+ char const* encoding = "";
|
|
|
|
|
|
get_mime_type(conn->ctx, path, &mime_vec);
|
|
get_mime_type(conn->ctx, path, &mime_vec);
|
|
cl = filep->size;
|
|
cl = filep->size;
|
|
conn->status_code = 200;
|
|
conn->status_code = 200;
|
|
range[0] = '\0';
|
|
range[0] = '\0';
|
|
|
|
|
|
|
|
+ // if this file is in fact a pre-gzipped file, rewrite its filename
|
|
|
|
+ // it's important to rewrite the filename after resolving
|
|
|
|
+ // the mime type from it, to preserve the actual file's type
|
|
|
|
+ if (filep->gzipped) {
|
|
|
|
+ snprintf(gz_path, sizeof(gz_path), "%s.gz", path);
|
|
|
|
+ path = gz_path;
|
|
|
|
+ encoding = "Content-Encoding: gzip\r\n";
|
|
|
|
+ }
|
|
|
|
+
|
|
if (!mg_fopen(conn, path, "rb", filep)) {
|
|
if (!mg_fopen(conn, path, "rb", filep)) {
|
|
send_http_error(conn, 500, http_500_error,
|
|
send_http_error(conn, 500, http_500_error,
|
|
"fopen(%s): %s", path, strerror(ERRNO));
|
|
"fopen(%s): %s", path, strerror(ERRNO));
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+
|
|
fclose_on_exec(filep);
|
|
fclose_on_exec(filep);
|
|
|
|
|
|
// If Range: header specified, act accordingly
|
|
// If Range: header specified, act accordingly
|
|
@@ -2846,6 +2879,12 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
|
|
hdr = mg_get_header(conn, "Range");
|
|
hdr = mg_get_header(conn, "Range");
|
|
if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
|
|
if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
|
|
r1 >= 0 && r2 >= 0) {
|
|
r1 >= 0 && r2 >= 0) {
|
|
|
|
+ // actually, range requests don't play well with a pre-gzipped
|
|
|
|
+ // file (since the range is specified in the uncmpressed space)
|
|
|
|
+ if (filep->gzipped) {
|
|
|
|
+ send_http_error(conn, 501, "Not Implemented", "range requests in gzipped files are not supported");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
conn->status_code = 206;
|
|
conn->status_code = 206;
|
|
cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1;
|
|
cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1;
|
|
mg_snprintf(conn, range, sizeof(range),
|
|
mg_snprintf(conn, range, sizeof(range),
|
|
@@ -2871,9 +2910,9 @@ static void handle_file_request(struct mg_connection *conn, const char *path,
|
|
"Content-Length: %" INT64_FMT "\r\n"
|
|
"Content-Length: %" INT64_FMT "\r\n"
|
|
"Connection: %s\r\n"
|
|
"Connection: %s\r\n"
|
|
"Accept-Ranges: bytes\r\n"
|
|
"Accept-Ranges: bytes\r\n"
|
|
- "%s\r\n",
|
|
|
|
|
|
+ "%s%s\r\n",
|
|
conn->status_code, msg, date, lm, etag, (int) mime_vec.len,
|
|
conn->status_code, msg, date, lm, etag, (int) mime_vec.len,
|
|
- mime_vec.ptr, cl, suggest_connection_header(conn), range);
|
|
|
|
|
|
+ mime_vec.ptr, cl, suggest_connection_header(conn), range, encoding);
|
|
|
|
|
|
if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
|
|
if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
|
|
send_file_data(conn, filep, r1, cl);
|
|
send_file_data(conn, filep, r1, cl);
|