Skip to content

Commit

Permalink
Merge pull request #46 from camptocamp/ogc-api-avoid-stream-already-read
Browse files Browse the repository at this point in the history
OGC API: Avoid "stream already consumed" errors on shared fetch calls
  • Loading branch information
jahow authored May 29, 2024
2 parents e9b434a + a902cc6 commit ad6d9ab
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 15 deletions.
6 changes: 5 additions & 1 deletion src/ogc-api/endpoint.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ beforeAll(() => {
return {
ok: true,
headers: new Headers(),
clone: function () {
return this;
},
json: () =>
new Promise((resolve) => {
resolve(JSON.parse(contents));
Expand Down Expand Up @@ -1633,7 +1636,8 @@ describe('OgcApiEndpoint', () => {
await expect(endpoint.info).rejects.toEqual(
new EndpointError(
`The endpoint appears non-conforming, the following error was encountered:
The document at http://local/sample-data/notjson?f=json does not appear to be valid JSON.`
The document at http://local/sample-data/notjson?f=json does not appear to be valid JSON. Error was: Unexpected token 'h', "hello world
" is not valid JSON`
)
);
});
Expand Down
13 changes: 8 additions & 5 deletions src/ogc-api/link-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ export function fetchDocument<T extends OgcApiDocument>(
if (!resp.ok) {
throw new Error(`The document at ${urlObj} could not be fetched.`);
}
return resp.json().catch(() => {
throw new Error(
`The document at ${urlObj} does not appear to be valid JSON.`
);
}) as Promise<T>;
return resp
.clone()
.json()
.catch((e) => {
throw new Error(
`The document at ${urlObj} does not appear to be valid JSON. Error was: ${e.message}`
);
}) as Promise<T>;
});
}

Expand Down
11 changes: 10 additions & 1 deletion src/shared/http-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('HTTP utils', () => {
beforeAll(() => {
fetchBehaviour = 'ok';
originalFetch = global.fetch; // keep reference of native impl
global.fetch = jest.fn((xmlString, opts) => {
global.fetch = jest.fn().mockImplementation((xmlString, opts) => {
const noCors = opts && opts.mode === 'no-cors';
const headers = { get: () => null };
switch (fetchBehaviour) {
Expand All @@ -45,12 +45,18 @@ describe('HTTP utils', () => {
status: 200,
ok: true,
headers,
clone: function () {
return this;
},
});
case 'httpError':
return Promise.resolve({
text: () => Promise.resolve('<error>Random error</error>'),
status: 401,
ok: false,
clone: function () {
return this;
},
});
case 'corsError':
if (noCors)
Expand All @@ -71,6 +77,9 @@ describe('HTTP utils', () => {
headers,
arrayBuffer: () =>
Promise.resolve(Buffer.from(sampleXml, 'utf-8')),
clone: function () {
return this;
},
}),
10
);
Expand Down
19 changes: 11 additions & 8 deletions test-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import * as util from 'util';
import CacheMock from 'browser-cache-mock';
import 'isomorphic-fetch';
import { TextDecoder } from 'util';
import { Buffer } from './node_modules/buffer';
import { Buffer } from './node_modules/buffer/index.js';

globalThis.Buffer = Buffer;

// mock the global fetch API
window.fetchResponseFactory = (url) => '<empty></empty>';
window.originalFetch = window.fetch;
window.mockFetch = jest.fn((url) =>
window.mockFetch = jest.fn().mockImplementation((url) =>
Promise.resolve({
text: () => Promise.resolve(globalThis.fetchResponseFactory(url)),
json: () =>
Expand All @@ -21,6 +21,9 @@ window.mockFetch = jest.fn((url) =>
Promise.resolve(
Buffer.from(globalThis.fetchResponseFactory(url), 'utf-8')
),
clone: function () {
return this;
},
status: 200,
ok: true,
headers: { get: () => null },
Expand All @@ -43,9 +46,9 @@ window.caches = {
global.Worker = function Worker(filePath) {
let getScopeVar;
let messageQueue = [];
let inside = mitt();
let outside = mitt();
let scope = {
const inside = mitt();
const outside = mitt();
const scope = {
onmessage: null,
dispatchEvent: inside.emit,
addEventListener: inside.on,
Expand All @@ -57,7 +60,7 @@ global.Worker = function Worker(filePath) {
importScripts() {},
};
inside.on('message', (e) => {
let f = scope.onmessage || getScopeVar('onmessage');
const f = scope.onmessage || getScopeVar('onmessage');
if (f) f.call(scope, e);
});
this.addEventListener = outside.on;
Expand Down Expand Up @@ -88,14 +91,14 @@ global.Worker = function Worker(filePath) {
result.outputFiles[0].contents
);
let vars = 'var self=this,global=self,globalThis=self';
for (let k in scope) vars += `,${k}=self.${k}`;
for (const k in scope) vars += `,${k}=self.${k}`;
getScopeVar = Function(
vars +
';\n' +
code +
'\nreturn function(n){return n=="onmessage"?onmessage:null;}'
).call(scope);
let q = messageQueue;
const q = messageQueue;
messageQueue = null;
q.forEach(this.postMessage);
})
Expand Down

0 comments on commit ad6d9ab

Please sign in to comment.