Skip to content

Commit 2525630

Browse files
authored
Add option to force GET requests if URL length is low
This enables cases such as comunica/comunica#1551
1 parent c278465 commit 2525630

File tree

3 files changed

+53
-4
lines changed

3 files changed

+53
-4
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ const myFetcher = new SparqlEndpointFetcher({
5454
prefixVariableQuestionMark: false,
5555
// Timeout for setting up server connection (Once a connection has been made, and the response is being parsed, the timeout does not apply anymore).
5656
timeout: 5000,
57+
// If the url length is below this number, HTTP GET is used regardless of the value of this.method, defaults to 0.
58+
forceGetIfUrlLengthBelow: 600,
5759
});
5860
```
5961

lib/SparqlEndpointFetcher.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export class SparqlEndpointFetcher {
2020

2121
protected readonly method: 'GET' | 'POST';
2222
protected readonly timeout?: number;
23+
protected readonly forceGetIfUrlLengthBelow: number;
2324
public additionalUrlParams: URLSearchParams;
2425
protected readonly defaultHeaders: Headers;
2526
public readonly fetchCb?: (input: Request | string, init?: RequestInit) => Promise<Response>;
@@ -31,6 +32,7 @@ export class SparqlEndpointFetcher {
3132
public constructor(args?: ISparqlEndpointFetcherArgs) {
3233
this.method = args?.method ?? 'POST';
3334
this.timeout = args?.timeout;
35+
this.forceGetIfUrlLengthBelow = args?.forceGetIfUrlLengthBelow ?? 0;
3436
this.additionalUrlParams = args?.additionalUrlParams ?? new URLSearchParams();
3537
this.defaultHeaders = args?.defaultHeaders ?? new Headers();
3638
this.fetchCb = args?.fetch;
@@ -74,7 +76,7 @@ export class SparqlEndpointFetcher {
7476
* This will parse the update query and thrown an exception on syntax errors.
7577
*
7678
* @param {string} query An update query.
77-
* @return {'UNKNOWN' | UpdateTypes} The included update operations.
79+
* @return {'UNKNOWN' | IUpdateTypes} The included update operations.
7880
*/
7981
public getUpdateTypes(query: string): 'UNKNOWN' | IUpdateTypes {
8082
const parsedQuery = new SparqlParser({ sparqlStar: true }).parse(query);
@@ -191,14 +193,24 @@ export class SparqlEndpointFetcher {
191193
query: string,
192194
acceptHeader: string,
193195
): Promise<[ string, NodeJS.ReadableStream ]> {
194-
let url: string = this.method === 'POST' ? endpoint : `${endpoint}?query=${encodeURIComponent(query)}`;
196+
let method: 'GET' | 'POST';
197+
let url: string;
198+
199+
if (this.method === 'POST' && this.forceGetIfUrlLengthBelow <= endpoint.length) {
200+
method = this.method;
201+
url = endpoint;
202+
} else {
203+
const getEndpoint = `${endpoint}?query=${encodeURIComponent(query)}`;
204+
method = this.method === 'GET' || getEndpoint.length < this.forceGetIfUrlLengthBelow ? 'GET' : 'POST';
205+
url = method === 'POST' ? endpoint : getEndpoint;
206+
}
195207

196208
// Initiate request
197209
let body: URLSearchParams | undefined;
198210
const headers: Headers = new Headers(this.defaultHeaders);
199211
headers.append('Accept', acceptHeader);
200212

201-
if (this.method === 'POST') {
213+
if (method === 'POST') {
202214
headers.append('Content-Type', 'application/x-www-form-urlencoded');
203215
body = new URLSearchParams();
204216
body.set('query', query);
@@ -210,7 +222,7 @@ export class SparqlEndpointFetcher {
210222
url += `&${this.additionalUrlParams.toString()}`;
211223
}
212224

213-
return this.handleFetchCall(url, { headers, method: this.method, body });
225+
return this.handleFetchCall(url, { headers, method, body });
214226
}
215227

216228
/**
@@ -270,6 +282,7 @@ export interface ISparqlEndpointFetcherArgs extends ISparqlJsonParserArgs, ISpar
270282
method?: 'POST' | 'GET';
271283
additionalUrlParams?: URLSearchParams;
272284
timeout?: number;
285+
forceGetIfUrlLengthBelow?: number;
273286
defaultHeaders?: Headers;
274287
/**
275288
* A custom fetch function.

test/SparqlEndpointFetcher-test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,40 @@ describe('SparqlEndpointFetcher', () => {
303303
);
304304
});
305305

306+
it('should use HTTP GET when url length is below forceGetIfUrlLengthBelow', async() => {
307+
const fetchCbThis = jest.fn(() => Promise.resolve(new Response(streamifyString('dummy'))));
308+
const fetcherThis = new SparqlEndpointFetcher({
309+
method: 'POST',
310+
fetch: fetchCbThis,
311+
forceGetIfUrlLengthBelow: 600,
312+
});
313+
await fetcherThis.fetchRawStream(endpoint, querySelect, 'myacceptheader');
314+
const headers: Headers = new Headers();
315+
headers.append('Accept', 'myacceptheader');
316+
expect(fetchCbThis).toHaveBeenCalledWith(
317+
'https://dbpedia.org/sparql?query=SELECT%20*%20WHERE%20%7B%20%3Fs%20%3Fp%20%3Fo%20%7D',
318+
expect.objectContaining({ headers, method: 'GET' }),
319+
);
320+
});
321+
322+
it('should use HTTP POST when url length is above or the same as forceGetIfUrlLengthBelow', async() => {
323+
const fetchCbThis = jest.fn(() => Promise.resolve(new Response(streamifyString('dummy'))));
324+
const fetcherThis = new SparqlEndpointFetcher({
325+
method: 'POST',
326+
fetch: fetchCbThis,
327+
forceGetIfUrlLengthBelow: 30,
328+
});
329+
await fetcherThis.fetchRawStream(endpoint, querySelect, 'myacceptheader');
330+
const headers: Headers = new Headers();
331+
headers.append('Accept', 'myacceptheader');
332+
const body = new URLSearchParams();
333+
body.set('query', querySelect);
334+
expect(fetchCbThis).toHaveBeenCalledWith(
335+
'https://dbpedia.org/sparql',
336+
expect.objectContaining({ headers, method: 'POST', body }),
337+
);
338+
});
339+
306340
it('should reject for an invalid server response', async() => {
307341
const fetchCbThis = () => Promise.resolve(<Response> {
308342
body: streamifyString('this is an invalid response'),

0 commit comments

Comments
 (0)