Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(binding-http): fix content negotiation #906

Merged
merged 2 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions packages/binding-http/src/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,16 +544,27 @@ export default class HttpServer implements ProtocolServer {

private async handleTdRequest(thing: ExposedThing, req: http.IncomingMessage, res: http.ServerResponse) {
const td = thing.getThingDescription();
let accept = req.headers.accept;
const contentSerdes = ContentSerdes.get();

// TODO: Parameters need to be considered here as well
const acceptValues = req.headers.accept?.split(",").map((acceptValue) => acceptValue.split(";")[0]) ?? [
ContentSerdes.TD,
];

// TODO: Better handling of wildcard values
if (accept === "*/*") {
accept = null;
}
const filteredAcceptValues = acceptValues
.map((acceptValue) => {
if (acceptValue === "*/*") {
return ContentSerdes.TD;
}

return acceptValue;
})
.filter((acceptValue) => contentSerdes.isSupported(acceptValue));

if (filteredAcceptValues.length > 0) {
const contentType = filteredAcceptValues[0];

if (accept == null || contentSerdes.isSupported(accept)) {
const contentType = accept ?? ContentSerdes.TD;
const content = contentSerdes.valueToContent(thing.getThingDescription(), undefined, contentType);
const payload = await content.toBuffer();

Expand All @@ -565,10 +576,10 @@ export default class HttpServer implements ProtocolServer {
return;
}

debug(`Request contained an accept header with value ${accept} which is not supported.`);
debug(`Request contained an accept header with the values ${acceptValues}, none of which are supported.`);

res.writeHead(406);
res.end(`Content-Type ${accept} is not supported by this resource.`);
res.end(`Accept header contained no Content-Types supported by this resource. (Was ${acceptValues})`);
}

private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
Expand Down
47 changes: 39 additions & 8 deletions packages/binding-http/test/http-server-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,15 +562,46 @@ class HttpServerTest {

const uri = `http://localhost:${httpServer.getPort()}/test/`;

const defaultContentTypeResponse = await fetch(uri);
expect(defaultContentTypeResponse.headers.get("Content-Type")).to.equal("application/td+json");

const negotiatedContentTypeResponse = await fetch(uri, {
headers: {
Accept: "application/json",
const testCases = [
{
inputHeaders: {},
expected: "application/td+json",
expectedResponseCode: 200,
},
});
expect(negotiatedContentTypeResponse.headers.get("Content-Type")).to.equal("application/json");
{
inputHeaders: { Accept: "application/json" },
expected: "application/json",
expectedResponseCode: 200,
},
{
inputHeaders: { Accept: "*/*,application/json" },
expected: "application/td+json",
expectedResponseCode: 200,
},
{
inputHeaders: { Accept: "*/*" },
expected: "application/td+json",
expectedResponseCode: 200,
},
{
inputHeaders: { Accept: "foo/cbar;baz=fuzz,application/json,foo/cbar" },
expected: "application/json",
expectedResponseCode: 200,
},
{
inputHeaders: { Accept: "foo/cbar;baz=fuzz,foo/cbar" },
expected: null,
expectedResponseCode: 406,
},
];

for (const testCase of testCases) {
const negotiatedContentTypeResponse = await fetch(uri, {
headers: testCase.inputHeaders,
});
expect(negotiatedContentTypeResponse.headers.get("Content-Type")).to.equal(testCase.expected);
expect(negotiatedContentTypeResponse.status).to.equal(testCase.expectedResponseCode);
}

return httpServer.stop();
}
Expand Down