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: support custom agent in node http handler #489

Merged
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
6 changes: 3 additions & 3 deletions clients/client-rds-data/RdsDataServiceClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import {
Client as SmithyClient,
SmithyResolvedConfiguration
} from "@aws-sdk/smithy-client";
import { HttpOptions as __HttpOptions } from "@aws-sdk/types";
import { HttpHandlerOptions as __HttpHandlerOptions } from "@aws-sdk/types";

export type ServiceInputTypes =
| RollbackTransactionRequest
Expand Down Expand Up @@ -158,7 +158,7 @@ export type RdsDataServiceConfig = RDSDataRuntimeDependencies &
UserAgentInputConfig;

export type RdsDataServiceResolvedConfig = SmithyResolvedConfiguration<
__HttpOptions
__HttpHandlerOptions
> &
Required<RDSDataRuntimeDependencies> &
AwsAuthResolvedConfig &
Expand All @@ -168,7 +168,7 @@ export type RdsDataServiceResolvedConfig = SmithyResolvedConfiguration<
UserAgentResolvedConfig;

export class RdsDataService extends SmithyClient<
__HttpOptions,
__HttpHandlerOptions,
ServiceInputTypes,
ServiceOutputTypes,
RdsDataServiceResolvedConfig
Expand Down
4 changes: 2 additions & 2 deletions clients/client-rds-data/commands/ExecuteStatementCommand.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command } from "@aws-sdk/smithy-client";
import { getSerdePlugin } from "@aws-sdk/middleware-serde";
import {
HttpOptions,
HttpHandlerOptions as __HttpHandlerOptions,
Handler,
HandlerExecutionContext,
FinalizeHandlerArguments,
Expand Down Expand Up @@ -32,7 +32,7 @@ export class ExecuteStatementCommand extends Command<
resolveMiddleware(
clientStack: MiddlewareStack<ServiceInputTypes, ServiceOutputTypes>,
configuration: RdsDataServiceResolvedConfig,
options?: HttpOptions
options?: __HttpHandlerOptions
): Handler<ExecuteStatementRequest, ExecuteStatementResponse> {
const { requestHandler } = configuration;

Expand Down
4 changes: 2 additions & 2 deletions packages/fetch-http-handler/src/fetch-http-handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HttpOptions, HeaderBag, HttpHandlerOptions } from "@aws-sdk/types";
import { HeaderBag, HttpHandlerOptions } from "@aws-sdk/types";
import { HttpHandler, HttpRequest, HttpResponse } from "@aws-sdk/protocol-http";
import { requestTimeout } from "./request-timeout";
import { buildQueryString } from "@aws-sdk/querystring-builder";
Expand All @@ -8,7 +8,7 @@ declare var AbortController: any;
/**
* Represents the http options that can be passed to a browser http client.
*/
export interface BrowserHttpOptions extends HttpOptions {
export interface BrowserHttpOptions {
/**
* The number of milliseconds a request can take before being automatically
* terminated.
Expand Down
40 changes: 28 additions & 12 deletions packages/node-http-handler/src/node-http-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ import {
import { AddressInfo } from "net";

describe("NodeHttpHandler", () => {
describe("constructor", () => {
it("can set httpAgent and httpsAgent", () => {
let maxSockets = Math.round(Math.random() * 50);
let nodeHttpHandler = new NodeHttpHandler({
httpAgent: new http.Agent({ maxSockets })
});
expect((nodeHttpHandler as any).httpAgent.maxSockets).toEqual(maxSockets);
maxSockets = Math.round(Math.random() * 50);
nodeHttpHandler = new NodeHttpHandler({
httpsAgent: new https.Agent({ maxSockets })
});
expect((nodeHttpHandler as any).httpsAgent.maxSockets).toEqual(
maxSockets
);
});
});
describe("http", () => {
const mockHttpServer: HttpServer = createMockHttpServer().listen(54321);

Expand Down Expand Up @@ -89,15 +105,15 @@ describe("NodeHttpHandler", () => {
);
const nodeHttpHandler = new NodeHttpHandler();

let response = await nodeHttpHandler.handle(
{
let { response } = await nodeHttpHandler.handle(
new HttpRequest({
hostname: "localhost",
method: "GET",
port: (mockHttpServer.address() as AddressInfo).port,
port: (mockHttpsServer.address() as AddressInfo).port,
protocol: "https:",
path: "/",
headers: {}
},
}),
{}
);

Expand All @@ -124,16 +140,16 @@ describe("NodeHttpHandler", () => {
});

const nodeHttpHandler = new NodeHttpHandler();
let response = await nodeHttpHandler.handle(
{
let { response } = await nodeHttpHandler.handle(
new HttpRequest({
hostname: "localhost",
method: "PUT",
port: (mockHttpServer.address() as AddressInfo).port,
port: (mockHttpsServer.address() as AddressInfo).port,
protocol: "https:",
path: "/",
headers: {},
body
},
}),
{}
);

Expand Down Expand Up @@ -214,16 +230,16 @@ describe("NodeHttpHandler", () => {
);
const nodeHttpHandler = new NodeHttpHandler();

let response = await nodeHttpHandler.handle(
{
let { response } = await nodeHttpHandler.handle(
new HttpRequest({
hostname: "localhost",
method: "PUT",
port: (mockHttpServer.address() as AddressInfo).port,
port: (mockHttpsServer.address() as AddressInfo).port,
protocol: "https:",
path: "/",
headers: {},
body
},
}),
{}
);

Expand Down
45 changes: 37 additions & 8 deletions packages/node-http-handler/src/node-http-handler.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,50 @@
import * as https from "https";
import * as http from "http";
import { buildQueryString } from "@aws-sdk/querystring-builder";
import { HttpOptions, NodeHttpOptions } from "@aws-sdk/types";
import { HttpHandlerOptions } from "@aws-sdk/types";
import { HttpHandler, HttpRequest, HttpResponse } from "@aws-sdk/protocol-http";
import { setConnectionTimeout } from "./set-connection-timeout";
import { setSocketTimeout } from "./set-socket-timeout";
import { writeRequestBody } from "./write-request-body";
import { getTransformedHeaders } from "./get-transformed-headers";

/**
* Represents the http options that can be passed to a node http client.
*/
export interface NodeHttpOptions {
/**
* The maximum time in milliseconds that the connection phase of a request
* may take before the connection attempt is abandoned.
*/
connectionTimeout?: number;

/**
* The maximum time in milliseconds that a socket may remain idle before it
* is closed.
*/
socketTimeout?: number;

httpAgent?: http.Agent;
httpsAgent?: https.Agent;
}

export class NodeHttpHandler implements HttpHandler {
private readonly httpAgent: http.Agent;
private readonly httpsAgent: https.Agent;
private readonly connectionTimeout?: number;
private readonly socketTimeout?: number;

constructor(private readonly httpOptions: NodeHttpOptions = {}) {
const { keepAlive = true } = httpOptions;
this.httpAgent = new http.Agent({ keepAlive });
this.httpsAgent = new https.Agent({ keepAlive });
constructor({
connectionTimeout,
socketTimeout,
httpAgent,
httpsAgent
}: NodeHttpOptions = {}) {
this.connectionTimeout = connectionTimeout;
this.socketTimeout = socketTimeout;
const keepAlive = true;
this.httpAgent = httpAgent || new http.Agent({ keepAlive });
this.httpsAgent = httpsAgent || new https.Agent({ keepAlive });
}

destroy(): void {
Expand All @@ -25,7 +54,7 @@ export class NodeHttpHandler implements HttpHandler {

handle(
request: HttpRequest,
{ abortSignal }: HttpOptions
{ abortSignal }: HttpHandlerOptions
): Promise<{ response: HttpResponse }> {
return new Promise((resolve, reject) => {
// if the request was already aborted, prevent doing extra work
Expand Down Expand Up @@ -61,8 +90,8 @@ export class NodeHttpHandler implements HttpHandler {
req.on("error", reject);

// wire-up any timeout logic
setConnectionTimeout(req, reject, this.httpOptions.connectionTimeout);
setSocketTimeout(req, reject, this.httpOptions.socketTimeout);
setConnectionTimeout(req, reject, this.connectionTimeout);
setSocketTimeout(req, reject, this.socketTimeout);

// wire-up abort logic
if (abortSignal) {
Expand Down
22 changes: 20 additions & 2 deletions packages/node-http-handler/src/node-http2-handler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import { connect, constants, ClientHttp2Session } from "http2";

import { buildQueryString } from "@aws-sdk/querystring-builder";
import { HttpOptions, NodeHttp2Options } from "@aws-sdk/types";
import { HttpHandlerOptions } from "@aws-sdk/types";
import { HttpHandler, HttpRequest, HttpResponse } from "@aws-sdk/protocol-http";

import { writeRequestBody } from "./write-request-body";
import { getTransformedHeaders } from "./get-transformed-headers";

/**
* Represents the http2 options that can be passed to a node http2 client.
*/
export interface NodeHttp2Options {
/**
* The maximum time in milliseconds that a stream may remain idle before it
* is closed.
*/
requestTimeout?: number;

/**
* The maximum time in milliseconds that a session or socket may remain idle
* before it is closed.
* https://nodejs.org/docs/latest-v12.x/api/http2.html#http2_http2session_and_sockets
*/
sessionTimeout?: number;
}

export class NodeHttp2Handler implements HttpHandler {
private readonly connectionPool: Map<string, ClientHttp2Session>;

Expand All @@ -23,7 +41,7 @@ export class NodeHttp2Handler implements HttpHandler {

handle(
request: HttpRequest,
{ abortSignal }: HttpOptions
{ abortSignal }: HttpHandlerOptions
): Promise<{ response: HttpResponse }> {
return new Promise((resolve, reject) => {
// if the request was already aborted, prevent doing extra work
Expand Down
4 changes: 2 additions & 2 deletions packages/protocol-http/src/httpHandler.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { HttpRequest } from "./httpRequest";
import { HttpResponse } from "./httpResponse";
import { RequestHandler, HttpOptions } from "@aws-sdk/types";
import { RequestHandler, HttpHandlerOptions } from "@aws-sdk/types";

export type HttpHandler = RequestHandler<
HttpRequest,
HttpResponse,
HttpOptions
HttpHandlerOptions
>;
59 changes: 0 additions & 59 deletions packages/types/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,62 +100,3 @@ export interface ResolvedHttpResponse extends HttpResponse {
export interface HttpHandlerOptions {
abortSignal?: AbortSignal;
}

/**
* Represents the http options that can be shared across environments.
*/
export type HttpOptions = BrowserHttpOptions &
NodeHttpOptions & { abortSignal?: AbortSignal };

/**
* Represents the http options that can be passed to a browser http client.
*/
export interface BrowserHttpOptions {
/**
* The number of milliseconds a request can take before being automatically
* terminated.
*/
requestTimeout?: number;
}

/**
* Represents the http options that can be passed to a node http client.
*/
export interface NodeHttpOptions {
/**
* The maximum time in milliseconds that the connection phase of a request
* may take before the connection attempt is abandoned.
*/
connectionTimeout?: number;

/**
* Whether sockets should be kept open even when there are no outstanding
* requests so that future requests can forgo having to reestablish a TCP or
* TLS connection.
*/
keepAlive?: boolean;

/**
* The maximum time in milliseconds that a socket may remain idle before it
* is closed.
*/
socketTimeout?: number;
}

/**
* Represents the http2 options that can be passed to a node http2 client.
*/
export interface NodeHttp2Options extends HttpOptions {
/**
* The maximum time in milliseconds that a stream may remain idle before it
* is closed.
*/
requestTimeout?: number;

/**
* The maximum time in milliseconds that a session or socket may remain idle
* before it is closed.
* https://nodejs.org/docs/latest-v12.x/api/http2.html#http2_http2session_and_sockets
*/
sessionTimeout?: number;
}