From d29cb588373dff875ed82cda709ce0bf67c1de54 Mon Sep 17 00:00:00 2001 From: Miles Ziemer <45497130+milesziemer@users.noreply.github.com> Date: Tue, 6 Jun 2023 06:32:49 -0400 Subject: [PATCH] feat(protocol-http): add reason to HttpResponse (#4772) Adds `reason` property to HttpResponse, which is a simple message explaining the status code. The property is optional, so adding it is backward compatible. Http handlers were updated to populate this property when constructing the HttpResponse, except for the Node http2 handler, because http2 doesn't support an equivalent. Tests were added to Http handlers to make sure the property is populated when available. --- .../src/fetch-http-handler.spec.ts | 22 +++++++++++++++++++ .../src/fetch-http-handler.ts | 2 ++ .../src/node-http-handler.spec.ts | 2 ++ .../src/node-http-handler.ts | 1 + packages/protocol-http/src/httpResponse.ts | 3 +++ packages/types/src/http.ts | 1 + 6 files changed, 31 insertions(+) diff --git a/packages/fetch-http-handler/src/fetch-http-handler.spec.ts b/packages/fetch-http-handler/src/fetch-http-handler.spec.ts index adf1eebe7579..7f6d6c75633f 100644 --- a/packages/fetch-http-handler/src/fetch-http-handler.spec.ts +++ b/packages/fetch-http-handler/src/fetch-http-handler.spec.ts @@ -290,6 +290,28 @@ describe(FetchHttpHandler.name, () => { expect(mockRequest.mock.calls[0][1]).not.toHaveProperty("signal"); }); + it("creates correct HTTPResponse object", async () => { + const mockResponse = { + headers: { + entries: jest.fn().mockReturnValue([["foo", "bar"]]), + }, + blob: jest.fn().mockResolvedValue(new Blob(["FOO"])), + status: 200, + statusText: "foo", + }; + const mockFetch = jest.fn().mockResolvedValue(mockResponse); + (global as any).fetch = mockFetch; + + const fetchHttpHandler = new FetchHttpHandler(); + const { response } = await fetchHttpHandler.handle({} as any, {}); + + expect(mockFetch.mock.calls.length).toBe(1); + expect(response.headers).toStrictEqual({ foo: "bar" }); + expect(response.reason).toBe("foo"); + expect(response.statusCode).toBe(200); + expect(await blobToText(response.body)).toBe("FOO"); + }); + describe("#destroy", () => { it("should be callable and return nothing", () => { const httpHandler = new FetchHttpHandler(); diff --git a/packages/fetch-http-handler/src/fetch-http-handler.ts b/packages/fetch-http-handler/src/fetch-http-handler.ts index 5b9f5cb5fe46..47b5ccacf1b8 100644 --- a/packages/fetch-http-handler/src/fetch-http-handler.ts +++ b/packages/fetch-http-handler/src/fetch-http-handler.ts @@ -91,6 +91,7 @@ export class FetchHttpHandler implements HttpHandler { return response.blob().then((body) => ({ response: new HttpResponse({ headers: transformedHeaders, + reason: response.statusText, statusCode: response.status, body, }), @@ -100,6 +101,7 @@ export class FetchHttpHandler implements HttpHandler { return { response: new HttpResponse({ headers: transformedHeaders, + reason: response.statusText, statusCode: response.status, body: response.body, }), diff --git a/packages/node-http-handler/src/node-http-handler.spec.ts b/packages/node-http-handler/src/node-http-handler.spec.ts index 7ecfa1a93eb0..5d87a8bd924e 100644 --- a/packages/node-http-handler/src/node-http-handler.spec.ts +++ b/packages/node-http-handler/src/node-http-handler.spec.ts @@ -141,6 +141,7 @@ describe("NodeHttpHandler", () => { it("can send http requests", async () => { const mockResponse = { statusCode: 200, + statusText: "OK", headers: {}, body: "test", }; @@ -160,6 +161,7 @@ describe("NodeHttpHandler", () => { ); expect(response.statusCode).toEqual(mockResponse.statusCode); + expect(response.reason).toEqual(mockResponse.statusText); expect(response.headers).toBeDefined(); expect(response.headers).toMatchObject(mockResponse.headers); expect(response.body).toBeDefined(); diff --git a/packages/node-http-handler/src/node-http-handler.ts b/packages/node-http-handler/src/node-http-handler.ts index 2fa5d6a4e685..948efb29efe5 100644 --- a/packages/node-http-handler/src/node-http-handler.ts +++ b/packages/node-http-handler/src/node-http-handler.ts @@ -134,6 +134,7 @@ export class NodeHttpHandler implements HttpHandler { const req = requestFunc(nodeHttpsOptions, (res) => { const httpResponse = new HttpResponse({ statusCode: res.statusCode || -1, + reason: res.statusMessage, headers: getTransformedHeaders(res.headers), body: res, }); diff --git a/packages/protocol-http/src/httpResponse.ts b/packages/protocol-http/src/httpResponse.ts index 52b24008e706..adca1cba94e9 100644 --- a/packages/protocol-http/src/httpResponse.ts +++ b/packages/protocol-http/src/httpResponse.ts @@ -2,17 +2,20 @@ import { HeaderBag, HttpMessage, HttpResponse as IHttpResponse } from "@aws-sdk/ type HttpResponseOptions = Partial & { statusCode: number; + reason?: string; }; export interface HttpResponse extends IHttpResponse {} export class HttpResponse { public statusCode: number; + public reason?: string; public headers: HeaderBag; public body?: any; constructor(options: HttpResponseOptions) { this.statusCode = options.statusCode; + this.reason = options.reason; this.headers = options.headers || {}; this.body = options.body; } diff --git a/packages/types/src/http.ts b/packages/types/src/http.ts index a5c31bdf0774..49e20cb55c69 100644 --- a/packages/types/src/http.ts +++ b/packages/types/src/http.ts @@ -98,6 +98,7 @@ export interface HttpRequest extends HttpMessage, Endpoint { */ export interface HttpResponse extends HttpMessage { statusCode: number; + reason?: string; } /**