diff options
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/http/01_http.js (renamed from extensions/net/03_http.js) | 7 | ||||
-rw-r--r-- | extensions/http/Cargo.toml | 25 | ||||
-rw-r--r-- | extensions/http/README.md | 4 | ||||
-rw-r--r-- | extensions/http/lib.deno_http.unstable.d.ts | 65 | ||||
-rw-r--r-- | extensions/http/lib.rs (renamed from extensions/net/ops_http.rs) | 132 | ||||
-rw-r--r-- | extensions/net/Cargo.toml | 9 | ||||
-rw-r--r-- | extensions/net/lib.deno_net.unstable.d.ts | 44 | ||||
-rw-r--r-- | extensions/net/lib.rs | 3 |
8 files changed, 148 insertions, 141 deletions
diff --git a/extensions/net/03_http.js b/extensions/http/01_http.js index db2d0a3b1..4bcdf1f07 100644 --- a/extensions/net/03_http.js +++ b/extensions/http/01_http.js @@ -32,11 +32,6 @@ Uint8Array, } = window.__bootstrap.primordials; - function serveHttp(conn) { - const rid = core.opSync("op_http_start", conn.rid); - return new HttpConn(rid); - } - const connErrorSymbol = Symbol("connError"); class HttpConn { @@ -373,7 +368,7 @@ } window.__bootstrap.http = { - serveHttp, + HttpConn, upgradeWebSocket, }; })(this); diff --git a/extensions/http/Cargo.toml b/extensions/http/Cargo.toml new file mode 100644 index 000000000..8909301a6 --- /dev/null +++ b/extensions/http/Cargo.toml @@ -0,0 +1,25 @@ +# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +[package] +name = "deno_http" +version = "0.1.0" +edition = "2018" +description = "HTTP server implementation for Deno" +authors = ["the Deno authors"] +license = "MIT" +readme = "README.md" +repository = "https://github.com/denoland/deno" + +[lib] +path = "lib.rs" + +[dependencies] +base64 = "0.13.0" +bytes = "1" +deno_core = { version = "0.92.0", path = "../../core" } +deno_websocket = { version = "0.15.1", path = "../websocket" } +hyper = { version = "0.14.9", features = ["server", "stream", "http1", "http2", "runtime"] } +ring = "0.16.20" +serde = { version = "1.0.125", features = ["derive"] } +tokio = { version = "1.8.0", features = ["full"] } +tokio-util = "0.6.7" diff --git a/extensions/http/README.md b/extensions/http/README.md new file mode 100644 index 000000000..ab557017a --- /dev/null +++ b/extensions/http/README.md @@ -0,0 +1,4 @@ +# deno_http + +This crate implements server-side HTTP based on primitives from the +[Fetch API](https://fetch.spec.whatwg.org/). diff --git a/extensions/http/lib.deno_http.unstable.d.ts b/extensions/http/lib.deno_http.unstable.d.ts new file mode 100644 index 000000000..30ffe121e --- /dev/null +++ b/extensions/http/lib.deno_http.unstable.d.ts @@ -0,0 +1,65 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +/// <reference no-default-lib="true" /> +/// <reference lib="esnext" /> + +declare namespace Deno { + export interface RequestEvent { + readonly request: Request; + respondWith(r: Response | Promise<Response>): Promise<void>; + } + + export interface HttpConn extends AsyncIterable<RequestEvent> { + readonly rid: number; + + nextRequest(): Promise<RequestEvent | null>; + close(): void; + } + + export interface WebSocketUpgrade { + response: Response; + websocket: WebSocket; + } + + export interface UpgradeWebSocketOptions { + protocol?: string; + } + + /** **UNSTABLE**: new API, yet to be vetted. + * + * Used to upgrade an incoming HTTP request to a WebSocket. + * + * Given a request, returns a pair of WebSocket and Response. The original + * request must be responded to with the returned response for the websocket + * upgrade to be successful. + * + * ```ts + * const conn = await Deno.connect({ port: 80, hostname: "127.0.0.1" }); + * const httpConn = Deno.serveHttp(conn); + * const e = await httpConn.nextRequest(); + * if (e) { + * const { websocket, response } = Deno.upgradeWebSocket(e.request); + * websocket.onopen = () => { + * websocket.send("Hello World!"); + * }; + * websocket.onmessage = (e) => { + * console.log(e.data); + * websocket.close(); + * }; + * websocket.onclose = () => console.log("WebSocket has been closed."); + * websocket.onerror = (e) => console.error("WebSocket error:", e.message); + * e.respondWith(response); + * } + * ``` + * + * If the request body is disturbed (read from) before the upgrade is + * completed, upgrading fails. + * + * This operation does not yet consume the request or open the websocket. This + * only happens once the returned response has been passed to `respondWith`. + */ + export function upgradeWebSocket( + request: Request, + options?: UpgradeWebSocketOptions, + ): WebSocketUpgrade; +} diff --git a/extensions/net/ops_http.rs b/extensions/http/lib.rs index 782ec91d0..a8d92ab46 100644 --- a/extensions/net/ops_http.rs +++ b/extensions/http/lib.rs @@ -1,7 +1,5 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use crate::io::TcpStreamResource; -use crate::io::TlsStreamResource; use deno_core::error::bad_resource_id; use deno_core::error::null_opbuf; use deno_core::error::type_error; @@ -10,13 +8,14 @@ use deno_core::futures::future::poll_fn; use deno_core::futures::FutureExt; use deno_core::futures::Stream; use deno_core::futures::StreamExt; +use deno_core::include_js_files; use deno_core::op_async; use deno_core::op_sync; use deno_core::AsyncRefCell; use deno_core::ByteString; use deno_core::CancelHandle; use deno_core::CancelTryFuture; -use deno_core::OpPair; +use deno_core::Extension; use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; @@ -35,31 +34,43 @@ use std::borrow::Cow; use std::cell::RefCell; use std::future::Future; use std::net::SocketAddr; +use std::path::PathBuf; use std::pin::Pin; use std::rc::Rc; use std::task::Context; use std::task::Poll; +use tokio::io::AsyncRead; use tokio::io::AsyncReadExt; +use tokio::io::AsyncWrite; use tokio::sync::oneshot; use tokio_util::io::StreamReader; -pub fn init() -> Vec<OpPair> { - vec![ - ("op_http_start", op_sync(op_http_start)), - ("op_http_request_next", op_async(op_http_request_next)), - ("op_http_request_read", op_async(op_http_request_read)), - ("op_http_response", op_async(op_http_response)), - ("op_http_response_write", op_async(op_http_response_write)), - ("op_http_response_close", op_async(op_http_response_close)), - ( - "op_http_websocket_accept_header", - op_sync(op_http_websocket_accept_header), - ), - ( - "op_http_upgrade_websocket", - op_async(op_http_upgrade_websocket), - ), - ] +pub fn get_unstable_declaration() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_http.unstable.d.ts") +} + +pub fn init() -> Extension { + Extension::builder() + .js(include_js_files!( + prefix "deno:extensions/http", + "01_http.js", + )) + .ops(vec![ + ("op_http_request_next", op_async(op_http_request_next)), + ("op_http_request_read", op_async(op_http_request_read)), + ("op_http_response", op_async(op_http_response)), + ("op_http_response_write", op_async(op_http_response_write)), + ("op_http_response_close", op_async(op_http_response_close)), + ( + "op_http_websocket_accept_header", + op_sync(op_http_websocket_accept_header), + ), + ( + "op_http_upgrade_websocket", + op_async(op_http_upgrade_websocket), + ), + ]) + .build() } struct ServiceInner { @@ -106,13 +117,13 @@ type ConnFuture = Pin<Box<dyn Future<Output = hyper::Result<()>>>>; struct Conn { scheme: &'static str, + addr: SocketAddr, conn: Rc<RefCell<ConnFuture>>, } struct ConnResource { hyper_connection: Conn, deno_service: Service, - addr: SocketAddr, cancel: CancelHandle, } @@ -221,7 +232,7 @@ async fn op_http_request_next( } else if let Some(host) = req.headers().get("HOST") { Cow::Borrowed(host.to_str()?) } else { - Cow::Owned(conn_resource.addr.to_string()) + Cow::Owned(conn_resource.hyper_connection.addr.to_string()) }; let path = req.uri().path_and_query().map_or("/", |p| p.as_str()); format!("{}://{}{}", scheme, host, path) @@ -299,69 +310,30 @@ fn should_ignore_error(e: &AnyError) -> bool { false } -fn op_http_start( +pub fn start_http<IO: AsyncRead + AsyncWrite + Unpin + Send + 'static>( state: &mut OpState, - tcp_stream_rid: ResourceId, - _: (), + io: IO, + addr: SocketAddr, + scheme: &'static str, ) -> Result<ResourceId, AnyError> { let deno_service = Service::default(); - if let Some(resource_rc) = state - .resource_table - .take::<TcpStreamResource>(tcp_stream_rid) - { - let resource = Rc::try_unwrap(resource_rc) - .expect("Only a single use of this resource should happen"); - let (read_half, write_half) = resource.into_inner(); - let tcp_stream = read_half.reunite(write_half)?; - let addr = tcp_stream.local_addr()?; - let hyper_connection = Http::new() - .with_executor(LocalExecutor) - .serve_connection(tcp_stream, deno_service.clone()) - .with_upgrades(); - let conn = Pin::new(Box::new(hyper_connection)); - let conn_resource = ConnResource { - hyper_connection: Conn { - conn: Rc::new(RefCell::new(conn)), - scheme: "http", - }, - deno_service, + let hyper_connection = Http::new() + .with_executor(LocalExecutor) + .serve_connection(io, deno_service.clone()) + .with_upgrades(); + let conn = Pin::new(Box::new(hyper_connection)); + let conn_resource = ConnResource { + hyper_connection: Conn { + scheme, addr, - cancel: CancelHandle::default(), - }; - let rid = state.resource_table.add(conn_resource); - return Ok(rid); - } - - if let Some(resource_rc) = state - .resource_table - .take::<TlsStreamResource>(tcp_stream_rid) - { - let resource = Rc::try_unwrap(resource_rc) - .expect("Only a single use of this resource should happen"); - let (read_half, write_half) = resource.into_inner(); - let tls_stream = read_half.reunite(write_half); - let addr = tls_stream.get_ref().0.local_addr()?; - - let hyper_connection = Http::new() - .with_executor(LocalExecutor) - .serve_connection(tls_stream, deno_service.clone()) - .with_upgrades(); - let conn = Pin::new(Box::new(hyper_connection)); - let conn_resource = ConnResource { - hyper_connection: Conn { - conn: Rc::new(RefCell::new(conn)), - scheme: "https", - }, - deno_service, - addr, - cancel: CancelHandle::default(), - }; - let rid = state.resource_table.add(conn_resource); - return Ok(rid); - } - - Err(bad_resource_id()) + conn: Rc::new(RefCell::new(conn)), + }, + deno_service, + cancel: CancelHandle::default(), + }; + let rid = state.resource_table.add(conn_resource); + Ok(rid) } // We use a tuple instead of struct to avoid serialization overhead of the keys. diff --git a/extensions/net/Cargo.toml b/extensions/net/Cargo.toml index c6219cad4..352308925 100644 --- a/extensions/net/Cargo.toml +++ b/extensions/net/Cargo.toml @@ -15,19 +15,12 @@ path = "lib.rs" [dependencies] deno_core = { version = "0.92.0", path = "../../core" } -deno_websocket = { version = "0.15.0", path = "../websocket" } -base64 = "0.13.0" -bytes = "1" log = "0.4.14" lazy_static = "1.4.0" -http = "0.2.4" -hyper = { version = "0.14.10", features = ["server", "stream", "http1", "http2", "runtime"] } -ring = "0.16.20" -rustls = "0.19.1" +rustls = "0.19.0" serde = { version = "1.0.126", features = ["derive"] } tokio = { version = "1.8.1", features = ["full"] } -tokio-util = { version = "0.6", features = ["io"] } webpki = "0.21.4" webpki-roots = "0.21.1" trust-dns-proto = "0.20.3" diff --git a/extensions/net/lib.deno_net.unstable.d.ts b/extensions/net/lib.deno_net.unstable.d.ts index c47558edc..adeeb1466 100644 --- a/extensions/net/lib.deno_net.unstable.d.ts +++ b/extensions/net/lib.deno_net.unstable.d.ts @@ -229,48 +229,4 @@ declare namespace Deno { */ alpnProtocols?: string[]; } - - export interface RequestEvent { - readonly request: Request; - respondWith(r: Response | Promise<Response>): Promise<void>; - } - - export interface HttpConn extends AsyncIterable<RequestEvent> { - readonly rid: number; - - nextRequest(): Promise<RequestEvent | null>; - close(): void; - } - - /** **UNSTABLE**: new API, yet to be vetted. - * - * Services HTTP requests given a TCP or TLS socket. - * - * ```ts - * const conn = await Deno.connect({ port: 80, hostname: "127.0.0.1" }); - * const httpConn = Deno.serveHttp(conn); - * const e = await httpConn.nextRequest(); - * if (e) { - * e.respondWith(new Response("Hello World")); - * } - * ``` - * - * If `httpConn.nextRequest()` encounters an error or returns `null` - * then the underlying HttpConn resource is closed automatically. - */ - export function serveHttp(conn: Conn): HttpConn; - - export interface WebSocketUpgrade { - response: Response; - websocket: WebSocket; - } - - export interface UpgradeWebSocketOptions { - protocol?: string; - } - - export function upgradeWebSocket( - request: Request, - options?: UpgradeWebSocketOptions, - ): WebSocketUpgrade; } diff --git a/extensions/net/lib.rs b/extensions/net/lib.rs index d1e836fce..f3281a2fb 100644 --- a/extensions/net/lib.rs +++ b/extensions/net/lib.rs @@ -2,7 +2,6 @@ pub mod io; pub mod ops; -pub mod ops_http; pub mod ops_tls; #[cfg(unix)] pub mod ops_unix; @@ -94,14 +93,12 @@ pub fn init<P: NetPermissions + 'static>(unstable: bool) -> Extension { ops_to_register.extend(io::init()); ops_to_register.extend(ops::init::<P>()); ops_to_register.extend(ops_tls::init::<P>()); - ops_to_register.extend(ops_http::init()); Extension::builder() .js(include_js_files!( prefix "deno:extensions/net", "01_net.js", "02_tls.js", - "03_http.js", "04_net_unstable.js", )) .ops(ops_to_register) |