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

WIP: http2: improve compatibility with http1, add tests #15633

Closed

Conversation

apapirovski
Copy link
Member

@apapirovski apapirovski commented Sep 27, 2017

This PR brings near full compatibility with express's use of http1 API. List of updates:

  • Add complete prop for Http2ServerRequest
  • Add method setter since some user-land modules use this
  • Add socket and connection on Http2ServerResponse
  • Major rework of writeHead, write, end, addHeader and removeHeader. A lot of user-land modules, including express, rely on the fact that in http1 all write methods call writeHead if headers haven't been written yet.
  • Add support for http1 style handling of HEAD requests (expects end call and triggers callback, even if writeHead already happened)
  • Remove closure created for unsetting stream reference

Please let me know if I can provide more info for any of these changes. I know there's a lot here and some of the logic is a bit complicated in order to replicate http1 behaviour. It came a long way since my 1st pass at it.

(Re: express, after this PR only 5 tests are failing and they're a result of other modules relying on specific http1-only behaviour. This should bode well for compatibility with other popular libraries too.)

Thanks in advance for any and all reviews!

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

http2, test

@nodejs-github-bot nodejs-github-bot added the http2 Issues or PRs related to the http2 subsystem. label Sep 27, 2017
@@ -234,6 +235,10 @@ class Http2ServerRequest extends Readable {
return this[kHeaders][HTTP2_HEADER_METHOD];
}

set method(method) {
this[kHeaders][HTTP2_HEADER_METHOD] = method;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this include a basic type check to ensure that method is a non-empty string?

Copy link
Member Author

@apapirovski apapirovski Sep 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. http1 obviously didn't have it since it's not a getter/setter but I don't see why we couldn't do that for http2.

get closed() {
return this[kState].closed;

get socket() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we shouldn't more proactively warn people away from accessing this... e.g. by using an explicit runtime deprecation warning right from the start.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to statusMessageWarn? We could definitely do that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, exactly like that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as there is a way to retrieve the remote IP address for the socket (and local port, etc.); ideally symmetrically with http/1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we will keep the socket available but just add a one-time warning re: its usage.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point and a good reminder! I had that marked down quite some time ago as a todo for an API that would make fetching those details easier but hadn't gotten around to it. We can make that data available via the Http2Session object so that the socket property does not have to be accessed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we will keep the socket available but just add a one-time warning re: its usage.

So getting the remote IP will print a warning?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just getting it via the request and response objects should print a warning. We should add apis that allow this information to be retrieved independently of that so that there's a path that (a) does not print the warning and (b) does not mean users are accessing the socket directly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Would that land prior to landing a warning on the socket getter, then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably do those at the same time actually.

Extensive rework of Http2ServerResponse behaviours based on
the expressjs test suite in order to get as close as
possible to mimicking http1. Adds tests for all added and
changed behaviour.
@apapirovski apapirovski force-pushed the patch-http2-compat-head-and-more branch from 258f895 to 680e507 Compare September 28, 2017 13:06
@apapirovski
Copy link
Member Author

apapirovski commented Sep 28, 2017

Made the requested changes. Will work on adding the ability to access the address info next (within this PR) unless @jasnell would prefer to do it or prefers it separated out into a new PR.

On that note, we can't add 'session' getter to request or response (conflicts with existing user-land modules), do we just make users get it via request.stream.session or different alias or do we expose the address stuff on the stream too?

@jasnell
Copy link
Member

jasnell commented Sep 28, 2017

Go for it! I'm going to be tied up in meetings and flights for most of the day so definitely have at it and don't wait on me! :-)

@apapirovski apapirovski changed the title http2: improve compatibility with http1, add tests WIP: http2: improve compatibility with http1, add tests Sep 29, 2017
@apapirovski
Copy link
Member Author

apapirovski commented Sep 29, 2017

This is undergoing somewhat major changes atm so please no reviews for now as it'll need to be reviewed after the next commit. I'm working on a solution for the socket problem and we should be able to get http2 to work with on-finished as a result (which is the biggest blocker for full express compatibility).

I will in all likelihood open a new PR once that work is done (and close this one, putting it in Ref for the commit) since a lot of the changes made here will look different + there'll be new ones.

@apapirovski
Copy link
Member Author

apapirovski commented Sep 29, 2017

I have basically 100% compatibility with express, on-finished and finalhandler now (all tests are passing). Working on test cases covering all the edge cases, might take a day or two before I have a PR — depending on my schedule.

(I'm using a proxy object for the socket and applying things like 'on', 'emit', 'readable', etc. to the Http2Stream instead while anything unaccounted for hits the socket. As a result of some other changes, performance for compatibility mode actually improved despite the usage of a Proxy.)

Closing this now. Will open a new PR when done.

@mcollina
Copy link
Member

I think you inadvertently pushed the closed button, I'm reopening.

@mcollina mcollina reopened this Sep 30, 2017
@mcollina mcollina closed this Sep 30, 2017
@mcollina
Copy link
Member

Oh my bad, I didn't read the last sentence

apapirovski added a commit to apapirovski/node that referenced this pull request Oct 3, 2017
Extensive re-work of http1 compatibility layer based on tests in
express, on-finished and finalhandler. Fix handling of HEAD
method to match http1. Adjust write, end, etc. to call writeHead
as in http1 and as expected by user-land modules. Add socket
proxy that instead uses the Http2Stream for the vast majority of
socket interactions. Add and change tests to closer represent
http1 behaviour.

Refs: nodejs#15633
Refs: https://github.com/expressjs/express/tree/master/test
Refs: https://github.com/jshttp/on-finished/blob/master/test/test.js
Refs: https://github.com/pillarjs/finalhandler/blob/master/test/test.js
mcollina pushed a commit that referenced this pull request Oct 6, 2017
Extensive re-work of http1 compatibility layer based on tests in
express, on-finished and finalhandler. Fix handling of HEAD
method to match http1. Adjust write, end, etc. to call writeHead
as in http1 and as expected by user-land modules. Add socket
proxy that instead uses the Http2Stream for the vast majority of
socket interactions. Add and change tests to closer represent
http1 behaviour.

Refs: #15633
Refs: https://github.com/expressjs/express/tree/master/test
Refs: https://github.com/jshttp/on-finished/blob/master/test/test.js
Refs: https://github.com/pillarjs/finalhandler/blob/master/test/test.js
PR-URL: #15702
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
MylesBorins pushed a commit that referenced this pull request Oct 7, 2017
Extensive re-work of http1 compatibility layer based on tests in
express, on-finished and finalhandler. Fix handling of HEAD
method to match http1. Adjust write, end, etc. to call writeHead
as in http1 and as expected by user-land modules. Add socket
proxy that instead uses the Http2Stream for the vast majority of
socket interactions. Add and change tests to closer represent
http1 behaviour.

Refs: #15633
Refs: https://github.com/expressjs/express/tree/master/test
Refs: https://github.com/jshttp/on-finished/blob/master/test/test.js
Refs: https://github.com/pillarjs/finalhandler/blob/master/test/test.js
PR-URL: #15702
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
MylesBorins pushed a commit that referenced this pull request Oct 11, 2017
Extensive re-work of http1 compatibility layer based on tests in
express, on-finished and finalhandler. Fix handling of HEAD
method to match http1. Adjust write, end, etc. to call writeHead
as in http1 and as expected by user-land modules. Add socket
proxy that instead uses the Http2Stream for the vast majority of
socket interactions. Add and change tests to closer represent
http1 behaviour.

Refs: #15633
Refs: https://github.com/expressjs/express/tree/master/test
Refs: https://github.com/jshttp/on-finished/blob/master/test/test.js
Refs: https://github.com/pillarjs/finalhandler/blob/master/test/test.js
PR-URL: #15702
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
addaleax pushed a commit to addaleax/ayo that referenced this pull request Oct 12, 2017
Extensive re-work of http1 compatibility layer based on tests in
express, on-finished and finalhandler. Fix handling of HEAD
method to match http1. Adjust write, end, etc. to call writeHead
as in http1 and as expected by user-land modules. Add socket
proxy that instead uses the Http2Stream for the vast majority of
socket interactions. Add and change tests to closer represent
http1 behaviour.

Refs: nodejs/node#15633
Refs: https://github.com/expressjs/express/tree/master/test
Refs: https://github.com/jshttp/on-finished/blob/master/test/test.js
Refs: https://github.com/pillarjs/finalhandler/blob/master/test/test.js
PR-URL: nodejs/node#15702
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
@apapirovski apapirovski deleted the patch-http2-compat-head-and-more branch October 14, 2017 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http2 Issues or PRs related to the http2 subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants