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

feat(ext/http): Rework Deno.serve using hyper 1.0-rc3 #18619

Merged
merged 15 commits into from Apr 22, 2023

Conversation

mmastrac
Copy link
Member

@mmastrac mmastrac commented Apr 6, 2023

This is a rewrite of the Deno.serve API to live on top of hyper 1.0-rc3. The code should be more maintainable long-term, and avoids some of the slower mpsc patterns that made the older code less efficient than it could have been.

Missing features:

  • upgradeHttp and upgradeHttpRaw (upgradeWebSocket is available, however).
  • Automatic compression is unavailable on responses.

Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

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

A bit of a drive-by review

core/ops.rs Outdated Show resolved Hide resolved
ext/http/01_http.js Outdated Show resolved Hide resolved
ext/http/01_http.js Outdated Show resolved Hide resolved
ext/http/01_http.js Outdated Show resolved Hide resolved
ext/http/01_http.js Outdated Show resolved Hide resolved
ext/http/http2.rs Outdated Show resolved Hide resolved
@mmastrac mmastrac force-pushed the http branch 2 times, most recently from dc58d9c to 1549020 Compare April 15, 2023 01:05
bartlomieju added a commit that referenced this pull request Apr 16, 2023
#18718)

…polled ops (#18611)"

This reverts commit 4317055.

Reverting because we discovered while working on
#18619 that it doesn't work
correctly (sometimes waker just stops working).
@mmastrac mmastrac force-pushed the http branch 2 times, most recently from 9fd62a6 to 3c7971c Compare April 17, 2023 23:54
levex pushed a commit that referenced this pull request Apr 18, 2023
#18718)

…polled ops (#18611)"

This reverts commit 4317055.

Reverting because we discovered while working on
#18619 that it doesn't work
correctly (sometimes waker just stops working).
Comment on lines +1058 to +1064
"hyper 0.14.26",
"hyper 1.0.0-rc.3",
Copy link
Member

Choose a reason for hiding this comment

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

For posterity: this is on purpose - we are going to migrate "Deno.serve()" API to Hyper 1.0.0, while "Deno.serveHttp" will stay on the older version. After merging this PR we will migrate "Deno.serveHttp()" to use "Deno.serve()" under the hood and remove old Hyper version.

}
}

