-
Notifications
You must be signed in to change notification settings - Fork 619
Http Handlers #57
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
Http Handlers #57
Conversation
This PR has now been updated. I've tested the node http handler middleware on node.js, and the fetch handler middleware in chrome/firefox. |
packages/types/src/middleware.ts
Outdated
/** | ||
* An object that contains options to pass to the underlying http client. | ||
*/ | ||
httpOptions?: HttpOptions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix: HttpOptions -> HttpType
…nted as a class. Model added as HandlerArgs
Updates:
Example usage: const httpHandler = new NodeHttpHandler(/* HttpOptions */{
socketTimeout: 20 //ms
});
const restParser = new RestParser(
new JsonParser(
fromBase64
),
streamCollector,
toUtf8,
fromBase64
);
const coreHandler = new CoreHandler(
httpHandler,
restParser
);
/** Fake calling coreHandler at end of Middleware Stack */
let response = await coreHandler.handle({
request: signedRequest,
input: {},
model: ListFunctions
}); |
I'm not crazy about including the model in the arguments passed from handler to handler, but I'm also not sure I like how complex the alternatives would need to be. Those might include:
interface CoreHandlerCtor<Input, Output, Stream> {
new (context: HandlerExecutionContext): Handler<Input, Output, Stream>;
} The middleware stack's 2 Feels a bit more sound but maybe too complicated. It would also require instantiating more core handlers, which may not be as performant as simply including more properties in the HandlerArguments object. |
…o CoreHandler constructor
…g port to address issue
sigh I made code changes so that the model is no longer part of the handler args. This should be ready for a final review. Unfortunately, my node-http-handler tests (relies on a mock server) are failing with a networking error, and it only happens in code build (works using the same and other versions of node.js on local machine). I've tried the suggested tips online...so I'm approaching the point where I may just rip out using an actual server. ...I'll see how it looks in the morning. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, pending the CodeBuild-only test failures getting sorted out.
I had a couple nits and questions, but none of them were blocking.
packages/core-handler/src/index.ts
Outdated
InputType extends object, | ||
OutputType extends MetadataBearer, | ||
StreamType = Uint8Array | ||
> implements Handler<InputType, OutputType, StreamType> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nit: extra indentation
// if the request was already aborted, prevent doing extra work | ||
if (abortSignal && abortSignal.aborted) { | ||
const abortError = new Error('Request aborted'); | ||
abortError.name = 'AbortError'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've seen this pair of lines in a few places. Might make sense to have an AbortError constructor that automatically sets the name to 'AbortError'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there was an error in JS implementations when subclassing an error. Basically, instanceof Error checks returned false on subclasses of errors (I think there was a work-around if you weren't using es2015 classes).
That said, I can at the very least create refactor this into a function.
// pipe automatically handles end | ||
return; | ||
} else if (request.body) { | ||
req.write(request.body); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can just use req.end(request.body)
here to eliminate both the need for the separate call to .end
on line 93 and the early return on line 89.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, that'll work!
return; | ||
} | ||
|
||
request.on('socket', function(this: ClientRequest, socket: Socket) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how I feel about calling a function parameter this
. On my first reading of the callback, I thought you were able to call this.abort()
due to some undocumented binding on node's part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as comment below: this
isn't a function parameter, it's used by Typescript to get the function's context.
Side-note: VSCode recognizes this and uses a different color for this
than other parameters 😄
import {StreamCollector} from '@aws/types'; | ||
import {Collector} from './collector'; | ||
|
||
export const streamCollector: StreamCollector<Readable> = function streamCollector( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just export function streamCollector
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it this way so I could define the type StreamCollector<Readable>
, and enforce that the function conforms to the StreamCollector interface. I had tried writing it as just a function and use the as type
at the end, but that didn't work.
If you know a way to accomplish this, I'd be all for changing this to a function.
export const streamCollector: StreamCollector<Readable> = function streamCollector( | ||
stream | ||
): Promise<Uint8Array> { | ||
return new Promise((resolve, reject) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't occur when collecting an HTTP stream, but you would want to throw if the provided stream has an encoding set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I looked into this some more. There isn't a public interface for getting the encoding of the source stream. (stream._readableState.encoding
)
That said, I'm not sure we need to worry about it. Strings will automatically get converted to buffers as data is being piped. The user would have to set the encoding on the source stream to be different than what the actual source is, which at that point they're essentially shooting themselves in the foot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point. Objection withdrawn.
}); | ||
collector.on('error', reject); | ||
collector.on('finish', function(this: Collector) { | ||
const bytes = new Uint8Array(Buffer.concat(this.bufferedBytes)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still a bit thrown off by the use of this
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to record the explanation somewhere:
TS 2.0 introduced a way to specify what this
should be inside of a function:
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html
See Specifying the type of this for functions
* Represents the http options that can be passed to a browser http client. | ||
*/ | ||
export interface BrowserHttpOptions extends HttpOptions { | ||
requestTimeout?: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we have to manually timeout requests when using fetch, couldn't this property just be part of HttpOptions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes... I didn't want to also write this functionality for Node.js unless it was specifically asked for, which is why I only put it on the BrowserHttpOptions for now.
If you feel strongly that we should have request timeouts (separate from connection/socket-read timeouts) in node.js as well, I can add that.
} else { | ||
return response.arrayBuffer() | ||
.then(buffer => { | ||
httpResponse.body = buffer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be changed to httpResponse.body = new Uint8Array(buffer)
? This won't copy the buffer and allows us to declare the StreamType
of browser requests to be ReadableStream|Uint8Array
, which is easier for end users to disambiguate than ReadableStream|ArrayBuffer
due to the availability of ArrayBuffer.isView
.
* Adds stream collectors * Adds node-http-handler and fetch-http-handler * Adds core-handler
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread. |
This is an early WIP. The stream collectors shouldn't change much, but the http handlers will still change. Currently working on http tests.
Posting this as a reference.