You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I've done some more investigation since #11495 and believe I've found the issue.
If a server unexpectedly closes a connection during a call to fetch(), the next call to fetch() will fail.
This seems like a bug for 2 reasons:
Deno should probably check that the existing persistent connection is still valid (connected) before attempting to re-use it.
This behavior makes it really difficult to debug which fetch() is causing an error.
Steps to reproduce
Run this code against a server that closes a connection early.
constport=9999constbrokenURL=`http://127.0.0.1:${port}/`// If the server correctly reads all of the body, then there's no problem:constfixedURL=`http://127.0.0.1:${port}/?drain=true`// Bigger files seem to trigger this more reliably:constbigFile=newUint8Array(1024*1024*50)asyncfunctionget(): Promise<void>{// GETs always work OK because there's no body.constr=awaitfetch(brokenURL)if(!r.ok)throw{result: r,status: r.status,statusText: r.statusText}}asyncfunctionput(url: string){constr=awaitfetch(url,{method: "PUT",body: bigFile})if(!r.ok)throw{result: r,status: r.status,statusText: r.statusText}console.log("Put finished")}// These work fine:awaitget()awaitget()awaitget()awaitget()awaitget()awaitget()// This causes the keepalive connection to error, but in such a way that this// call to Deno's fetch() still succeedsawaitput(brokenURL)// This tries to reuse the keepalive connection that has been closed and dies.// IMO, this is a bug. As with a connection pool, it's best to make sure the// existing connection is still valid before trying to reuse it.awaitget()
I've reproduced this behavior in Actix-Web and Tide. Yes, it's likely best behavior to always read the body, but this is an easy error to make and likely a case that folks writing Deno scripts will run into again. I'm hoping to spare them my headaches. :)
Actix-Web
use std::io;use actix_web::{App,HttpResponse,HttpServer, web::{Payload,Query, route}};use futures::StreamExt;use serde::Deserialize;/// Responds to an HTTP request before the full body has been read. /// In Actix-web, this causes the conneciton to be dropped before the/// client expects, which causes difficult-to-debug behavior in Deno's/// fetch().///asyncfnfast_responder(Query(params):Query<Params>,mutreq_body:Payload,) -> HttpResponse{// If I wait for the full body *before* sending a response, things work:if params.drain.unwrap_or(false){while req_body.next().await.is_some(){}}HttpResponse::Ok().finish()}fnmain() -> io::Result<()>{let port = 9999;let server = HttpServer::new(|| {App::new().configure(|cfg| {
cfg.route("/",route().to(fast_responder));})}).bind(format!("127.0.0.1:{}", port))?;letmut system = actix_web::rt::System::new("web server");
system.block_on(server.run())?;Ok(())}#[derive(Deserialize)]structParams{drain:Option<bool>}
I've done some more investigation since #11495 and believe I've found the issue.
If a server unexpectedly closes a connection during a call to
fetch()
, the next call tofetch()
will fail.This seems like a bug for 2 reasons:
Steps to reproduce
Run this code against a server that closes a connection early.
I've reproduced this behavior in Actix-Web and Tide. Yes, it's likely best behavior to always read the body, but this is an easy error to make and likely a case that folks writing Deno scripts will run into again. I'm hoping to spare them my headaches. :)
Actix-Web
Tide
The text was updated successfully, but these errors were encountered: