UNPKG

r2-streamer-js

Version:

Readium 2 'streamer' for NodeJS (TypeScript)

351 lines 17.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.serverAssets = serverAssets; var tslib_1 = require("tslib"); var debug_ = require("debug"); var express = require("express"); var mime = require("mime-types"); var path = require("path"); var zipHasEntry_1 = require("r2-shared-js/dist/es5/src/_utils/zipHasEntry"); var transformer_1 = require("r2-shared-js/dist/es5/src/transform/transformer"); var RangeUtils_1 = require("r2-utils-js/dist/es5/src/_utils/http/RangeUtils"); var BufferUtils_1 = require("r2-utils-js/dist/es5/src/_utils/stream/BufferUtils"); var url_signed_expiry_1 = require("./url-signed-expiry"); var request_ext_1 = require("./request-ext"); var debug = debug_("r2:streamer#http/server-assets"); function serverAssets(server, routerPathBase64) { var _this = this; var routerAssets = express.Router({ strict: false }); routerAssets.get("/", function (req, res) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var reqparams, isShow, isHead, pathBase64Str, publication, err_1, zipInternal, err, zip, pathInZip, err, isDivina, link, findLinkRecursive, relativePath, _i, _a, l, _b, _c, l, _d, _e, l, err, ok, err, mediaType, isText, isEncrypted, isPartialByteRangeRequest, partialByteBegin, partialByteEnd, ranges, err, zipStream_, _f, err_2, doTransform, sessionInfo, fullUrl, transformedStream, err_3, err, zipData, err_4, partialByteLength, rangeHeader; return tslib_1.__generator(this, function (_g) { switch (_g.label) { case 0: reqparams = req.params; if (!reqparams.pathBase64) { reqparams.pathBase64 = req.pathBase64; } if (!reqparams.asset) { reqparams.asset = req.asset; } if (reqparams.asset && typeof reqparams.asset !== "string") { if (Array.isArray(reqparams.asset)) { reqparams.asset = reqparams.asset.join("/"); } } if (!reqparams.lcpPass64) { reqparams.lcpPass64 = req.lcpPass64; } isShow = req.query.show; isHead = req.method.toLowerCase() === "head"; if (isHead) { debug("HEAD !!!!!!!!!!!!!!!!!!!"); } pathBase64Str = Buffer.from(reqparams.pathBase64, "base64").toString("utf8"); _g.label = 1; case 1: _g.trys.push([1, 3, , 4]); return [4, server.loadOrGetCachedPublication(pathBase64Str)]; case 2: publication = _g.sent(); return [3, 4]; case 3: err_1 = _g.sent(); debug(err_1); res.status(500).send("<html><body><p>Internal Server Error</p><p>" + err_1 + "</p></body></html>"); return [2]; case 4: zipInternal = publication.findFromInternal("zip"); if (!zipInternal) { err = "No publication zip!"; debug(err); res.status(500).send("<html><body><p>Internal Server Error</p><p>" + err + "</p></body></html>"); return [2]; } zip = zipInternal.Value; pathInZip = reqparams.asset; if (!(0, zipHasEntry_1.zipHasEntry)(zip, pathInZip, undefined)) { err = "Asset not in zip! " + pathInZip; debug(err); res.status(500).send("<html><body><p>Internal Server Error</p><p>" + err + "</p></body></html>"); return [2]; } isDivina = publication.Metadata && publication.Metadata.RDFType && (/https?:\/\/schema\.org\/ComicStory$/.test(publication.Metadata.RDFType) || /https?:\/\/schema\.org\/VisualNarrative$/.test(publication.Metadata.RDFType)); findLinkRecursive = function (relativePath, l) { if (l.Href === relativePath) { return l; } var found; if (l.Children) { for (var _i = 0, _a = l.Children; _i < _a.length; _i++) { var child = _a[_i]; found = findLinkRecursive(relativePath, child); if (found) { return found; } } } if (l.Alternate) { for (var _b = 0, _c = l.Alternate; _b < _c.length; _b++) { var alt = _c[_b]; found = findLinkRecursive(relativePath, alt); if (found) { return found; } } } return undefined; }; if ((publication.Resources || publication.Spine || publication.Links) && pathInZip.indexOf("META-INF/") !== 0 && !/\.opf$/i.test(pathInZip)) { relativePath = pathInZip; if (publication.Resources) { for (_i = 0, _a = publication.Resources; _i < _a.length; _i++) { l = _a[_i]; link = findLinkRecursive(relativePath, l); if (link) { break; } } } if (!link) { if (publication.Spine) { for (_b = 0, _c = publication.Spine; _b < _c.length; _b++) { l = _c[_b]; link = findLinkRecursive(relativePath, l); if (link) { break; } } } } if (!link) { if (publication.Links) { for (_d = 0, _e = publication.Links; _d < _e.length; _d++) { l = _e[_d]; link = findLinkRecursive(relativePath, l); if (link) { break; } } } } if (!link && !isDivina) { err = "Asset not declared in publication spine/resources! " + relativePath; debug(err); res.status(500).send("<html><body><p>Internal Server Error</p><p>" + err + "</p></body></html>"); return [2]; } } if (server.isSecured() && !link && (pathInZip.indexOf("META-INF/") === 0 || /\.opf$/i.test(pathInZip))) { res.status(200).send("<html><body></body></html>"); return [2]; } if (server.enableSignedExpiry) { ok = (0, url_signed_expiry_1.verifyExpiringResourceURL)(req.query[url_signed_expiry_1.URL_SIGNED_EXPIRY_QUERY_PARAM_NAME], pathBase64Str, pathInZip); if (!ok) { err = "Asset expired?! " + pathInZip; debug(err); res.status(401).send("<html><body><p>Internal Server Error</p><p>" + err + "</p></body></html>"); return [2]; } } mediaType = mime.lookup(pathInZip); if (link && link.TypeLink) { mediaType = link.TypeLink; } isText = (typeof mediaType === "string") && (mediaType.indexOf("text/") === 0 || mediaType.indexOf("application/xhtml") === 0 || mediaType.indexOf("application/xml") === 0 || mediaType.indexOf("application/json") === 0 || mediaType.indexOf("application/svg") === 0 || mediaType.indexOf("application/smil") === 0 || mediaType.indexOf("+json") > 0 || mediaType.indexOf("+smil") > 0 || mediaType.indexOf("+svg") > 0 || mediaType.indexOf("+xhtml") > 0 || mediaType.indexOf("+xml") > 0); isEncrypted = link && link.Properties && link.Properties.Encrypted; isPartialByteRangeRequest = ((req.headers && req.headers.range) ? true : false); partialByteBegin = 0; partialByteEnd = -1; if (isPartialByteRangeRequest) { debug(req.headers.range); ranges = (0, RangeUtils_1.parseRangeHeader)(req.headers.range); if (ranges && ranges.length) { if (ranges.length > 1) { err = "Too many HTTP ranges: " + req.headers.range; debug(err); res.status(416).send("<html><body><p>Internal Server Error</p><p>" + err + "</p></body></html>"); return [2]; } partialByteBegin = ranges[0].begin; partialByteEnd = ranges[0].end; if (partialByteBegin < 0) { partialByteBegin = 0; } } debug("".concat(pathInZip, " >> ").concat(partialByteBegin, "-").concat(partialByteEnd)); } _g.label = 5; case 5: _g.trys.push([5, 10, , 11]); if (!(isPartialByteRangeRequest && !isEncrypted)) return [3, 7]; return [4, zip.entryStreamRangePromise(pathInZip, partialByteBegin, partialByteEnd)]; case 6: _f = _g.sent(); return [3, 9]; case 7: return [4, zip.entryStreamPromise(pathInZip)]; case 8: _f = _g.sent(); _g.label = 9; case 9: zipStream_ = _f; return [3, 11]; case 10: err_2 = _g.sent(); debug(err_2); res.status(500).send("<html><body><p>Internal Server Error</p><p>" + err_2 + "</p></body></html>"); return [2]; case 11: doTransform = true; sessionInfo = req.query[request_ext_1.URL_PARAM_SESSION_INFO]; if (!(doTransform && link)) return [3, 16]; fullUrl = "".concat(server.serverUrl()).concat(req.originalUrl); transformedStream = void 0; _g.label = 12; case 12: _g.trys.push([12, 14, , 15]); return [4, transformer_1.Transformers.tryStream(publication, link, fullUrl, zipStream_, isPartialByteRangeRequest, partialByteBegin, partialByteEnd, sessionInfo)]; case 13: transformedStream = _g.sent(); return [3, 15]; case 14: err_3 = _g.sent(); debug(err_3); res.status(500).send("<html><body><p>Internal Server Error</p><p>" + err_3 + "</p></body></html>"); return [2]; case 15: if (transformedStream) { if (transformedStream !== zipStream_) { debug("Asset transformed ok: " + link.Href); } zipStream_ = transformedStream; } else { err = "Transform fail (encryption scheme not supported?)"; debug(err); res.status(500).send("<html><body><p>Internal Server Error</p><p>" + err + "</p></body></html>"); return [2]; } _g.label = 16; case 16: if (!isShow) return [3, 21]; zipData = void 0; _g.label = 17; case 17: _g.trys.push([17, 19, , 20]); return [4, (0, BufferUtils_1.streamToBufferPromise)(zipStream_.stream)]; case 18: zipData = _g.sent(); return [3, 20]; case 19: err_4 = _g.sent(); debug(err_4); res.status(500).send("<html><body><p>Internal Server Error</p><p>" + err_4 + "</p></body></html>"); return [2]; case 20: if (zipData) { debug("CHECK: " + zipStream_.length + " ==> " + zipData.length); } res.status(200).send("<html><body>" + "<h1>" + path.basename(pathBase64Str) + "</h1>" + "<h2>" + mediaType + "</h2>" + ((isText && zipData) ? ("<p><pre>" + zipData.toString("utf8").replace(/&/g, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;") .replace(/"/g, "&quot;") .replace(/'/g, "&apos;") + "</pre></p>") : "<p>BINARY</p>") + "</body></html>"); return [2]; case 21: server.setResponseCORS(res); if (isPartialByteRangeRequest || isEncrypted) { server.setResponseCacheHeaders(res, false); } else { server.setResponseCacheHeaders(res, true); } if (mediaType) { res.set("Content-Type", mediaType); } res.setHeader("Accept-Ranges", "bytes"); if (isPartialByteRangeRequest) { if (partialByteEnd < 0) { partialByteEnd = zipStream_.length - 1; } partialByteLength = isPartialByteRangeRequest ? partialByteEnd - partialByteBegin + 1 : zipStream_.length; res.setHeader("Content-Length", "".concat(partialByteLength)); rangeHeader = "bytes ".concat(partialByteBegin, "-").concat(partialByteEnd, "/").concat(zipStream_.length); res.setHeader("Content-Range", rangeHeader); res.status(206); } else { res.setHeader("Content-Length", "".concat(zipStream_.length)); res.status(200); } if (isHead) { res.end(); } else { zipStream_.stream .on("error", function f() { debug("ZIP ERROR " + pathInZip); }) .pipe(res) .on("error", function f() { debug("RES ERROR " + pathInZip); }) .on("close", function f() { res.end(); }); } return [2]; } }); }); }); routerPathBase64.param("asset", function (req, _res, next, value, _name) { if (typeof value !== "string") { if (Array.isArray(value)) { value = value.join("/"); } } if (value) { value = value.replace(/\/\/+/g, "/"); } req.asset = value; next(); }); routerPathBase64.use("/:" + request_ext_1._pathBase64 + "/*" + request_ext_1._asset, routerAssets); } //# sourceMappingURL=server-assets.js.map