2019-03-11 13:41:55 +05:30
|
|
|
import { Request, Response } from "express";
|
|
|
|
import { parseRangeHeader } from "./parseRangeHeader";
|
|
|
|
import { RangeParserError } from "./RangeParserError";
|
|
|
|
import { Logger } from "./Logger";
|
|
|
|
import { ContentProvider } from "./ContentProvider";
|
|
|
|
import { ContentDoesNotExistError } from "./ContentDoesNotExistError";
|
|
|
|
import {
|
|
|
|
getRangeHeader,
|
|
|
|
setContentRangeHeader,
|
|
|
|
setContentTypeHeader,
|
|
|
|
setContentDispositionHeader,
|
|
|
|
setAcceptRangesHeader,
|
|
|
|
setContentLengthHeader,
|
|
|
|
setCacheControlHeaderNoCache
|
|
|
|
} from "./utils";
|
2019-03-12 10:53:04 +05:30
|
|
|
export function createPartialContentHandler(contentProvider: ContentProvider, logger: Logger) {
|
2019-03-11 13:41:55 +05:30
|
|
|
return async function handler(req: Request, res: Response) {
|
|
|
|
let content;
|
|
|
|
try {
|
|
|
|
content = await contentProvider(req);
|
|
|
|
} catch (error) {
|
2019-03-19 16:26:50 +05:30
|
|
|
logger.debug("createPartialContentHandler: ContentProvider threw exception: ", error);
|
2019-03-11 13:41:55 +05:30
|
|
|
if (error instanceof ContentDoesNotExistError) {
|
2019-03-12 18:28:57 +05:30
|
|
|
return res.status(404).send(error.message);
|
2019-03-11 13:41:55 +05:30
|
|
|
}
|
|
|
|
return res.sendStatus(500);
|
|
|
|
}
|
|
|
|
let { getStream, mimeType, fileName, totalSize } = content;
|
|
|
|
const rangeHeader = getRangeHeader(req);
|
|
|
|
let range;
|
|
|
|
try {
|
|
|
|
range = parseRangeHeader(rangeHeader, totalSize, logger);
|
|
|
|
} catch (error) {
|
2019-03-19 16:26:50 +05:30
|
|
|
logger.debug(`createPartialContentHandler: parseRangeHeader error: `, error);
|
2019-03-11 13:41:55 +05:30
|
|
|
if (error instanceof RangeParserError) {
|
|
|
|
setContentRangeHeader(null, totalSize, res);
|
|
|
|
return res.status(416).send(`Invalid value for Range: ${rangeHeader}`);
|
|
|
|
}
|
|
|
|
return res.sendStatus(500);
|
|
|
|
}
|
|
|
|
setContentTypeHeader(mimeType, res);
|
|
|
|
setContentDispositionHeader(fileName, res);
|
|
|
|
setAcceptRangesHeader(res);
|
|
|
|
// If range is not specified, or the file is empty, return the full stream
|
|
|
|
if (range === null) {
|
2019-03-19 16:26:50 +05:30
|
|
|
logger.debug("createPartialContentHandler: No range found, returning full content.");
|
2019-03-11 13:41:55 +05:30
|
|
|
setContentLengthHeader(totalSize, res);
|
|
|
|
return getStream().pipe(res);
|
|
|
|
}
|
|
|
|
setContentRangeHeader(range, totalSize, res);
|
|
|
|
let { start, end } = range;
|
|
|
|
setContentLengthHeader(start === end ? 0 : end - start + 1, res);
|
|
|
|
setCacheControlHeaderNoCache(res);
|
|
|
|
// Return 206 Partial Content status
|
2019-03-19 16:26:50 +05:30
|
|
|
logger.debug("createPartialContentHandler: Returning partial content for range: ", JSON.stringify(range));
|
2019-03-11 13:41:55 +05:30
|
|
|
res.status(206);
|
2019-03-19 15:05:08 +05:30
|
|
|
|
|
|
|
return getStream(range).pipe(res);
|
2019-03-11 13:41:55 +05:30
|
|
|
};
|
|
|
|
}
|