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 super::events::METHODS
249 .contains(&method)
250 .then(|| super::events::instance_call(recv, method, args.to_vec()))
251}
252
253fn value_bytes(v: Option<&Value>) -> Vec<u8> {
255 let Some(v) = v else { return Vec::new() };
256 let is_buffer =
257 with_host(|h| matches!(h.get(v), Some(JsObj::Object(p)) if p.contains_key("@@bytes")));
258 if is_buffer {
259 return with_host(|h| match h.get(v) {
260 Some(JsObj::Object(p)) => match p.get("@@bytes").and_then(|b| h.get(b)) {
261 Some(JsObj::Array(items)) => items.iter().map(|x| h.to_number(x) as u8).collect(),
262 _ => Vec::new(),
263 },
264 _ => Vec::new(),
265 });
266 }
267 with_host(|h| h.str_of(v)).into_bytes()
268}
269
270fn object_pairs(obj: &Value) -> Vec<(String, String)> {
273 with_host(|h| match h.get(obj) {
274 Some(JsObj::Object(p)) => p
275 .iter()
276 .filter(|(k, _)| !k.starts_with("@@") && !k.starts_with('#'))
277 .map(|(k, v)| (k.clone(), h.str_of(v)))
278 .collect(),
279 _ => Vec::new(),
280 })
281}
282
283pub fn call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
287 Some(match method {
288 "createSecureServer" => create_secure_server(args),
289 "createServer" => Err(
290 "Error: http2.createServer (cleartext h2c / prior-knowledge) \
291 is not implemented in node-js; use http2.createSecureServer (h2 over TLS)"
292 .to_string(),
293 ),
294 "connect" => Err(
295 "Error: http2.connect (HTTP/2 client) is not implemented in node-js; \
296 only the HTTP/2 server (http2.createSecureServer) is implemented"
297 .to_string(),
298 ),
299 "getDefaultSettings" => Ok(default_settings_object()),
300 "getPackedSettings" => Ok(pack_settings(args)),
301 "getUnpackedSettings" => unpack_settings(args),
302 _ => return None,
303 })
304}
305
306pub fn constant(name: &str) -> Option<Value> {
309 match name {
310 "constants" => Some(constants_object()),
311 "Http2ServerRequest" => Some(with_host(|h| {
314 h.alloc(JsObj::Builtin("Http2ServerRequest".into()))
315 })),
316 "Http2ServerResponse" => Some(with_host(|h| {
317 h.alloc(JsObj::Builtin("Http2ServerResponse".into()))
318 })),
319 _ => None,
320 }
321}
322
323fn constants_object() -> Value {
326 with_host(|h| {
327 let mut m = IndexMap::new();
328 let put_i = |m: &mut IndexMap<String, Value>, k: &str, v: i64| {
329 m.insert(k.to_string(), Value::Float(v as f64));
330 };
331 put_i(&mut m, "HTTP_STATUS_OK", 200);
333 put_i(&mut m, "HTTP_STATUS_NO_CONTENT", 204);
334 put_i(&mut m, "HTTP_STATUS_MOVED_PERMANENTLY", 301);
335 put_i(&mut m, "HTTP_STATUS_FOUND", 302);
336 put_i(&mut m, "HTTP_STATUS_NOT_MODIFIED", 304);
337 put_i(&mut m, "HTTP_STATUS_BAD_REQUEST", 400);
338 put_i(&mut m, "HTTP_STATUS_UNAUTHORIZED", 401);
339 put_i(&mut m, "HTTP_STATUS_FORBIDDEN", 403);
340 put_i(&mut m, "HTTP_STATUS_NOT_FOUND", 404);
341 put_i(&mut m, "HTTP_STATUS_INTERNAL_SERVER_ERROR", 500);
342 put_i(&mut m, "NGHTTP2_NO_ERROR", 0x0);
344 put_i(&mut m, "NGHTTP2_PROTOCOL_ERROR", 0x1);
345 put_i(&mut m, "NGHTTP2_INTERNAL_ERROR", 0x2);
346 put_i(&mut m, "NGHTTP2_FLOW_CONTROL_ERROR", 0x3);
347 put_i(&mut m, "NGHTTP2_SETTINGS_TIMEOUT", 0x4);
348 put_i(&mut m, "NGHTTP2_STREAM_CLOSED", 0x5);
349 put_i(&mut m, "NGHTTP2_FRAME_SIZE_ERROR", 0x6);
350 put_i(&mut m, "NGHTTP2_REFUSED_STREAM", 0x7);
351 put_i(&mut m, "NGHTTP2_CANCEL", 0x8);
352 put_i(&mut m, "NGHTTP2_COMPRESSION_ERROR", 0x9);
353 put_i(&mut m, "NGHTTP2_ENHANCE_YOUR_CALM", 0xb);
354 put_i(&mut m, "NGHTTP2_SETTINGS_HEADER_TABLE_SIZE", 0x1);
356 put_i(&mut m, "NGHTTP2_SETTINGS_ENABLE_PUSH", 0x2);
357 put_i(&mut m, "NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS", 0x3);
358 put_i(&mut m, "NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE", 0x4);
359 put_i(&mut m, "NGHTTP2_SETTINGS_MAX_FRAME_SIZE", 0x5);
360 put_i(&mut m, "NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE", 0x6);
361 let hdr =
363 |m: &mut IndexMap<String, Value>, k: &str, v: &str, h: &mut crate::host::JsHost| {
364 let s = h.new_str(v);
365 m.insert(k.to_string(), s);
366 };
367 hdr(&mut m, "HTTP2_HEADER_STATUS", ":status", h);
368 hdr(&mut m, "HTTP2_HEADER_METHOD", ":method", h);
369 hdr(&mut m, "HTTP2_HEADER_AUTHORITY", ":authority", h);
370 hdr(&mut m, "HTTP2_HEADER_SCHEME", ":scheme", h);
371 hdr(&mut m, "HTTP2_HEADER_PATH", ":path", h);
372 hdr(&mut m, "HTTP2_HEADER_CONTENT_TYPE", "content-type", h);
373 hdr(&mut m, "HTTP2_HEADER_CONTENT_LENGTH", "content-length", h);
374 hdr(&mut m, "HTTP2_METHOD_GET", "GET", h);
375 hdr(&mut m, "HTTP2_METHOD_POST", "POST", h);
376 h.new_object(m)
377 })
378}
379
380fn default_settings_object() -> Value {
382 with_host(|h| {
383 let mut m = IndexMap::new();
384 m.insert("headerTableSize".into(), Value::Float(4096.0));
385 m.insert("enablePush".into(), Value::Bool(false));
386 m.insert("initialWindowSize".into(), Value::Float(65535.0));
387 m.insert("maxFrameSize".into(), Value::Float(MAX_FRAME_SIZE as f64));
388 m.insert("maxConcurrentStreams".into(), Value::Float(100.0));
389 h.new_object(m)
390 })
391}
392
393fn push_setting(out: &mut Vec<u8>, id: u16, val: u32) {
397 out.extend_from_slice(&id.to_be_bytes());
398 out.extend_from_slice(&val.to_be_bytes());
399}
400
401fn pack_settings(args: &[Value]) -> Value {
407 let settings = args.first().cloned().unwrap_or(Value::Undef);
408 let num = |key: &str| -> Option<u32> {
409 get_prop(&settings, key)
410 .filter(|v| !matches!(v, Value::Undef))
411 .map(|v| with_host(|h| h.to_number(&v)) as u32)
412 };
413 let mut out: Vec<u8> = Vec::new();
414 if let Some(v) = num("headerTableSize") {
415 push_setting(&mut out, 0x1, v);
416 }
417 if let Some(p) = get_prop(&settings, "enablePush").filter(|v| !matches!(v, Value::Undef)) {
418 let on = with_host(|h| h.truthy(&p));
419 push_setting(&mut out, 0x2, u32::from(on));
420 }
421 if let Some(v) = num("maxConcurrentStreams") {
422 push_setting(&mut out, 0x3, v);
423 }
424 if let Some(v) = num("initialWindowSize") {
425 push_setting(&mut out, 0x4, v);
426 }
427 if let Some(v) = num("maxFrameSize") {
428 push_setting(&mut out, 0x5, v);
429 }
430 if let Some(v) = num("maxHeaderListSize").or_else(|| num("maxHeaderSize")) {
431 push_setting(&mut out, 0x6, v);
432 }
433 super::buffer::from_bytes(&out)
434}
435
436fn unpack_settings(args: &[Value]) -> Result<Value, String> {
443 let bytes = value_bytes(args.first());
444 if bytes.len() % 6 != 0 {
445 return Err("RangeError [ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH]: \
446 Packed settings length must be a multiple of six"
447 .to_string());
448 }
449 let mut m = IndexMap::new();
450 for chunk in bytes.chunks_exact(6) {
451 let id = u16::from_be_bytes([chunk[0], chunk[1]]);
452 let val = u32::from_be_bytes([chunk[2], chunk[3], chunk[4], chunk[5]]);
453 match id {
454 0x1 => {
455 m.insert("headerTableSize".to_string(), Value::Float(val as f64));
456 }
457 0x2 => {
458 m.insert("enablePush".to_string(), Value::Bool(val != 0));
459 }
460 0x3 => {
461 m.insert("maxConcurrentStreams".to_string(), Value::Float(val as f64));
462 }
463 0x4 => {
464 m.insert("initialWindowSize".to_string(), Value::Float(val as f64));
465 }
466 0x5 => {
467 m.insert("maxFrameSize".to_string(), Value::Float(val as f64));
468 }
469 0x6 => {
472 m.insert("maxHeaderSize".to_string(), Value::Float(val as f64));
473 m.insert("maxHeaderListSize".to_string(), Value::Float(val as f64));
474 }
475 _ => {}
477 }
478 }
479 Ok(with_host(|h| h.new_object(m)))
480}
481
482fn create_secure_server(args: &[Value]) -> Result<Value, String> {
489 let mut options: Option<Value> = None;
490 let mut handler: Option<Value> = None;
491 for a in args {
492 if with_host(|h| crate::host::is_callable(h, a)) {
493 handler = Some(a.clone());
494 } else if matches!(a, Value::Obj(_)) {
495 options = Some(a.clone());
496 }
497 }
498 let opts = options.ok_or_else(|| {
499 crate::host::type_error("http2.createSecureServer requires options with `key` and `cert`")
500 })?;
501 let cert = value_bytes(get_prop(&opts, "cert").as_ref());
502 let key = value_bytes(get_prop(&opts, "key").as_ref());
503 if cert.is_empty() || key.is_empty() {
504 return Err(crate::host::type_error(
505 "http2.createSecureServer requires `key` and `cert`",
506 ));
507 }
508 let config = build_h2_server_config(&cert, &key)?;
509
510 let server = new_emitter_object("Http2Server", IndexMap::new());
511 if let Some(cb) = handler {
512 super::events::instance_call(&server, "on", vec![with_host(|h| h.new_str("request")), cb])?;
514 }
515 PENDING_CONFIGS.with(|p| p.borrow_mut().push((server.clone(), config)));
516 Ok(server)
517}
518
519fn build_h2_server_config(cert_pem: &[u8], key_pem: &[u8]) -> Result<Arc<ServerConfig>, String> {
521 let certs: Vec<CertificateDer<'static>> = rustls_pemfile::certs(&mut &cert_pem[..])
522 .collect::<Result<_, _>>()
523 .map_err(|e| format!("Error: http2: bad certificate PEM: {e}"))?;
524 if certs.is_empty() {
525 return Err("Error: http2: no certificates found in `cert`".to_string());
526 }
527 let key: PrivateKeyDer<'static> = rustls_pemfile::private_key(&mut &key_pem[..])
528 .map_err(|e| format!("Error: http2: bad private key PEM: {e}"))?
529 .ok_or_else(|| "Error: http2: no private key found in `key`".to_string())?;
530 let mut cfg = ServerConfig::builder()
531 .with_no_client_auth()
532 .with_single_cert(certs, key)
533 .map_err(|e| format!("Error: http2: invalid key/cert: {e}"))?;
534 cfg.alpn_protocols = vec![b"h2".to_vec()];
536 Ok(Arc::new(cfg))
537}
538
539fn take_pending_config(server: &Value) -> Option<Arc<ServerConfig>> {
540 PENDING_CONFIGS.with(|p| {
541 let mut p = p.borrow_mut();
542 p.iter()
543 .position(|(s, _)| s == server)
544 .map(|pos| p.remove(pos).1)
545 })
546}
547
548pub fn instance_call(
551 tag: &str,
552 recv: &Value,
553 method: &str,
554 args: Vec<Value>,
555) -> Result<Value, String> {
556 match tag {
557 "Http2Server" => server_call(recv, method, args),
558 "Http2Stream" => stream_call(recv, method, args),
559 "Http2Session" => session_call(recv, method, args),
560 _ => Err(crate::host::type_error(&format!(
561 "{method} is not a function"
562 ))),
563 }
564}
565
566fn server_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
567 if let Some(r) = emitter_dispatch(recv, method, &args) {
568 return r;
569 }
570 match method {
571 "listen" => server_listen(recv, &args),
572 "close" => server_close(recv, &args),
573 "address" => Ok(get_prop(recv, "@@address").unwrap_or(Value::Undef)),
574 "setTimeout" => Ok(recv.clone()),
575 _ => Err(crate::host::type_error(&format!(
576 "server.{method} is not a function"
577 ))),
578 }
579}
580
581fn server_listen(recv: &Value, args: &[Value]) -> Result<Value, String> {
584 let port = with_host(|h| args.first().map(|v| h.to_number(v)).unwrap_or(0.0)) as u16;
585 let mut host = "0.0.0.0".to_string();
586 let mut cb: Option<Value> = None;
587 for a in &args[1.min(args.len())..] {
588 if with_host(|h| h.as_str(a)).is_some() {
589 host = with_host(|h| h.str_of(a));
590 } else if with_host(|h| crate::host::is_callable(h, a)) {
591 cb = Some(a.clone());
592 }
593 }
594
595 let config = take_pending_config(recv)
596 .ok_or_else(|| crate::host::type_error("http2 server has no secure context"))?;
597 let listener = TcpListener::bind((host.as_str(), port))
598 .map_err(|e| format!("Error: listen EADDRINUSE: {e}"))?;
599 let local = listener.local_addr().ok();
600
601 let id = next_server_id();
602 set_prop(recv, "@@serverid", Value::Float(id as f64));
603 if let Some(addr) = local {
604 let mut a = IndexMap::new();
605 a.insert("port".into(), Value::Float(addr.port() as f64));
606 a.insert(
607 "address".into(),
608 with_host(|h| h.new_str(addr.ip().to_string())),
609 );
610 a.insert(
611 "family".into(),
612 with_host(|h| h.new_str(if addr.is_ipv6() { "IPv6" } else { "IPv4" })),
613 );
614 let addr_obj = with_host(|h| h.new_object(a));
615 set_prop(recv, "@@address", addr_obj);
616 }
617 let stop = Arc::new(AtomicBool::new(false));
618 H2.with(|s| {
619 s.borrow_mut().servers.insert(
620 id,
621 H2ServerRec {
622 emitter: recv.clone(),
623 stop: stop.clone(),
624 },
625 );
626 });
627 with_host(|h| h.incr_handle());
628
629 let io_tx = with_host(|h| h.io_sender());
630 listener.set_nonblocking(true).ok();
631 std::thread::spawn(move || loop {
632 if stop.load(Ordering::Acquire) {
633 break;
634 }
635 match listener.accept() {
636 Ok((stream, _addr)) => {
637 let cfg = config.clone();
638 let tx = io_tx.clone();
639 std::thread::spawn(move || serve_connection(id, stream, cfg, tx));
640 }
641 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
642 std::thread::sleep(std::time::Duration::from_millis(5));
643 }
644 Err(_) => break,
645 }
646 });
647
648 let server = recv.clone();
649 let _ = with_host(|h| h.io_sender()).send(Box::new(move || {
650 super::events::instance_call(&server, "emit", vec![with_host(|h| h.new_str("listening"))])?;
651 if let Some(cb) = cb {
652 invoke(&cb, Vec::new(), None)?;
653 }
654 Ok(())
655 }));
656 Ok(recv.clone())
657}
658
659fn server_close(recv: &Value, args: &[Value]) -> Result<Value, String> {
660 if let Some(id) = u64_prop(recv, "@@serverid") {
661 let rec = H2.with(|s| s.borrow_mut().servers.remove(&id));
662 if let Some(rec) = rec {
663 rec.stop.store(true, Ordering::Release);
664 with_host(|h| h.decr_handle());
665 let _ = with_host(|h| h.io_sender()).send(Box::new(|| Ok(())));
666 }
667 }
668 if let Some(cb) = args
669 .first()
670 .filter(|v| with_host(|h| crate::host::is_callable(h, v)))
671 {
672 invoke(cb, Vec::new(), None)?;
673 }
674 super::events::instance_call(recv, "emit", vec![with_host(|h| h.new_str("close"))])?;
675 Ok(recv.clone())
676}
677
678fn serve_connection(
684 server_id: u64,
685 mut sock: TcpStream,
686 config: Arc<ServerConfig>,
687 io_tx: Sender<IoTask>,
688) {
689 sock.set_nonblocking(false).ok();
694 let mut conn = match ServerConnection::new(config) {
695 Ok(c) => c,
696 Err(_) => return,
697 };
698 if conn.complete_io(&mut sock).is_err() {
699 return;
700 }
701 let is_h2 = conn.alpn_protocol().map(|p| p == b"h2").unwrap_or(false);
703 let mut stream = StreamOwned::new(conn, sock);
704 if !is_h2 {
705 stream.conn.send_close_notify();
707 let _ = stream.flush();
708 let _ = stream.sock.shutdown(std::net::Shutdown::Both);
709 return;
710 }
711 let (tx, rx) = std::sync::mpsc::channel::<H2Cmd>();
713
714 let session_key = next_session_key();
716 {
717 let tx_sess = tx.clone();
718 let _ = io_tx.send(Box::new(move || {
719 on_session(server_id, session_key, tx_sess)
720 }));
721 }
722
723 if h2_debug() {
724 eprintln!("[http2] connection {session_key}: ALPN h2 negotiated, starting framing loop");
725 }
726
727 let mut ok = true;
733 ok &= write_frame(&mut stream, FT_SETTINGS, 0, 0, &[]).is_ok();
734 ok &= stream.flush().is_ok();
735 stream
738 .sock
739 .set_read_timeout(Some(std::time::Duration::from_millis(20)))
740 .ok();
741
742 let mut decoder = Decoder::new();
743 let mut encoder = Encoder::new();
744 let mut inbuf: Vec<u8> = Vec::new();
745 let mut got_preface = false;
746 let mut max_stream_id: u32 = 0;
747 let mut id_to_key: HashMap<u32, u64> = HashMap::new();
749 let mut buf = [0u8; MAX_FRAME_SIZE];
750
751 'conn: loop {
752 if !ok {
756 break;
757 }
758 loop {
762 match rx.try_recv() {
763 Ok(cmd) => {
764 if h2_debug() {
765 eprintln!(
766 "[http2] connection {session_key}: draining {}",
767 cmd_name(&cmd)
768 );
769 }
770 if !apply_cmd(&mut stream, &mut encoder, max_stream_id, cmd) {
771 if h2_debug() {
772 eprintln!("[http2] connection {session_key}: write failed, closing");
773 }
774 break 'conn;
775 }
776 }
777 Err(std::sync::mpsc::TryRecvError::Empty) => break,
778 Err(std::sync::mpsc::TryRecvError::Disconnected) => break,
779 }
780 }
781
782 match stream.read(&mut buf) {
784 Ok(0) => {
785 if h2_debug() {
786 eprintln!("[http2] connection {session_key}: read EOF (Ok 0)");
787 }
788 break;
789 }
790 Ok(n) => inbuf.extend_from_slice(&buf[..n]),
791 Err(ref e)
792 if matches!(
793 e.kind(),
794 std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
795 ) =>
796 {
797 continue;
798 }
799 Err(e) => {
800 if h2_debug() {
801 eprintln!(
802 "[http2] connection {session_key}: read error {:?} ({e})",
803 e.kind()
804 );
805 }
806 break;
807 }
808 }
809
810 if !got_preface {
812 if inbuf.len() < PREFACE.len() {
813 continue;
814 }
815 if &inbuf[..PREFACE.len()] != PREFACE {
816 break; }
818 inbuf.drain(..PREFACE.len());
819 got_preface = true;
820 }
821
822 loop {
824 if inbuf.len() < 9 {
825 break;
826 }
827 let len =
828 ((inbuf[0] as usize) << 16) | ((inbuf[1] as usize) << 8) | (inbuf[2] as usize);
829 if inbuf.len() < 9 + len {
830 break; }
832 let ftype = inbuf[3];
833 let flags = inbuf[4];
834 let stream_id =
835 u32::from_be_bytes([inbuf[5], inbuf[6], inbuf[7], inbuf[8]]) & 0x7fff_ffff;
836 let payload: Vec<u8> = inbuf[9..9 + len].to_vec();
837 inbuf.drain(..9 + len);
838 if h2_debug() {
839 eprintln!(
840 "[http2] connection {session_key}: recv frame type={ftype} flags={flags:#04x} \
841 stream={stream_id} len={len}"
842 );
843 }
844
845 match ftype {
846 FT_SETTINGS => {
847 if flags & FL_ACK == 0 {
848 if write_frame(&mut stream, FT_SETTINGS, FL_ACK, 0, &[])
850 .and_then(|_| stream.flush())
851 .is_err()
852 {
853 break 'conn;
854 }
855 }
856 }
857 FT_PING => {
858 if flags & FL_ACK == 0 {
859 if write_frame(&mut stream, FT_PING, FL_ACK, 0, &payload)
861 .and_then(|_| stream.flush())
862 .is_err()
863 {
864 break 'conn;
865 }
866 }
867 }
868 FT_HEADERS => {
869 if stream_id > max_stream_id {
870 max_stream_id = stream_id;
871 }
872 if flags & FL_END_HEADERS == 0 {
873 continue;
875 }
876 let block = strip_headers_padding_priority(&payload, flags);
877 let decoded = match decoder.decode(&block) {
878 Ok(d) => d,
879 Err(_) => continue,
880 };
881 let headers: Vec<(String, String)> = decoded
882 .into_iter()
883 .map(|(k, v)| {
884 (
885 String::from_utf8_lossy(&k).into_owned(),
886 String::from_utf8_lossy(&v).into_owned(),
887 )
888 })
889 .collect();
890 let end_stream = flags & FL_END_STREAM != 0;
891 let key = next_stream_key();
892 id_to_key.insert(stream_id, key);
893 let tx_stream = tx.clone();
894 let _ = io_tx.send(Box::new(move || {
895 on_headers(server_id, key, stream_id, headers, end_stream, tx_stream)
896 }));
897 }
898 FT_DATA => {
899 if let Some(&key) = id_to_key.get(&stream_id) {
900 let data = strip_data_padding(&payload, flags);
901 let end_stream = flags & FL_END_STREAM != 0;
902 let _ = io_tx.send(Box::new(move || on_data(key, data, end_stream)));
903 }
904 }
905 FT_GOAWAY => break 'conn,
906 FT_WINDOW_UPDATE | FT_PRIORITY | FT_RST_STREAM | FT_CONTINUATION => {}
909 _ => {}
911 }
912 }
913 }
914
915 if h2_debug() {
917 eprintln!(
918 "[http2] connection {session_key}: framing loop exited, sending GOAWAY + closing"
919 );
920 }
921 let mut goaway = Vec::with_capacity(8);
922 goaway.extend_from_slice(&(max_stream_id & 0x7fff_ffff).to_be_bytes());
923 goaway.extend_from_slice(&0u32.to_be_bytes()); let _ = write_frame(&mut stream, FT_GOAWAY, 0, 0, &goaway);
925 let _ = stream.flush();
926 stream.conn.send_close_notify();
927 let _ = stream.flush();
928 let _ = stream.sock.shutdown(std::net::Shutdown::Both);
929
930 let stream_keys: Vec<u64> = id_to_key.values().copied().collect();
933 let _ = io_tx.send(Box::new(move || on_session_close(session_key, stream_keys)));
934}
935
936fn cmd_name(cmd: &H2Cmd) -> &'static str {
938 match cmd {
939 H2Cmd::Respond { .. } => "respond(HEADERS)",
940 H2Cmd::Data { .. } => "data(DATA)",
941 H2Cmd::Close { .. } => "close(RST_STREAM)",
942 H2Cmd::Goaway => "goaway(GOAWAY)",
943 }
944}
945
946fn apply_cmd(
949 stream: &mut StreamOwned<ServerConnection, TcpStream>,
950 encoder: &mut Encoder<'_>,
951 max_stream_id: u32,
952 cmd: H2Cmd,
953) -> bool {
954 match cmd {
955 H2Cmd::Respond {
956 stream_id,
957 headers,
958 end,
959 } => {
960 let block = encode_header_block(encoder, &headers);
961 if h2_debug() {
962 eprintln!(
963 "[http2] write HEADERS stream={stream_id} end_stream={end} \
964 hpack_len={} headers={headers:?}",
965 block.len()
966 );
967 }
968 let flags = FL_END_HEADERS | if end { FL_END_STREAM } else { 0 };
969 write_frame(stream, FT_HEADERS, flags, stream_id, &block)
970 .and_then(|_| stream.flush())
971 .is_ok()
972 }
973 H2Cmd::Data {
974 stream_id,
975 data,
976 end,
977 } => {
978 if h2_debug() {
979 eprintln!(
980 "[http2] write DATA stream={stream_id} len={} end_stream={end}",
981 data.len()
982 );
983 }
984 send_data(stream, stream_id, &data, end)
985 }
986 H2Cmd::Close { stream_id } => {
987 write_frame(stream, FT_RST_STREAM, 0, stream_id, &0u32.to_be_bytes())
989 .and_then(|_| stream.flush())
990 .is_ok()
991 }
992 H2Cmd::Goaway => {
993 let mut g = Vec::with_capacity(8);
994 g.extend_from_slice(&(max_stream_id & 0x7fff_ffff).to_be_bytes());
995 g.extend_from_slice(&0u32.to_be_bytes());
996 let _ = write_frame(stream, FT_GOAWAY, 0, 0, &g);
997 let _ = stream.flush();
998 false
999 }
1000 }
1001}
1002
1003fn send_data(
1006 stream: &mut StreamOwned<ServerConnection, TcpStream>,
1007 stream_id: u32,
1008 data: &[u8],
1009 end: bool,
1010) -> bool {
1011 if data.is_empty() {
1012 let flags = if end { FL_END_STREAM } else { 0 };
1013 return write_frame(stream, FT_DATA, flags, stream_id, &[])
1014 .and_then(|_| stream.flush())
1015 .is_ok();
1016 }
1017 let chunks: Vec<&[u8]> = data.chunks(MAX_FRAME_SIZE).collect();
1018 let last = chunks.len() - 1;
1019 for (i, chunk) in chunks.iter().enumerate() {
1020 let flags = if end && i == last { FL_END_STREAM } else { 0 };
1021 if write_frame(stream, FT_DATA, flags, stream_id, chunk).is_err() {
1022 return false;
1023 }
1024 }
1025 stream.flush().is_ok()
1026}
1027
1028fn write_frame<W: Write>(
1031 w: &mut W,
1032 ftype: u8,
1033 flags: u8,
1034 stream_id: u32,
1035 payload: &[u8],
1036) -> std::io::Result<()> {
1037 let len = payload.len();
1038 let mut hdr = [0u8; 9];
1039 hdr[0] = (len >> 16) as u8;
1040 hdr[1] = (len >> 8) as u8;
1041 hdr[2] = len as u8;
1042 hdr[3] = ftype;
1043 hdr[4] = flags;
1044 hdr[5..9].copy_from_slice(&(stream_id & 0x7fff_ffff).to_be_bytes());
1045 w.write_all(&hdr)?;
1046 w.write_all(payload)?;
1047 Ok(())
1048}
1049
1050fn encode_header_block(encoder: &mut Encoder<'_>, headers: &[(String, String)]) -> Vec<u8> {
1052 let owned: Vec<(Vec<u8>, Vec<u8>)> = headers
1053 .iter()
1054 .map(|(k, v)| (k.as_bytes().to_vec(), v.as_bytes().to_vec()))
1055 .collect();
1056 encoder.encode(owned.iter().map(|(k, v)| (k.as_slice(), v.as_slice())))
1057}
1058
1059fn strip_headers_padding_priority(payload: &[u8], flags: u8) -> Vec<u8> {
1062 let mut start = 0usize;
1063 let mut pad_len = 0usize;
1064 if flags & FL_PADDED != 0 && !payload.is_empty() {
1065 pad_len = payload[0] as usize;
1066 start = 1;
1067 }
1068 if flags & FL_PRIORITY != 0 {
1069 start += 5; }
1071 let end = payload.len().saturating_sub(pad_len);
1072 if start > end {
1073 return Vec::new();
1074 }
1075 payload[start..end].to_vec()
1076}
1077
1078fn strip_data_padding(payload: &[u8], flags: u8) -> Vec<u8> {
1080 if flags & FL_PADDED != 0 && !payload.is_empty() {
1081 let pad_len = payload[0] as usize;
1082 let end = payload.len().saturating_sub(pad_len);
1083 if 1 <= end {
1084 return payload[1..end].to_vec();
1085 }
1086 return Vec::new();
1087 }
1088 payload.to_vec()
1089}
1090
1091fn on_session(server_id: u64, session_key: u64, tx: Sender<H2Cmd>) -> Result<(), String> {
1098 with_host(|h| h.incr_handle());
1099 let server = H2.with(|s| {
1100 s.borrow()
1101 .servers
1102 .get(&server_id)
1103 .map(|r| r.emitter.clone())
1104 });
1105 let Some(server) = server else { return Ok(()) };
1106 let mut extra = IndexMap::new();
1107 extra.insert("@@h2session".into(), Value::Float(session_key as f64));
1108 let session = new_emitter_object("Http2Session", extra);
1109 H2.with(|s| {
1110 s.borrow_mut().sessions.insert(
1111 session_key,
1112 H2SessionRec {
1113 emitter: session.clone(),
1114 tx,
1115 },
1116 );
1117 });
1118 if let Err(e) = super::events::instance_call(
1119 &server,
1120 "emit",
1121 vec![with_host(|h| h.new_str("session")), session],
1122 ) {
1123 report_handler_error("session", &e);
1124 }
1125 Ok(())
1126}
1127
1128fn on_session_close(session_key: u64, stream_keys: Vec<u64>) -> Result<(), String> {
1131 H2.with(|s| {
1132 let mut st = s.borrow_mut();
1133 st.sessions.remove(&session_key);
1134 for k in &stream_keys {
1135 st.streams.remove(k);
1136 }
1137 });
1138 with_host(|h| h.decr_handle());
1139 let _ = with_host(|h| h.io_sender()).send(Box::new(|| Ok(())));
1141 Ok(())
1142}
1143
1144fn on_headers(
1147 server_id: u64,
1148 stream_key: u64,
1149 stream_id: u32,
1150 headers: Vec<(String, String)>,
1151 end_stream: bool,
1152 tx: Sender<H2Cmd>,
1153) -> Result<(), String> {
1154 let server = H2.with(|s| {
1155 s.borrow()
1156 .servers
1157 .get(&server_id)
1158 .map(|r| r.emitter.clone())
1159 });
1160 let Some(server) = server else { return Ok(()) };
1161
1162 let headers_obj = with_host(|h| {
1164 let mut m = IndexMap::new();
1165 for (k, v) in &headers {
1166 m.insert(k.clone(), h.new_str(v.clone()));
1167 }
1168 h.new_object(m)
1169 });
1170
1171 let mut extra = IndexMap::new();
1173 extra.insert("@@h2key".into(), Value::Float(stream_key as f64));
1174 extra.insert("id".into(), Value::Float(stream_id as f64));
1175 let stream_obj = new_emitter_object("Http2Stream", extra);
1176 H2.with(|s| {
1177 s.borrow_mut().streams.insert(
1178 stream_key,
1179 H2StreamRec {
1180 emitter: stream_obj.clone(),
1181 tx,
1182 stream_id,
1183 responded: false,
1184 },
1185 );
1186 });
1187
1188 let method = header_value(&headers, ":method").unwrap_or_else(|| "GET".to_string());
1189 let path = header_value(&headers, ":path").unwrap_or_else(|| "/".to_string());
1190 if h2_debug() {
1191 eprintln!("[http2] dispatch stream={stream_id} {method} {path} (end_stream={end_stream})");
1192 }
1193
1194 if let Err(e) = super::events::instance_call(
1202 &server,
1203 "emit",
1204 vec![
1205 with_host(|h| h.new_str("stream")),
1206 stream_obj.clone(),
1207 headers_obj.clone(),
1208 ],
1209 ) {
1210 report_handler_error("stream", &e);
1211 return Ok(());
1212 }
1213
1214 let req = super::events::new_emitter();
1217 set_prop(&req, "method", with_host(|h| h.new_str(method)));
1218 set_prop(&req, "url", with_host(|h| h.new_str(path)));
1219 set_prop(&req, "headers", headers_obj);
1220 if let Err(e) = super::events::instance_call(
1221 &server,
1222 "emit",
1223 vec![with_host(|h| h.new_str("request")), req, stream_obj.clone()],
1224 ) {
1225 report_handler_error("request", &e);
1226 return Ok(());
1227 }
1228
1229 if end_stream {
1231 if let Err(e) =
1232 super::events::instance_call(&stream_obj, "emit", vec![with_host(|h| h.new_str("end"))])
1233 {
1234 report_handler_error("stream.end", &e);
1235 }
1236 }
1237 Ok(())
1238}
1239
1240fn report_handler_error(event: &str, err: &str) {
1245 eprintln!("http2: uncaught error in '{event}' handler: {err}");
1246}
1247
1248fn h2_debug() -> bool {
1251 std::env::var_os("HTTP2_DEBUG").is_some()
1252}
1253
1254fn on_data(stream_key: u64, data: Vec<u8>, end_stream: bool) -> Result<(), String> {
1256 let stream = H2.with(|s| {
1257 s.borrow()
1258 .streams
1259 .get(&stream_key)
1260 .map(|r| r.emitter.clone())
1261 });
1262 let Some(stream) = stream else { return Ok(()) };
1263 if !data.is_empty() {
1264 let chunk = super::buffer::from_bytes(&data);
1265 if let Err(e) = super::events::instance_call(
1266 &stream,
1267 "emit",
1268 vec![with_host(|h| h.new_str("data")), chunk],
1269 ) {
1270 report_handler_error("data", &e);
1271 return Ok(());
1272 }
1273 }
1274 if end_stream {
1275 if let Err(e) =
1276 super::events::instance_call(&stream, "emit", vec![with_host(|h| h.new_str("end"))])
1277 {
1278 report_handler_error("end", &e);
1279 }
1280 }
1281 Ok(())
1282}
1283
1284fn header_value(headers: &[(String, String)], name: &str) -> Option<String> {
1285 headers
1286 .iter()
1287 .find(|(k, _)| k == name)
1288 .map(|(_, v)| v.clone())
1289}
1290
1291fn stream_key_of(recv: &Value) -> Option<u64> {
1294 u64_prop(recv, "@@h2key")
1295}
1296
1297fn stream_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
1298 if let Some(r) = emitter_dispatch(recv, method, &args) {
1299 return r;
1300 }
1301 match method {
1302 "respond" => {
1303 let hdrs = args.first().map(object_pairs).unwrap_or_default();
1304 let end = args
1306 .get(1)
1307 .and_then(|o| get_prop(o, "endStream"))
1308 .map(|v| with_host(|h| h.truthy(&v)))
1309 .unwrap_or(false);
1310 do_respond(recv, hdrs, end)?;
1311 Ok(recv.clone())
1312 }
1313 "writeHead" => {
1315 let status =
1316 with_host(|h| args.first().map(|v| h.to_number(v)).unwrap_or(200.0)) as u32;
1317 let mut hdrs: Vec<(String, String)> = Vec::new();
1318 for a in args.iter().skip(1) {
1319 if matches!(a, Value::Obj(_)) {
1320 hdrs = object_pairs(a);
1321 break;
1322 }
1323 }
1324 hdrs.insert(0, (":status".to_string(), status.to_string()));
1326 do_respond(recv, hdrs, false)?;
1327 Ok(recv.clone())
1328 }
1329 "setHeader" => {
1330 let k = with_host(|h| h.str_of(&args.first().cloned().unwrap_or(Value::Undef)));
1332 let v = with_host(|h| h.str_of(&args.get(1).cloned().unwrap_or(Value::Undef)));
1333 let bag = pending_headers_obj(recv);
1334 set_prop(&bag, &k, with_host(|h| h.new_str(v)));
1335 Ok(Value::Undef)
1336 }
1337 "getHeader" => {
1338 let k = with_host(|h| h.str_of(&args.first().cloned().unwrap_or(Value::Undef)));
1339 Ok(get_prop(recv, "@@pendingHeaders")
1340 .and_then(|bag| get_prop(&bag, &k))
1341 .unwrap_or(Value::Undef))
1342 }
1343 "removeHeader" => {
1344 let k = with_host(|h| h.str_of(&args.first().cloned().unwrap_or(Value::Undef)));
1345 if let Some(bag) = get_prop(recv, "@@pendingHeaders") {
1346 with_host(|h| {
1347 if let Some(JsObj::Object(p)) = h.get_mut(&bag) {
1348 p.shift_remove(&k);
1349 }
1350 });
1351 }
1352 Ok(Value::Undef)
1353 }
1354 "write" => {
1355 ensure_responded(recv)?;
1356 let bytes = value_bytes(args.first());
1357 send_stream_data(recv, bytes, false);
1358 Ok(Value::Bool(true))
1359 }
1360 "end" => {
1361 ensure_responded(recv)?;
1362 let bytes = args
1363 .first()
1364 .filter(|v| !matches!(v, Value::Undef))
1365 .map(|v| value_bytes(Some(v)))
1366 .unwrap_or_default();
1367 send_stream_data(recv, bytes, true);
1368 super::events::instance_call(recv, "emit", vec![with_host(|h| h.new_str("finish"))])?;
1369 Ok(recv.clone())
1370 }
1371 "close" => {
1372 if let Some(key) = stream_key_of(recv) {
1373 let sent = H2.with(|s| {
1374 s.borrow().streams.get(&key).map(|r| {
1375 let _ = r.tx.send(H2Cmd::Close {
1376 stream_id: r.stream_id,
1377 });
1378 })
1379 });
1380 let _ = sent;
1381 }
1382 Ok(recv.clone())
1383 }
1384 "setEncoding" | "setTimeout" | "pause" | "resume" => Ok(recv.clone()),
1385 _ => Err(crate::host::type_error(&format!(
1386 "stream.{method} is not a function"
1387 ))),
1388 }
1389}
1390
1391fn pending_headers_obj(recv: &Value) -> Value {
1393 if let Some(bag) = get_prop(recv, "@@pendingHeaders") {
1394 return bag;
1395 }
1396 let bag = with_host(|h| h.new_object(IndexMap::new()));
1397 set_prop(recv, "@@pendingHeaders", bag.clone());
1398 bag
1399}
1400
1401fn do_respond(recv: &Value, mut headers: Vec<(String, String)>, end: bool) -> Result<(), String> {
1404 if let Some(bag) = get_prop(recv, "@@pendingHeaders") {
1406 for (k, v) in object_pairs(&bag) {
1407 if !headers.iter().any(|(hk, _)| hk.eq_ignore_ascii_case(&k)) {
1408 headers.push((k, v));
1409 }
1410 }
1411 }
1412 let status = headers
1415 .iter()
1416 .find(|(k, _)| k == ":status")
1417 .map(|(_, v)| v.clone())
1418 .unwrap_or_else(|| "200".to_string());
1419 let mut ordered: Vec<(String, String)> = vec![(":status".to_string(), status)];
1420 for (k, v) in headers.into_iter() {
1421 if k == ":status" {
1422 continue;
1423 }
1424 ordered.push((k.to_ascii_lowercase(), v));
1426 }
1427
1428 let Some(key) = stream_key_of(recv) else {
1429 return Ok(());
1430 };
1431 H2.with(|s| {
1432 if let Some(r) = s.borrow_mut().streams.get_mut(&key) {
1433 if !r.responded {
1434 r.responded = true;
1435 let _ = r.tx.send(H2Cmd::Respond {
1436 stream_id: r.stream_id,
1437 headers: ordered,
1438 end,
1439 });
1440 }
1441 }
1442 });
1443 Ok(())
1444}
1445
1446fn ensure_responded(recv: &Value) -> Result<(), String> {
1448 let Some(key) = stream_key_of(recv) else {
1449 return Ok(());
1450 };
1451 let responded = H2.with(|s| {
1452 s.borrow()
1453 .streams
1454 .get(&key)
1455 .map(|r| r.responded)
1456 .unwrap_or(true)
1457 });
1458 if !responded {
1459 do_respond(recv, Vec::new(), false)?;
1460 }
1461 Ok(())
1462}
1463
1464fn send_stream_data(recv: &Value, data: Vec<u8>, end: bool) {
1466 if let Some(key) = stream_key_of(recv) {
1467 H2.with(|s| {
1468 if let Some(r) = s.borrow().streams.get(&key) {
1469 let _ = r.tx.send(H2Cmd::Data {
1470 stream_id: r.stream_id,
1471 data,
1472 end,
1473 });
1474 }
1475 });
1476 }
1477}
1478
1479fn session_call(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
1482 if let Some(r) = emitter_dispatch(recv, method, &args) {
1483 return r;
1484 }
1485 match method {
1486 "close" | "destroy" | "goaway" => {
1487 if let Some(key) = u64_prop(recv, "@@h2session") {
1488 H2.with(|s| {
1489 if let Some(r) = s.borrow().sessions.get(&key) {
1490 let _ = r.tx.send(H2Cmd::Goaway);
1491 }
1492 });
1493 }
1494 super::events::instance_call(recv, "emit", vec![with_host(|h| h.new_str("close"))])?;
1495 Ok(recv.clone())
1496 }
1497 "settings" | "ping" | "ref" | "unref" | "setTimeout" => Ok(recv.clone()),
1498 _ => Err(crate::host::type_error(&format!(
1499 "session.{method} is not a function"
1500 ))),
1501 }
1502}
1503
1504pub fn new_emitter_object(tag: &str, mut extra: IndexMap<String, Value>) -> Value {
1509 with_host(|h| {
1510 let on = h.new_object(IndexMap::new());
1511 let once = h.new_object(IndexMap::new());
1512 let mut m = IndexMap::new();
1513 m.insert("@@native".into(), h.new_str(tag));
1514 m.insert("@@on".into(), on);
1515 m.insert("@@once".into(), once);
1516 for (k, v) in extra.drain(..) {
1517 m.insert(k, v);
1518 }
1519 h.new_object(m)
1520 })
1521}