mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-07 17:05:38 +02:00
Use the URL global instead of the deprecated url.parse
The Node.js url.parse API (https://nodejs.org/api/url.html#urlparseurlstring-parsequerystring-slashesdenotehost) is deprecated because it's prone to security issues (to the point that Node.js doesn't even publish CVEs for it anymore). The official reccomendation is to instead use the global URL constructor, available both in Node.js and in browsers. Node.js filesystem APIs accept URL objects as parameter, so this also avoids a few URL->filepath conversions.
This commit is contained in:
parent
ab052db5b3
commit
229ad1bb2c
4 changed files with 83 additions and 124 deletions
|
@ -26,29 +26,20 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileUriRegex = /^file:\/\/\/[a-zA-Z]:\//;
|
const urlRegex = /^[a-z][a-z0-9\-+.]+:/i;
|
||||||
|
|
||||||
function parseUrl(sourceUrl) {
|
function parseUrlOrPath(sourceUrl) {
|
||||||
|
if (urlRegex.test(sourceUrl)) {
|
||||||
|
return new URL(sourceUrl);
|
||||||
|
}
|
||||||
const url = NodePackages.get("url");
|
const url = NodePackages.get("url");
|
||||||
const parsedUrl = url.parse(sourceUrl);
|
return new URL(url.pathToFileURL(sourceUrl));
|
||||||
if (parsedUrl.protocol === "file:" || parsedUrl.host) {
|
|
||||||
return parsedUrl;
|
|
||||||
}
|
|
||||||
// Prepending 'file:///' to Windows absolute path.
|
|
||||||
if (/^[a-z]:[/\\]/i.test(sourceUrl)) {
|
|
||||||
return url.parse(`file:///${sourceUrl}`);
|
|
||||||
}
|
|
||||||
// Changes protocol to 'file:' if url refers to filesystem.
|
|
||||||
if (!parsedUrl.host) {
|
|
||||||
parsedUrl.protocol = "file:";
|
|
||||||
}
|
|
||||||
return parsedUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PDFNodeStream {
|
class PDFNodeStream {
|
||||||
constructor(source) {
|
constructor(source) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.url = parseUrl(source.url);
|
this.url = parseUrlOrPath(source.url);
|
||||||
this.isHttp =
|
this.isHttp =
|
||||||
this.url.protocol === "http:" || this.url.protocol === "https:";
|
this.url.protocol === "http:" || this.url.protocol === "https:";
|
||||||
// Check if url refers to filesystem.
|
// Check if url refers to filesystem.
|
||||||
|
@ -287,18 +278,6 @@ class BaseRangeReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRequestOptions(parsedUrl, headers) {
|
|
||||||
return {
|
|
||||||
protocol: parsedUrl.protocol,
|
|
||||||
auth: parsedUrl.auth,
|
|
||||||
host: parsedUrl.hostname,
|
|
||||||
port: parsedUrl.port,
|
|
||||||
path: parsedUrl.path,
|
|
||||||
method: "GET",
|
|
||||||
headers,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class PDFNodeStreamFullReader extends BaseFullReader {
|
class PDFNodeStreamFullReader extends BaseFullReader {
|
||||||
constructor(stream) {
|
constructor(stream) {
|
||||||
super(stream);
|
super(stream);
|
||||||
|
@ -337,13 +316,15 @@ class PDFNodeStreamFullReader extends BaseFullReader {
|
||||||
if (this._url.protocol === "http:") {
|
if (this._url.protocol === "http:") {
|
||||||
const http = NodePackages.get("http");
|
const http = NodePackages.get("http");
|
||||||
this._request = http.request(
|
this._request = http.request(
|
||||||
createRequestOptions(this._url, stream.httpHeaders),
|
this._url,
|
||||||
|
{ headers: stream.httpHeaders },
|
||||||
handleResponse
|
handleResponse
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const https = NodePackages.get("https");
|
const https = NodePackages.get("https");
|
||||||
this._request = https.request(
|
this._request = https.request(
|
||||||
createRequestOptions(this._url, stream.httpHeaders),
|
this._url,
|
||||||
|
{ headers: stream.httpHeaders },
|
||||||
handleResponse
|
handleResponse
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -386,13 +367,15 @@ class PDFNodeStreamRangeReader extends BaseRangeReader {
|
||||||
if (this._url.protocol === "http:") {
|
if (this._url.protocol === "http:") {
|
||||||
const http = NodePackages.get("http");
|
const http = NodePackages.get("http");
|
||||||
this._request = http.request(
|
this._request = http.request(
|
||||||
createRequestOptions(this._url, this._httpHeaders),
|
this._url,
|
||||||
|
{ headers: this._httpHeaders },
|
||||||
handleResponse
|
handleResponse
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const https = NodePackages.get("https");
|
const https = NodePackages.get("https");
|
||||||
this._request = https.request(
|
this._request = https.request(
|
||||||
createRequestOptions(this._url, this._httpHeaders),
|
this._url,
|
||||||
|
{ headers: this._httpHeaders },
|
||||||
handleResponse
|
handleResponse
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -408,25 +391,18 @@ class PDFNodeStreamFsFullReader extends BaseFullReader {
|
||||||
constructor(stream) {
|
constructor(stream) {
|
||||||
super(stream);
|
super(stream);
|
||||||
|
|
||||||
let path = decodeURIComponent(this._url.path);
|
|
||||||
|
|
||||||
// Remove the extra slash to get right path from url like `file:///C:/`
|
|
||||||
if (fileUriRegex.test(this._url.href)) {
|
|
||||||
path = path.replace(/^\//, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
const fs = NodePackages.get("fs");
|
const fs = NodePackages.get("fs");
|
||||||
fs.promises.lstat(path).then(
|
fs.promises.lstat(this._url).then(
|
||||||
stat => {
|
stat => {
|
||||||
// Setting right content length.
|
// Setting right content length.
|
||||||
this._contentLength = stat.size;
|
this._contentLength = stat.size;
|
||||||
|
|
||||||
this._setReadableStream(fs.createReadStream(path));
|
this._setReadableStream(fs.createReadStream(this._url));
|
||||||
this._headersCapability.resolve();
|
this._headersCapability.resolve();
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
if (error.code === "ENOENT") {
|
if (error.code === "ENOENT") {
|
||||||
error = new MissingPDFException(`Missing PDF "${path}".`);
|
error = new MissingPDFException(`Missing PDF "${this._url}".`);
|
||||||
}
|
}
|
||||||
this._storedError = error;
|
this._storedError = error;
|
||||||
this._headersCapability.reject(error);
|
this._headersCapability.reject(error);
|
||||||
|
@ -439,15 +415,10 @@ class PDFNodeStreamFsRangeReader extends BaseRangeReader {
|
||||||
constructor(stream, start, end) {
|
constructor(stream, start, end) {
|
||||||
super(stream);
|
super(stream);
|
||||||
|
|
||||||
let path = decodeURIComponent(this._url.path);
|
|
||||||
|
|
||||||
// Remove the extra slash to get right path from url like `file:///C:/`
|
|
||||||
if (fileUriRegex.test(this._url.href)) {
|
|
||||||
path = path.replace(/^\//, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
const fs = NodePackages.get("fs");
|
const fs = NodePackages.get("fs");
|
||||||
this._setReadableStream(fs.createReadStream(path, { start, end: end - 1 }));
|
this._setReadableStream(
|
||||||
|
fs.createReadStream(this._url, { start, end: end - 1 })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ import path from "path";
|
||||||
import puppeteer from "puppeteer";
|
import puppeteer from "puppeteer";
|
||||||
import readline from "readline";
|
import readline from "readline";
|
||||||
import { translateFont } from "./font/ttxdriver.mjs";
|
import { translateFont } from "./font/ttxdriver.mjs";
|
||||||
import url from "url";
|
|
||||||
import { WebServer } from "./webserver.mjs";
|
import { WebServer } from "./webserver.mjs";
|
||||||
import yargs from "yargs";
|
import yargs from "yargs";
|
||||||
|
|
||||||
|
@ -670,8 +669,7 @@ function checkRefTestResults(browser, id, results) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function refTestPostHandler(req, res) {
|
function refTestPostHandler(parsedUrl, req, res) {
|
||||||
var parsedUrl = url.parse(req.url, true);
|
|
||||||
var pathname = parsedUrl.pathname;
|
var pathname = parsedUrl.pathname;
|
||||||
if (
|
if (
|
||||||
pathname !== "/tellMeToQuit" &&
|
pathname !== "/tellMeToQuit" &&
|
||||||
|
@ -691,7 +689,7 @@ function refTestPostHandler(req, res) {
|
||||||
|
|
||||||
var session;
|
var session;
|
||||||
if (pathname === "/tellMeToQuit") {
|
if (pathname === "/tellMeToQuit") {
|
||||||
session = getSession(parsedUrl.query.browser);
|
session = getSession(parsedUrl.searchParams.get("browser"));
|
||||||
monitorBrowserTimeout(session, null);
|
monitorBrowserTimeout(session, null);
|
||||||
closeSession(session.name);
|
closeSession(session.name);
|
||||||
return;
|
return;
|
||||||
|
@ -821,8 +819,7 @@ async function startIntegrationTest() {
|
||||||
await Promise.all(sessions.map(session => closeSession(session.name)));
|
await Promise.all(sessions.map(session => closeSession(session.name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function unitTestPostHandler(req, res) {
|
function unitTestPostHandler(parsedUrl, req, res) {
|
||||||
var parsedUrl = url.parse(req.url);
|
|
||||||
var pathname = parsedUrl.pathname;
|
var pathname = parsedUrl.pathname;
|
||||||
if (
|
if (
|
||||||
pathname !== "/tellMeToQuit" &&
|
pathname !== "/tellMeToQuit" &&
|
||||||
|
|
|
@ -24,17 +24,13 @@ if (!isNodeJS) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = await __non_webpack_import__("path");
|
|
||||||
const url = await __non_webpack_import__("url");
|
const url = await __non_webpack_import__("url");
|
||||||
|
|
||||||
describe("node_stream", function () {
|
describe("node_stream", function () {
|
||||||
let tempServer = null;
|
let tempServer = null;
|
||||||
|
|
||||||
const pdf = url.parse(
|
const cwdURL = url.pathToFileURL(process.cwd()) + "/";
|
||||||
encodeURI(
|
const pdf = new URL("./test/pdfs/tracemonkey.pdf", cwdURL).href;
|
||||||
"file://" + path.join(process.cwd(), "./test/pdfs/tracemonkey.pdf")
|
|
||||||
)
|
|
||||||
).href;
|
|
||||||
const pdfLength = 1016315;
|
const pdfLength = 1016315;
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(function () {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import fs from "fs";
|
||||||
import fsPromises from "fs/promises";
|
import fsPromises from "fs/promises";
|
||||||
import http from "http";
|
import http from "http";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { pathToFileURL } from "url";
|
||||||
|
|
||||||
const MIME_TYPES = {
|
const MIME_TYPES = {
|
||||||
".css": "text/css",
|
".css": "text/css",
|
||||||
|
@ -42,7 +43,8 @@ const DEFAULT_MIME_TYPE = "application/octet-stream";
|
||||||
|
|
||||||
class WebServer {
|
class WebServer {
|
||||||
constructor({ root, host, port, cacheExpirationTime }) {
|
constructor({ root, host, port, cacheExpirationTime }) {
|
||||||
this.root = root || ".";
|
const cwdURL = pathToFileURL(process.cwd()) + "/";
|
||||||
|
this.rootURL = new URL(`${root || "."}/`, cwdURL);
|
||||||
this.host = host || "localhost";
|
this.host = host || "localhost";
|
||||||
this.port = port || 0;
|
this.port = port || 0;
|
||||||
this.server = null;
|
this.server = null;
|
||||||
|
@ -82,27 +84,10 @@ class WebServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async #handler(request, response) {
|
async #handler(request, response) {
|
||||||
// Validate and parse the request URL.
|
// URLs are normalized and automatically disallow directory traversal
|
||||||
const url = request.url.replaceAll("//", "/");
|
// attacks. For example, http://HOST:PORT/../../../../../../../etc/passwd
|
||||||
const urlParts = /([^?]*)((?:\?(.*))?)/.exec(url);
|
// is equivalent to http://HOST:PORT/etc/passwd.
|
||||||
let pathPart;
|
const url = new URL(`http://${this.host}:${this.port}${request.url}`);
|
||||||
try {
|
|
||||||
// Guard against directory traversal attacks such as
|
|
||||||
// `/../../../../../../../etc/passwd`, which let you make GET requests
|
|
||||||
// for files outside of `this.root`.
|
|
||||||
pathPart = path.normalize(decodeURI(urlParts[1]));
|
|
||||||
// `path.normalize` returns a path on the basis of the current platform.
|
|
||||||
// Windows paths cause issues in `checkRequest` and underlying methods.
|
|
||||||
// Converting to a Unix path avoids platform checks in said functions.
|
|
||||||
pathPart = pathPart.replaceAll("\\", "/");
|
|
||||||
} catch {
|
|
||||||
// If the URI cannot be decoded, a `URIError` is thrown. This happens for
|
|
||||||
// malformed URIs such as `http://localhost:8888/%s%s` and should be
|
|
||||||
// handled as a bad request.
|
|
||||||
response.writeHead(400);
|
|
||||||
response.end("Bad request", "utf8");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the request method and execute method hooks.
|
// Validate the request method and execute method hooks.
|
||||||
const methodHooks = this.hooks[request.method];
|
const methodHooks = this.hooks[request.method];
|
||||||
|
@ -111,24 +96,34 @@ class WebServer {
|
||||||
response.end("Unsupported request method", "utf8");
|
response.end("Unsupported request method", "utf8");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const handled = methodHooks.some(hook => hook(request, response));
|
const handled = methodHooks.some(hook => hook(url, request, response));
|
||||||
if (handled) {
|
if (handled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the request and serve the file/folder contents.
|
// Check the request and serve the file/folder contents.
|
||||||
if (pathPart === "/favicon.ico") {
|
if (url.pathname === "/favicon.ico") {
|
||||||
pathPart = "test/resources/favicon.ico";
|
url.pathname = "/test/resources/favicon.ico";
|
||||||
}
|
}
|
||||||
await this.#checkRequest(request, response, url, urlParts, pathPart);
|
await this.#checkRequest(request, response, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #checkRequest(request, response, url, urlParts, pathPart) {
|
async #checkRequest(request, response, url) {
|
||||||
|
const localURL = new URL(`.${url.pathname}`, this.rootURL);
|
||||||
|
|
||||||
// Check if the file/folder exists.
|
// Check if the file/folder exists.
|
||||||
let filePath;
|
|
||||||
try {
|
try {
|
||||||
filePath = await fsPromises.realpath(path.join(this.root, pathPart));
|
await fsPromises.realpath(localURL);
|
||||||
} catch {
|
} catch (e) {
|
||||||
|
if (e instanceof URIError) {
|
||||||
|
// If the URI cannot be decoded, a `URIError` is thrown. This happens
|
||||||
|
// for malformed URIs such as `http://localhost:8888/%s%s` and should be
|
||||||
|
// handled as a bad request.
|
||||||
|
response.writeHead(400);
|
||||||
|
response.end("Bad request", "utf8");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
response.writeHead(404);
|
response.writeHead(404);
|
||||||
response.end();
|
response.end();
|
||||||
if (this.verbose) {
|
if (this.verbose) {
|
||||||
|
@ -140,7 +135,7 @@ class WebServer {
|
||||||
// Get the properties of the file/folder.
|
// Get the properties of the file/folder.
|
||||||
let stats;
|
let stats;
|
||||||
try {
|
try {
|
||||||
stats = await fsPromises.stat(filePath);
|
stats = await fsPromises.stat(localURL);
|
||||||
} catch {
|
} catch {
|
||||||
response.writeHead(500);
|
response.writeHead(500);
|
||||||
response.end();
|
response.end();
|
||||||
|
@ -150,15 +145,14 @@ class WebServer {
|
||||||
const isDir = stats.isDirectory();
|
const isDir = stats.isDirectory();
|
||||||
|
|
||||||
// If a folder is requested, serve the directory listing.
|
// If a folder is requested, serve the directory listing.
|
||||||
if (isDir && !/\/$/.test(pathPart)) {
|
if (isDir && !/\/$/.test(url.pathname)) {
|
||||||
response.setHeader("Location", `${pathPart}/${urlParts[2]}`);
|
response.setHeader("Location", `${url.pathname}/${url.search}`);
|
||||||
response.writeHead(301);
|
response.writeHead(301);
|
||||||
response.end("Redirected", "utf8");
|
response.end("Redirected", "utf8");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isDir) {
|
if (isDir) {
|
||||||
const queryPart = urlParts[3];
|
await this.#serveDirectoryIndex(response, url, localURL);
|
||||||
await this.#serveDirectoryIndex(response, pathPart, queryPart, filePath);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +176,7 @@ class WebServer {
|
||||||
}
|
}
|
||||||
this.#serveFileRange(
|
this.#serveFileRange(
|
||||||
response,
|
response,
|
||||||
filePath,
|
localURL,
|
||||||
fileSize,
|
fileSize,
|
||||||
start,
|
start,
|
||||||
isNaN(end) ? fileSize : end + 1
|
isNaN(end) ? fileSize : end + 1
|
||||||
|
@ -194,19 +188,19 @@ class WebServer {
|
||||||
if (this.verbose) {
|
if (this.verbose) {
|
||||||
console.log(url);
|
console.log(url);
|
||||||
}
|
}
|
||||||
this.#serveFile(response, filePath, fileSize);
|
this.#serveFile(response, localURL, fileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #serveDirectoryIndex(response, pathPart, queryPart, directory) {
|
async #serveDirectoryIndex(response, url, localUrl) {
|
||||||
response.setHeader("Content-Type", "text/html");
|
response.setHeader("Content-Type", "text/html");
|
||||||
response.writeHead(200);
|
response.writeHead(200);
|
||||||
|
|
||||||
if (queryPart === "frame") {
|
if (url.searchParams.has("frame")) {
|
||||||
response.end(
|
response.end(
|
||||||
`<html>
|
`<html>
|
||||||
<frameset cols=*,200>
|
<frameset cols=*,200>
|
||||||
<frame name=pdf>
|
<frame name=pdf>
|
||||||
<frame src="${encodeURI(pathPart)}?side">
|
<frame src="${url.pathname}?side">
|
||||||
</frameset>
|
</frameset>
|
||||||
</html>`,
|
</html>`,
|
||||||
"utf8"
|
"utf8"
|
||||||
|
@ -216,7 +210,7 @@ class WebServer {
|
||||||
|
|
||||||
let files;
|
let files;
|
||||||
try {
|
try {
|
||||||
files = await fsPromises.readdir(directory);
|
files = await fsPromises.readdir(localUrl);
|
||||||
} catch {
|
} catch {
|
||||||
response.end();
|
response.end();
|
||||||
return;
|
return;
|
||||||
|
@ -228,13 +222,13 @@ class WebServer {
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Index of ${pathPart}</h1>`
|
<h1>Index of ${url.pathname}</h1>`
|
||||||
);
|
);
|
||||||
if (pathPart !== "/") {
|
if (url.pathname !== "/") {
|
||||||
response.write('<a href="..">..</a><br>');
|
response.write('<a href="..">..</a><br>');
|
||||||
}
|
}
|
||||||
|
|
||||||
const all = queryPart === "all";
|
const all = url.searchParams.has("all");
|
||||||
const escapeHTML = untrusted =>
|
const escapeHTML = untrusted =>
|
||||||
// Escape untrusted input so that it can safely be used in a HTML response
|
// Escape untrusted input so that it can safely be used in a HTML response
|
||||||
// in HTML and in HTML attributes.
|
// in HTML and in HTML attributes.
|
||||||
|
@ -247,13 +241,13 @@ class WebServer {
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
let stat;
|
let stat;
|
||||||
const item = pathPart + file;
|
const item = url.pathname + file;
|
||||||
let href = "";
|
let href = "";
|
||||||
let label = "";
|
let label = "";
|
||||||
let extraAttributes = "";
|
let extraAttributes = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stat = fs.statSync(path.join(directory, file));
|
stat = fs.statSync(new URL(file, localUrl));
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
href = encodeURI(item);
|
href = encodeURI(item);
|
||||||
label = `${file} (${ex})`;
|
label = `${file} (${ex})`;
|
||||||
|
@ -284,7 +278,7 @@ class WebServer {
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
response.write("<p>No files found</p>");
|
response.write("<p>No files found</p>");
|
||||||
}
|
}
|
||||||
if (!all && queryPart !== "side") {
|
if (!all && !url.searchParams.has("side")) {
|
||||||
response.write(
|
response.write(
|
||||||
'<hr><p>(only PDF files are shown, <a href="?all">show all</a>)</p>'
|
'<hr><p>(only PDF files are shown, <a href="?all">show all</a>)</p>'
|
||||||
);
|
);
|
||||||
|
@ -292,8 +286,8 @@ class WebServer {
|
||||||
response.end("</body></html>");
|
response.end("</body></html>");
|
||||||
}
|
}
|
||||||
|
|
||||||
#serveFile(response, filePath, fileSize) {
|
#serveFile(response, fileURL, fileSize) {
|
||||||
const stream = fs.createReadStream(filePath, { flags: "rs" });
|
const stream = fs.createReadStream(fileURL, { flags: "rs" });
|
||||||
stream.on("error", error => {
|
stream.on("error", error => {
|
||||||
response.writeHead(500);
|
response.writeHead(500);
|
||||||
response.end();
|
response.end();
|
||||||
|
@ -302,7 +296,7 @@ class WebServer {
|
||||||
if (!this.disableRangeRequests) {
|
if (!this.disableRangeRequests) {
|
||||||
response.setHeader("Accept-Ranges", "bytes");
|
response.setHeader("Accept-Ranges", "bytes");
|
||||||
}
|
}
|
||||||
response.setHeader("Content-Type", this.#getContentType(filePath));
|
response.setHeader("Content-Type", this.#getContentType(fileURL));
|
||||||
response.setHeader("Content-Length", fileSize);
|
response.setHeader("Content-Length", fileSize);
|
||||||
if (this.cacheExpirationTime > 0) {
|
if (this.cacheExpirationTime > 0) {
|
||||||
const expireTime = new Date();
|
const expireTime = new Date();
|
||||||
|
@ -313,8 +307,8 @@ class WebServer {
|
||||||
stream.pipe(response);
|
stream.pipe(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#serveFileRange(response, filePath, fileSize, start, end) {
|
#serveFileRange(response, fileURL, fileSize, start, end) {
|
||||||
const stream = fs.createReadStream(filePath, {
|
const stream = fs.createReadStream(fileURL, {
|
||||||
flags: "rs",
|
flags: "rs",
|
||||||
start,
|
start,
|
||||||
end: end - 1,
|
end: end - 1,
|
||||||
|
@ -325,7 +319,7 @@ class WebServer {
|
||||||
});
|
});
|
||||||
|
|
||||||
response.setHeader("Accept-Ranges", "bytes");
|
response.setHeader("Accept-Ranges", "bytes");
|
||||||
response.setHeader("Content-Type", this.#getContentType(filePath));
|
response.setHeader("Content-Type", this.#getContentType(fileURL));
|
||||||
response.setHeader("Content-Length", end - start);
|
response.setHeader("Content-Length", end - start);
|
||||||
response.setHeader(
|
response.setHeader(
|
||||||
"Content-Range",
|
"Content-Range",
|
||||||
|
@ -335,8 +329,8 @@ class WebServer {
|
||||||
stream.pipe(response);
|
stream.pipe(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#getContentType(filePath) {
|
#getContentType(fileURL) {
|
||||||
const extension = path.extname(filePath).toLowerCase();
|
const extension = path.extname(fileURL.pathname).toLowerCase();
|
||||||
return MIME_TYPES[extension] || DEFAULT_MIME_TYPE;
|
return MIME_TYPES[extension] || DEFAULT_MIME_TYPE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,13 +339,14 @@ class WebServer {
|
||||||
// It is here instead of test.js so that when the test will still complete as
|
// It is here instead of test.js so that when the test will still complete as
|
||||||
// expected if the user does "gulp server" and then visits
|
// expected if the user does "gulp server" and then visits
|
||||||
// http://localhost:8888/test/unit/unit_test.html?spec=Cross-origin
|
// http://localhost:8888/test/unit/unit_test.html?spec=Cross-origin
|
||||||
function crossOriginHandler(request, response) {
|
function crossOriginHandler(url, request, response) {
|
||||||
if (request.url === "/test/pdfs/basicapi.pdf?cors=withCredentials") {
|
if (url.pathname === "/test/pdfs/basicapi.pdf") {
|
||||||
response.setHeader("Access-Control-Allow-Origin", request.headers.origin);
|
if (url.searchParams.get("cors") === "withCredentials") {
|
||||||
response.setHeader("Access-Control-Allow-Credentials", "true");
|
response.setHeader("Access-Control-Allow-Origin", request.headers.origin);
|
||||||
}
|
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||||
if (request.url === "/test/pdfs/basicapi.pdf?cors=withoutCredentials") {
|
} else if (url.searchParams.get("cors") === "withoutCredentials") {
|
||||||
response.setHeader("Access-Control-Allow-Origin", request.headers.origin);
|
response.setHeader("Access-Control-Allow-Origin", request.headers.origin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue