1use crate::host::{invoke, with_host, IoTask, JsObj};
74use fusevm::Value;
75use hpack::{Decoder, Encoder};
76use indexmap::IndexMap;
77use rustls::pki_types::{CertificateDer, PrivateKeyDer};
78use rustls::{ServerConfig, ServerConnection, StreamOwned};
79use std::collections::HashMap;
80use std::io::{Read, Write};
81use std::net::{TcpListener, TcpStream};
82use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
83use std::sync::mpsc::Sender;
84use std::sync::Arc;
85
86pub const METHODS: &[&str] = &[
88 "createSecureServer",
89 "createServer",
90 "connect",
91 "getDefaultSettings",
92 "getPackedSettings",
93 "getUnpackedSettings",
94];
95
96pub const SERVER_METHODS: &[&str] = &["listen", "close", "address", "setTimeout"];
99pub const STREAM_METHODS: &[&str] = &[
100 "respond",
101 "write",
102 "end",
103 "close",
104 "setEncoding",
105 "setTimeout",
106 "pause",
107 "resume",
108 "writeHead",
110 "setHeader",
111 "getHeader",
112 "removeHeader",
113];
114pub const SESSION_METHODS: &[&str] = &[
115 "settings",
116 "ping",
117 "goaway",
118 "close",
119 "destroy",
120 "ref",
121 "unref",
122 "setTimeout",
123];
124
125const PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
128
129const FT_DATA: u8 = 0x0;
131const FT_HEADERS: u8 = 0x1;
132const FT_PRIORITY: u8 = 0x2;
133const FT_RST_STREAM: u8 = 0x3;
134const FT_SETTINGS: u8 = 0x4;
135const FT_PING: u8 = 0x6;
136const FT_GOAWAY: u8 = 0x7;
137const FT_WINDOW_UPDATE: u8 = 0x8;
138const FT_CONTINUATION: u8 = 0x9;
139
140const FL_END_STREAM: u8 = 0x1;
142const FL_ACK: u8 = 0x1; const FL_END_HEADERS: u8 = 0x4;
144const FL_PADDED: u8 = 0x8;
145const FL_PRIORITY: u8 = 0x20;
146
147const MAX_FRAME_SIZE: usize = 16384;
149
150static NEXT_SERVER_ID: AtomicU64 = AtomicU64::new(1);
153static NEXT_STREAM_KEY: AtomicU64 = AtomicU64::new(1);
154static NEXT_SESSION_KEY: AtomicU64 = AtomicU64::new(1);
155
156fn next_server_id() -> u64 {
157 NEXT_SERVER_ID.fetch_add(1, Ordering::Relaxed)
158}
159fn next_stream_key() -> u64 {
160 NEXT_STREAM_KEY.fetch_add(1, Ordering::Relaxed)
161}
162fn next_session_key() -> u64 {
163 NEXT_SESSION_KEY.fetch_add(1, Ordering::Relaxed)
164}
165
166enum H2Cmd {
170 Respond {
173 stream_id: u32,
174 headers: Vec<(String, String)>,
175 end: bool,
176 },
177 Data {
179 stream_id: u32,
180 data: Vec<u8>,
181 end: bool,
182 },
183 Close { stream_id: u32 },
185 Goaway,
187}
188
189struct H2ServerRec {
192 emitter: Value,
193 stop: Arc<AtomicBool>,
194}
195
196struct H2StreamRec {
197 emitter: Value,
198 tx: Sender<H2Cmd>,
199 stream_id: u32,
200 responded: bool,
202}
203
204struct H2SessionRec {
205 #[allow(dead_code)]
206 emitter: Value,
207 tx: Sender<H2Cmd>,
208}
209
210#[derive(Default)]
211struct H2State {
212 servers: HashMap<u64, H2ServerRec>,
213 streams: HashMap<u64, H2StreamRec>,
214 sessions: HashMap<u64, H2SessionRec>,
215}
216
217thread_local! {
218 static H2: std::cell::RefCell<H2State> = std::cell::RefCell::new(H2State::default());
219 static PENDING_CONFIGS: std::cell::RefCell<Vec<(Value, Arc<ServerConfig>)>> =
222 const { std::cell::RefCell::new(Vec::new()) };
223}
224
225fn get_prop(recv: &Value, key: &str) -> Option<Value> {
228 with_host(|h| match h.get(recv) {
229 Some(JsObj::Object(p)) => p.get(key).cloned(),
230 _ => None,
231 })
232}
233
234fn set_prop(recv: &Value, key: &str, val: Value) {
235 with_host(|h| {
236 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
237 p.insert(key.to_string(), val);
238 }
239 });
240}
241
242fn u64_prop(recv: &Value, key: &str) -> Option<u64> {
243 get_prop(recv, key).map(|v| with_host(|h| h.to_number(&v)) as u64)
244}
245
246fn emitter_dispatch(recv: &Value, method: &str, args: &[Value]) -> Option<Result<Value, String>> {
248 match method {
249 "on"
250 | "addListener"
251 | "prependListener"
252 | "once"
253 | "prependOnceListener"
254 | "emit"
255 | "removeListener"
256 | "off"
257 | "removeAllListeners"
258 | "listenerCount"
259 | "eventNames"
260 | "setMaxListeners"
261 | "getMaxListeners"
262 | "listeners" => Some(super::events::instance_call(recv, method, args.to_vec())),
263 _ => None,
264 }
265}
266
267fn value_bytes(v: Option<&Value>) -> Vec<u8> {
269 let Some(v) = v else { return Vec::new() };
270 let is_buffer =
271 with_host(|h| matches!(h.get(v), Some(JsObj::Object(p)) if p.contains_key("@@bytes")));
272 if is_buffer {
273 return with_host(|h| match h.get(v) {
274 Some(JsObj::Object(p)) => match p.get("@@bytes").and_then(|b| h.get(b)) {
275 Some(JsObj::Array(items)) => items.iter().map(|x| h.to_number(x) as u8).collect(),
276 _ => Vec::new(),
277 },
278 _ => Vec::new(),
279 });
280 }
281 with_host(|h| h.str_of(v)).into_bytes()
282}
283
284fn object_pairs(obj: &Value) -> Vec<(String, String)> {
287 with_host(|h| match h.get(obj) {
288 Some(JsObj::Object(p)) => p
289 .iter()
290 .filter(|(k, _)| !k.starts_with("@@") && !k.starts_with('#'))
291 .map(|(k, v)| (k.clone(), h.str_of(v)))
292 .collect(),
293 _ => Vec::new(),
294 })
295}
296
297pub fn call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
301 Some(match method {
302 "createSecureServer" => create_secure_server(args),
303 "createServer" => Err(
304 "Error: http2.createServer (cleartext h2c / prior-knowledge) \
305 is not implemented in node-js; use http2.createSecureServer (h2 over TLS)"
306 .to_string(),
307 ),
308 "connect" => Err(
309 "Error: http2.connect (HTTP/2 client) is not implemented in node-js; \
310 only the HTTP/2 server (http2.createSecureServer) is implemented"
311 .to_string(),
312 ),
313 "getDefaultSettings" => Ok(default_settings_object()),
314 "getPackedSettings" => Ok(pack_settings(args)),
315 "getUnpackedSettings" => unpack_settings(args),
316 _ => return None,
317 })
318}
319
320pub fn constant(name: &str) -> Option<Value> {
323 match name {
324 "constants" => Some(constants_object()),
325 "Http2ServerRequest" => Some(with_host(|h| {
328 h.alloc(JsObj::Builtin("Http2ServerRequest".into()))
329 })),
330 "Http2ServerResponse" => Some(with_host(|h| {
331 h.alloc(JsObj::Builtin("Http2ServerResponse".into()))
332 })),
333 _ => None,
334 }
335}
336
337fn constants_object() -> Value {
340 with_host(|h| {
341 let mut m = IndexMap::new();
342 let put_i = |m: &mut IndexMap<String, Value>, k: &str, v: i64| {
343 m.insert(k.to_string(), Value::Float(v as f64));
344 };
345 put_i(&mut m, "HTTP_STATUS_OK", 200);
347 put_i(&mut m, "HTTP_STATUS_NO_CONTENT", 204);
348 put_i(&mut m, "HTTP_STATUS_MOVED_PERMANENTLY", 301);
349 put_i(&mut m, "HTTP_STATUS_FOUND", 302);
350 put_i(&mut m, "HTTP_STATUS_NOT_MODIFIED", 304);
351 put_i(&mut m, "HTTP_STATUS_BAD_REQUEST", 400);
352 put_i(&mut m, "HTTP_STATUS_UNAUTHORIZED", 401);
353 put_i(&mut m, "HTTP_STATUS_FORBIDDEN", 403);
354 put_i(&mut m, "HTTP_STATUS_NOT_FOUND", 404);
355 put_i(&mut m, "HTTP_STATUS_INTERNAL_SERVER_ERROR", 500);
356 put_i(&mut m, "NGHTTP2_NO_ERROR", 0x0);
358 put_i(&mut m, "NGHTTP2_PROTOCOL_ERROR", 0x1);
359 put_i(&mut m, "NGHTTP2_INTERNAL_ERROR", 0x2);
360 put_i(&mut m, "NGHTTP2_FLOW_CONTROL_ERROR", 0x3);
361 put_i(&mut m, "NGHTTP2_SETTINGS_TIMEOUT", 0x4);
362 put_i(&mut m, "NGHTTP2_STREAM_CLOSED", 0x5);
363 put_i(&mut m, "NGHTTP2_FRAME_SIZE_ERROR", 0x6);
364 put_i(&mut m, "NGHTTP2_REFUSED_STREAM", 0x7);
365 put_i(&mut m, "NGHTTP2_CANCEL", 0x8);
366 put_i(&mut m, "NGHTTP2_COMPRESSION_ERROR", 0x9);
367 put_i(&mut m, "NGHTTP2_ENHANCE_YOUR_CALM", 0xb);
368 put_i(&mut m, "NGHTTP2_SETTINGS_HEADER_TABLE_SIZE", 0x1);
370 put_i(&mut m, "NGHTTP2_SETTINGS_ENABLE_PUSH", 0x2);
371 put_i(&mut m, "NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS", 0x3);
372 put_i(&mut m, "NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE", 0x4);
373 put_i(&mut m, "NGHTTP2_SETTINGS_MAX_FRAME_SIZE", 0x5);
374 put_i(&mut m, "NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE", 0x6);
375 let hdr =
377 |m: &mut IndexMap<String, Value>, k: &str, v: &str, h: &mut crate::host::JsHost| {
378 let s = h.new_str(v);
379 m.insert(k.to_string(), s);
380 };
381 hdr(&mut m, "HTTP2_HEADER_STATUS", ":status", h);
382 hdr(&mut m, "HTTP2_HEADER_METHOD", ":method", h);
383 hdr(&mut m, "HTTP2_HEADER_AUTHORITY", ":authority", h);
384 hdr(&mut m, "HTTP2_HEADER_SCHEME", ":scheme", h);
385 hdr(&mut m, "HTTP2_HEADER_PATH", ":path", h);
386 hdr(&mut m, "HTTP2_HEADER_CONTENT_TYPE", "content-type", h);
387 hdr(&mut m, "HTTP2_HEADER_CONTENT_LENGTH", "content-length", h);
388 hdr(&mut m, "HTTP2_METHOD_GET", "GET", h);
389 hdr(&mut m, "HTTP2_METHOD_POST", "POST", h);
390 h.new_object(m)
391 })
392}
393
394fn default_settings_object() -> Value {
396 with_host(|h| {
397 let mut m = IndexMap::new();
398 m.insert("headerTableSize".into(), Value::Float(4096.0));
399 m.insert("enablePush".into(), Value::Bool(false));
400 m.insert("initialWindowSize".into(), Value::Float(65535.0));
401 m.insert("maxFrameSize".into(), Value::Float(MAX_FRAME_SIZE as f64));
402 m.insert("maxConcurrentStreams".into(), Value::Float(100.0));
403 h.new_object(m)
404 })
405}
406
407fn push_setting(out: &mut Vec<u8>, id: u16, val: u32) {
411 out.extend_from_slice(&id.to_be_bytes());
412 out.extend_from_slice(&val.to_be_bytes());
413}
414
415fn pack_settings(args: &[Value]) -> Value {
421 let settings = args.first().cloned().unwrap_or(Value::Undef);
422 let num = |key: &str| -> Option<u32> {
423 get_prop(&settings, key)
424 .filter(|v| !matches!(v, Value::Undef))
425 .map(|v| with_host(|h| h.to_number(&v)) as u32)
426 };
427 let mut out: Vec<u8> = Vec::new();
428 if let Some(v) = num("headerTableSize") {
429 push_setting(&mut out, 0x1, v);
430 }
431 if let Some(p) = get_prop(&settings, "enablePush").filter(|v| !matches!(v, Value::Undef)) {
432 let on = with_host(|h| h.truthy(&p));
433 push_setting(&mut out, 0x2, u32::from(on));
434 }
435 if let Some(v) = num("maxConcurrentStreams") {
436 push_setting(&mut out, 0x3, v);
437 }
438 if let Some(v) = num("initialWindowSize") {
439 push_setting(&mut out, 0x4, v);
440 }
441 if let Some(v) = num("maxFrameSize") {
442 push_setting(&mut out, 0x5, v);
443 }
444 if let Some(v) = num("maxHeaderListSize").or_else(|| num("maxHeaderSize")) {
445 push_setting(&mut out, 0x6, v);
446 }
447 super::buffer::from_bytes(&out)
448}
449
450fn unpack_settings(args: &[Value]) -> Result<Value, String> {
457 let bytes = value_bytes(args.first());
458 if bytes.len() % 6 != 0 {
459 return Err("RangeError [ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH]: \
460 Packed settings length must be a multiple of six"
461 .to_string());
462 }
463 let mut m = IndexMap::new();
464 for chunk in bytes.chunks_exact(6) {
465 let id = u16::from_be_bytes([chunk[0], chunk[1]]);
466 let val = u32::from_be_bytes([chunk[2], chunk[3], chunk[4], chunk[5]]);
467 match id {
468 0x1 => {
469 m.insert("headerTableSize".to_string(), Value::Float(val as f64));
470 }
471 0x2 => {
472 m.insert("enablePush".to_string(), Value::Bool(val != 0));
473 }
474 0x3 => {
475 m.insert("maxConcurrentStreams".to_string(), Value::Float(val as f64));
476 }
477 0x4 => {
478 m.insert("initialWindowSize".to_string(), Value::Float(val as f64));
479 }
480 0x5 => {
481 m.insert("maxFrameSize".to_string(), Value::Float(val as f64));
482 }
483 0x6 => {
486 m.insert("maxHeaderSize".to_string(), Value::Float(val as f64));
487 m.insert("maxHeaderListSize".to_string(), Value::Float(val as f64));
488 }
489 _ => {}
491 }
492 }
493 Ok(with_host(|h| h.new_object(m)))
494}
495
496fn create_secure_server(args: &[Value]) -> Result<Value, String> {
503 let mut options: Option<Value> = None;
504 let mut handler: Option<Value> = None;
505 for a in args {
506 if with_host(|h| crate::host::is_callable(h, a)) {
507 handler = Some(a.clone());
508 } else if matches!(a, Value::Obj(_)) {
509 options = Some(a.clone());
510 }
511 }
512 let opts = options.ok_or_else(|| {
513 crate::host::type_error("http2.createSecureServer requires options with `key` and `cert`")
514 })?;
515 let cert = value_bytes(get_prop(&opts, "cert").as_ref());
516 let key = value_bytes(get_prop(&opts, "key").as_ref());
517 if cert.is_empty() || key.is_empty() {
518 return Err(crate::host::type_error(
519 "http2.createSecureServer requires `key` and `cert`",
520 ));
521 }
522 let config = build_h2_server_config(&cert, &key)?;
523
524 let server = new_emitter_object("Http2Server", IndexMap::new());
525 if let Some(cb) = handler {
526 super::events::instance_call(&server, "on", vec![with_host(|h| h.new_str("request")), cb])?;
528 }
529 PENDING_CONFIGS.with(|p| p.borrow_mut().push((server.clone(), config)));
530 Ok(server)
531}
532
533fn build_h2_server_config(cert_pem: &[u8], key_pem: &[u8]) -> Result<Arc<ServerConfig>, String> {
535 let certs: Vec<CertificateDer<'static>> = rustls_pemfile::certs(&mut &cert_pem[..])
536 .collect::<Result<_, _>>()
537 .map_err(|e| format!("Error: http2: bad certificate PEM: {e}"))?;
538 if certs.is_empty() {
539 return Err("Error: http2: no certificates found in `cert`".to_string());
540 }
541 let key: PrivateKeyDer<'static> = rustls_pemfile::private_key(&mut &key_pem[..])
542 .map_err(|e| format!("Error: http2: bad private key PEM: {e}"))?
543 .ok_or_else(|| "Error: http2: no private key found in `key`".to_string())?;
544 let mut cfg = ServerConfig::builder()
545 .with_no_client_auth()
546 .with_single_cert(certs, key)
547 .map_err(|e| format!("Error: http2: invalid key/cert: {e}"))?;
548 cfg.alpn_protocols = vec![b"h2".to_vec()];
550 Ok(Arc::new(cfg))
551}
552
553fn take_pending_config(server: &Value) -> Option<Arc<ServerConfig>> {
554 PENDING_CONFIGS.with(|p| {
555 let mut p = p.borrow_mut();
556 p.iter()
557 .position(|(s, _)| s == server)
558 .map(|pos| p.remove(pos).1)
559 })
560}
561
562pub fn instance_call(
565 tag: &str,
566 recv: &Value,
567 method: &str,
568 args: Vec<Value>,
569) -> Result<Value, String> {
570 match tag {
571 "Http2Server" => server_call(recv, method, args),
572 "Http2Stream" => stream_call(recv, method, args),
573 "Http2Session" => session_call(recv, method, args),
574 _ => Err(crate::host::type_error(&format!(
575 "{method} is not a function"
576 ))),
577 }
578}
579
580fn server_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
581 if let Some(r) = emitter_dispatch(recv, method, &args) {
582 return r;
583 }
584 match method {
585 "listen" => server_listen(recv, &args),
586 "close" => server_close(recv, &args),
587 "address" => Ok(get_prop(recv, "@@address").unwrap_or(Value::Undef)),
588 "setTimeout" => Ok(recv.clone()),
589 _ => Err(crate::host::type_error(&format!(
590 "server.{method} is not a function"
591 ))),
592 }
593}
594
595fn server_listen(recv: &Value, args: &[Value]) -> Result<Value, String> {
598 let port = with_host(|h| args.first().map(|v| h.to_number(v)).unwrap_or(0.0)) as u16;
599 let mut host = "0.0.0.0".to_string();
600 let mut cb: Option<Value> = None;
601 for a in &args[1.min(args.len())..] {
602 if with_host(|h| h.as_str(a)).is_some() {
603 host = with_host(|h| h.str_of(a));
604 } else if with_host(|h| crate::host::is_callable(h, a)) {
605 cb = Some(a.clone());
606 }
607 }
608
609 let config = take_pending_config(recv)
610 .ok_or_else(|| crate::host::type_error("http2 server has no secure context"))?;
611 let listener = TcpListener::bind((host.as_str(), port))
612 .map_err(|e| format!("Error: listen EADDRINUSE: {e}"))?;
613 let local = listener.local_addr().ok();
614
615 let id = next_server_id();
616 set_prop(recv, "@@serverid", Value::Float(id as f64));
617 if let Some(addr) = local {
618 let mut a = IndexMap::new();
619 a.insert("port".into(), Value::Float(addr.port() as f64));
620 a.insert(
621 "address".into(),
622 with_host(|h| h.new_str(addr.ip().to_string())),
623 );
624 a.insert(
625 "family".into(),
626 with_host(|h| h.new_str(if addr.is_ipv6() { "IPv6" } else { "IPv4" })),
627 );
628 let addr_obj = with_host(|h| h.new_object(a));
629 set_prop(recv, "@@address", addr_obj);
630 }
631 let stop = Arc::new(AtomicBool::new(false));
632 H2.with(|s| {
633 s.borrow_mut().servers.insert(
634 id,
635 H2ServerRec {
636 emitter: recv.clone(),
637 stop: stop.clone(),
638 },
639 );
640 });
641 with_host(|h| h.incr_handle());
642
643 let io_tx = with_host(|h| h.io_sender());
644 listener.set_nonblocking(true).ok();
645 std::thread::spawn(move || loop {
646 if stop.load(Ordering::Acquire) {
647 break;
648 }
649 match listener.accept() {
650 Ok((stream, _addr)) => {
651 let cfg = config.clone();
652 let tx = io_tx.clone();
653 std::thread::spawn(move || serve_connection(id, stream, cfg, tx));
654 }
655 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
656 std::thread::sleep(std::time::Duration::from_millis(5));
657 }
658 Err(_) => break,
659 }
660 });
661
662 let server = recv.clone();
663 let _ = with_host(|h| h.io_sender()).send(Box::new(move || {
664 super::events::instance_call(&server, "emit", vec![with_host(|h| h.new_str("listening"))])?;
665 if let Some(cb) = cb {
666 invoke(&cb, Vec::new(), None)?;
667 }
668 Ok(())
669 }));
670 Ok(recv.clone())
671}
672
673fn server_close(recv: &Value, args: &[Value]) -> Result<Value, String> {
674 if let Some(id) = u64_prop(recv, "@@serverid") {
675 let rec = H2.with(|s| s.borrow_mut().servers.remove(&id));
676 if let Some(rec) = rec {
677 rec.stop.store(true, Ordering::Release);
678 with_host(|h| h.decr_handle());
679 let _ = with_host(|h| h.io_sender()).send(Box::new(|| Ok(())));
680 }
681 }
682 if let Some(cb) = args
683 .first()
684 .filter(|v| with_host(|h| crate::host::is_callable(h, v)))
685 {
686 invoke(cb, Vec::new(), None)?;
687 }
688 super::events::instance_call(recv, "emit", vec![with_host(|h| h.new_str("close"))])?;
689 Ok(recv.clone())
690}
691
692fn serve_connection(
698 server_id: u64,
699 mut sock: TcpStream,
700 config: Arc<ServerConfig>,
701 io_tx: Sender<IoTask>,
702) {
703 sock.set_nonblocking(false).ok();
708 let mut conn = match ServerConnection::new(config) {
709 Ok(c) => c,
710 Err(_) => return,
711 };
712 if conn.complete_io(&mut sock).is_err() {
713 return;
714 }
715 let is_h2 = conn.alpn_protocol().map(|p| p == b"h2").unwrap_or(false);
717 let mut stream = StreamOwned::new(conn, sock);
718 if !is_h2 {
719 stream.conn.send_close_notify();
721 let _ = stream.flush();
722 let _ = stream.sock.shutdown(std::net::Shutdown::Both);
723 return;
724 }
725 let (tx, rx) = std::sync::mpsc::channel::<H2Cmd>();
727
728 let session_key = next_session_key();
730 {
731 let tx_sess = tx.clone();
732 let _ = io_tx.send(Box::new(move || {
733 on_session(server_id, session_key, tx_sess)
734 }));
735 }
736
737 if h2_debug() {
738 eprintln!("[http2] connection {session_key}: ALPN h2 negotiated, starting framing loop");
739 }
740
741 let mut ok = true;
747 ok &= write_frame(&mut stream, FT_SETTINGS, 0, 0, &[]).is_ok();
748 ok &= stream.flush().is_ok();
749 stream
752 .sock
753 .set_read_timeout(Some(std::time::Duration::from_millis(20)))
754 .ok();
755
756 let mut decoder = Decoder::new();
757 let mut encoder = Encoder::new();
758 let mut inbuf: Vec<u8> = Vec::new();
759 let mut got_preface = false;
760 let mut max_stream_id: u32 = 0;
761 let mut id_to_key: HashMap<u32, u64> = HashMap::new();
763 let mut buf = [0u8; MAX_FRAME_SIZE];
764
765 'conn: loop {
766 if !ok {
770 break;
771 }
772 loop {
776 match rx.try_recv() {
777 Ok(cmd) => {
778 if h2_debug() {
779 eprintln!(
780 "[http2] connection {session_key}: draining {}",
781 cmd_name(&cmd)
782 );
783 }
784 if !apply_cmd(&mut stream, &mut encoder, max_stream_id, cmd) {
785 if h2_debug() {
786 eprintln!("[http2] connection {session_key}: write failed, closing");
787 }
788 break 'conn;
789 }
790 }
791 Err(std::sync::mpsc::TryRecvError::Empty) => break,
792 Err(std::sync::mpsc::TryRecvError::Disconnected) => break,
793 }
794 }
795
796 match stream.read(&mut buf) {
798 Ok(0) => {
799 if h2_debug() {
800 eprintln!("[http2] connection {session_key}: read EOF (Ok 0)");
801 }
802 break;
803 }
804 Ok(n) => inbuf.extend_from_slice(&buf[..n]),
805 Err(ref e)
806 if matches!(
807 e.kind(),
808 std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
809 ) =>
810 {
811 continue;
812 }
813 Err(e) => {
814 if h2_debug() {
815 eprintln!(
816 "[http2] connection {session_key}: read error {:?} ({e})",
817 e.kind()
818 );
819 }
820 break;
821 }
822 }
823
824 if !got_preface {
826 if inbuf.len() < PREFACE.len() {
827 continue;
828 }
829 if &inbuf[..PREFACE.len()] != PREFACE {
830 break; }
832 inbuf.drain(..PREFACE.len());
833 got_preface = true;
834 }
835
836 loop {
838 if inbuf.len() < 9 {
839 break;
840 }
841 let len =
842 ((inbuf[0] as usize) << 16) | ((inbuf[1] as usize) << 8) | (inbuf[2] as usize);
843 if inbuf.len() < 9 + len {
844 break; }
846 let ftype = inbuf[3];
847 let flags = inbuf[4];
848 let stream_id =
849 u32::from_be_bytes([inbuf[5], inbuf[6], inbuf[7], inbuf[8]]) & 0x7fff_ffff;
850 let payload: Vec<u8> = inbuf[9..9 + len].to_vec();
851 inbuf.drain(..9 + len);
852 if h2_debug() {
853 eprintln!(
854 "[http2] connection {session_key}: recv frame type={ftype} flags={flags:#04x} \
855 stream={stream_id} len={len}"
856 );
857 }
858
859 match ftype {
860 FT_SETTINGS => {
861 if flags & FL_ACK == 0 {
862 if write_frame(&mut stream, FT_SETTINGS, FL_ACK, 0, &[])
864 .and_then(|_| stream.flush())
865 .is_err()
866 {
867 break 'conn;
868 }
869 }
870 }
871 FT_PING => {
872 if flags & FL_ACK == 0 {
873 if write_frame(&mut stream, FT_PING, FL_ACK, 0, &payload)
875 .and_then(|_| stream.flush())
876 .is_err()
877 {
878 break 'conn;
879 }
880 }
881 }
882 FT_HEADERS => {
883 if stream_id > max_stream_id {
884 max_stream_id = stream_id;
885 }
886 if flags & FL_END_HEADERS == 0 {
887 continue;
889 }
890 let block = strip_headers_padding_priority(&payload, flags);
891 let decoded = match decoder.decode(&block) {
892 Ok(d) => d,
893 Err(_) => continue,
894 };
895 let headers: Vec<(String, String)> = decoded
896 .into_iter()
897 .map(|(k, v)| {
898 (
899 String::from_utf8_lossy(&k).into_owned(),
900 String::from_utf8_lossy(&v).into_owned(),
901 )
902 })
903 .collect();
904 let end_stream = flags & FL_END_STREAM != 0;
905 let key = next_stream_key();
906 id_to_key.insert(stream_id, key);
907 let tx_stream = tx.clone();
908 let _ = io_tx.send(Box::new(move || {
909 on_headers(server_id, key, stream_id, headers, end_stream, tx_stream)
910 }));
911 }
912 FT_DATA => {
913 if let Some(&key) = id_to_key.get(&stream_id) {
914 let data = strip_data_padding(&payload, flags);
915 let end_stream = flags & FL_END_STREAM != 0;
916 let _ = io_tx.send(Box::new(move || on_data(key, data, end_stream)));
917 }
918 }
919 FT_GOAWAY => break 'conn,
920 FT_WINDOW_UPDATE | FT_PRIORITY | FT_RST_STREAM | FT_CONTINUATION => {}
923 _ => {}
925 }
926 }
927 }
928
929 if h2_debug() {
931 eprintln!(
932 "[http2] connection {session_key}: framing loop exited, sending GOAWAY + closing"
933 );
934 }
935 let mut goaway = Vec::with_capacity(8);
936 goaway.extend_from_slice(&(max_stream_id & 0x7fff_ffff).to_be_bytes());
937 goaway.extend_from_slice(&0u32.to_be_bytes()); let _ = write_frame(&mut stream, FT_GOAWAY, 0, 0, &goaway);
939 let _ = stream.flush();
940 stream.conn.send_close_notify();
941 let _ = stream.flush();
942 let _ = stream.sock.shutdown(std::net::Shutdown::Both);
943
944 let stream_keys: Vec<u64> = id_to_key.values().copied().collect();
947 let _ = io_tx.send(Box::new(move || on_session_close(session_key, stream_keys)));
948}
949
950fn cmd_name(cmd: &H2Cmd) -> &'static str {
952 match cmd {
953 H2Cmd::Respond { .. } => "respond(HEADERS)",
954 H2Cmd::Data { .. } => "data(DATA)",
955 H2Cmd::Close { .. } => "close(RST_STREAM)",
956 H2Cmd::Goaway => "goaway(GOAWAY)",
957 }
958}
959
960fn apply_cmd(
963 stream: &mut StreamOwned<ServerConnection, TcpStream>,
964 encoder: &mut Encoder<'_>,
965 max_stream_id: u32,
966 cmd: H2Cmd,
967) -> bool {
968 match cmd {
969 H2Cmd::Respond {
970 stream_id,
971 headers,
972 end,
973 } => {
974 let block = encode_header_block(encoder, &headers);
975 if h2_debug() {
976 eprintln!(
977 "[http2] write HEADERS stream={stream_id} end_stream={end} \
978 hpack_len={} headers={headers:?}",
979 block.len()
980 );
981 }
982 let flags = FL_END_HEADERS | if end { FL_END_STREAM } else { 0 };
983 write_frame(stream, FT_HEADERS, flags, stream_id, &block)
984 .and_then(|_| stream.flush())
985 .is_ok()
986 }
987 H2Cmd::Data {
988 stream_id,
989 data,
990 end,
991 } => {
992 if h2_debug() {
993 eprintln!(
994 "[http2] write DATA stream={stream_id} len={} end_stream={end}",
995 data.len()
996 );
997 }
998 send_data(stream, stream_id, &data, end)
999 }
1000 H2Cmd::Close { stream_id } => {
1001 write_frame(stream, FT_RST_STREAM, 0, stream_id, &0u32.to_be_bytes())
1003 .and_then(|_| stream.flush())
1004 .is_ok()
1005 }
1006 H2Cmd::Goaway => {
1007 let mut g = Vec::with_capacity(8);
1008 g.extend_from_slice(&(max_stream_id & 0x7fff_ffff).to_be_bytes());
1009 g.extend_from_slice(&0u32.to_be_bytes());
1010 let _ = write_frame(stream, FT_GOAWAY, 0, 0, &g);
1011 let _ = stream.flush();
1012 false
1013 }
1014 }
1015}
1016
1017fn send_data(
1020 stream: &mut StreamOwned<ServerConnection, TcpStream>,
1021 stream_id: u32,
1022 data: &[u8],
1023 end: bool,
1024) -> bool {
1025 if data.is_empty() {
1026 let flags = if end { FL_END_STREAM } else { 0 };
1027 return write_frame(stream, FT_DATA, flags, stream_id, &[])
1028 .and_then(|_| stream.flush())
1029 .is_ok();
1030 }
1031 let chunks: Vec<&[u8]> = data.chunks(MAX_FRAME_SIZE).collect();
1032 let last = chunks.len() - 1;
1033 for (i, chunk) in chunks.iter().enumerate() {
1034 let flags = if end && i == last { FL_END_STREAM } else { 0 };
1035 if write_frame(stream, FT_DATA, flags, stream_id, chunk).is_err() {
1036 return false;
1037 }
1038 }
1039 stream.flush().is_ok()
1040}
1041
1042fn write_frame<W: Write>(
1045 w: &mut W,
1046 ftype: u8,
1047 flags: u8,
1048 stream_id: u32,
1049 payload: &[u8],
1050) -> std::io::Result<()> {
1051 let len = payload.len();
1052 let mut hdr = [0u8; 9];
1053 hdr[0] = (len >> 16) as u8;
1054 hdr[1] = (len >> 8) as u8;
1055 hdr[2] = len as u8;
1056 hdr[3] = ftype;
1057 hdr[4] = flags;
1058 hdr[5..9].copy_from_slice(&(stream_id & 0x7fff_ffff).to_be_bytes());
1059 w.write_all(&hdr)?;
1060 w.write_all(payload)?;
1061 Ok(())
1062}
1063
1064fn encode_header_block(encoder: &mut Encoder<'_>, headers: &[(String, String)]) -> Vec<u8> {
1066 let owned: Vec<(Vec<u8>, Vec<u8>)> = headers
1067 .iter()
1068 .map(|(k, v)| (k.as_bytes().to_vec(), v.as_bytes().to_vec()))
1069 .collect();
1070 encoder.encode(owned.iter().map(|(k, v)| (k.as_slice(), v.as_slice())))
1071}
1072
1073fn strip_headers_padding_priority(payload: &[u8], flags: u8) -> Vec<u8> {
1076 let mut start = 0usize;
1077 let mut pad_len = 0usize;
1078 if flags & FL_PADDED != 0 && !payload.is_empty() {
1079 pad_len = payload[0] as usize;
1080 start = 1;
1081 }
1082 if flags & FL_PRIORITY != 0 {
1083 start += 5; }
1085 let end = payload.len().saturating_sub(pad_len);
1086 if start > end {
1087 return Vec::new();
1088 }
1089 payload[start..end].to_vec()
1090}
1091
1092fn strip_data_padding(payload: &[u8], flags: u8) -> Vec<u8> {
1094 if flags & FL_PADDED != 0 && !payload.is_empty() {
1095 let pad_len = payload[0] as usize;
1096 let end = payload.len().saturating_sub(pad_len);
1097 if 1 <= end {
1098 return payload[1..end].to_vec();
1099 }
1100 return Vec::new();
1101 }
1102 payload.to_vec()
1103}
1104
1105fn on_session(server_id: u64, session_key: u64, tx: Sender<H2Cmd>) -> Result<(), String> {
1112 with_host(|h| h.incr_handle());
1113 let server = H2.with(|s| {
1114 s.borrow()
1115 .servers
1116 .get(&server_id)
1117 .map(|r| r.emitter.clone())
1118 });
1119 let Some(server) = server else { return Ok(()) };
1120 let mut extra = IndexMap::new();
1121 extra.insert("@@h2session".into(), Value::Float(session_key as f64));
1122 let session = new_emitter_object("Http2Session", extra);
1123 H2.with(|s| {
1124 s.borrow_mut().sessions.insert(
1125 session_key,
1126 H2SessionRec {
1127 emitter: session.clone(),
1128 tx,
1129 },
1130 );
1131 });
1132 if let Err(e) = super::events::instance_call(
1133 &server,
1134 "emit",
1135 vec![with_host(|h| h.new_str("session")), session],
1136 ) {
1137 report_handler_error("session", &e);
1138 }
1139 Ok(())
1140}
1141
1142fn on_session_close(session_key: u64, stream_keys: Vec<u64>) -> Result<(), String> {
1145 H2.with(|s| {
1146 let mut st = s.borrow_mut();
1147 st.sessions.remove(&session_key);
1148 for k in &stream_keys {
1149 st.streams.remove(k);
1150 }
1151 });
1152 with_host(|h| h.decr_handle());
1153 let _ = with_host(|h| h.io_sender()).send(Box::new(|| Ok(())));
1155 Ok(())
1156}
1157
1158fn on_headers(
1161 server_id: u64,
1162 stream_key: u64,
1163 stream_id: u32,
1164 headers: Vec<(String, String)>,
1165 end_stream: bool,
1166 tx: Sender<H2Cmd>,
1167) -> Result<(), String> {
1168 let server = H2.with(|s| {
1169 s.borrow()
1170 .servers
1171 .get(&server_id)
1172 .map(|r| r.emitter.clone())
1173 });
1174 let Some(server) = server else { return Ok(()) };
1175
1176 let headers_obj = with_host(|h| {
1178 let mut m = IndexMap::new();
1179 for (k, v) in &headers {
1180 m.insert(k.clone(), h.new_str(v.clone()));
1181 }
1182 h.new_object(m)
1183 });
1184
1185 let mut extra = IndexMap::new();
1187 extra.insert("@@h2key".into(), Value::Float(stream_key as f64));
1188 extra.insert("id".into(), Value::Float(stream_id as f64));
1189 let stream_obj = new_emitter_object("Http2Stream", extra);
1190 H2.with(|s| {
1191 s.borrow_mut().streams.insert(
1192 stream_key,
1193 H2StreamRec {
1194 emitter: stream_obj.clone(),
1195 tx,
1196 stream_id,
1197 responded: false,
1198 },
1199 );
1200 });
1201
1202 let method = header_value(&headers, ":method").unwrap_or_else(|| "GET".to_string());
1203 let path = header_value(&headers, ":path").unwrap_or_else(|| "/".to_string());
1204 if h2_debug() {
1205 eprintln!("[http2] dispatch stream={stream_id} {method} {path} (end_stream={end_stream})");
1206 }
1207
1208 if let Err(e) = super::events::instance_call(
1216 &server,
1217 "emit",
1218 vec![
1219 with_host(|h| h.new_str("stream")),
1220 stream_obj.clone(),
1221 headers_obj.clone(),
1222 ],
1223 ) {
1224 report_handler_error("stream", &e);
1225 return Ok(());
1226 }
1227
1228 let req = super::events::new_emitter();
1231 set_prop(&req, "method", with_host(|h| h.new_str(method)));
1232 set_prop(&req, "url", with_host(|h| h.new_str(path)));
1233 set_prop(&req, "headers", headers_obj);
1234 if let Err(e) = super::events::instance_call(
1235 &server,
1236 "emit",
1237 vec![with_host(|h| h.new_str("request")), req, stream_obj.clone()],
1238 ) {
1239 report_handler_error("request", &e);
1240 return Ok(());
1241 }
1242
1243 if end_stream {
1245 if let Err(e) =
1246 super::events::instance_call(&stream_obj, "emit", vec![with_host(|h| h.new_str("end"))])
1247 {
1248 report_handler_error("stream.end", &e);
1249 }
1250 }
1251 Ok(())
1252}
1253
1254fn report_handler_error(event: &str, err: &str) {
1259 eprintln!("http2: uncaught error in '{event}' handler: {err}");
1260}
1261
1262fn h2_debug() -> bool {
1265 std::env::var_os("HTTP2_DEBUG").is_some()
1266}
1267
1268fn on_data(stream_key: u64, data: Vec<u8>, end_stream: bool) -> Result<(), String> {
1270 let stream = H2.with(|s| {
1271 s.borrow()
1272 .streams
1273 .get(&stream_key)
1274 .map(|r| r.emitter.clone())
1275 });
1276 let Some(stream) = stream else { return Ok(()) };
1277 if !data.is_empty() {
1278 let chunk = super::buffer::from_bytes(&data);
1279 if let Err(e) = super::events::instance_call(
1280 &stream,
1281 "emit",
1282 vec![with_host(|h| h.new_str("data")), chunk],
1283 ) {
1284 report_handler_error("data", &e);
1285 return Ok(());
1286 }
1287 }
1288 if end_stream {
1289 if let Err(e) =
1290 super::events::instance_call(&stream, "emit", vec![with_host(|h| h.new_str("end"))])
1291 {
1292 report_handler_error("end", &e);
1293 }
1294 }
1295 Ok(())
1296}
1297
1298fn header_value(headers: &[(String, String)], name: &str) -> Option<String> {
1299 headers
1300 .iter()
1301 .find(|(k, _)| k == name)
1302 .map(|(_, v)| v.clone())
1303}
1304
1305fn stream_key_of(recv: &Value) -> Option<u64> {
1308 u64_prop(recv, "@@h2key")
1309}
1310
1311fn stream_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
1312 if let Some(r) = emitter_dispatch(recv, method, &args) {
1313 return r;
1314 }
1315 match method {
1316 "respond" => {
1317 let hdrs = args.first().map(object_pairs).unwrap_or_default();
1318 let end = args
1320 .get(1)
1321 .and_then(|o| get_prop(o, "endStream"))
1322 .map(|v| with_host(|h| h.truthy(&v)))
1323 .unwrap_or(false);
1324 do_respond(recv, hdrs, end)?;
1325 Ok(recv.clone())
1326 }
1327 "writeHead" => {
1329 let status =
1330 with_host(|h| args.first().map(|v| h.to_number(v)).unwrap_or(200.0)) as u32;
1331 let mut hdrs: Vec<(String, String)> = Vec::new();
1332 for a in args.iter().skip(1) {
1333 if matches!(a, Value::Obj(_)) {
1334 hdrs = object_pairs(a);
1335 break;
1336 }
1337 }
1338 hdrs.insert(0, (":status".to_string(), status.to_string()));
1340 do_respond(recv, hdrs, false)?;
1341 Ok(recv.clone())
1342 }
1343 "setHeader" => {
1344 let k = with_host(|h| h.str_of(&args.first().cloned().unwrap_or(Value::Undef)));
1346 let v = with_host(|h| h.str_of(&args.get(1).cloned().unwrap_or(Value::Undef)));
1347 let bag = pending_headers_obj(recv);
1348 set_prop(&bag, &k, with_host(|h| h.new_str(v)));
1349 Ok(Value::Undef)
1350 }
1351 "getHeader" => {
1352 let k = with_host(|h| h.str_of(&args.first().cloned().unwrap_or(Value::Undef)));
1353 Ok(get_prop(recv, "@@pendingHeaders")
1354 .and_then(|bag| get_prop(&bag, &k))
1355 .unwrap_or(Value::Undef))
1356 }
1357 "removeHeader" => {
1358 let k = with_host(|h| h.str_of(&args.first().cloned().unwrap_or(Value::Undef)));
1359 if let Some(bag) = get_prop(recv, "@@pendingHeaders") {
1360 with_host(|h| {
1361 if let Some(JsObj::Object(p)) = h.get_mut(&bag) {
1362 p.shift_remove(&k);
1363 }
1364 });
1365 }
1366 Ok(Value::Undef)
1367 }
1368 "write" => {
1369 ensure_responded(recv)?;
1370 let bytes = value_bytes(args.first());
1371 send_stream_data(recv, bytes, false);
1372 Ok(Value::Bool(true))
1373 }
1374 "end" => {
1375 ensure_responded(recv)?;
1376 let bytes = args
1377 .first()
1378 .filter(|v| !matches!(v, Value::Undef))
1379 .map(|v| value_bytes(Some(v)))
1380 .unwrap_or_default();
1381 send_stream_data(recv, bytes, true);
1382 super::events::instance_call(recv, "emit", vec![with_host(|h| h.new_str("finish"))])?;
1383 Ok(recv.clone())
1384 }
1385 "close" => {
1386 if let Some(key) = stream_key_of(recv) {
1387 let sent = H2.with(|s| {
1388 s.borrow().streams.get(&key).map(|r| {
1389 let _ = r.tx.send(H2Cmd::Close {
1390 stream_id: r.stream_id,
1391 });
1392 })
1393 });
1394 let _ = sent;
1395 }
1396 Ok(recv.clone())
1397 }
1398 "setEncoding" | "setTimeout" | "pause" | "resume" => Ok(recv.clone()),
1399 _ => Err(crate::host::type_error(&format!(
1400 "stream.{method} is not a function"
1401 ))),
1402 }
1403}
1404
1405fn pending_headers_obj(recv: &Value) -> Value {
1407 if let Some(bag) = get_prop(recv, "@@pendingHeaders") {
1408 return bag;
1409 }
1410 let bag = with_host(|h| h.new_object(IndexMap::new()));
1411 set_prop(recv, "@@pendingHeaders", bag.clone());
1412 bag
1413}
1414
1415fn do_respond(recv: &Value, mut headers: Vec<(String, String)>, end: bool) -> Result<(), String> {
1418 if let Some(bag) = get_prop(recv, "@@pendingHeaders") {
1420 for (k, v) in object_pairs(&bag) {
1421 if !headers.iter().any(|(hk, _)| hk.eq_ignore_ascii_case(&k)) {
1422 headers.push((k, v));
1423 }
1424 }
1425 }
1426 let status = headers
1429 .iter()
1430 .find(|(k, _)| k == ":status")
1431 .map(|(_, v)| v.clone())
1432 .unwrap_or_else(|| "200".to_string());
1433 let mut ordered: Vec<(String, String)> = vec![(":status".to_string(), status)];
1434 for (k, v) in headers.into_iter() {
1435 if k == ":status" {
1436 continue;
1437 }
1438 ordered.push((k.to_ascii_lowercase(), v));
1440 }
1441
1442 let Some(key) = stream_key_of(recv) else {
1443 return Ok(());
1444 };
1445 H2.with(|s| {
1446 if let Some(r) = s.borrow_mut().streams.get_mut(&key) {
1447 if !r.responded {
1448 r.responded = true;
1449 let _ = r.tx.send(H2Cmd::Respond {
1450 stream_id: r.stream_id,
1451 headers: ordered,
1452 end,
1453 });
1454 }
1455 }
1456 });
1457 Ok(())
1458}
1459
1460fn ensure_responded(recv: &Value) -> Result<(), String> {
1462 let Some(key) = stream_key_of(recv) else {
1463 return Ok(());
1464 };
1465 let responded = H2.with(|s| {
1466 s.borrow()
1467 .streams
1468 .get(&key)
1469 .map(|r| r.responded)
1470 .unwrap_or(true)
1471 });
1472 if !responded {
1473 do_respond(recv, Vec::new(), false)?;
1474 }
1475 Ok(())
1476}
1477
1478fn send_stream_data(recv: &Value, data: Vec<u8>, end: bool) {
1480 if let Some(key) = stream_key_of(recv) {
1481 H2.with(|s| {
1482 if let Some(r) = s.borrow().streams.get(&key) {
1483 let _ = r.tx.send(H2Cmd::Data {
1484 stream_id: r.stream_id,
1485 data,
1486 end,
1487 });
1488 }
1489 });
1490 }
1491}
1492
1493fn session_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
1496 if let Some(r) = emitter_dispatch(recv, method, &args) {
1497 return r;
1498 }
1499 match method {
1500 "close" | "destroy" | "goaway" => {
1501 if let Some(key) = u64_prop(recv, "@@h2session") {
1502 H2.with(|s| {
1503 if let Some(r) = s.borrow().sessions.get(&key) {
1504 let _ = r.tx.send(H2Cmd::Goaway);
1505 }
1506 });
1507 }
1508 super::events::instance_call(recv, "emit", vec![with_host(|h| h.new_str("close"))])?;
1509 Ok(recv.clone())
1510 }
1511 "settings" | "ping" | "ref" | "unref" | "setTimeout" => Ok(recv.clone()),
1512 _ => Err(crate::host::type_error(&format!(
1513 "session.{method} is not a function"
1514 ))),
1515 }
1516}
1517
1518pub fn new_emitter_object(tag: &str, mut extra: IndexMap<String, Value>) -> Value {
1523 with_host(|h| {
1524 let on = h.new_object(IndexMap::new());
1525 let once = h.new_object(IndexMap::new());
1526 let mut m = IndexMap::new();
1527 m.insert("@@native".into(), h.new_str(tag));
1528 m.insert("@@on".into(), on);
1529 m.insert("@@once".into(), once);
1530 for (k, v) in extra.drain(..) {
1531 m.insert(k, v);
1532 }
1533 h.new_object(m)
1534 })
1535}