diff --git a/ext/node/ops/http.rs b/ext/node/ops/http.rs index 57bcf69a47..1da630f97e 100644 --- a/ext/node/ops/http.rs +++ b/ext/node/ops/http.rs @@ -113,6 +113,9 @@ pub enum ConnError { #[error("Invalid URL {0}")] InvalidUrl(Url), #[class(type)] + #[error("Invalid Path {0}")] + InvalidPath(String), + #[class(type)] #[error(transparent)] InvalidHeaderName(#[from] http::header::InvalidHeaderName), #[class(type)] @@ -150,6 +153,7 @@ pub async fn op_node_http_request_with_conn

( state: Rc>, #[serde] method: ByteString, #[string] url: String, + #[string] request_path: Option, #[serde] headers: Vec<(ByteString, ByteString)>, #[smi] body: Option, #[smi] conn_rid: ResourceId, @@ -247,11 +251,17 @@ where *request.method_mut() = method.clone(); let path = url_parsed.path(); let query = url_parsed.query(); - *request.uri_mut() = query - .map(|q| format!("{}?{}", path, q)) - .unwrap_or_else(|| path.to_string()) - .parse() - .map_err(|_| ConnError::InvalidUrl(url_parsed.clone()))?; + if let Some(request_path) = request_path { + *request.uri_mut() = request_path + .parse() + .map_err(|_| ConnError::InvalidPath(request_path.clone()))?; + } else { + *request.uri_mut() = query + .map(|q| format!("{}?{}", path, q)) + .unwrap_or_else(|| path.to_string()) + .parse() + .map_err(|_| ConnError::InvalidUrl(url_parsed.clone()))?; + } *request.headers_mut() = header_map; if let Some((username, password)) = maybe_authority { diff --git a/ext/node/polyfills/http.ts b/ext/node/polyfills/http.ts index dd94c9d025..0438f9af22 100644 --- a/ext/node/polyfills/http.ts +++ b/ext/node/polyfills/http.ts @@ -479,6 +479,7 @@ class ClientRequest extends OutgoingMessage { this._req = await op_node_http_request_with_conn( this.method, url, + this._createRequestPath(), headers, this._bodyWriteRid, baseConnRid, @@ -817,6 +818,15 @@ class ClientRequest extends OutgoingMessage { return url.href; } + _createRequestPath(): string | undefined { + // If the path starts with protocol, pass this to op_node_http_request_with_conn + // This will be used as Request.uri in hyper for supporting http proxy + if (this.path?.startsWith("http://") || this.path?.startsWith("https://")) { + return this.path; + } + return undefined; + } + setTimeout(msecs: number, callback?: () => void) { if (msecs === 0) { if (this._timeout) { diff --git a/tests/unit_node/http_test.ts b/tests/unit_node/http_test.ts index b4f0d260aa..54803ab995 100644 --- a/tests/unit_node/http_test.ts +++ b/tests/unit_node/http_test.ts @@ -1892,3 +1892,27 @@ Deno.test("[node/http] an error with DNS propagates to request object", async () }); await promise; }); + +Deno.test("[node/http] supports proxy http request", async () => { + const { promise, resolve } = Promise.withResolvers(); + const server = Deno.serve({ port: 0, onListen }, (req) => { + console.log("server received", req.url); + assertEquals(req.url, "http://example.com/"); + return new Response("ok"); + }); + + function onListen({ port }: { port: number }) { + http.request({ + host: "localhost", + port, + path: "http://example.com", + }, async (res) => { + assertEquals(res.statusCode, 200); + assertEquals(await text(res), "ok"); + resolve(); + server.shutdown(); + }).end(); + } + await promise; + await server.finished; +});