@faulpeltz/dertunnel 0.1.0 → 0.2.1

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 +45 -2
  2. package/index.js +2 -1
  3. package/package.json +1 -1
package/client.d.ts CHANGED
@@ -1,16 +1,59 @@
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;
5
42
  servicePort?: number;
6
43
  localServer?: string;
7
- localPort: number;
44
+ localPort?: number;
45
+ localUrl?: string;
8
46
  endpoint?: string;
9
47
  endpointPrefix?: string;
10
48
  ignoreCertErrors?: boolean;
11
49
  waitForInitialConnection?: boolean;
50
+ protocols?: {
51
+ inspect: boolean;
52
+ },
12
53
  onConnected?: (assignedEndpoint: string) => void | Promise<void>;
13
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>;
14
57
  };
15
58
 
16
59
  export declare function connectTunnel(opts: ClientOptions): Promise<() => void>;
package/index.js CHANGED
@@ -1 +1,2 @@
1
- "use strict";var M=Object.create;var v=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var W=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var F=(e,t)=>{for(var r in t)v(e,r,{get:t[r],enumerable:!0})},P=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of G(t))!q.call(e,i)&&i!==r&&v(e,i,{get:()=>t[i],enumerable:!(n=V(t,i))||n.enumerable});return e};var J=(e,t,r)=>(r=e!=null?M(W(e)):{},P(t||!e||!e.__esModule?v(r,"default",{value:e,enumerable:!0}):r,e)),K=e=>P(v({},"__esModule",{value:!0}),e);var X={};F(X,{connectTunnel:()=>z});module.exports=K(X);var j=require("net"),A=J(require("tls"));var S=(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))(S||{});function T(e){return{name:Object.keys(S).find(t=>S[t]===e)??"UNKNOWN",isBinary:!!(e&64)}}var R=1;var Q=1*1024*1024,p=12,O=70,C=80,k=0,I=1,y=2,w=4,N=8,B=!!process.env.DERTUNNEL_DEBUG,D=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+p)return this.buffer=r,!0;if(r[n+k]!=O||r[n+I]!=C)return this.reset(),!1;let i=r.readUint16LE(n+y),f=r.readUint32LE(n+w),d=r.readUint32LE(n+N),u=d+p+n;if(d>Q)return this.reset(),!1;if(r.length>=u)try{let a=r.slice(n+p,u);B&&H(i,f,a,!1),await this.onMessage(i,f,a)}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 g(e,t,r,n){let i=Buffer.isBuffer(n)?n:n?Buffer.from(JSON.stringify(n)):void 0,f=Buffer.allocUnsafe(p+(i?i.length:0));f.writeUint8(O,k),f.writeUint8(C,I),f.writeUint16LE(t,y),f.writeUint32LE(r,w),f.writeUint32LE(i?i.length:0,N),i?.copy(f,p,0);let d=e.write(f);return B&&H(t,r,i,!0),d}function h(e){return JSON.parse(e.toString("utf8"))}function H(e,t,r,n){let{name:i,isBinary:f}=T(e);console.debug(`[msg-${n?"out":"in"} ${i} ${t>0?t:""}] ${r?f?`- ${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}var U="service.";async function z(e){let t=!1,r=!1,n,i=300,f=1.4,d=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)throw new Error("Missing required options");let u=_(),a=e.serviceHost;a.startsWith(U)||(a=U+a);let o=()=>{n?.destroy(),n=A.default.connect({timeout:1e4,host:a,port:e.servicePort||443,servername:a,...e.ignoreCertErrors?{checkServerIdentity:(l,L)=>{}}:{}},()=>{n.setKeepAlive(!0,5e3),g(n,1,0,{version:R,user:e.user,token:e.token,endpoint:e.endpoint,endpointPrefix:e.endpointPrefix})}),n.setMaxListeners(0);let m=new Map,$=new D(async(l,L,E)=>{if(!t&&l===2){let c=h(E);if(!c.success){n?.destroy();let s="Server connection error: "+c.error;u.reject(new Error(s)),e.onError?.(s);return}e.onConnected?.(c.endpoint||""),u.resolve(),t=!0,b.reconnectTime=i}if(l===3){n?.destroy();let c=h(E);c.error&&e.onError?.(c.error);return}if(t){if(l===7)g(n,8,0);else if(l===65){let c=m.get(L);c&&(c.bytesSent+=E.length,c.write(E))}else if(l===4){let c=h(E),s=(0,j.createConnection)({host:e.localServer??"localhost",port:e.localPort,allowHalfOpen:!1});s.channelId=c.channelId,s.bytesReceived=s.bytesSent=0,m.set(c.channelId,s),s.on("close",()=>{m.delete(c.channelId),g(n,6,0,{channelId:s.channelId})}),s.on("data",x=>{s.bytesReceived+=x.length,!g(n,66,s.channelId,x)&&!s.isPaused()&&(s.pause(),n.once("drain",()=>{s.resume()}))}),s.on("error",x=>{})}else if(l===5){let c=h(E),s=m.get(c.channelId);s&&(m.delete(c.channelId),s.end(()=>s.destroy()))}}});n.on("error",l=>{t=!1,!r&&(u.reject(l),e.onError?.("Server connection error: "+l.message))}),n.on("close",()=>{t=!1,m.forEach(l=>l.destroy()),!r&&(setTimeout(o,b.reconnectTime),b.reconnectTime=Math.min(d,b.reconnectTime*f))}),n.on("data",l=>{r||$.receive(l)||n?.destroy()})};return o(),e.waitForInitialConnection!==!1&&await u,()=>{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.0",
3
+ "version": "0.2.1",
4
4
  "description": "A client for the dertunnel public endpoint forwarder",
5
5
  "main": "./index.js",
6
6
  "typings": "./client.d.ts",