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
Conversation
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.
A bit of a drive-by review
dc58d9c
to
1549020
Compare
9fd62a6
to
3c7971c
Compare
"hyper 0.14.26", | ||
"hyper 1.0.0-rc.3", |
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.
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 { |
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 you describe the purpose of this class? Can't we piggy back off of Request
somehow?
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 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.
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.
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
// upgradeHttpRaw is async | ||
if (upgradeType == "upgradeHttpRaw") { | ||
|
||
} | ||
|
||
// upgradeWebSocket is sync | ||
if (upgradeType == "upgradeWebSocket") { | ||
let [ response, ws ] = originalArgs; |
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.
Yikes, 3 different methods of upgrading seems like trouble 😬
ext/http/http_next.rs
Outdated
// 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 { |
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 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
} | ||
} | ||
|
||
class InnerRequest { |
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.
Okay, I'm a bit worried about amount of garbage produced, but let's address that in a follow up
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.
Are we going to port Deno.serveHttp
to use this new backing?
} | ||
|
||
// upgradeWebSocket is sync | ||
if (upgradeType == "upgradeWebSocket") { |
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.
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)
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.
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
if (this.#slabId === undefined) { | ||
throw new TypeError("request closed"); | ||
} |
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.
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?
return std::task::Poll::Ready(None); | ||
} | ||
// Re-arm the future | ||
*future = stm.clone().read(64 * 1024); |
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 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
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.
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.
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.
Ok, maybe a good follow up.
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, totally. I'll keep this for a future task as we might see some wins here for some tasks.
Yes, see #18619 (comment) |
134b689
to
4e39eaa
Compare
92e2f42
to
35bf426
Compare
b29f5c7
to
65863d2
Compare
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.
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
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 with the above comment addressed. Great work @mmastrac !
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.
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).
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
andupgradeHttpRaw
(upgradeWebSocket
is available, however).