class InnerRequest {
Copy link
Member

Choose a reason for hiding this comment

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

Could you describe the purpose of this class? Can't we piggy back off of Request somehow?

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 thought I had replied to this earlier, but the InnerRequest is actually wrapped in a Request which handles all the webidl identity and some level of caching.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, I'm a bit worried about amount of garbage produced, but let's address that in a follow up

ext/http/00_serve.js Outdated Show resolved Hide resolved
ext/http/00_serve.js Outdated Show resolved Hide resolved
Comment on lines 117 to 124
// upgradeHttpRaw is async
if (upgradeType == "upgradeHttpRaw") {

}

// upgradeWebSocket is sync
if (upgradeType == "upgradeWebSocket") {
let [ response, ws ] = originalArgs;
Copy link
Member

Choose a reason for hiding this comment

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

Yikes, 3 different methods of upgrading seems like trouble 😬

ext/http/http_next.rs Outdated Show resolved Hide resolved
ext/http/http_next.rs Outdated Show resolved Hide resolved
ext/http/http_next.rs Outdated Show resolved Hide resolved
ext/http/http_next.rs Show resolved Hide resolved
Comment on lines 714 to 571
// TODO(mmastrac): This is faster if we can use tokio::spawn but then the send bounds get us
let safe_future = SafeFutureForSingleThread(Box::pin(async {
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 still thinking that maybe we shouldn't use spawn_local here but make it an op that returns a Future - that way we're still in the same Tokio task as the main loop - let's revisit in a follow up

cli/tests/unit/serve_test.ts Show resolved Hide resolved
}
}

class InnerRequest {
Copy link
Member

Choose a reason for hiding this comment

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

Okay, I'm a bit worried about amount of garbage produced, but let's address that in a follow up

ext/http/00_serve.js Outdated Show resolved Hide resolved
ext/http/00_serve.js Outdated Show resolved Hide resolved
ext/http/00_serve.js Outdated Show resolved Hide resolved
ext/http/http_next.rs Outdated Show resolved Hide resolved
ext/http/http_next.rs Outdated Show resolved Hide resolved
ext/http/http_next.rs Outdated Show resolved Hide resolved
ext/http/http_next.rs Outdated Show resolved Hide resolved
ext/http/request_body.rs Outdated Show resolved Hide resolved
@mmastrac mmastrac changed the title [WIP] Extremely experimental HTTP rewrite Rework Deno.serve to live on top of Hyper 1.0 Apr 19, 2023
@mmastrac mmastrac marked this pull request as ready for review April 19, 2023 22:19
@mmastrac mmastrac changed the title Rework Deno.serve to live on top of Hyper 1.0 feat(ext/http): Rework Deno.serve to live on top of Hyper 1.0 Apr 19, 2023
@mmastrac mmastrac changed the title feat(ext/http): Rework Deno.serve to live on top of Hyper 1.0 feat(ext/http): Rework Deno.serve using hyper 1.0-rc3 Apr 19, 2023
Copy link
Member

@lucacasonato lucacasonato left a comment

Choose a reason for hiding this comment

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

Are we going to port Deno.serveHttp to use this new backing?

ext/http/01_http.js Outdated Show resolved Hide resolved
}

// upgradeWebSocket is sync
if (upgradeType == "upgradeWebSocket") {
Copy link
Member

Choose a reason for hiding this comment

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

The new implementation of upgradeWebSocket does not seem correct. The actual upgrade should not begin until the token Response from the Deno.upgradeWebSocket function has been returned from the serve handler. Deno.upgradeWebSocket should not initiate I/O. It only sets up the token response that can initiate an upgrade later. (It entangles a WebSocket object with a token Response object)

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah yeah. This should probably be reflected in the documentation for upgradeWebSocket.

While I understand not wanting to break existing code, the old designs of these APIs are particularly nasty and deadlock-prone (upgradeHttp even has a deadlock warning in the doc page). Deno.serve is unstable and I think it's better to do things the right way in this API and migrate serveHttp over to the same in Deno 2.0.

As Deno.serve is unstable, there should be no breaking API concerns. Creating a response that the user has to shuttle off to the right place is a massive footgun and it seems like a big mistake to do that here rather than update the documentation

Comment on lines +153 to +215
if (this.#slabId === undefined) {
throw new TypeError("request closed");
}
Copy link
Member

Choose a reason for hiding this comment

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

It is exceedingly likely that this will break existing code, because significant existing code using the existing HTTP API relies on the fact that headers and url etc can be accessed even after the response is sent. An example is the GA4 handler in dotland.

Has there been any investigative work on this?

ext/http/00_serve.js Outdated Show resolved Hide resolved
ext/http/00_serve.js Outdated Show resolved Hide resolved
ext/http/http_next.rs Outdated Show resolved Hide resolved
ext/http/response_body.rs Show resolved Hide resolved
return std::task::Poll::Ready(None);
}
// Re-arm the future
*future = stm.clone().read(64 * 1024);
Copy link
Member

Choose a reason for hiding this comment

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

I am not convinced always using 64kb is good.

deno_core's op_read_all dynamically grows the buffer over time until diminishing returns, and uses resource.size_hint to inform an optimal starting buffer size. Something like this would probably be useful here too.

https://github.com/denoland/deno/blob/main/core/ops_builtin.rs#L209-L275

Copy link
Member Author

@mmastrac mmastrac Apr 20, 2023

Choose a reason for hiding this comment

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

64kB is exactly what's being read/written in op_http_write_resource right now so we're not really losing anything there, and there's already quite a bit of work in this PR.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, maybe a good follow up.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, totally. I'll keep this for a future task as we might see some wins here for some tasks.

ext/http/response_body.rs Outdated Show resolved Hide resolved
ext/net/raw.rs Outdated Show resolved Hide resolved
@bartlomieju
Copy link
Member

Are we going to port Deno.serveHttp to use this new backing?

Yes, see #18619 (comment)

ext/http/http_next.rs Outdated Show resolved Hide resolved
@mmastrac mmastrac force-pushed the http branch 2 times, most recently from 134b689 to 4e39eaa Compare April 21, 2023 15:00
@mmastrac mmastrac force-pushed the http branch 2 times, most recently from 92e2f42 to 35bf426 Compare April 21, 2023 18:50
@mmastrac mmastrac force-pushed the http branch 2 times, most recently from b29f5c7 to 65863d2 Compare April 21, 2023 18:53
Copy link
Member

@littledivy littledivy left a comment

Choose a reason for hiding this comment

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

op_ws_create is not working properly. wss stream WPT are failing.

It works If I revert back to tokio_rustls TlsConnector. See littledivy@8beb63e

ext/websocket/lib.rs Outdated Show resolved Hide resolved
ext/websocket/lib.rs Outdated Show resolved Hide resolved
ext/websocket/lib.rs Outdated Show resolved Hide resolved
Copy link
Member

@littledivy littledivy left a comment

Choose a reason for hiding this comment

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

LGTM with the above comment addressed. Great work @mmastrac !

Copy link
Member

@lucacasonato lucacasonato left a comment

Choose a reason for hiding this comment

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

Sorry for the late comment, but I just realized that this PR breaks h2c (plain text HTTP/2).

We should support this, as Deno.serveHttp supports it. It is widely used, for example when using h2c on Google Cloud Run, or Fly.io.

We need a test to ensure h2c support (or we need to add this as an immediate follow up).

@mmastrac mmastrac merged commit bdffcb4 into denoland:main Apr 22, 2023
11 checks passed
mmastrac added a commit that referenced this pull request Apr 23, 2023
…s requests cannot cause a panic (#18810)

Fix a bug where we weren't saving `slabId` in #18619, plus add some
robustness checks around multiple upgrades (w/test).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants