UNPKG

12 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.serverAssets = void 0;
4const tslib_1 = require("tslib");
5const debug_ = require("debug");
6const express = require("express");
7const mime = require("mime-types");
8const path = require("path");
9const zipHasEntry_1 = require("r2-shared-js/dist/es7-es2016/src/_utils/zipHasEntry");
10const transformer_1 = require("r2-shared-js/dist/es7-es2016/src/transform/transformer");
11const RangeUtils_1 = require("r2-utils-js/dist/es7-es2016/src/_utils/http/RangeUtils");
12const BufferUtils_1 = require("r2-utils-js/dist/es7-es2016/src/_utils/stream/BufferUtils");
13const request_ext_1 = require("./request-ext");
14const debug = debug_("r2:streamer#http/server-assets");
15function serverAssets(server, routerPathBase64) {
16 const routerAssets = express.Router({ strict: false });
17 routerAssets.get("/", (req, res) => tslib_1.__awaiter(this, void 0, void 0, function* () {
18 const reqparams = req.params;
19 if (!reqparams.pathBase64) {
20 reqparams.pathBase64 = req.pathBase64;
21 }
22 if (!reqparams.asset) {
23 reqparams.asset = req.asset;
24 }
25 if (!reqparams.lcpPass64) {
26 reqparams.lcpPass64 = req.lcpPass64;
27 }
28 const isShow = req.query.show;
29 const isHead = req.method.toLowerCase() === "head";
30 if (isHead) {
31 debug("HEAD !!!!!!!!!!!!!!!!!!!");
32 }
33 const pathBase64Str = Buffer.from(reqparams.pathBase64, "base64").toString("utf8");
34 let publication;
35 try {
36 publication = yield server.loadOrGetCachedPublication(pathBase64Str);
37 }
38 catch (err) {
39 debug(err);
40 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
41 + err + "</p></body></html>");
42 return;
43 }
44 const zipInternal = publication.findFromInternal("zip");
45 if (!zipInternal) {
46 const err = "No publication zip!";
47 debug(err);
48 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
49 + err + "</p></body></html>");
50 return;
51 }
52 const zip = zipInternal.Value;
53 const pathInZip = reqparams.asset;
54 if (!zipHasEntry_1.zipHasEntry(zip, pathInZip, undefined)) {
55 const err = "Asset not in zip! " + pathInZip;
56 debug(err);
57 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
58 + err + "</p></body></html>");
59 return;
60 }
61 const isDivina = publication.Metadata && publication.Metadata.RDFType &&
62 (/http[s]?:\/\/schema\.org\/ComicStory$/.test(publication.Metadata.RDFType) ||
63 /http[s]?:\/\/schema\.org\/VisualNarrative$/.test(publication.Metadata.RDFType));
64 let link;
65 const findLinkRecursive = (relativePath, l) => {
66 if (l.Href === relativePath) {
67 return l;
68 }
69 let found;
70 if (l.Children) {
71 for (const child of l.Children) {
72 found = findLinkRecursive(relativePath, child);
73 if (found) {
74 return found;
75 }
76 }
77 }
78 if (l.Alternate) {
79 for (const alt of l.Alternate) {
80 found = findLinkRecursive(relativePath, alt);
81 if (found) {
82 return found;
83 }
84 }
85 }
86 return undefined;
87 };
88 if ((publication.Resources || publication.Spine || publication.Links)
89 && pathInZip.indexOf("META-INF/") !== 0
90 && !pathInZip.endsWith(".opf")) {
91 const relativePath = pathInZip;
92 if (publication.Resources) {
93 for (const l of publication.Resources) {
94 link = findLinkRecursive(relativePath, l);
95 if (link) {
96 break;
97 }
98 }
99 }
100 if (!link) {
101 if (publication.Spine) {
102 for (const l of publication.Spine) {
103 link = findLinkRecursive(relativePath, l);
104 if (link) {
105 break;
106 }
107 }
108 }
109 }
110 if (!link) {
111 if (publication.Links) {
112 for (const l of publication.Links) {
113 link = findLinkRecursive(relativePath, l);
114 if (link) {
115 break;
116 }
117 }
118 }
119 }
120 if (!link &&
121 !isDivina) {
122 const err = "Asset not declared in publication spine/resources!" + relativePath;
123 debug(err);
124 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
125 + err + "</p></body></html>");
126 return;
127 }
128 }
129 if (server.isSecured() && !link &&
130 (pathInZip.indexOf("META-INF/") === 0 || pathInZip.endsWith(".opf"))) {
131 res.status(200).send("<html><body></body></html>");
132 return;
133 }
134 let mediaType = mime.lookup(pathInZip);
135 if (link && link.TypeLink) {
136 mediaType = link.TypeLink;
137 }
138 const isText = (typeof mediaType === "string") && (mediaType.indexOf("text/") === 0 ||
139 mediaType.indexOf("application/xhtml") === 0 ||
140 mediaType.indexOf("application/xml") === 0 ||
141 mediaType.indexOf("application/json") === 0 ||
142 mediaType.indexOf("application/svg") === 0 ||
143 mediaType.indexOf("application/smil") === 0 ||
144 mediaType.indexOf("+json") > 0 ||
145 mediaType.indexOf("+smil") > 0 ||
146 mediaType.indexOf("+svg") > 0 ||
147 mediaType.indexOf("+xhtml") > 0 ||
148 mediaType.indexOf("+xml") > 0);
149 const isEncrypted = link && link.Properties && link.Properties.Encrypted;
150 const isPartialByteRangeRequest = ((req.headers && req.headers.range) ? true : false);
151 let partialByteBegin = 0;
152 let partialByteEnd = -1;
153 if (isPartialByteRangeRequest) {
154 debug(req.headers.range);
155 const ranges = RangeUtils_1.parseRangeHeader(req.headers.range);
156 if (ranges && ranges.length) {
157 if (ranges.length > 1) {
158 const err = "Too many HTTP ranges: " + req.headers.range;
159 debug(err);
160 res.status(416).send("<html><body><p>Internal Server Error</p><p>"
161 + err + "</p></body></html>");
162 return;
163 }
164 partialByteBegin = ranges[0].begin;
165 partialByteEnd = ranges[0].end;
166 if (partialByteBegin < 0) {
167 partialByteBegin = 0;
168 }
169 }
170 debug(`${pathInZip} >> ${partialByteBegin}-${partialByteEnd}`);
171 }
172 let zipStream_;
173 try {
174 zipStream_ = isPartialByteRangeRequest && !isEncrypted ?
175 yield zip.entryStreamRangePromise(pathInZip, partialByteBegin, partialByteEnd) :
176 yield zip.entryStreamPromise(pathInZip);
177 }
178 catch (err) {
179 debug(err);
180 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
181 + err + "</p></body></html>");
182 return;
183 }
184 const doTransform = true;
185 const sessionInfo = req.query[request_ext_1.URL_PARAM_SESSION_INFO];
186 if (doTransform && link) {
187 const fullUrl = `${server.serverUrl()}${req.originalUrl}`;
188 let transformedStream;
189 try {
190 transformedStream = yield transformer_1.Transformers.tryStream(publication, link, fullUrl, zipStream_, isPartialByteRangeRequest, partialByteBegin, partialByteEnd, sessionInfo);
191 }
192 catch (err) {
193 debug(err);
194 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
195 + err + "</p></body></html>");
196 return;
197 }
198 if (transformedStream) {
199 if (transformedStream !== zipStream_) {
200 debug("Asset transformed ok: " + link.Href);
201 }
202 zipStream_ = transformedStream;
203 }
204 else {
205 const err = "Transform fail (encryption scheme not supported?)";
206 debug(err);
207 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
208 + err + "</p></body></html>");
209 return;
210 }
211 }
212 if (isShow) {
213 let zipData;
214 try {
215 zipData = yield BufferUtils_1.streamToBufferPromise(zipStream_.stream);
216 }
217 catch (err) {
218 debug(err);
219 res.status(500).send("<html><body><p>Internal Server Error</p><p>"
220 + err + "</p></body></html>");
221 return;
222 }
223 if (zipData) {
224 debug("CHECK: " + zipStream_.length + " ==> " + zipData.length);
225 }
226 res.status(200).send("<html><body>" +
227 "<h1>" + path.basename(pathBase64Str) + "</h1>" +
228 "<h2>" + mediaType + "</h2>" +
229 ((isText && zipData) ?
230 ("<p><pre>" +
231 zipData.toString("utf8").replace(/&/g, "&amp;")
232 .replace(/</g, "&lt;")
233 .replace(/>/g, "&gt;")
234 .replace(/"/g, "&quot;")
235 .replace(/'/g, "&apos;") +
236 "</pre></p>")
237 : "<p>BINARY</p>") + "</body></html>");
238 return;
239 }
240 server.setResponseCORS(res);
241 if (isPartialByteRangeRequest || isEncrypted) {
242 server.setResponseCacheHeaders(res, false);
243 }
244 else {
245 server.setResponseCacheHeaders(res, true);
246 }
247 if (mediaType) {
248 res.set("Content-Type", mediaType);
249 }
250 res.setHeader("Accept-Ranges", "bytes");
251 if (isPartialByteRangeRequest) {
252 if (partialByteEnd < 0) {
253 partialByteEnd = zipStream_.length - 1;
254 }
255 const partialByteLength = isPartialByteRangeRequest ?
256 partialByteEnd - partialByteBegin + 1 :
257 zipStream_.length;
258 res.setHeader("Content-Length", `${partialByteLength}`);
259 const rangeHeader = `bytes ${partialByteBegin}-${partialByteEnd}/${zipStream_.length}`;
260 res.setHeader("Content-Range", rangeHeader);
261 res.status(206);
262 }
263 else {
264 res.setHeader("Content-Length", `${zipStream_.length}`);
265 res.status(200);
266 }
267 if (isHead) {
268 res.end();
269 }
270 else {
271 zipStream_.stream
272 .on("error", function f() {
273 debug("ZIP ERROR " + pathInZip);
274 })
275 .pipe(res)
276 .on("error", function f() {
277 debug("RES ERROR " + pathInZip);
278 })
279 .on("close", function f() {
280 res.end();
281 });
282 }
283 }));
284 routerPathBase64.param("asset", (req, _res, next, value, _name) => {
285 if (value) {
286 value = value.replace(/\/\/+/g, "/");
287 }
288 req.asset = value;
289 next();
290 });
291 routerPathBase64.use("/:" + request_ext_1._pathBase64 + "/:" + request_ext_1._asset + "(*)", routerAssets);
292}
293exports.serverAssets = serverAssets;
294//# sourceMappingURL=server-assets.js.map
\No newline at end of file