Skip to content

Commit 9d3858c

Browse files
authored
Merge pull request #1283 from relu91/fix-854
Support for default accept header in http client
2 parents 31a519d + bc4f3ae commit 9d3858c

2 files changed

Lines changed: 135 additions & 4 deletions

File tree

packages/binding-http/src/http-client-impl.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ export default class HttpClient implements ProtocolClient {
112112
}
113113

114114
public async readResource(form: HttpForm): Promise<Content> {
115-
const request = await this.generateFetchRequest(form, "GET");
115+
// See https://www.w3.org/TR/wot-thing-description11/#contentType-usage
116+
// Case: 1B
117+
const headers = form.contentType != null ? [["accept", form.contentType]] : [["accept", ContentSerdes.DEFAULT]];
118+
const request = await this.generateFetchRequest(form, "GET", { headers });
116119
debug(`HttpClient (readResource) sending ${request.method} to ${request.url}`);
117120

118121
const result = await this.fetch(request);
@@ -176,6 +179,15 @@ export default class HttpClient implements ProtocolClient {
176179

177180
public async invokeResource(form: HttpForm, content?: Content): Promise<Content> {
178181
const headers = content != null ? [["content-type", content.type]] : [];
182+
// See https://www.w3.org/TR/wot-thing-description11/#contentType-usage
183+
// Cases: 1C and 2A
184+
if (form.response?.contentType != null) {
185+
headers.push(["accept", form.response?.contentType]);
186+
} else if (form.contentType != null) {
187+
headers.push(["accept", form.contentType]);
188+
} else {
189+
headers.push(["accept", ContentSerdes.DEFAULT]);
190+
}
179191

180192
const request = await this.generateFetchRequest(form, "POST", {
181193
headers,
@@ -347,12 +359,20 @@ export default class HttpClient implements ProtocolClient {
347359

348360
const headers = form["htv:headers"] as Array<HttpHeader>;
349361
for (const option of headers) {
362+
// override defaults
363+
requestInit.headers = requestInit.headers.filter(
364+
(header) => header[0].toLowerCase() !== option["htv:fieldName"].toLowerCase()
365+
);
350366
requestInit.headers.push([option["htv:fieldName"], option["htv:fieldValue"]]);
351367
}
352368
} else if (typeof form["htv:headers"] === "object") {
353369
debug(`HttpClient got Form SINGLE-ENTRY 'headers' ${form["htv:headers"]}`);
354370

355371
const option = form["htv:headers"] as HttpHeader;
372+
// override defaults
373+
requestInit.headers = requestInit.headers.filter(
374+
(header) => header[0].toLowerCase() !== option["htv:fieldName"].toLowerCase()
375+
);
356376
requestInit.headers.push([option["htv:fieldName"], option["htv:fieldValue"]]);
357377
}
358378

packages/binding-http/test/http-client-test.ts

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ interface TestVector {
5050
method?: string;
5151
schema?: DataSchema;
5252
payload?: DataSchemaValue;
53+
headers?: Record<string, string>;
5354
form: Form;
5455
}
5556

@@ -153,6 +154,10 @@ class TestHttpServer implements ProtocolServer {
153154
expect(req.method).to.equal(this.testVector.method);
154155
expect(req.url).to.equal(new URL(this.testVector.form.href).pathname);
155156

157+
if (this.testVector.headers) {
158+
expect(req.headers).to.include(this.testVector.headers);
159+
}
160+
156161
if (this.testVector.payload !== undefined) {
157162
// load payload
158163
const body: Array<Uint8Array> = [];
@@ -211,6 +216,9 @@ class HttpClientTest1 {
211216
form: <HttpForm>{
212217
href: `http://localhost:${port1}/`,
213218
},
219+
headers: {
220+
accept: "application/json",
221+
},
214222
};
215223
HttpClientTest1.httpServer.setTestVector(inputVector1);
216224
const resource = await this.client.readResource(inputVector1.form);
@@ -225,6 +233,9 @@ class HttpClientTest1 {
225233
form: <HttpForm>{
226234
href: `http://localhost:${port1}/`,
227235
},
236+
headers: {
237+
"content-type": "application/json",
238+
},
228239
payload: "test",
229240
};
230241
HttpClientTest1.httpServer.setTestVector(inputVector2);
@@ -239,6 +250,10 @@ class HttpClientTest1 {
239250
form: <HttpForm>{
240251
href: `http://localhost:${port1}/`,
241252
},
253+
headers: {
254+
"content-type": "application/json",
255+
accept: "application/json",
256+
},
242257
payload: "test",
243258
};
244259
HttpClientTest1.httpServer.setTestVector(inputVector3);
@@ -269,7 +284,46 @@ class HttpClientTest1 {
269284
body.toString("ascii").should.eql("");
270285
}
271286

272-
@test async "should apply form information - read with POST instead of PUT"() {
287+
@test async "should apply form information - read with not default content-type"() {
288+
// read with defaults
289+
const inputVector1 = {
290+
op: ["readproperty"],
291+
form: <HttpForm>{
292+
href: `http://localhost:${port1}/`,
293+
contentType: "text/plain",
294+
},
295+
headers: {
296+
accept: "text/plain",
297+
},
298+
};
299+
HttpClientTest1.httpServer.setTestVector(inputVector1);
300+
const resource = await this.client.readResource(inputVector1.form);
301+
const body = await resource.toBuffer();
302+
body.toString("ascii").should.eql("");
303+
}
304+
305+
@test async "should apply form information - read with header override"() {
306+
// read with defaults
307+
const inputVector1 = {
308+
op: ["readproperty"],
309+
form: <HttpForm>{
310+
href: `http://localhost:${port1}/`,
311+
"htv:headers": {
312+
"htv:fieldName": "accept",
313+
"htv:fieldValue": "text/plain",
314+
},
315+
},
316+
headers: {
317+
accept: "text/plain",
318+
},
319+
};
320+
HttpClientTest1.httpServer.setTestVector(inputVector1);
321+
const resource = await this.client.readResource(inputVector1.form);
322+
const body = await resource.toBuffer();
323+
body.toString("ascii").should.eql("");
324+
}
325+
326+
@test async "should apply form information - write with POST instead of PUT"() {
273327
// write with POST instead of PUT
274328
const inputVector2 = {
275329
op: ["writeproperty"],
@@ -283,7 +337,7 @@ class HttpClientTest1 {
283337
await this.client.writeResource(inputVector2.form, new DefaultContent(Readable.from(inputVector2.payload)));
284338
}
285339

286-
@test async "should apply form information - read with PUT instead of GET"() {
340+
@test async "should apply form information - invoke with PUT instead of GET"() {
287341
// invoke with PUT instead of POST
288342
const inputVector3 = {
289343
op: ["invokeaction"],
@@ -297,7 +351,7 @@ class HttpClientTest1 {
297351
await this.client.invokeResource(inputVector3.form, new DefaultContent(Readable.from(inputVector3.payload)));
298352
}
299353

300-
@test async "should apply form information - read with DELETE instead of POST"() {
354+
@test async "should apply form information - invoke with DELETE instead of POST"() {
301355
// invoke with DELETE instead of POST
302356
const inputVector4 = {
303357
op: ["invokeaction"],
@@ -309,6 +363,63 @@ class HttpClientTest1 {
309363
HttpClientTest1.httpServer.setTestVector(inputVector4);
310364
await this.client.invokeResource(inputVector4.form);
311365
}
366+
367+
@test async "should apply form information - invoke with not default content-type and no inputs"() {
368+
// invoke with DELETE instead of POST
369+
const inputVector4 = {
370+
op: ["invokeaction"],
371+
form: <HttpForm>{
372+
href: `http://localhost:${port1}/`,
373+
contentType: "text/plain",
374+
},
375+
headers: {
376+
accept: "text/plain",
377+
},
378+
};
379+
HttpClientTest1.httpServer.setTestVector(inputVector4);
380+
await this.client.invokeResource(inputVector4.form);
381+
}
382+
383+
@test async "should apply form information - invoke with not default content-type"() {
384+
// invoke with DELETE instead of POST
385+
const inputVector4 = {
386+
op: ["invokeaction"],
387+
form: <HttpForm>{
388+
href: `http://localhost:${port1}/`,
389+
contentType: "text/plain",
390+
},
391+
headers: {
392+
"content-type": "text/plain",
393+
accept: "text/plain",
394+
},
395+
payload: "test",
396+
};
397+
HttpClientTest1.httpServer.setTestVector(inputVector4);
398+
await this.client.invokeResource(
399+
inputVector4.form,
400+
new Content("text/plain", Readable.from(inputVector4.payload))
401+
);
402+
}
403+
404+
@test async "should apply form information - invoke with default content-type and response content-type"() {
405+
// invoke with DELETE instead of POST
406+
const inputVector4 = {
407+
op: ["invokeaction"],
408+
form: <HttpForm>{
409+
href: `http://localhost:${port1}/`,
410+
response: {
411+
contentType: "text/plain",
412+
},
413+
},
414+
headers: {
415+
"content-type": "application/json",
416+
accept: "text/plain",
417+
},
418+
payload: "test",
419+
};
420+
HttpClientTest1.httpServer.setTestVector(inputVector4);
421+
await this.client.invokeResource(inputVector4.form, new DefaultContent(Readable.from(inputVector4.payload)));
422+
}
312423
}
313424

314425
@suite("HTTP client subscriptions")

0 commit comments

Comments
 (0)