Skip to content

Commit 7b4ebc9

Browse files
authored
Add option to force direct POST requests
This allows queries to be sent as application/sparql-query instead of the default application/x-www-form-urlencoded. Related to comunica/comunica#1482
1 parent c18c7c1 commit 7b4ebc9

File tree

3 files changed

+60
-6
lines changed

3 files changed

+60
-6
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ const myFetcher = new SparqlEndpointFetcher({
4343
// A custom HTTP method for issuing (non-update) queries, defaults to POST. Update queries are always issued via POST.
4444
method: 'POST',
4545
// A set of additional parameters that well be added to fetchAsk, fetchBindings & fetchTriples requests
46+
// With a GET request, these are encoded in the URL.
47+
// With a POST request when directPost is true (see below), these are also encoded in the URL.
48+
// With other POST requests, it's encoded in the body.
4649
additionalUrlParams: new URLSearchParams({ infer: 'true', sameAs: 'false' }),
4750
// Optional default headers that will be included in each request
4851
defaultHeaders: new Headers(),
@@ -56,6 +59,10 @@ const myFetcher = new SparqlEndpointFetcher({
5659
timeout: 5000,
5760
// If the url length is below this number, HTTP GET is used regardless of the value of this.method, defaults to 0.
5861
forceGetIfUrlLengthBelow: 600,
62+
// If true, POST requests will be sent as application/sparql-query (unencoded query string).
63+
// If false, POST requests will be sent as application/x-www-form-urlencoded (URL-encoded query string).
64+
// Defaults to false.
65+
directPost: false,
5966
});
6067
```
6168

lib/SparqlEndpointFetcher.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export class SparqlEndpointFetcher {
2121
protected readonly method: 'GET' | 'POST';
2222
protected readonly timeout?: number;
2323
protected readonly forceGetIfUrlLengthBelow: number;
24+
protected readonly directPost: boolean;
2425
public additionalUrlParams: URLSearchParams;
2526
protected readonly defaultHeaders: Headers;
2627
public readonly fetchCb?: (input: Request | string, init?: RequestInit) => Promise<Response>;
@@ -33,6 +34,7 @@ export class SparqlEndpointFetcher {
3334
this.method = args?.method ?? 'POST';
3435
this.timeout = args?.timeout;
3536
this.forceGetIfUrlLengthBelow = args?.forceGetIfUrlLengthBelow ?? 0;
37+
this.directPost = args?.directPost ?? false;
3638
this.additionalUrlParams = args?.additionalUrlParams ?? new URLSearchParams();
3739
this.defaultHeaders = args?.defaultHeaders ?? new Headers();
3840
this.fetchCb = args?.fetch;
@@ -206,16 +208,24 @@ export class SparqlEndpointFetcher {
206208
}
207209

208210
// Initiate request
209-
let body: URLSearchParams | undefined;
211+
let body: URLSearchParams | string | undefined;
210212
const headers: Headers = new Headers(this.defaultHeaders);
211213
headers.append('Accept', acceptHeader);
212214

213215
if (method === 'POST') {
214-
headers.append('Content-Type', 'application/x-www-form-urlencoded');
215-
body = new URLSearchParams();
216-
body.set('query', query);
217-
for (const [ key, value ] of this.additionalUrlParams.entries()) {
218-
body.set(key, value);
216+
if (this.directPost) {
217+
headers.append('Content-Type', 'application/sparql-query');
218+
body = query;
219+
if (this.additionalUrlParams.toString().length > 0) {
220+
url += `?${this.additionalUrlParams.toString()}`;
221+
}
222+
} else {
223+
headers.append('Content-Type', 'application/x-www-form-urlencoded');
224+
body = new URLSearchParams();
225+
body.set('query', query);
226+
for (const [ key, value ] of this.additionalUrlParams.entries()) {
227+
body.set(key, value);
228+
}
219229
}
220230
headers.append('Content-Length', body.toString().length.toString());
221231
} else if (this.additionalUrlParams.toString().length > 0) {
@@ -283,6 +293,7 @@ export interface ISparqlEndpointFetcherArgs extends ISparqlJsonParserArgs, ISpar
283293
additionalUrlParams?: URLSearchParams;
284294
timeout?: number;
285295
forceGetIfUrlLengthBelow?: number;
296+
directPost?: boolean;
286297
defaultHeaders?: Headers;
287298
/**
288299
* A custom fetch function.

test/SparqlEndpointFetcher-test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,42 @@ describe('SparqlEndpointFetcher', () => {
337337
);
338338
});
339339

340+
it('should use direct HTTP POST when directPost is true', async() => {
341+
const fetchCbThis = jest.fn(() => Promise.resolve(new Response(streamifyString('dummy'))));
342+
const fetcherThis = new SparqlEndpointFetcher({
343+
method: 'POST',
344+
fetch: fetchCbThis,
345+
directPost: true,
346+
});
347+
await fetcherThis.fetchRawStream(endpoint, querySelect, 'myacceptheader');
348+
const headers: Headers = new Headers();
349+
headers.append('Accept', 'myacceptheader');
350+
const body = querySelect;
351+
expect(fetchCbThis).toHaveBeenCalledWith(
352+
'https://dbpedia.org/sparql',
353+
expect.objectContaining({ headers, method: 'POST', body }),
354+
);
355+
});
356+
357+
it('should use direct HTTP POST when directPost is true with additional URL parameters', async() => {
358+
const fetchCbThis = jest.fn(() => Promise.resolve(new Response(streamifyString('dummy'))));
359+
const additionalUrlParams = new URLSearchParams({ infer: 'true', sameAs: 'false' });
360+
const fetcherThis = new SparqlEndpointFetcher({
361+
method: 'POST',
362+
fetch: fetchCbThis,
363+
directPost: true,
364+
additionalUrlParams,
365+
});
366+
await fetcherThis.fetchRawStream(endpoint, querySelect, 'myacceptheader');
367+
const headers: Headers = new Headers();
368+
headers.append('Accept', 'myacceptheader');
369+
const body = querySelect;
370+
expect(fetchCbThis).toHaveBeenCalledWith(
371+
'https://dbpedia.org/sparql?infer=true&sameAs=false',
372+
expect.objectContaining({ headers, method: 'POST', body }),
373+
);
374+
});
375+
340376
it('should reject for an invalid server response', async() => {
341377
const fetchCbThis = () => Promise.resolve(<Response> {
342378
body: streamifyString('this is an invalid response'),

0 commit comments

Comments
 (0)