node_stream_spec.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /* Copyright 2017 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. /* globals __non_webpack_require__ */
  16. import { AbortException } from "../../src/shared/util.js";
  17. import { isNodeJS } from "../../src/shared/is_node.js";
  18. import { PDFNodeStream } from "../../src/display/node_stream.js";
  19. // Ensure that these tests only run in Node.js environments.
  20. if (!isNodeJS) {
  21. throw new Error(
  22. 'The "node_stream" unit-tests can only be run in Node.js environments.'
  23. );
  24. }
  25. const path = __non_webpack_require__("path");
  26. const url = __non_webpack_require__("url");
  27. const http = __non_webpack_require__("http");
  28. const fs = __non_webpack_require__("fs");
  29. describe("node_stream", function () {
  30. let server = null;
  31. let port = null;
  32. const pdf = url.parse(
  33. encodeURI(
  34. "file://" + path.join(process.cwd(), "./test/pdfs/tracemonkey.pdf")
  35. )
  36. ).href;
  37. const pdfLength = 1016315;
  38. beforeAll(function () {
  39. // Create http server to serve pdf data for tests.
  40. server = http
  41. .createServer((request, response) => {
  42. const filePath = process.cwd() + "/test/pdfs" + request.url;
  43. fs.lstat(filePath, (error, stat) => {
  44. if (error) {
  45. response.writeHead(404);
  46. response.end(`File ${request.url} not found!`);
  47. return;
  48. }
  49. if (!request.headers.range) {
  50. const contentLength = stat.size;
  51. const stream = fs.createReadStream(filePath);
  52. response.writeHead(200, {
  53. "Content-Type": "application/pdf",
  54. "Content-Length": contentLength,
  55. "Accept-Ranges": "bytes",
  56. });
  57. stream.pipe(response);
  58. } else {
  59. const [start, end] = request.headers.range
  60. .split("=")[1]
  61. .split("-")
  62. .map(x => {
  63. return Number(x);
  64. });
  65. const stream = fs.createReadStream(filePath, { start, end });
  66. response.writeHead(206, {
  67. "Content-Type": "application/pdf",
  68. });
  69. stream.pipe(response);
  70. }
  71. });
  72. })
  73. .listen(0); /* Listen on a random free port */
  74. port = server.address().port;
  75. });
  76. afterAll(function () {
  77. // Close the server from accepting new connections after all test finishes.
  78. server.close();
  79. });
  80. it("read both http(s) and filesystem pdf files", async function () {
  81. const stream1 = new PDFNodeStream({
  82. url: `http://127.0.0.1:${port}/tracemonkey.pdf`,
  83. rangeChunkSize: 65536,
  84. disableStream: true,
  85. disableRange: true,
  86. });
  87. const stream2 = new PDFNodeStream({
  88. url: pdf,
  89. rangeChunkSize: 65536,
  90. disableStream: true,
  91. disableRange: true,
  92. });
  93. const fullReader1 = stream1.getFullReader();
  94. const fullReader2 = stream2.getFullReader();
  95. let isStreamingSupported1, isRangeSupported1;
  96. const promise1 = fullReader1.headersReady.then(() => {
  97. isStreamingSupported1 = fullReader1.isStreamingSupported;
  98. isRangeSupported1 = fullReader1.isRangeSupported;
  99. });
  100. let isStreamingSupported2, isRangeSupported2;
  101. const promise2 = fullReader2.headersReady.then(() => {
  102. isStreamingSupported2 = fullReader2.isStreamingSupported;
  103. isRangeSupported2 = fullReader2.isRangeSupported;
  104. });
  105. let len1 = 0,
  106. len2 = 0;
  107. const read1 = function () {
  108. return fullReader1.read().then(function (result) {
  109. if (result.done) {
  110. return undefined;
  111. }
  112. len1 += result.value.byteLength;
  113. return read1();
  114. });
  115. };
  116. const read2 = function () {
  117. return fullReader2.read().then(function (result) {
  118. if (result.done) {
  119. return undefined;
  120. }
  121. len2 += result.value.byteLength;
  122. return read2();
  123. });
  124. };
  125. await Promise.all([read1(), read2(), promise1, promise2]);
  126. expect(isStreamingSupported1).toEqual(false);
  127. expect(isRangeSupported1).toEqual(false);
  128. expect(isStreamingSupported2).toEqual(false);
  129. expect(isRangeSupported2).toEqual(false);
  130. expect(len1).toEqual(pdfLength);
  131. expect(len1).toEqual(len2);
  132. });
  133. it("read custom ranges for both http(s) and filesystem urls", async function () {
  134. const rangeSize = 32768;
  135. const stream1 = new PDFNodeStream({
  136. url: `http://127.0.0.1:${port}/tracemonkey.pdf`,
  137. length: pdfLength,
  138. rangeChunkSize: rangeSize,
  139. disableStream: true,
  140. disableRange: false,
  141. });
  142. const stream2 = new PDFNodeStream({
  143. url: pdf,
  144. length: pdfLength,
  145. rangeChunkSize: rangeSize,
  146. disableStream: true,
  147. disableRange: false,
  148. });
  149. const fullReader1 = stream1.getFullReader();
  150. const fullReader2 = stream2.getFullReader();
  151. let isStreamingSupported1, isRangeSupported1, fullReaderCancelled1;
  152. let isStreamingSupported2, isRangeSupported2, fullReaderCancelled2;
  153. const promise1 = fullReader1.headersReady.then(function () {
  154. isStreamingSupported1 = fullReader1.isStreamingSupported;
  155. isRangeSupported1 = fullReader1.isRangeSupported;
  156. // we shall be able to close the full reader without issues
  157. fullReader1.cancel(new AbortException("Don't need fullReader1."));
  158. fullReaderCancelled1 = true;
  159. });
  160. const promise2 = fullReader2.headersReady.then(function () {
  161. isStreamingSupported2 = fullReader2.isStreamingSupported;
  162. isRangeSupported2 = fullReader2.isRangeSupported;
  163. fullReader2.cancel(new AbortException("Don't need fullReader2."));
  164. fullReaderCancelled2 = true;
  165. });
  166. // Skipping fullReader results, requesting something from the PDF end.
  167. const tailSize = pdfLength % rangeSize || rangeSize;
  168. const range11Reader = stream1.getRangeReader(
  169. pdfLength - tailSize - rangeSize,
  170. pdfLength - tailSize
  171. );
  172. const range12Reader = stream1.getRangeReader(
  173. pdfLength - tailSize,
  174. pdfLength
  175. );
  176. const range21Reader = stream2.getRangeReader(
  177. pdfLength - tailSize - rangeSize,
  178. pdfLength - tailSize
  179. );
  180. const range22Reader = stream2.getRangeReader(
  181. pdfLength - tailSize,
  182. pdfLength
  183. );
  184. const result11 = { value: 0 },
  185. result12 = { value: 0 };
  186. const result21 = { value: 0 },
  187. result22 = { value: 0 };
  188. const read = function (reader, lenResult) {
  189. return reader.read().then(function (result) {
  190. if (result.done) {
  191. return undefined;
  192. }
  193. lenResult.value += result.value.byteLength;
  194. return read(reader, lenResult);
  195. });
  196. };
  197. await Promise.all([
  198. read(range11Reader, result11),
  199. read(range12Reader, result12),
  200. read(range21Reader, result21),
  201. read(range22Reader, result22),
  202. promise1,
  203. promise2,
  204. ]);
  205. expect(result11.value).toEqual(rangeSize);
  206. expect(result12.value).toEqual(tailSize);
  207. expect(result21.value).toEqual(rangeSize);
  208. expect(result22.value).toEqual(tailSize);
  209. expect(isStreamingSupported1).toEqual(false);
  210. expect(isRangeSupported1).toEqual(true);
  211. expect(fullReaderCancelled1).toEqual(true);
  212. expect(isStreamingSupported2).toEqual(false);
  213. expect(isRangeSupported2).toEqual(true);
  214. expect(fullReaderCancelled2).toEqual(true);
  215. });
  216. });