@faulpeltz/dertunnel 0.1.1 → 0.2.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.
Files changed (3) hide show
  1. package/client.d.ts +43 -3
  2. package/index.js +2 -1
  3. package/package.json +1 -1
package/client.d.ts CHANGED
@@ -1,4 +1,41 @@
1
- export declare type ClientOptions = {
1
+ export type ProtocolDataType =
2
+ "http-request-header" |
3
+ "http-request-body" |
4
+ "http-response-header" |
5
+ "http-response-body";
6
+
7
+ export type ProtocolData = {
8
+ id: string;
9
+ type: ProtocolDataType;
10
+ method: string;
11
+ path: string;
12
+ status: number;
13
+ statusText: string;
14
+ headers: HttpProtocolHeaders;
15
+ content?: Buffer;
16
+ startAt?: number;
17
+ endAt?: number;
18
+ };
19
+
20
+ export type HttpProtocolHeaders = { name: string, value: string }[];
21
+
22
+ export type ClientEndpointStats = {
23
+ currentConnections: number;
24
+ totalConnections: number;
25
+ connectionErrors: number;
26
+ chunksReceived: number;
27
+ chunksSent: number;
28
+ bytesReceived: number;
29
+ bytesSent: number;
30
+ };
31
+
32
+ type ClientEndpointEvent =
33
+ "connect" |
34
+ "disconnect" |
35
+ "data" |
36
+ "error";
37
+
38
+ export type ClientOptions = {
2
39
  user: string;
3
40
  token: string;
4
41
  serviceHost: string;
@@ -10,8 +47,11 @@ export declare type ClientOptions = {
10
47
  endpointPrefix?: string;
11
48
  ignoreCertErrors?: boolean;
12
49
  waitForInitialConnection?: boolean;
50
+ protocols?: {
51
+ inspect: boolean;
52
+ },
13
53
  onConnected?: (assignedEndpoint: string) => void | Promise<void>;
14
54
  onError?: (err: string) => void | Promise<void>;
55
+ onProtocolData?: (type: ProtocolDataType, cid: string, data: ProtocolData) => void | Promise<void>;
56
+ onEndpoint?: (event: ClientEndpointEvent, stats: ClientEndpointStats, message: string) => void | Promise<void>;
15
57
  };
16
-
17
- export declare function connectTunnel(opts: ClientOptions): Promise<() => void>;
package/index.js CHANGED
@@ -1 +1,2 @@
1
- "use strict";var W=Object.create;var S=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var F=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var Q=(e,t)=>{for(var r in t)S(e,r,{get:t[r],enumerable:!0})},y=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of K(t))!J.call(e,i)&&i!==r&&S(e,i,{get:()=>t[i],enumerable:!(n=q(t,i))||n.enumerable});return e};var z=(e,t,r)=>(r=e!=null?W(F(e)):{},y(t||!e||!e.__esModule?S(r,"default",{value:e,enumerable:!0}):r,e)),X=e=>y(S({},"__esModule",{value:!0}),e);var ne={};Q(ne,{connectTunnel:()=>ee});module.exports=X(ne);var M=require("net"),V=z(require("tls"));var x=(o=>(o[o.HELLO_REQ=1]="HELLO_REQ",o[o.HELLO_RESP=2]="HELLO_RESP",o[o.END=3]="END",o[o.SERVER_OPEN=4]="SERVER_OPEN",o[o.SERVER_CLOSE=5]="SERVER_CLOSE",o[o.CLIENT_CLOSE=6]="CLIENT_CLOSE",o[o.PING=7]="PING",o[o.PONG=8]="PONG",o[o.SERVER_DATA=65]="SERVER_DATA",o[o.CLIENT_DATA=66]="CLIENT_DATA",o))(x||{});function I(e){return{name:Object.keys(x).find(t=>x[t]===e)??"UNKNOWN",isBinary:!!(e&64)}}var R=1;var Y="service";var L=Y+".",O=5e3;var Z=1*1024*1024,E=12,k=70,w=80,N=0,A=1,_=2,H=4,B=8,U=!!process.env.DERTUNNEL_DEBUG,T=class{constructor(t){this.onMessage=t}buffer=void 0;async receive(t){let r=this.buffer?Buffer.concat([this.buffer,t]):t,n=0;for(;n<r.length;){if(r.length<n+E)return this.buffer=r,!0;if(r[n+N]!=k||r[n+A]!=w)return this.reset(),!1;let i=r.readUint16LE(n+_),l=r.readUint32LE(n+H),m=r.readUint32LE(n+B),u=m+E+n;if(m>Z)return this.reset(),!1;if(r.length>=u)try{let d=r.slice(n+E,u);U&&j(i,l,d,!1),await this.onMessage(i,l,d)}catch{return this.reset(),!1}else break;n=u}return this.buffer=r.length>n?r.slice(n):void 0,!0}reset(){this.buffer=void 0}};function v(e,t,r,n){let i=Buffer.isBuffer(n)?n:n?Buffer.from(JSON.stringify(n)):void 0,l=Buffer.allocUnsafe(E+(i?i.length:0));l.writeUint8(k,N),l.writeUint8(w,A),l.writeUint16LE(t,_),l.writeUint32LE(r,H),l.writeUint32LE(i?i.length:0,B),i?.copy(l,E,0);let m=e.write(l);return U&&j(t,r,i,!0),m}function h(e){return JSON.parse(e.toString("utf8"))}function j(e,t,r,n){let{name:i,isBinary:l}=I(e);console.debug(`[msg-${n?"out":"in"} ${i} ${t>0?t:""}] ${r?l?`- ${r.length} payload bytes`:r.toString("utf8"):"- empty"}`)}function $(){let e,t,r=new Promise((n,i)=>{e=n,t=i});return r.resolve=e,r.reject=t,r}async function ee(e){let t=!1,r=!1,n,i=300,l=1.4,m=1e4,b={reconnectTime:i};if(!e.endpoint&&!e.endpointPrefix)throw new Error("Must either define 'endpoint' or 'endpointPrefix'");if(!e.serviceHost||!e.user||!e.token||!e.localPort&&!e.localUrl)throw new Error("Missing required options");let u,d;if(e.localPort)u=e.localServer??"localhost",d=e.localPort;else{let f=new URL(e.localUrl);if(f.protocol!=="http:")throw new Error("Local endpoints only support http protocol");u=f.hostname,d=Number.parseInt(f.port??"80")}let o=$(),g=e.serviceHost;g.startsWith(L)||(g=L+g);let P=()=>{n?.destroy(),n=V.default.connect({timeout:1e4,host:g,port:e.servicePort||443,servername:g,...e.ignoreCertErrors?{checkServerIdentity:(a,C)=>{}}:{}},()=>{n.setKeepAlive(!0,O),v(n,1,0,{version:R,user:e.user,token:e.token,endpoint:e.endpoint,endpointPrefix:e.endpointPrefix})}),n.setMaxListeners(0);let f=new Map,G=new T(async(a,C,p)=>{if(!t&&a===2){let c=h(p);if(!c.success){n?.destroy();let s="Server connection error: "+c.error;o.reject(new Error(s)),e.onError?.(s);return}e.onConnected?.(c.endpoint||""),o.resolve(),t=!0,b.reconnectTime=i}if(a===3){n?.destroy();let c=h(p);c.error&&e.onError?.(c.error);return}if(t){if(a===7)v(n,8,0);else if(a===65){let c=f.get(C);c&&(c.bytesSent+=p.length,c.write(p))}else if(a===4){let c=h(p),s=(0,M.createConnection)({host:u,port:d,allowHalfOpen:!1});s.channelId=c.channelId,s.bytesReceived=s.bytesSent=0,f.set(c.channelId,s),s.on("close",()=>{f.delete(c.channelId),v(n,6,0,{channelId:s.channelId})}),s.on("data",D=>{s.bytesReceived+=D.length,!v(n,66,s.channelId,D)&&!s.isPaused()&&(s.pause(),n.once("drain",()=>{s.resume()}))}),s.on("error",D=>{})}else if(a===5){let c=h(p),s=f.get(c.channelId);s&&(f.delete(c.channelId),s.end(()=>s.destroy()))}}});n.on("error",a=>{t=!1,!r&&(o.reject(a),e.onError?.("Server connection error: "+a.message))}),n.on("close",()=>{t=!1,f.forEach(a=>a.destroy()),!r&&(setTimeout(P,b.reconnectTime),b.reconnectTime=Math.min(m,b.reconnectTime*l))}),n.on("data",a=>{r||G.receive(a)||n?.destroy()})};return P(),e.waitForInitialConnection!==!1&&await o,()=>{r=!0,n?.destroy()}}0&&(module.exports={connectTunnel});
1
+ "use strict";var xe=Object.create;var O=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var Ee=Object.getOwnPropertyNames;var Te=Object.getPrototypeOf,ye=Object.prototype.hasOwnProperty;var Se=(t,n)=>{for(var r in n)O(t,r,{get:n[r],enumerable:!0})},z=(t,n,r,e)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of Ee(n))!ye.call(t,o)&&o!==r&&O(t,o,{get:()=>n[o],enumerable:!(e=ve(n,o))||e.enumerable});return t};var V=(t,n,r)=>(r=t!=null?xe(Te(t)):{},z(n||!t||!t.__esModule?O(r,"default",{value:t,enumerable:!0}):r,t)),De=t=>z(O({},"__esModule",{value:!0}),t);var Ie={};Se(Ie,{connectTunnel:()=>He});module.exports=De(Ie);var he=require("net"),fe=V(require("tls"));var B=(i=>(i[i.HELLO_REQ=1]="HELLO_REQ",i[i.HELLO_RESP=2]="HELLO_RESP",i[i.END=3]="END",i[i.SERVER_OPEN=4]="SERVER_OPEN",i[i.SERVER_CLOSE=5]="SERVER_CLOSE",i[i.CLIENT_CLOSE=6]="CLIENT_CLOSE",i[i.PING=7]="PING",i[i.PONG=8]="PONG",i[i.SERVER_DATA=65]="SERVER_DATA",i[i.CLIENT_DATA=66]="CLIENT_DATA",i))(B||{});function F(t){return{name:Object.keys(B).find(n=>B[n]===t)??"UNKNOWN",isBinary:!!(t&64)}}var K=1;var Pe="service";var U=Pe+".",W=5e3;var Ce=1*1024*1024,P=12,X=70,J=80,Q=0,Y=1,Z=2,ee=4,te=8,ne=!!process.env.DERTUNNEL_DEBUG,w=class{constructor(n){this.onMessage=n}buffer=void 0;async receive(n){let r=this.buffer?Buffer.concat([this.buffer,n]):n,e=0;for(;e<r.length;){if(r.length<e+P)return this.buffer=r,!0;if(r[e+Q]!=X||r[e+Y]!=J)return this.reset(),!1;let o=r.readUint16LE(e+Z),s=r.readUint32LE(e+ee),a=r.readUint32LE(e+te),d=a+P+e;if(a>Ce)return this.reset(),!1;if(r.length>=d)try{let f=r.slice(e+P,d);ne&&re(o,s,f,!1),await this.onMessage(o,s,f)}catch{return this.reset(),!1}else break;e=d}return this.buffer=r.length>e?r.slice(e):void 0,!0}reset(){this.buffer=void 0}};function C(t,n,r,e){let o=Buffer.isBuffer(e)?e:e?Buffer.from(JSON.stringify(e)):void 0,s=Buffer.allocUnsafe(P+(o?o.length:0));s.writeUint8(X,Q),s.writeUint8(J,Y),s.writeUint16LE(n,Z),s.writeUint32LE(r,ee),s.writeUint32LE(o?o.length:0,te),o?.copy(s,P,0);let a=t.write(s);return ne&&re(n,r,o,!0),a}function k(t){return JSON.parse(t.toString("utf8"))}function re(t,n,r,e){let{name:o,isBinary:s}=F(t);console.debug(`[msg-${e?"out":"in"} ${o} ${n>0?n:""}] ${r?s?`- ${r.length} payload bytes`:r.toString("utf8"):"- empty"}`)}var q=V(require("crypto"),1);var ke=128,v,y,Le=t=>{!v||v.length<t?(v=Buffer.allocUnsafe(t*ke),q.default.randomFillSync(v),y=0):y+t>v.length&&(q.default.randomFillSync(v),y=0),y+=t},Re=t=>(Le(t-=0),v.subarray(y-t,y)),Ae=(t,n,r)=>{let e=(2<<31-Math.clz32(t.length-1|1))-1,o=Math.ceil(1.6*e*n/t.length);return(s=n)=>{let a="";for(;;){let u=r(o),d=o;for(;d--;)if(a+=t[u[d]&e]||"",a.length===s)return a}}},M=(t,n=21)=>Ae(t,n,Re);var H=M("0123456789abcdef",8),Ke=M("0123456789abcdefghijklmnopqrstuvwxyz",24);function oe(){let t,n,r=new Promise((e,o)=>{t=e,n=o});return r.resolve=t,r.reject=n,r}var I=class{constructor(n,r){this.interval=n;this.func=r}lastUpdated=0;updateTimer;update(n,r){let e=new Date().valueOf();r||e-this.lastUpdated>this.interval?(clearTimeout(this.updateTimer),this.func(n),this.lastUpdated=e):this.updateTimer=setTimeout(()=>{this.func(n),this.updateTimer=void 0},this.interval-e+this.lastUpdated).unref()}};var ue=4096,S=13,D=10,se=new Uint8Array([72,84,84,80,47,49,46,49,S,D]),Oe=new Uint8Array([72,84,84,80,47,49,46,49,32]),j=0,_=1,N=2,$=3,ie=4,de=5,L=class{constructor(n){this.onParsed=n}state=j;id="";contentData;headers=[];expectedBodyLen=0;isRequest=!1;method="";path="";status=0;statusText="";startAt=0;onData(n){let e=0,o=0;for(;e<n.length&&e!==-1&&o++<100;)if(this.state===j||this.state===_||this.state===N){this.contentData=void 0;let s=e;if((e=n.indexOf(se,s))>=0)for(let a=0;a<1;a++){let u=e+se.length;for(;e>0&&n[e-1]!==D&&n[e-1]!==S;)e--;let d=n.slice(e,u).toString("ascii").split(" ");if(d.length!==3){e=u;break}let f=ce(n,u);if(f){e=u+f.length,this.headers=le(f);let{nextState:i,len:p}=ae(this.headers,!0);this.state=i,this.expectedBodyLen=p}else break;this.isRequest=!0,this.id=H(12),this.method=d[0].toUpperCase(),this.path=decodeURI(d[1]),this.onParsed?.({id:this.id,type:"http-request-header",headers:this.headers,method:this.method,path:this.path,status:0,statusText:"",startAt:this.startAt=new Date().valueOf()})}else if((e=n.indexOf(Oe,s))>=0){let a=e,u=n.length;for(;e<u&&!(n[e+1]===S&&n[e+2]===D);)e++;for(let d=0;d<1;d++){let f=n.slice(a,e+1).toString("ascii").split(" ");if(f.length<3)break;let i=ce(n,a);if(i){e=a+i.length,this.headers=le(i);let{nextState:R,len:m}=ae(this.headers,!1);this.state=R,this.expectedBodyLen=m}else break;let p=Number.parseInt(f[1]);if(!Number.isFinite(p)||p<100||p>999)break;this.isRequest=!1,this.status=p,this.statusText=f.slice(2).join(" "),this.onParsed?.({id:this.id,type:"http-response-header",headers:this.headers,status:this.status,statusText:this.statusText,method:this.method,path:this.path,endAt:new Date().valueOf()})}}}else if(this.state===$){let{ok:s,p:a,size:u}=Be(n,e);e=a,u>0?(this.expectedBodyLen=u,this.state=s?ie:j):s&&(this.isRequest?this.onParsed?.({id:this.id,type:"http-request-body",headers:this.headers,method:this.method,path:this.path,status:this.status,statusText:this.statusText,content:this.contentData,startAt:this.startAt,endAt:new Date().valueOf()}):this.onParsed?.({id:this.id,type:"http-response-body",headers:this.headers,method:this.method,path:this.path,status:this.status,statusText:this.statusText,content:this.contentData,startAt:this.startAt,endAt:new Date().valueOf()}),e+=2,this.state=this.isRequest?N:_)}else if(this.state===ie){let s=Math.min(n.length-e,this.expectedBodyLen),a=n.slice(e,e+this.expectedBodyLen);this.contentData=this.contentData?Buffer.concat([this.contentData,a]):Buffer.from(a),this.expectedBodyLen-=s,e+=s,this.expectedBodyLen===0&&(e+=2,this.state=$)}else if(this.state===de){let s=Math.min(n.length-e,this.expectedBodyLen),a=n.slice(e,e+this.expectedBodyLen);this.contentData=this.contentData?Buffer.concat([this.contentData,a]):Buffer.from(a),this.expectedBodyLen-=s,e+=s,this.expectedBodyLen===0&&(this.isRequest?this.onParsed?.({id:this.id,type:"http-request-body",headers:this.headers,method:this.method,path:this.path,status:this.status,statusText:this.statusText,content:this.contentData,startAt:this.startAt,endAt:new Date().valueOf()}):this.onParsed?.({id:this.id,type:"http-response-body",headers:this.headers,method:this.method,path:this.path,status:this.status,statusText:this.statusText,content:this.contentData,startAt:this.startAt,endAt:new Date().valueOf()}),this.state=this.isRequest?N:_)}if(o>=100)throw new Error("Internal parser error: "+this.state)}};function ae(t,n){let r=Number.parseInt(t.find(e=>e.name==="content-length")?.value??"0");return t.find(e=>e.name==="transfer-encoding")?.value.includes("chunked")?{nextState:$,isChunked:!0,len:0}:r>0?{nextState:de,isChunked:!1,len:r}:{nextState:n?N:_,isChunked:!1,len:0}}function ce(t,n){let r=n,e=Math.min(t.length-3,n+ue);for(;r<e&&!(t[r]===S&&t[r+1]===D&&t[r+2]===S&&t[r+3]===D);)r++;return r<e?t.slice(n,r+4):void 0}function le(t){let n=[],r=t.toString("ascii").split(`\r
2
+ `);for(let e of r){let o=e.indexOf(":");if(o<0)continue;let s=e.substring(0,o).toLowerCase().trim(),a=e.substring(o+1).trim();s.length>0&&a.length>0&&n.push({name:s,value:a})}return n}function Be(t,n){let r=0,e=n,o=!1,s=we(t,n);return s!==void 0&&(r=Number.parseInt(s,16))>=0&&Number.isFinite(r)&&(e=n+s.length+2,o=!0),{ok:o,p:e,size:r}}function we(t,n){let r=n,e=Math.min(t.length-1,n+ue);for(;r<e&&!(t[r]===S&&t[r+1]===D);)r++;return r<e?t.slice(n,r).toString("ascii"):void 0}async function He(t){let n=!1,r=!1,e,o=300,s=1.4,a=1e4,u={reconnectTime:o};if(!t.endpoint&&!t.endpointPrefix)throw new Error("Must either define 'endpoint' or 'endpointPrefix'");if(!t.serviceHost||!t.user||!t.token||!t.localPort&&!t.localUrl)throw new Error("Missing required options");let d,f;if(t.localPort)d=t.localServer??"localhost",f=t.localPort;else{let m=new URL(t.localUrl);if(m.protocol!=="http:")throw new Error("Local endpoints only support http protocol");d=m.hostname,f=Number.parseInt(m.port??"80")}let i=oe(),p=t.serviceHost;p.startsWith(U)||(p=U+p);let R=()=>{let m=new I(200,()=>E("data")),g={bytesReceived:0,bytesSent:0,chunksReceived:0,chunksSent:0,currentConnections:0,totalConnections:0,connectionErrors:0};function E(h,A){try{t.onEndpoint?.(h,g,A??"")}catch{}}e?.destroy(),t.ignoreCertErrors&&(process.env.NODE_TLS_REJECT_UNAUTHORIZED="0"),e=fe.default.connect({timeout:1e4,host:p,port:t.servicePort||443,servername:p,...t.ignoreCertErrors?{checkServerIdentity:(h,A)=>{}}:{}},()=>{e.setKeepAlive(!0,W),C(e,1,0,{version:K,user:t.user,token:t.token,endpoint:t.endpoint,endpointPrefix:t.endpointPrefix})}),e.setMaxListeners(0);let T=new Map,pe=new w(async(h,A,b)=>{if(!n&&h===2){let l=k(b);if(!l.success){e?.destroy();let c="Server connection error: "+l.error;i.reject(new Error(c)),t.onError?.(c);return}t.onConnected?.(l.endpoint||""),m.update(void 0,!0),i.resolve(),n=!0,u.reconnectTime=o}if(h===3){e?.destroy();let l=k(b);l.error&&t.onError?.(l.error);return}if(n){if(h===7)C(e,8,0);else if(h===65){let l=T.get(A);if(l){if(g.bytesSent+=b.length,g.chunksSent++,l.bytesSent+=b.length,l.parserOut)try{l.parserOut.onData(b)}catch(c){E("error",c?.message)}l.write(b),m.update(void 0,!1)}}else if(h===4){let l=k(b),c=(0,he.createConnection)({host:d,port:f,allowHalfOpen:!1});c.channelId=l.channelId;let me=H(),G=x=>{try{t.onProtocolData?.(x.type,me,x)}catch{}};t.protocols?.inspect&&(c.parserIn=new L(G),c.parserOut=new L(G)),c.bytesReceived=c.bytesSent=0,T.set(l.channelId,c),c.on("connect",()=>{g.currentConnections++,g.totalConnections++,E("connect"),m.update(void 0,!0)}),c.on("close",()=>{g.currentConnections--,T.delete(l.channelId),C(e,6,0,{channelId:c.channelId}),E("disconnect"),m.update(void 0,!0)}),c.on("data",x=>{if(c.bytesReceived+=x.length,g.bytesReceived+=x.length,g.chunksReceived++,c.parserIn)try{c.parserIn.onData(x)}catch(be){E("error",be?.message)}!C(e,66,c.channelId,x)&&!c.isPaused()&&(c.pause(),e.once("drain",()=>{c.resume()})),m.update(void 0,!1)}),c.on("error",x=>{g.connectionErrors++,E("error",x?.message),m.update(void 0,!0)})}else if(h===5){let l=k(b),c=T.get(l.channelId);c&&(T.delete(l.channelId),c.end(()=>c.destroy()))}}});e.on("error",h=>{n=!1,!r&&(i.reject(h),t.onError?.("Server connection error: "+h.message))}),e.on("close",()=>{n=!1,T.forEach(h=>h.destroy()),!r&&(setTimeout(R,u.reconnectTime),u.reconnectTime=Math.min(a,u.reconnectTime*s))}),e.on("data",h=>{r||pe.receive(h)||e?.destroy()})};return R(),t.waitForInitialConnection!==!1&&await i,()=>{r=!0,e?.destroy()}}0&&(module.exports={connectTunnel});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faulpeltz/dertunnel",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "A client for the dertunnel public endpoint forwarder",
5
5
  "main": "./index.js",
6
6
  "typings": "./client.d.ts",