@gjsify/http2 0.51.1 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{__esmMin as e,__exportAll as t}from"./_virtual/_rolldown/runtime.js";import n from"gi://GLib?version=2.0";import r from"gi://Gio?version=2.0";import{hasNativeHttp2 as i,loadNativeHttp2 as a}from"@gjsify/http2-native";var o=t({Http2NativeDispatcher:()=>Http2NativeDispatcher});function bytesAreH2Preface(e){if(e.length<s)return!1;for(let t=0;t<s;t++)if(e[t]!==l[t])return!1;return!0}function headersRecordToArrays(e){let t=[],n=[];for(let[r,i]of Object.entries(e))if(Array.isArray(i))for(let e of i)t.push(r),n.push(String(e));else t.push(r),n.push(String(i));return{names:t,values:n}}var s,c,Http2NativeDispatcher,l,u=e(()=>{s=24,c=16*1024,Http2NativeDispatcher=class{_service=null;_connections=new Set;_listenPort=0;_handler;_prefaceGate;_onNonH2c;constructor(e){this._handler=e.handler,this._prefaceGate=e.prefaceGate??!1,this._onNonH2c=e.onNonH2c??null}static available(){return i()}listen(e){if(!i())throw Error(`@gjsify/http2-native prebuild not loadable — dispatcher unavailable`);this._service=new r.SocketService;let t=e;if(e>0){if(!this._service.add_inet_port(e,null))throw Error(`Failed to bind native HTTP/2 dispatcher on port ${e}`)}else{let e=r.InetAddress.new_from_string(`0.0.0.0`)??r.InetAddress.new_any(r.SocketFamily.IPV4),n=r.InetSocketAddress.new(e,0),[i,a]=this._service.add_address(n,r.SocketType.STREAM,r.SocketProtocol.TCP,null);if(!i)throw Error(`Failed to bind native HTTP/2 dispatcher on ephemeral port`);let o=a;o&&typeof o.get_port==`function`&&(t=o.get_port())}return this._listenPort=t,this._service.connect(`incoming`,(e,t)=>(this._acceptConnection(t),!0)),this._service.start(),t}close(){this._service&&=(this._service.stop(),this._service.close(),null);for(let e of this._connections)this._closeConnection(e);this._connections.clear()}_acceptConnection(e){let t=e.get_socket();t.set_blocking(!1);let r=e.get_remote_address(),i=r?r.get_address().to_string():`127.0.0.1`,a=r&&typeof r.get_port==`function`?r.get_port():0;if(this._prefaceGate)try{if(t.condition_check(n.IOCondition.IN)&n.IOCondition.IN){let[n,r]=t.receive(c,null);if(n>=s&&bytesAreH2Preface(r)){this._setupNativeConnection(e,t,r.slice(0,n),i,a);return}this._onNonH2c&&this._onNonH2c(e,r.slice(0,n));return}}catch{}this._setupNativeConnection(e,t,null,i,a)}_setupNativeConnection(e,t,i,o,s){let l=a();if(!l){console.error(`Http2NativeDispatcher: @gjsify/http2-native loaded at listen() but failed here — dropping connection`);try{e.close(null)}catch{}return}let u=l.SessionBridge.new_server();if(!u){console.error(`Http2NativeDispatcher: SessionBridge.new_server() returned null — dropping connection`);try{e.close(null)}catch{}return}let d={bridge:u,socket:t,inStream:e.get_input_stream(),outStream:e.get_output_stream(),inSource:null,streams:new Map,pendingWrite:null,closed:!1,remoteAddress:o,remotePort:s};this._connections.add(d),u.connect(`headers-received`,(e,t,n,r)=>{this._onHeaders(d,t,n,r)}),u.connect(`data-received`,(e,t,n,r)=>{this._onData(d,t,n,r)}),u.connect(`stream-closed`,(e,t,n)=>{this._onStreamClosed(d,t,n)}),u.connect(`frame-send-ready`,()=>{this._flushOutput(d)}),u.connect(`goaway-received`,()=>{this._closeConnection(d)}),i&&i.byteLength>0&&(u.feed_input(n.Bytes.new(i)),u.dispatch_pending(),this._flushOutput(d)),this._flushOutput(d);let f=d.inStream.create_source(null),cb=()=>{if(d.closed)return!1;try{let e=d.inStream.read_bytes(c,null);if(e.get_size()===0)return this._closeConnection(d),!1;u.feed_input(e),u.dispatch_pending(),this._flushOutput(d)}catch(e){return e instanceof n.Error&&e.matches(r.io_error_quark(),r.IOErrorEnum.WOULD_BLOCK)?!0:(this._closeConnection(d),!1)}return!0};f.set_callback(cb),f.attach(n.MainContext.default()),d.inSource=f}_flushOutput(e){if(e.closed)return;let t=e.bridge.drain_output();if(t.get_size()===0)return;let n=t.get_data();try{e.outStream.write(n,null)}catch{this._closeConnection(e)}}_onHeaders(e,t,n,r){let i=n.deep_unpack(),a={};for(let[e,t]of i){let n=e.toLowerCase();if(n in a){let e=a[n];Array.isArray(e)?e.push(t):a[n]=[e,t]}else a[n]=t}let o={endStreamFromPeer:r,bodyBuffer:[],bodyResolvers:[],bodyClosed:r,responseSubmitted:!1};e.streams.set(t,o);let s={streamId:t,headers:a,endStream:r,remoteAddress:e.remoteAddress,remotePort:e.remotePort,localPort:this._listenPort,body:this._makeBodyIterator(o),backend:this._makeBackend(e,t)};Promise.resolve().then(()=>{try{this._handler(s)}catch(e){let t=e;console.error(`Http2NativeDispatcher: handler threw:`,t.message??String(e),t.stack??``)}})}_onData(e,t,n,r){let i=e.streams.get(t);if(i){if(n.get_size()>0){let e=Buffer.from(n.get_data());i.bodyResolvers.length>0?i.bodyResolvers.shift()({value:e,done:!1}):i.bodyBuffer.push(e)}if(r)for(i.bodyClosed=!0;i.bodyResolvers.length>0;)i.bodyResolvers.shift()({value:void 0,done:!0})}}_onStreamClosed(e,t,n){let r=e.streams.get(t);if(r)for(r.bodyClosed=!0;r.bodyResolvers.length>0;)r.bodyResolvers.shift()({value:void 0,done:!0});e.streams.delete(t)}_makeBackend(e,t){return{streamId:t,respond:(n,r)=>{this._submitResponse(e,t,n,r)},writeData:(n,r)=>{this._submitData(e,t,n,r)},reset:(n=0)=>{e.bridge.submit_rst_stream(t,n),this._flushOutput(e)},pushPromise:n=>{let{names:r,values:i}=headersRecordToArrays(n),a=e.bridge.submit_push_promise(t,r,i);return this._flushOutput(e),a?(e.streams.set(a,{endStreamFromPeer:!0,bodyBuffer:[],bodyResolvers:[],bodyClosed:!0,responseSubmitted:!1}),this._makeBackend(e,a)):null}}}_submitResponse(e,t,n,r){let i=e.streams.get(t);if(!i||i.responseSubmitted)return;i.responseSubmitted=!0;let{names:a,values:o}=headersRecordToArrays(n);e.bridge.submit_response(t,a,o,r),this._flushOutput(e)}_submitData(e,t,r,i){if(r.length===0&&!i)return;let a=n.Bytes.new(new Uint8Array(r.buffer,r.byteOffset,r.byteLength));e.bridge.submit_data(t,a,i),this._flushOutput(e)}_makeBodyIterator(e){return{[Symbol.asyncIterator](){return{next(){return e.bodyBuffer.length>0?Promise.resolve({value:e.bodyBuffer.shift(),done:!1}):e.bodyClosed?Promise.resolve({value:void 0,done:!0}):new Promise(t=>{e.bodyResolvers.push(t)})}}}}}_closeConnection(e){if(e.closed)return;e.closed=!0,e.bridge.submit_goaway(0,0);try{let t=e.bridge.drain_output();t.get_size()>0&&e.outStream.write(t.get_data(),null)}catch{}e.inSource&&=(e.inSource.destroy(),null);try{e.socket.shutdown(!1,!0)}catch{}for(let t of e.streams.values())for(t.bodyClosed=!0;t.bodyResolvers.length>0;)t.bodyResolvers.shift()({value:void 0,done:!0});let finalClose=()=>{try{e.outStream.close(null)}catch{}try{e.inStream.close(null)}catch{}try{e.socket.close()}catch{}e.bridge.close()};n.idle_add(n.PRIORITY_DEFAULT,()=>(finalClose(),!1)),this._connections.delete(e)}},l=new Uint8Array([80,82,73,32,42,32,72,84,84,80,47,50,46,48,13,10,13,10,83,77,13,10,13,10])});u();export{Http2NativeDispatcher,u as init_native_dispatcher,o as native_dispatcher_exports};
1
+ import{__esmMin as e,__exportAll as t}from"./_virtual/_rolldown/runtime.js";import{closeSocketService as n,releaseListenPort as r}from"@gjsify/utils";import i from"gi://GLib?version=2.0";import a from"gi://Gio?version=2.0";import{hasNativeHttp2 as o,loadNativeHttp2 as s}from"@gjsify/http2-native";var c=t({Http2NativeDispatcher:()=>Http2NativeDispatcher});function bytesAreH2Preface(e){if(e.length<l)return!1;for(let t=0;t<l;t++)if(e[t]!==d[t])return!1;return!0}function headersRecordToArrays(e){let t=[],n=[];for(let[r,i]of Object.entries(e))if(Array.isArray(i))for(let e of i)t.push(r),n.push(String(e));else t.push(r),n.push(String(i));return{names:t,values:n}}var l,u,Http2NativeDispatcher,d,f=e(()=>{l=24,u=16*1024,Http2NativeDispatcher=class{_service=null;_connections=new Set;_listenPort=0;_handler;_prefaceGate;_onNonH2c;constructor(e){this._handler=e.handler,this._prefaceGate=e.prefaceGate??!1,this._onNonH2c=e.onNonH2c??null}static available(){return o()}listen(e){if(!o())throw Error(`@gjsify/http2-native prebuild not loadable — dispatcher unavailable`);r(e),this._service=new a.SocketService;let t=e;if(e>0){if(!this._service.add_inet_port(e,null))throw Error(`Failed to bind native HTTP/2 dispatcher on port ${e}`)}else{let e=a.InetAddress.new_from_string(`0.0.0.0`)??a.InetAddress.new_any(a.SocketFamily.IPV4),n=a.InetSocketAddress.new(e,0),[r,i]=this._service.add_address(n,a.SocketType.STREAM,a.SocketProtocol.TCP,null);if(!r)throw Error(`Failed to bind native HTTP/2 dispatcher on ephemeral port`);let o=i;o&&typeof o.get_port==`function`&&(t=o.get_port())}return this._listenPort=t,this._service.connect(`incoming`,(e,t)=>(this._acceptConnection(t),!0)),this._service.start(),t}close(){this._service&&=(n(this._service,[this._listenPort]),null);for(let e of this._connections)this._closeConnection(e);this._connections.clear()}_acceptConnection(e){let t=e.get_socket();t.set_blocking(!1);let n=e.get_remote_address(),r=n?n.get_address().to_string():`127.0.0.1`,a=n&&typeof n.get_port==`function`?n.get_port():0;if(this._prefaceGate)try{if(t.condition_check(i.IOCondition.IN)&i.IOCondition.IN){let[n,i]=t.receive(u,null);if(n>=l&&bytesAreH2Preface(i)){this._setupNativeConnection(e,t,i.slice(0,n),r,a);return}this._onNonH2c&&this._onNonH2c(e,i.slice(0,n));return}}catch{}this._setupNativeConnection(e,t,null,r,a)}_setupNativeConnection(e,t,n,r,o){let c=s();if(!c){console.error(`Http2NativeDispatcher: @gjsify/http2-native loaded at listen() but failed here — dropping connection`);try{e.close(null)}catch{}return}let l=c.SessionBridge.new_server();if(!l){console.error(`Http2NativeDispatcher: SessionBridge.new_server() returned null — dropping connection`);try{e.close(null)}catch{}return}let d={bridge:l,socket:t,inStream:e.get_input_stream(),outStream:e.get_output_stream(),inSource:null,streams:new Map,pendingWrite:null,closed:!1,remoteAddress:r,remotePort:o};this._connections.add(d),l.connect(`headers-received`,(e,t,n,r)=>{this._onHeaders(d,t,n,r)}),l.connect(`data-received`,(e,t,n,r)=>{this._onData(d,t,n,r)}),l.connect(`stream-closed`,(e,t,n)=>{this._onStreamClosed(d,t,n)}),l.connect(`frame-send-ready`,()=>{this._flushOutput(d)}),l.connect(`goaway-received`,()=>{this._closeConnection(d)}),n&&n.byteLength>0&&(l.feed_input(i.Bytes.new(n)),l.dispatch_pending(),this._flushOutput(d)),this._flushOutput(d);let f=d.inStream.create_source(null),cb=()=>{if(d.closed)return!1;try{let e=d.inStream.read_bytes(u,null);if(e.get_size()===0)return this._closeConnection(d),!1;l.feed_input(e),l.dispatch_pending(),this._flushOutput(d)}catch(e){return e instanceof i.Error&&e.matches(a.io_error_quark(),a.IOErrorEnum.WOULD_BLOCK)?!0:(this._closeConnection(d),!1)}return!0};f.set_callback(cb),f.attach(i.MainContext.default()),d.inSource=f}_flushOutput(e){if(e.closed)return;let t=e.bridge.drain_output();if(t.get_size()===0)return;let n=t.get_data();try{e.outStream.write(n,null)}catch{this._closeConnection(e)}}_onHeaders(e,t,n,r){let i=n.deep_unpack(),a={};for(let[e,t]of i){let n=e.toLowerCase();if(n in a){let e=a[n];Array.isArray(e)?e.push(t):a[n]=[e,t]}else a[n]=t}let o={endStreamFromPeer:r,bodyBuffer:[],bodyResolvers:[],bodyClosed:r,responseSubmitted:!1};e.streams.set(t,o);let s={streamId:t,headers:a,endStream:r,remoteAddress:e.remoteAddress,remotePort:e.remotePort,localPort:this._listenPort,body:this._makeBodyIterator(o),backend:this._makeBackend(e,t)};Promise.resolve().then(()=>{try{this._handler(s)}catch(e){let t=e;console.error(`Http2NativeDispatcher: handler threw:`,t.message??String(e),t.stack??``)}})}_onData(e,t,n,r){let i=e.streams.get(t);if(i){if(n.get_size()>0){let e=Buffer.from(n.get_data());i.bodyResolvers.length>0?i.bodyResolvers.shift()({value:e,done:!1}):i.bodyBuffer.push(e)}if(r)for(i.bodyClosed=!0;i.bodyResolvers.length>0;)i.bodyResolvers.shift()({value:void 0,done:!0})}}_onStreamClosed(e,t,n){let r=e.streams.get(t);if(r)for(r.bodyClosed=!0;r.bodyResolvers.length>0;)r.bodyResolvers.shift()({value:void 0,done:!0});e.streams.delete(t)}_makeBackend(e,t){return{streamId:t,respond:(n,r)=>{this._submitResponse(e,t,n,r)},writeData:(n,r)=>{this._submitData(e,t,n,r)},reset:(n=0)=>{e.bridge.submit_rst_stream(t,n),this._flushOutput(e)},pushPromise:n=>{let{names:r,values:i}=headersRecordToArrays(n),a=e.bridge.submit_push_promise(t,r,i);return this._flushOutput(e),a?(e.streams.set(a,{endStreamFromPeer:!0,bodyBuffer:[],bodyResolvers:[],bodyClosed:!0,responseSubmitted:!1}),this._makeBackend(e,a)):null}}}_submitResponse(e,t,n,r){let i=e.streams.get(t);if(!i||i.responseSubmitted)return;i.responseSubmitted=!0;let{names:a,values:o}=headersRecordToArrays(n);e.bridge.submit_response(t,a,o,r),this._flushOutput(e)}_submitData(e,t,n,r){if(n.length===0&&!r)return;let a=i.Bytes.new(new Uint8Array(n.buffer,n.byteOffset,n.byteLength));e.bridge.submit_data(t,a,r),this._flushOutput(e)}_makeBodyIterator(e){return{[Symbol.asyncIterator](){return{next(){return e.bodyBuffer.length>0?Promise.resolve({value:e.bodyBuffer.shift(),done:!1}):e.bodyClosed?Promise.resolve({value:void 0,done:!0}):new Promise(t=>{e.bodyResolvers.push(t)})}}}}}_closeConnection(e){if(e.closed)return;e.closed=!0,e.bridge.submit_goaway(0,0);try{let t=e.bridge.drain_output();t.get_size()>0&&e.outStream.write(t.get_data(),null)}catch{}e.inSource&&=(e.inSource.destroy(),null);try{e.socket.shutdown(!1,!0)}catch{}for(let t of e.streams.values())for(t.bodyClosed=!0;t.bodyResolvers.length>0;)t.bodyResolvers.shift()({value:void 0,done:!0});let finalClose=()=>{try{e.outStream.close(null)}catch{}try{e.inStream.close(null)}catch{}try{e.socket.close()}catch{}e.bridge.close()};i.idle_add(i.PRIORITY_DEFAULT,()=>(finalClose(),!1)),this._connections.delete(e)}},d=new Uint8Array([80,82,73,32,42,32,72,84,84,80,47,50,46,48,13,10,13,10,83,77,13,10,13,10])});f();export{Http2NativeDispatcher,f as init_native_dispatcher,c as native_dispatcher_exports};
@@ -1,4 +1,4 @@
1
- import{__toCommonJS as e}from"../_virtual/_rolldown/runtime.js";import{Http2ServerRequest as t}from"./request.js";import{Http2ServerResponse as n,ServerHttp2Stream as r}from"./response.js";import{ServerHttp2Session as i}from"./session.js";import{init_native_dispatcher as a,native_dispatcher_exports as o}from"../native-dispatcher.js";import s from"@girs/soup-3.0";import c from"@girs/gio-2.0";import l from"@girs/glib-2.0";import{EventEmitter as u}from"node:events";import{Buffer as d}from"node:buffer";import{deferEmit as f,ensureMainLoop as p}from"@gjsify/utils/core";const m=new Set;var Http2Server=class extends u{listening=!1;maxHeadersCount=2e3;timeout=0;_soupServer=null;_nativeDispatcher=null;_address=null;_options;get soupServer(){return this._soupServer}get nativeDispatcher(){return this._nativeDispatcher}constructor(e,t){super(),typeof e==`function`&&(t=e,e={}),this._options=e??{},t&&this.on(`request`,t)}listen(...e){let t=0,n=`0.0.0.0`,r;for(let i of e)typeof i==`number`?t=i:typeof i==`string`?n=i:typeof i==`function`&&(r=i);r&&this.once(`listening`,r);try{let e=this._options.nativeDispatcher??`auto`,r=e===`force`||e===`auto`&&this._options.allowHTTP1===!1;if(e===`off`&&this._options.allowHTTP1===!1)throw Error(`createServer({ allowHTTP1: false }) requires the native dispatcher; nativeDispatcher cannot be "off" in this configuration`);if(r)return this._startNativeListen(t,n),p(),f(this,`listening`),m.add(this),this;this._soupServer=new s.Server({}),this._configureSoupServer(this._soupServer),this._soupServer.add_handler(null,(e,t,n)=>{this._handleRequest(t)}),this._soupServer.listen_local(t,s.ServerListenOptions.IPV4_ONLY),p();let i=this._soupServer.get_listeners(),a=t;if(i&&i.length>0){let e=i[0].get_local_address();e&&typeof e.get_port==`function`&&(a=e.get_port())}this.listening=!0,this._address={port:a,family:`IPv4`,address:n},m.add(this),f(this,`listening`)}catch(e){let t=e instanceof Error?e:Error(String(e));if(this.listenerCount(`error`)===0)throw t;f(this,`error`,t)}return this}_startNativeListen(t,n){let{Http2NativeDispatcher:r}=(a(),e(o));if(!r.available())throw Error(`@gjsify/http2-native prebuild is not loadable. createServer({ allowHTTP1: false }) requires the native HTTP/2 dispatcher. Ensure GjsifyHttp2-1.0.typelib is installed.`);this._nativeDispatcher=new r({handler:e=>this._handleNativeStream(e)});let i=this._nativeDispatcher.listen(t);this.listening=!0,this._address={port:i,family:`IPv4`,address:n}}_handleNativeStream(e){let a=new t,adapt=e=>({streamId:e.streamId,submitResponse:(t,n,r,i)=>{let a={":status":t};for(let[e,t]of r)a[e]=t;e.respond(a,i)},submitData:(t,n)=>e.writeData(t,n),reset:t=>e.reset(t),pushPromise:t=>{let n=e.pushPromise(t);return n?adapt(n):null}}),o=new n(null,adapt(e.backend)),s=new r(o,new i,{streamId:e.streamId});a._setStream(s),o._setStream(s);let c=e.headers;a.method=String(c[`:method`]??`GET`),a.url=String(c[`:path`]??`/`),a.authority=String(c[`:authority`]??``),a.scheme=String(c[`:scheme`]??`http`),a.httpVersion=`2.0`,a.httpVersionMajor=2,a.httpVersionMinor=0;for(let[e,t]of Object.entries(c))if(!e.startsWith(`:`))if(a.headers[e]=t,Array.isArray(t))for(let n of t)a.rawHeaders.push(e,n);else a.rawHeaders.push(e,t);a.socket={remoteAddress:e.remoteAddress,remotePort:e.remotePort,localAddress:this._address?.address??`127.0.0.1`,localPort:e.localPort,encrypted:!1},(async()=>{try{for await(let t of e.body)a._pushBody(t);a._pushBody(null)}catch{a._pushBody(null)}})();let l={...c};this.emit(`stream`,s,l),this.emit(`request`,a,o)}_configureSoupServer(e){}_handleRequest(e){let a=new t,o=new n(e);a.method=e.get_method();let l=e.get_uri(),u=l.get_path(),d=l.get_query();a.url=d?u+`?`+d:u,a.authority=l.get_host()??``,a.scheme=l.get_scheme()??`http`,e.get_http_version()===s.HTTPVersion.HTTP_2_0?(a.httpVersion=`2.0`,a.httpVersionMajor=2,a.httpVersionMinor=0):(a.httpVersion=`1.1`,a.httpVersionMajor=1,a.httpVersionMinor=1),e.get_request_headers().foreach((e,t)=>{let n=e.toLowerCase();if(a.rawHeaders.push(e,t),n in a.headers){let e=a.headers[n];Array.isArray(e)?e.push(t):a.headers[n]=[e,t]}else a.headers[n]=t});let f=e.get_remote_host()??`127.0.0.1`,p=e.get_remote_address();a.socket={remoteAddress:f,remotePort:p instanceof c.InetSocketAddress?p.get_port():0,localAddress:this._address?.address??`127.0.0.1`,localPort:this._address?.port??0,encrypted:this instanceof Http2SecureServer};let m=e.get_request_body();m?.data&&m.data.length>0?a._pushBody(m.data):a._pushBody(null);let h={":method":a.method,":path":a.url,":authority":a.authority,":scheme":a.scheme,...a.headers};e.pause(),o.on(`finish`,()=>e.unpause());let g=new r(o,new i);a._setStream(g),o._setStream(g),this.emit(`stream`,g,h),this.emit(`request`,a,o)}address(){return this._address}close(e){return e&&this.once(`close`,e),this._soupServer&&=(this._soupServer.disconnect(),null),this._nativeDispatcher&&=(this._nativeDispatcher.close(),null),this.listening=!1,m.delete(this),f(this,`close`),this}setTimeout(e,t){return this.timeout=e,t&&this.on(`timeout`,t),this}},Http2SecureServer=class extends Http2Server{_tlsCert=null;constructor(e,t){if(super(e,t),e.cert&&e.key){let t=_toPemString(e.cert),n=_toPemString(e.key);this._tlsCert=_createTlsCertificate(t,n)}else e.pfx}_configureSoupServer(e){this._tlsCert&&e.set_tls_certificate(this._tlsCert)}setSecureContext(e){if(e.cert&&e.key){let t=_toPemString(e.cert),n=_toPemString(e.key);this._tlsCert=_createTlsCertificate(t,n),this._soupServer&&this._tlsCert&&this._soupServer.set_tls_certificate(this._tlsCert)}}};function _toPemString(e){return Array.isArray(e)?e.map(_toPemString).join(`
2
- `):d.isBuffer(e)?e.toString(`utf8`):e}function _createTlsCertificate(e,t){let n=e.trimEnd()+`
1
+ import{__toCommonJS as e}from"../_virtual/_rolldown/runtime.js";import{Http2ServerRequest as t}from"./request.js";import{Http2ServerResponse as n,ServerHttp2Stream as r}from"./response.js";import{ServerHttp2Session as i}from"./session.js";import{init_native_dispatcher as a,native_dispatcher_exports as o}from"../native-dispatcher.js";import s from"@girs/soup-3.0";import c from"@girs/gio-2.0";import{EventEmitter as l}from"node:events";import{Buffer as u}from"node:buffer";import{deferEmit as d,ensureMainLoop as f}from"@gjsify/utils/core";const p=new Set;var Http2Server=class extends l{listening=!1;maxHeadersCount=2e3;timeout=0;_soupServer=null;_nativeDispatcher=null;_address=null;_options;get soupServer(){return this._soupServer}get nativeDispatcher(){return this._nativeDispatcher}constructor(e,t){super(),typeof e==`function`&&(t=e,e={}),this._options=e??{},t&&this.on(`request`,t)}listen(...e){let t=0,n=`0.0.0.0`,r;for(let i of e)typeof i==`number`?t=i:typeof i==`string`?n=i:typeof i==`function`&&(r=i);r&&this.once(`listening`,r);try{let e=this._options.nativeDispatcher??`auto`,r=e===`force`||e===`auto`&&this._options.allowHTTP1===!1;if(e===`off`&&this._options.allowHTTP1===!1)throw Error(`createServer({ allowHTTP1: false }) requires the native dispatcher; nativeDispatcher cannot be "off" in this configuration`);let i=this._tlsSetupError(r);if(i)throw i;if(r)return this._startNativeListen(t,n),f(),d(this,`listening`),p.add(this),this;this._soupServer=new s.Server({}),this._configureSoupServer(this._soupServer),this._soupServer.add_handler(null,(e,t,n)=>{this._handleRequest(t)});let a=this._soupServer.is_https()?s.ServerListenOptions.IPV4_ONLY|s.ServerListenOptions.HTTPS:s.ServerListenOptions.IPV4_ONLY;this._soupServer.listen_local(t,a),f();let o=this._soupServer.get_listeners(),c=t;if(o&&o.length>0){let e=o[0].get_local_address();e&&typeof e.get_port==`function`&&(c=e.get_port())}this.listening=!0,this._address={port:c,family:`IPv4`,address:n},p.add(this),d(this,`listening`)}catch(e){let t=e instanceof Error?e:Error(String(e));if(this.listenerCount(`error`)===0)throw t;d(this,`error`,t)}return this}_startNativeListen(t,n){let{Http2NativeDispatcher:r}=(a(),e(o));if(!r.available())throw Error(`@gjsify/http2-native prebuild is not loadable. createServer({ allowHTTP1: false }) requires the native HTTP/2 dispatcher. Ensure GjsifyHttp2-1.0.typelib is installed.`);this._nativeDispatcher=new r({handler:e=>this._handleNativeStream(e)});let i=this._nativeDispatcher.listen(t);this.listening=!0,this._address={port:i,family:`IPv4`,address:n}}_handleNativeStream(e){let a=new t,adapt=e=>({streamId:e.streamId,submitResponse:(t,n,r,i)=>{let a={":status":t};for(let[e,t]of r)a[e]=t;e.respond(a,i)},submitData:(t,n)=>e.writeData(t,n),reset:t=>e.reset(t),pushPromise:t=>{let n=e.pushPromise(t);return n?adapt(n):null}}),o=new n(null,adapt(e.backend)),s=new r(o,new i,{streamId:e.streamId});a._setStream(s),o._setStream(s);let c=e.headers;a.method=String(c[`:method`]??`GET`),a.url=String(c[`:path`]??`/`),a.authority=String(c[`:authority`]??``),a.scheme=String(c[`:scheme`]??`http`),a.httpVersion=`2.0`,a.httpVersionMajor=2,a.httpVersionMinor=0;for(let[e,t]of Object.entries(c))if(!e.startsWith(`:`))if(a.headers[e]=t,Array.isArray(t))for(let n of t)a.rawHeaders.push(e,n);else a.rawHeaders.push(e,t);a.socket={remoteAddress:e.remoteAddress,remotePort:e.remotePort,localAddress:this._address?.address??`127.0.0.1`,localPort:e.localPort,encrypted:!1},(async()=>{try{for await(let t of e.body)a._pushBody(t);a._pushBody(null)}catch{a._pushBody(null)}})();let l={...c};this.emit(`stream`,s,l),this.emit(`request`,a,o)}_configureSoupServer(e){}_tlsSetupError(e){return null}_handleRequest(e){let a=new t,o=new n(e);a.method=e.get_method();let l=e.get_uri(),u=l.get_path(),d=l.get_query();a.url=d?u+`?`+d:u,a.authority=l.get_host()??``,a.scheme=l.get_scheme()??`http`,e.get_http_version()===s.HTTPVersion.HTTP_2_0?(a.httpVersion=`2.0`,a.httpVersionMajor=2,a.httpVersionMinor=0):(a.httpVersion=`1.1`,a.httpVersionMajor=1,a.httpVersionMinor=1),e.get_request_headers().foreach((e,t)=>{let n=e.toLowerCase();if(a.rawHeaders.push(e,t),n in a.headers){let e=a.headers[n];Array.isArray(e)?e.push(t):a.headers[n]=[e,t]}else a.headers[n]=t});let f=e.get_remote_host()??`127.0.0.1`,p=e.get_remote_address();a.socket={remoteAddress:f,remotePort:p instanceof c.InetSocketAddress?p.get_port():0,localAddress:this._address?.address??`127.0.0.1`,localPort:this._address?.port??0,encrypted:this instanceof Http2SecureServer};let m=e.get_request_body();m?.data&&m.data.length>0?a._pushBody(m.data):a._pushBody(null);let h={":method":a.method,":path":a.url,":authority":a.authority,":scheme":a.scheme,...a.headers};e.pause(),o.on(`finish`,()=>e.unpause());let g=new r(o,new i);a._setStream(g),o._setStream(g),this.emit(`stream`,g,h),this.emit(`request`,a,o)}address(){return this._address}close(e){return e&&this.once(`close`,e),this._soupServer&&=(this._soupServer.disconnect(),null),this._nativeDispatcher&&=(this._nativeDispatcher.close(),null),this.listening=!1,p.delete(this),d(this,`close`),this}setTimeout(e,t){return this.timeout=e,t&&this.on(`timeout`,t),this}},Http2SecureServer=class extends Http2Server{_tlsCert=null;constructor(e,t){if(super(e,t),e.cert&&e.key){let t=_toPemString(e.cert),n=_toPemString(e.key);this._tlsCert=_createTlsCertificate(t,n)}else e.pfx}_tlsSetupError(e){let t=this._options;return this._tlsCert?e?Error(`http2.createSecureServer: allowHTTP1: false would serve cleartext h2c`):t.requestCert?Error(`http2.createSecureServer: requestCert is not implemented on GJS`):null:Error(`http2.createSecureServer: no certificate — pass key and cert (PEM)`)}_configureSoupServer(e){this._tlsCert&&e.set_tls_certificate(this._tlsCert)}setSecureContext(e){if(e.cert&&e.key){let t=_toPemString(e.cert),n=_toPemString(e.key);this._tlsCert=_createTlsCertificate(t,n),this._soupServer&&this._tlsCert&&this._soupServer.set_tls_certificate(this._tlsCert)}}};function _toPemString(e){return Array.isArray(e)?e.map(_toPemString).join(`
2
+ `):u.isBuffer(e)?e.toString(`utf8`):e}function _createTlsCertificate(e,t){let n=e.trimEnd()+`
3
3
  `+t.trimEnd()+`
4
- `;try{return c.TlsCertificate.new_from_pem(n,-1)}catch{let n=l.get_tmp_dir(),r=l.build_filenamev([n,`gjsify-http2-cert.pem`]),i=l.build_filenamev([n,`gjsify-http2-key.pem`]);try{return l.file_set_contents(r,e),l.file_set_contents(i,t),c.TlsCertificate.new_from_files(r,i)}finally{try{c.File.new_for_path(r).delete(null)}catch{}try{c.File.new_for_path(i).delete(null)}catch{}}}}export{Http2SecureServer,Http2Server};
4
+ `;return c.TlsCertificate.new_from_pem(n,-1)}export{Http2SecureServer,Http2Server};
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -59,6 +59,8 @@ export declare class Http2Server extends EventEmitter {
59
59
  /** @internal Handler for streams arriving on the native dispatcher. */
60
60
  private _handleNativeStream;
61
61
  protected _configureSoupServer(_server: Soup.Server): void;
62
+ /** Why this server cannot listen securely; null when it needs no TLS or has all it needs. */
63
+ protected _tlsSetupError(_nativePath: boolean): Error | null;
62
64
  private _handleRequest;
63
65
  address(): {
64
66
  port: number;
@@ -81,6 +83,7 @@ export interface SecureServerOptions extends ServerOptions {
81
83
  export declare class Http2SecureServer extends Http2Server {
82
84
  private _tlsCert;
83
85
  constructor(options: SecureServerOptions, handler?: (req: Http2ServerRequest, res: Http2ServerResponse) => void);
86
+ protected _tlsSetupError(nativePath: boolean): Error | null;
84
87
  protected _configureSoupServer(server: Soup.Server): void;
85
88
  setSecureContext(options: SecureServerOptions): void;
86
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/http2",
3
- "version": "0.51.1",
3
+ "version": "0.53.0",
4
4
  "description": "Node.js http2 module for Gjs",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -33,19 +33,19 @@
33
33
  "http2"
34
34
  ],
35
35
  "devDependencies": {
36
- "@gjsify/cli": "^0.51.1",
37
- "@gjsify/unit": "^0.51.1",
36
+ "@gjsify/cli": "^0.53.0",
37
+ "@gjsify/unit": "^0.53.0",
38
38
  "@types/node": "^25.9.2",
39
39
  "typescript": "^6.0.3"
40
40
  },
41
41
  "dependencies": {
42
- "@girs/gio-2.0": "5.0.0",
43
- "@girs/glib-2.0": "5.0.0",
44
- "@girs/gobject-2.0": "5.0.0",
45
- "@girs/soup-3.0": "5.0.0",
46
- "@gjsify/events": "^0.51.1",
47
- "@gjsify/http2-native": "^0.51.1",
48
- "@gjsify/utils": "^0.51.1"
42
+ "@girs/gio-2.0": "5.4.0",
43
+ "@girs/glib-2.0": "5.4.0",
44
+ "@girs/gobject-2.0": "5.4.0",
45
+ "@girs/soup-3.0": "5.4.0",
46
+ "@gjsify/events": "^0.53.0",
47
+ "@gjsify/http2-native": "^0.53.0",
48
+ "@gjsify/utils": "^0.53.0"
49
49
  },
50
50
  "gjsify": {
51
51
  "runtimes": {