@symbo.ls/tunnel 3.14.9 → 3.14.10
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.
package/CHANGELOG.md
CHANGED
package/dist/cjs/src/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var B=Object.create;var y=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,G=Object.prototype.hasOwnProperty;var Q=(r,n)=>{for(var o in n)y(r,o,{get:n[o],enumerable:!0})},q=(r,n,o,h)=>{if(n&&typeof n=="object"||typeof n=="function")for(let c of H(n))!G.call(r,c)&&c!==o&&y(r,c,{get:()=>n[c],enumerable:!(h=x(n,c))||h.enumerable});return r};var v=(r,n,o)=>(o=r!=null?B(A(r)):{},q(n||!r||!r.__esModule?y(o,"default",{value:r,enumerable:!0}):o,r)),Y=r=>q(y({},"__esModule",{value:!0}),r);var K={};Q(K,{createTunnelClient:()=>J});module.exports=Y(K);var R=v(require("ws"),1),U=v(require("node:http"),1),j=require("node:https"),i=require("./protocol.js");const J=(r={})=>{const{host:n=process.env.TUNNEL_HOST||"tunnel.symbo.ls",port:o=443,secure:h=o===443,localPort:c,localHost:w="127.0.0.1",subdomain:P=null,token:_=process.env.TUNNEL_AUTH_TOKEN||null,reconnect:b=!0,reconnectDelayMs:D=2e3,onRegistered:k=()=>{},onError:N=e=>console.error("[tunnel] error:",e.message||e),onClose:L=()=>{},onRequest:C=()=>{},onResponse:$=()=>{},log:m=(...e)=>console.log("[tunnel]",...e)}=r;if(!c)throw new Error("createTunnelClient: localPort is required");let l=null,T=!1;const S=new Map,I=()=>{const e=h?"wss":"ws",s=new URLSearchParams;P&&s.set("subdomain",P);const a=s.toString();return`${e}://${n}${o&&o!==80&&o!==443?":"+o:""}/_tunnel${a?"?"+a:""}`},p=e=>{l&&l.readyState===R.default.OPEN&&l.send(JSON.stringify(e))},M=e=>{const s=Date.now();C({id:e.id,method:e.method,url:e.url,headers:e.headers});let a=0,t=0;const d=45e3,u=U.default.request({host:w,port:c,method:e.method,path:e.url,headers:{...e.headers,host:`${w}:${c}`},timeout:d},E=>{t=E.statusCode,p({type:i.MSG.RESPONSE,id:e.id,status:t,headers:E.headers}),u.setTimeout(d),E.on("data",f=>{a+=f.length,p({type:i.MSG.RESPONSE_BODY,id:e.id,chunk:f.toString("base64")}),u.setTimeout(d)}),E.on("end",()=>{p({type:i.MSG.RESPONSE_END,id:e.id}),S.delete(e.id),$({id:e.id,method:e.method,url:e.url,status:t,bytes:a,durationMs:Date.now()-s})})});u.on("timeout",()=>{u.destroy(new Error(`local server idle for ${d}ms`))}),u.on("error",E=>{const f=t>0;f||(p({type:i.MSG.RESPONSE,id:e.id,status:504,headers:{"content-type":"text/plain"}}),p({type:i.MSG.RESPONSE_BODY,id:e.id,chunk:Buffer.from(`Local server error: ${E.message}`).toString("base64")})),p({type:i.MSG.RESPONSE_END,id:e.id}),S.delete(e.id),$({id:e.id,method:e.method,url:e.url,status:f?t:504,bytes:a,durationMs:Date.now()-s,error:E.message})}),S.set(e.id,{localReq:u})},O=()=>{const e=I();m(`connecting to ${e}`),l=new R.default(e,{headers:_?{"x-tunnel-token":_}:{}});let s=null;l.on("open",()=>{m("connected"),s=setInterval(()=>{l.readyState===R.default.OPEN&&p({type:i.MSG.PING,id:"keepalive"})},25e3)}),l.on("message",a=>{let t;try{t=JSON.parse(a.toString())}catch{return}if(t.type===i.MSG.REGISTERED){m(`registered \u2192 ${t.publicUrl}`),k(t);return}if(t.type===i.MSG.ERROR){N(new Error(t.message||"tunnel error"));return}if(t.type===i.MSG.REQUEST){M(t);return}const d=S.get(t.id);d&&(t.type===i.MSG.REQUEST_BODY?t.chunk&&d.localReq.write(Buffer.from(t.chunk,"base64")):t.type===i.MSG.REQUEST_END&&d.localReq.end())}),l.on("close",()=>{s&&(clearInterval(s),s=null),L(),!(T||!b)&&setTimeout(O,D)}),l.on("unexpected-response",async(a,t)=>{let d="";try{for await(const u of t)d+=u.toString("utf8")}catch{}N(new Error(`HTTP ${t.statusCode}: ${d.slice(0,480)||"(empty body)"}`)),s&&(clearInterval(s),s=null),!T&&b&&setTimeout(O,D)}),l.on("error",a=>N(a))};return O(),{close:()=>{T=!0,l&&l.close()}}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var f=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var T=(r,e)=>{for(var n in e)f(r,n,{get:e[n],enumerable:!0})},_=(r,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of E(e))!R.call(r,s)&&s!==n&&f(r,s,{get:()=>e[s],enumerable:!(t=b(e,s))||t.enumerable});return r};var O=r=>_(f({},"__esModule",{value:!0}),r);var D={};T(D,{TunnelRoom:()=>P});module.exports=O(D);var o=require("../src/protocol.js");class P{constructor(e,n){this.state=e,this.env=n,this.pending=new Map}async fetch(e){const n=new URL(e.url);if(n.pathname==="/_register")return this.register(e,n);const[t]=this.state.getWebSockets("client");if(!t){const s=await this.state.storage.get("subdomain")||"this subdomain";return new Response(`No tunnel registered for ${s}.`,{status:502})}return this.proxyRequest(e,t)}async register(e,n){if(e.headers.get("upgrade")!=="websocket")return new Response("expected websocket",{status:426});const t=n.searchParams.get("subdomain"),s=n.searchParams.get("root")||"tunnel.symbo.ls";await this.state.storage.put("subdomain",t),await this.state.storage.put("root",s);for(const p of this.state.getWebSockets("client"))try{p.close(1e3,"replaced")}catch{}const i=new WebSocketPair,[a,d]=[i[0],i[1]];return this.state.acceptWebSocket(d,["client"]),d.send(JSON.stringify({type:o.MSG.REGISTERED,subdomain:t,publicUrl:`https://${t}.${s}`})),new Response(null,{status:101,webSocket:a})}async webSocketMessage(e,n){let t;try{t=JSON.parse(typeof n=="string"?n:new TextDecoder().decode(n))}catch{return}if(t.type===o.MSG.PING){e.send(JSON.stringify({type:o.MSG.PONG,id:t.id}));return}const s=t.id&&this.pending.get(t.id);if(s){if(s.resetActivity&&s.resetActivity(),t.type===o.MSG.RESPONSE){const i=new ReadableStream({start:a=>{s.controller=a}});s.headSent=!0,s.resolveHead(new Response(i,{status:t.status||200,headers:N(t.headers||{})}))}else if(t.type===o.MSG.RESPONSE_BODY)t.chunk&&s.controller&&s.controller.enqueue(A(t.chunk));else if(t.type===o.MSG.RESPONSE_END){try{s.controller&&s.controller.close()}catch{}s.cleanup&&s.cleanup(),this.pending.delete(t.id)}}}async webSocketClose(e,n,t,s){this.failAllPending("tunnel closed")}async webSocketError(e,n){this.failAllPending("tunnel error")}failAllPending(e){for(const[,n]of this.pending)if(n.cleanup&&n.cleanup(),!n.headSent)n.resolveHead(new Response(e,{status:502}));else try{n.controller.error(new Error(e))}catch{}this.pending.clear()}async proxyRequest(e,n){const t=(0,o.randomId)(),s=new URL(e.url),i={};for(const[u,w]of e.headers)i[u]=w;const a=["GET","HEAD"].includes(e.method)?null:new Uint8Array(await e.arrayBuffer()),d=6e4,p=45e3;let h=null,c=null;const g=()=>{h&&(clearTimeout(h),h=null),c&&(clearTimeout(c),c=null)},y=()=>{c&&clearTimeout(c),c=setTimeout(()=>{if(this.pending.has(t)){this.pending.delete(t);try{l.controller&&l.controller.error(new Error("tunnel body idle"))}catch{}}},p)},l={controller:null,headSent:!1,cleanup:g,resetActivity:y},S=new Promise(u=>{l.resolveHead=u});this.pending.set(t,l),n.send(JSON.stringify({type:o.MSG.REQUEST,id:t,method:e.method,url:s.pathname+s.search,headers:i})),a&&a.byteLength&&n.send(JSON.stringify({type:o.MSG.REQUEST_BODY,id:t,chunk:v(a)})),n.send(JSON.stringify({type:o.MSG.REQUEST_END,id:t}));const m=new Promise(u=>{h=setTimeout(()=>{this.pending.has(t)&&!l.headSent&&(this.pending.delete(t),g(),u(new Response("Tunnel request timed out",{status:504})))},d)});return y(),Promise.race([S,m])}}const k=new Set(["connection","keep-alive","transfer-encoding","upgrade","proxy-authenticate","proxy-authorization","te","trailer","content-length"]),N=r=>{const e={};for(const[n,t]of Object.entries(r))k.has(n.toLowerCase())||(e[n]=t);return e},v=r=>{let e="";for(let n=0;n<r.length;n++)e+=String.fromCharCode(r[n]);return btoa(e)},A=r=>{const e=atob(r),n=new Uint8Array(e.length);for(let t=0;t<e.length;t++)n[t]=e.charCodeAt(t);return n};
|
package/dist/esm/src/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import R from"ws";import L from"node:http";import"node:https";import{MSG as n}from"./protocol.js";const H=(_={})=>{const{host:b=process.env.TUNNEL_HOST||"tunnel.symbo.ls",port:c=443,secure:D=c===443,localPort:E,localHost:N="127.0.0.1",subdomain:m=null,token:T=process.env.TUNNEL_AUTH_TOKEN||null,reconnect:O=!0,reconnectDelayMs:w=2e3,onRegistered:$=()=>{},onError:f=e=>console.error("[tunnel] error:",e.message||e),onClose:q=()=>{},onRequest:v=()=>{},onResponse:P=()=>{},log:h=(...e)=>console.log("[tunnel]",...e)}=_;if(!E)throw new Error("createTunnelClient: localPort is required");let o=null,S=!1;const p=new Map,U=()=>{const e=D?"wss":"ws",r=new URLSearchParams;m&&r.set("subdomain",m);const s=r.toString();return`${e}://${b}${c&&c!==80&&c!==443?":"+c:""}/_tunnel${s?"?"+s:""}`},a=e=>{o&&o.readyState===R.OPEN&&o.send(JSON.stringify(e))},k=e=>{const r=Date.now();v({id:e.id,method:e.method,url:e.url,headers:e.headers});let s=0,t=0;const i=45e3,l=L.request({host:N,port:E,method:e.method,path:e.url,headers:{...e.headers,host:`${N}:${E}`},timeout:i},d=>{t=d.statusCode,a({type:n.RESPONSE,id:e.id,status:t,headers:d.headers}),l.setTimeout(i),d.on("data",u=>{s+=u.length,a({type:n.RESPONSE_BODY,id:e.id,chunk:u.toString("base64")}),l.setTimeout(i)}),d.on("end",()=>{a({type:n.RESPONSE_END,id:e.id}),p.delete(e.id),P({id:e.id,method:e.method,url:e.url,status:t,bytes:s,durationMs:Date.now()-r})})});l.on("timeout",()=>{l.destroy(new Error(`local server idle for ${i}ms`))}),l.on("error",d=>{const u=t>0;u||(a({type:n.RESPONSE,id:e.id,status:504,headers:{"content-type":"text/plain"}}),a({type:n.RESPONSE_BODY,id:e.id,chunk:Buffer.from(`Local server error: ${d.message}`).toString("base64")})),a({type:n.RESPONSE_END,id:e.id}),p.delete(e.id),P({id:e.id,method:e.method,url:e.url,status:u?t:504,bytes:s,durationMs:Date.now()-r,error:d.message})}),p.set(e.id,{localReq:l})},y=()=>{const e=U();h(`connecting to ${e}`),o=new R(e,{headers:T?{"x-tunnel-token":T}:{}});let r=null;o.on("open",()=>{h("connected"),r=setInterval(()=>{o.readyState===R.OPEN&&a({type:n.PING,id:"keepalive"})},25e3)}),o.on("message",s=>{let t;try{t=JSON.parse(s.toString())}catch{return}if(t.type===n.REGISTERED){h(`registered \u2192 ${t.publicUrl}`),$(t);return}if(t.type===n.ERROR){f(new Error(t.message||"tunnel error"));return}if(t.type===n.REQUEST){k(t);return}const i=p.get(t.id);i&&(t.type===n.REQUEST_BODY?t.chunk&&i.localReq.write(Buffer.from(t.chunk,"base64")):t.type===n.REQUEST_END&&i.localReq.end())}),o.on("close",()=>{r&&(clearInterval(r),r=null),q(),!(S||!O)&&setTimeout(y,w)}),o.on("unexpected-response",async(s,t)=>{let i="";try{for await(const l of t)i+=l.toString("utf8")}catch{}f(new Error(`HTTP ${t.statusCode}: ${i.slice(0,480)||"(empty body)"}`)),r&&(clearInterval(r),r=null),!S&&O&&setTimeout(y,w)}),o.on("error",s=>f(s))};return y(),{close:()=>{S=!0,o&&o.close()}}};export{H as createTunnelClient};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MSG as r,randomId as
|
|
1
|
+
import{MSG as r,randomId as w}from"../src/protocol.js";class O{constructor(n,t){this.state=n,this.env=t,this.pending=new Map}async fetch(n){const t=new URL(n.url);if(t.pathname==="/_register")return this.register(n,t);const[e]=this.state.getWebSockets("client");if(!e){const s=await this.state.storage.get("subdomain")||"this subdomain";return new Response(`No tunnel registered for ${s}.`,{status:502})}return this.proxyRequest(n,e)}async register(n,t){if(n.headers.get("upgrade")!=="websocket")return new Response("expected websocket",{status:426});const e=t.searchParams.get("subdomain"),s=t.searchParams.get("root")||"tunnel.symbo.ls";await this.state.storage.put("subdomain",e),await this.state.storage.put("root",s);for(const p of this.state.getWebSockets("client"))try{p.close(1e3,"replaced")}catch{}const i=new WebSocketPair,[a,d]=[i[0],i[1]];return this.state.acceptWebSocket(d,["client"]),d.send(JSON.stringify({type:r.REGISTERED,subdomain:e,publicUrl:`https://${e}.${s}`})),new Response(null,{status:101,webSocket:a})}async webSocketMessage(n,t){let e;try{e=JSON.parse(typeof t=="string"?t:new TextDecoder().decode(t))}catch{return}if(e.type===r.PING){n.send(JSON.stringify({type:r.PONG,id:e.id}));return}const s=e.id&&this.pending.get(e.id);if(s){if(s.resetActivity&&s.resetActivity(),e.type===r.RESPONSE){const i=new ReadableStream({start:a=>{s.controller=a}});s.headSent=!0,s.resolveHead(new Response(i,{status:e.status||200,headers:E(e.headers||{})}))}else if(e.type===r.RESPONSE_BODY)e.chunk&&s.controller&&s.controller.enqueue(T(e.chunk));else if(e.type===r.RESPONSE_END){try{s.controller&&s.controller.close()}catch{}s.cleanup&&s.cleanup(),this.pending.delete(e.id)}}}async webSocketClose(n,t,e,s){this.failAllPending("tunnel closed")}async webSocketError(n,t){this.failAllPending("tunnel error")}failAllPending(n){for(const[,t]of this.pending)if(t.cleanup&&t.cleanup(),!t.headSent)t.resolveHead(new Response(n,{status:502}));else try{t.controller.error(new Error(n))}catch{}this.pending.clear()}async proxyRequest(n,t){const e=w(),s=new URL(n.url),i={};for(const[u,m]of n.headers)i[u]=m;const a=["GET","HEAD"].includes(n.method)?null:new Uint8Array(await n.arrayBuffer()),d=6e4,p=45e3;let h=null,c=null;const f=()=>{h&&(clearTimeout(h),h=null),c&&(clearTimeout(c),c=null)},g=()=>{c&&clearTimeout(c),c=setTimeout(()=>{if(this.pending.has(e)){this.pending.delete(e);try{l.controller&&l.controller.error(new Error("tunnel body idle"))}catch{}}},p)},l={controller:null,headSent:!1,cleanup:f,resetActivity:g},y=new Promise(u=>{l.resolveHead=u});this.pending.set(e,l),t.send(JSON.stringify({type:r.REQUEST,id:e,method:n.method,url:s.pathname+s.search,headers:i})),a&&a.byteLength&&t.send(JSON.stringify({type:r.REQUEST_BODY,id:e,chunk:R(a)})),t.send(JSON.stringify({type:r.REQUEST_END,id:e}));const S=new Promise(u=>{h=setTimeout(()=>{this.pending.has(e)&&!l.headSent&&(this.pending.delete(e),f(),u(new Response("Tunnel request timed out",{status:504})))},d)});return g(),Promise.race([y,S])}}const b=new Set(["connection","keep-alive","transfer-encoding","upgrade","proxy-authenticate","proxy-authorization","te","trailer","content-length"]),E=o=>{const n={};for(const[t,e]of Object.entries(o))b.has(t.toLowerCase())||(n[t]=e);return n},R=o=>{let n="";for(let t=0;t<o.length;t++)n+=String.fromCharCode(o[t]);return btoa(n)},T=o=>{const n=atob(o),t=new Uint8Array(n.length);for(let e=0;e<n.length;e++)t[e]=n.charCodeAt(e);return t};export{O as TunnelRoom};
|