├── .gitignore ├── LICENSE ├── README.md ├── got-tls-proxy ├── go.mod ├── go.sum ├── main.go └── proxyService │ ├── app.go │ └── interface.go ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── examples │ └── getRequest.ts ├── index.ts ├── interface.ts ├── resources │ ├── got-tls-proxy │ ├── got-tls-proxy-linux │ └── got-tls-proxy.exe └── util │ └── main.ts ├── tests └── main.test.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/node,macos 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,macos 3 | build 4 | 5 | ### macOS ### 6 | # General 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | 15 | # Thumbnails 16 | ._* 17 | 18 | # Files that might appear in the root of a volume 19 | .DocumentRevisions-V100 20 | .fseventsd 21 | .Spotlight-V100 22 | .TemporaryItems 23 | .Trashes 24 | .VolumeIcon.icns 25 | .com.apple.timemachine.donotpresent 26 | 27 | # Directories potentially created on remote AFP share 28 | .AppleDB 29 | .AppleDesktop 30 | Network Trash Folder 31 | Temporary Items 32 | .apdisk 33 | 34 | ### Node ### 35 | # Logs 36 | logs 37 | *.log 38 | npm-debug.log* 39 | yarn-debug.log* 40 | yarn-error.log* 41 | lerna-debug.log* 42 | .pnpm-debug.log* 43 | 44 | # Diagnostic reports (https://nodejs.org/api/report.html) 45 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 46 | 47 | # Runtime data 48 | pids 49 | *.pid 50 | *.seed 51 | *.pid.lock 52 | 53 | # Directory for instrumented libs generated by jscoverage/JSCover 54 | lib-cov 55 | 56 | # Coverage directory used by tools like istanbul 57 | coverage 58 | *.lcov 59 | 60 | # nyc test coverage 61 | .nyc_output 62 | 63 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 64 | .grunt 65 | 66 | # Bower dependency directory (https://bower.io/) 67 | bower_components 68 | 69 | # node-waf configuration 70 | .lock-wscript 71 | 72 | # Compiled binary addons (https://nodejs.org/api/addons.html) 73 | build/Release 74 | 75 | # Dependency directories 76 | node_modules/ 77 | jspm_packages/ 78 | 79 | # Snowpack dependency directory (https://snowpack.dev/) 80 | web_modules/ 81 | 82 | # TypeScript cache 83 | *.tsbuildinfo 84 | 85 | # Optional npm cache directory 86 | .npm 87 | 88 | # Optional eslint cache 89 | .eslintcache 90 | 91 | # Microbundle cache 92 | .rpt2_cache/ 93 | .rts2_cache_cjs/ 94 | .rts2_cache_es/ 95 | .rts2_cache_umd/ 96 | 97 | # Optional REPL history 98 | .node_repl_history 99 | 100 | # Output of 'npm pack' 101 | *.tgz 102 | 103 | # Yarn Integrity file 104 | .yarn-integrity 105 | 106 | # dotenv environment variables file 107 | .env 108 | .env.test 109 | .env.production 110 | 111 | # parcel-bundler cache (https://parceljs.org/) 112 | .cache 113 | .parcel-cache 114 | 115 | # Next.js build output 116 | .next 117 | out 118 | 119 | # Nuxt.js build / generate output 120 | .nuxt 121 | dist 122 | 123 | # Gatsby files 124 | .cache/ 125 | # Comment in the public line in if your project uses Gatsby and not Next.js 126 | # https://nextjs.org/blog/next-9-1#public-directory-support 127 | # public 128 | 129 | # vuepress build output 130 | .vuepress/dist 131 | 132 | # Serverless directories 133 | .serverless/ 134 | 135 | # FuseBox cache 136 | .fusebox/ 137 | 138 | # DynamoDB Local files 139 | .dynamodb/ 140 | 141 | # TernJS port file 142 | .tern-port 143 | 144 | # Stores VSCode versions used for testing VSCode extensions 145 | .vscode-test 146 | 147 | # yarn v2 148 | .yarn/cache 149 | .yarn/unplugged 150 | .yarn/build-state.yml 151 | .yarn/install-state.gz 152 | .pnp.* 153 | 154 | # End of https://www.toptal.com/developers/gitignore/api/node,macos 155 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 evade 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # got-tls 2 | 3 | A node websocket api version of https://github.com/Carcraftz/TLS-Fingerprint-API to spoof TLS fingerprint to prevent your requests from being fingerprinted. 4 | > im fairly new to golang / websockets pull requests are welcome 5 | 6 | > building instructions coming soon 7 | 8 | > currently only windows / linux. Mac support coming soon 9 | ## Usage 10 | 11 | Server 12 | ``connect`` 13 | 14 | To Start The Proxy Server Simply Import Server From The Package And Call The connect() Function. This Should Be Done Once In Your Project. 15 | 16 | > Server.isConnected - return boolean whether or not the server has started and our websocket has connected 17 | 18 | ```js 19 | const { Server } = require('got-tls') 20 | 21 | Server.connect() 22 | ``` 23 | 24 | Request 25 | ``GET`` ``POST`` ``PUT`` ``PATCH`` ``HEAD`` ``DELETE`` ``OPTIONS`` ``TRACE`` 26 | 27 | ```js 28 | import { got } from 'got-tls' 29 | 30 | let response = await got.get('https://httpbin.org/anything', { 31 | headers: { 32 | "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36" 33 | } 34 | }) 35 | ``` 36 | 37 | ## got(url, options) 38 | 39 | url 40 | 41 | ``Type: string`` 42 | 43 | > The URL to request. 44 | 45 | options 46 | 47 | ``Type: object`` 48 | 49 | > Request Options 50 | 51 | ## Options 52 | 53 | headers 54 | 55 | ``Type: object`` 56 | 57 | > Request headers. 58 | 59 | json 60 | 61 | ``Type: object`` 62 | 63 | > JSON body. 64 | 65 | body 66 | 67 | ``Type: string`` 68 | 69 | > Request body. 70 | 71 | form 72 | 73 | ``Type: object`` 74 | 75 | > Request form. 76 | 77 | cookieJar 78 | 79 | ``Type: tough.CookieJar instance`` 80 | 81 | > Cookie support. 82 | 83 | Timeout 84 | 85 | ``Type: string`` 86 | ``Default: 20`` 87 | 88 | > Request timeout. 89 | 90 | Debug 91 | 92 | ``Type: boolean`` 93 | ``Default: false`` 94 | 95 | > whether or not to print out messages on proxy server 96 | 97 | Redirect 98 | 99 | ``Type: boolean`` 100 | ``Default: true`` 101 | 102 | > whether or not to follow redirects 103 | 104 | Proxy 105 | 106 | ``Type: string`` 107 | ``Default: null`` 108 | 109 | > Proxy to use for request e.g http://user:pass@ip:port 110 | 111 | -------------------------------------------------------------------------------- /got-tls-proxy/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/evade99/got-tls-proxy 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/Carcraftz/cclient v0.0.0-20210907233050-29fc0b0a880a // indirect 7 | github.com/Carcraftz/fhttp v0.0.0-20210902023210-28df3f3a12b5 // indirect 8 | github.com/Carcraftz/utls v0.0.0-20210907185630-32782f880d54 // indirect 9 | github.com/andybalholm/brotli v1.0.3 // indirect 10 | github.com/dsnet/compress v0.0.1 // indirect 11 | github.com/fatih/color v1.13.0 // indirect 12 | github.com/gorilla/websocket v1.4.2 // indirect 13 | github.com/mattn/go-colorable v0.1.9 // indirect 14 | github.com/mattn/go-isatty v0.0.14 // indirect 15 | gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect 16 | gitlab.com/yawning/utls.git v0.0.12-1 // indirect 17 | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect 18 | golang.org/x/net v0.0.0-20210610132358-84b48f89b13b // indirect 19 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect 20 | golang.org/x/text v0.3.6 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /got-tls-proxy/go.sum: -------------------------------------------------------------------------------- 1 | github.com/Carcraftz/cclient v0.0.0-20210907233050-29fc0b0a880a h1:QSXhbGGcuTeTM/9vpX4FvU258jNBLL1urkYk3CiCd4U= 2 | github.com/Carcraftz/cclient v0.0.0-20210907233050-29fc0b0a880a/go.mod h1:kpZ5+5p4q3CH/N4hNQ9KNskbODppFoggsY3LtWVkum8= 3 | github.com/Carcraftz/fhttp v0.0.0-20210902023210-28df3f3a12b5 h1:3PC+6kPd6PEgQwnTckBhPIXrzY7MD7hPkUdFU2Q7rEA= 4 | github.com/Carcraftz/fhttp v0.0.0-20210902023210-28df3f3a12b5/go.mod h1:13BabbJ/YWQFq8iGu8FVYQQbFuP9jsUyqGnDg9UuxHQ= 5 | github.com/Carcraftz/utls v0.0.0-20210907185630-32782f880d54 h1:V/84tXJdc3wi5/kBU4iVNCxUU+XC3+wSGKFf/tRG+k4= 6 | github.com/Carcraftz/utls v0.0.0-20210907185630-32782f880d54/go.mod h1:YMKKxFhs/MzFaQP80rFaWsO78e/pYjtjgrlCbu5Rpps= 7 | github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= 8 | github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 9 | github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= 10 | github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= 11 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 12 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= 13 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 14 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 15 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 16 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 17 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 18 | github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= 19 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 20 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 21 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 22 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 23 | github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= 24 | gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= 25 | gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= 26 | gitlab.com/yawning/utls.git v0.0.12-1 h1:RL6O0MP2YI0KghuEU/uGN6+8b4183eqNWoYgx7CXD0U= 27 | gitlab.com/yawning/utls.git v0.0.12-1/go.mod h1:3ONKiSFR9Im/c3t5RKmMJTVdmZN496FNyk3mjrY1dyo= 28 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 29 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 30 | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= 31 | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 32 | golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 33 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 34 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 35 | golang.org/x/net v0.0.0-20210610132358-84b48f89b13b h1:k+E048sYJHyVnsr1GDrRZWQ32D2C7lWs9JRc0bel53A= 36 | golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 37 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 38 | golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 39 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 40 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 41 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 42 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 43 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 44 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 45 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= 46 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 47 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= 48 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 49 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 50 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 51 | golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 52 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 53 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 54 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 55 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= 56 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 57 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 58 | -------------------------------------------------------------------------------- /got-tls-proxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "net" 8 | "net/http" 9 | "os" 10 | "sync" 11 | "time" 12 | 13 | "github.com/fatih/color" 14 | 15 | proxy "github.com/evade99/got-tls-proxy/proxyService" 16 | "github.com/gorilla/websocket" 17 | ) 18 | 19 | var upgrader = websocket.Upgrader{ 20 | ReadBufferSize: 1024, 21 | WriteBufferSize: 1024, 22 | } 23 | 24 | type WsConn struct { 25 | Conn *websocket.Conn 26 | Mux sync.Mutex 27 | } 28 | 29 | func logger(t string, message string) { 30 | now := time.Now().Format("2006-01-02 15:04:05.000000") 31 | magenta := color.New(color.FgMagenta).SprintFunc() 32 | if t == "info" { 33 | cyan := color.New(color.FgCyan).SprintFunc() 34 | yellow := color.New(color.FgYellow).SprintFunc() 35 | fmt.Printf("[%s] [%s] > %s.\n", yellow(t), magenta(now), cyan(message)) 36 | } 37 | if t == "error" { 38 | red := color.New(color.FgRed).SprintFunc() 39 | yellow := color.New(color.FgYellow).SprintFunc() 40 | fmt.Printf("[%s] [%s] > %s.\n", yellow(t), magenta(now), red(message)) 41 | } 42 | if t == "success" { 43 | green := color.New(color.FgGreen).SprintFunc() 44 | yellow := color.New(color.FgYellow).SprintFunc() 45 | fmt.Printf("[%s] [%s] > %s.\n", yellow(t), magenta(now), green(message)) 46 | } 47 | 48 | } 49 | 50 | func (c *WsConn) execute(messageType int, p []byte) { 51 | 52 | var responseData = proxy.ProxyRequest(p) 53 | 54 | jsonData, err := json.Marshal(responseData) 55 | 56 | if err != nil { 57 | 58 | logger("error", "Error Reading Message - "+err.Error()) 59 | 60 | return 61 | } 62 | 63 | c.Mux.Lock() 64 | 65 | defer c.Mux.Unlock() 66 | 67 | if err := c.Conn.WriteMessage(messageType, jsonData); err != nil { 68 | logger("error", "Error Writing Message - "+err.Error()) 69 | return 70 | } 71 | 72 | } 73 | 74 | func reader(conn *websocket.Conn) { 75 | c := WsConn{Conn: conn} 76 | 77 | for { 78 | 79 | messageType, p, err := conn.ReadMessage() 80 | 81 | if err != nil { 82 | 83 | logger("error", "Error Reading Message - "+err.Error()) 84 | 85 | return 86 | } 87 | 88 | go c.execute(messageType, p) 89 | 90 | } 91 | } 92 | 93 | func clientEndpoint(w http.ResponseWriter, r *http.Request) { 94 | 95 | ws, err := upgrader.Upgrade(w, r, nil) 96 | 97 | if err != nil { 98 | log.Println(err) 99 | } 100 | 101 | logger("info", "Node Client Connected :)") 102 | 103 | reader(ws) 104 | 105 | } 106 | 107 | func setupRoutes() { 108 | http.HandleFunc("/client", clientEndpoint) 109 | } 110 | 111 | func main() { 112 | 113 | // var rLimit syscall.Rlimit 114 | // err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) 115 | 116 | // if err != nil { 117 | // logger("error", "Error Getting Rlimit "+err.Error()) 118 | // } 119 | 120 | // rLimit.Max = 64000 121 | // rLimit.Cur = 64000 122 | 123 | // err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) 124 | 125 | // if err != nil { 126 | // logger("error", "Error Setting Rlimit "+err.Error()) 127 | // } 128 | 129 | // err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) 130 | // if err != nil { 131 | // logger("error", "Error Getting Rlimit "+err.Error()) 132 | // } 133 | 134 | setupRoutes() 135 | 136 | logger("info", "Starting Client...") 137 | 138 | l, err := net.Listen("tcp", ":"+os.Getenv("PROXY_PORT")) 139 | 140 | if err != nil { 141 | logger("error", "Failed To Start Client - "+err.Error()) 142 | } 143 | 144 | logger("success", "Successfully Started Client") 145 | 146 | if err := http.Serve(l, nil); err != nil { 147 | logger("error", "Failed To Start Client - "+err.Error()) 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /got-tls-proxy/proxyService/app.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "compress/zlib" 7 | "encoding/json" 8 | "fmt" 9 | "io/ioutil" 10 | "net/url" 11 | "strconv" 12 | "strings" 13 | "time" 14 | 15 | "github.com/fatih/color" 16 | 17 | "github.com/Carcraftz/cclient" 18 | "github.com/andybalholm/brotli" 19 | 20 | http "github.com/Carcraftz/fhttp" 21 | tls "github.com/Carcraftz/utls" 22 | ) 23 | 24 | func log(t string, request RequestData, message string) { 25 | if request.Debug == true { 26 | now := time.Now().Format("2006-01-02 15:04:05.000000") 27 | magenta := color.New(color.FgMagenta).SprintFunc() 28 | if t == "info" { 29 | cyan := color.New(color.FgCyan).SprintFunc() 30 | yellow := color.New(color.FgYellow).SprintFunc() 31 | fmt.Printf("[%s] [%s] > %s.\n", magenta(now), yellow(request.ID), cyan(message)) 32 | } 33 | if t == "error" { 34 | red := color.New(color.FgRed).SprintFunc() 35 | yellow := color.New(color.FgYellow).SprintFunc() 36 | fmt.Printf("[%s] [%s] > %s.\n", magenta(now), yellow(request.ID), red(message)) 37 | } 38 | if t == "success" { 39 | green := color.New(color.FgGreen).SprintFunc() 40 | yellow := color.New(color.FgYellow).SprintFunc() 41 | fmt.Printf("[%s] [%s] > %s.\n", magenta(now), yellow(request.ID), green(message)) 42 | } 43 | } 44 | } 45 | 46 | func ProxyRequest(data []byte) ResponseData { 47 | 48 | var requestData RequestData 49 | var allowRedirect = true 50 | var timeout = 20 51 | var err error 52 | 53 | if err = json.Unmarshal(data, &requestData); err != nil { 54 | return ResponseData{ 55 | Success: false, 56 | Message: "Error: Invalid Request Data", 57 | } 58 | } 59 | 60 | var responseData = ResponseData{ 61 | ID: requestData.ID, 62 | Method: requestData.Method, 63 | URL: requestData.URL, 64 | } 65 | 66 | log("info", requestData, "Starting...") 67 | 68 | if requestData.URL == "" { 69 | log("error", requestData, "Missing Request Url") 70 | return ResponseData{ 71 | ID: requestData.ID, 72 | Success: false, 73 | Message: "Error: Missing Request Url", 74 | } 75 | } 76 | 77 | if requestData.Method == "" { 78 | log("error", requestData, "Missing Request Method") 79 | return ResponseData{ 80 | ID: requestData.ID, 81 | Success: false, 82 | Message: "Error: Missing Request Method", 83 | } 84 | } 85 | 86 | if len(requestData.Headers) == 0 { 87 | log("error", requestData, "Missing Request Headers") 88 | return ResponseData{ 89 | ID: requestData.ID, 90 | Success: false, 91 | Message: "Error: Missing Request Headers", 92 | } 93 | } 94 | 95 | if requestData.Headers["User-Agent"] == "" { 96 | log("error", requestData, "Missing UserAgent") 97 | return ResponseData{ 98 | ID: requestData.ID, 99 | Success: false, 100 | Message: "Error: Missing UserAgent", 101 | } 102 | } 103 | 104 | if requestData.Redirect == false { 105 | allowRedirect = false 106 | } 107 | 108 | if requestData.Timeout != "" { 109 | timeout, err = strconv.Atoi(requestData.Timeout) 110 | if err != nil { 111 | timeout = 20 112 | } 113 | } 114 | 115 | if timeout > 60 { 116 | log("error", requestData, "Timeout Cannot Be Longer Than 60 Seconds") 117 | return ResponseData{ 118 | ID: requestData.ID, 119 | Success: false, 120 | Message: "Error: Timeout Cannot Be Longer Than 60 Seconds", 121 | } 122 | } 123 | 124 | var tlsClient tls.ClientHelloID 125 | 126 | if strings.Contains(strings.ToLower(requestData.Headers["User-Agent"]), "chrome") { 127 | tlsClient = tls.HelloChrome_Auto 128 | } else if strings.Contains(strings.ToLower(requestData.Headers["User-Agent"]), "firefox") { 129 | tlsClient = tls.HelloFirefox_Auto 130 | } else { 131 | tlsClient = tls.HelloIOS_Auto 132 | } 133 | 134 | client, err := cclient.NewClient(tlsClient, requestData.Proxy, allowRedirect, time.Duration(timeout)) 135 | 136 | if err != nil { 137 | log("error", requestData, "Failed To Initiate Request") 138 | return ResponseData{ 139 | ID: requestData.ID, 140 | Success: false, 141 | Message: "Error: Failed To Initiate Request", 142 | } 143 | } 144 | 145 | var req *http.Request 146 | 147 | req, err = http.NewRequest(requestData.Method, requestData.URL, bytes.NewBuffer([]byte(requestData.Body))) 148 | 149 | if err != nil { 150 | log("error", requestData, "Failed To Execute Request") 151 | return ResponseData{ 152 | ID: requestData.ID, 153 | Success: false, 154 | Message: "Error: Failed To Execute Request", 155 | } 156 | } 157 | 158 | headermap := make(map[string]string) 159 | 160 | headerorderkey := []string{} 161 | 162 | for _, key := range Masterheaderorder { 163 | for k, v := range requestData.Headers { 164 | lowercasekey := strings.ToLower(k) 165 | if key == lowercasekey { 166 | headermap[k] = v 167 | headerorderkey = append(headerorderkey, lowercasekey) 168 | } 169 | } 170 | } 171 | 172 | req.Header = http.Header{ 173 | http.HeaderOrderKey: headerorderkey, 174 | http.PHeaderOrderKey: {":method", ":authority", ":scheme", ":path"}, 175 | } 176 | 177 | for k, v := range req.Header { 178 | if _, ok := headermap[k]; !ok { 179 | headermap[k] = v[0] 180 | headerorderkey = append(headerorderkey, strings.ToLower(k)) 181 | } 182 | } 183 | 184 | for k, v := range requestData.Headers { 185 | if k != "Content-Length" && !strings.Contains(k, "Poptls") { 186 | req.Header.Set(k, v) 187 | } 188 | } 189 | 190 | u, err := url.Parse(requestData.URL) 191 | if err != nil { 192 | log("error", requestData, "Failed To Get Host") 193 | return ResponseData{ 194 | ID: requestData.ID, 195 | Success: false, 196 | Message: "Error: Failed To Get Host", 197 | } 198 | } 199 | 200 | req.Header.Set("Host", u.Host) 201 | 202 | resp, err := client.Do(req) 203 | 204 | if err != nil { 205 | log("error", requestData, "Failed Request "+err.Error()) 206 | return ResponseData{ 207 | ID: requestData.ID, 208 | Success: false, 209 | Message: "Error: Failed Request " + err.Error(), 210 | } 211 | } 212 | 213 | defer resp.Body.Close() 214 | 215 | responseData.Headers = map[string][]interface{}{} 216 | 217 | for k, v := range resp.Header { 218 | if k != "Content-Length" && k != "Content-Encoding" { 219 | for _, kv := range v { 220 | responseData.Headers[k] = append(responseData.Headers[k], kv) 221 | } 222 | } 223 | } 224 | 225 | responseData.StatusCode = resp.StatusCode 226 | 227 | encoding := resp.Header["Content-Encoding"] 228 | 229 | body, err := ioutil.ReadAll(resp.Body) 230 | 231 | finalres := "" 232 | 233 | if err != nil { 234 | log("error", requestData, "Failed Request - Getting Content") 235 | return ResponseData{ 236 | ID: requestData.ID, 237 | Success: false, 238 | Message: "Error: Failed Request - Getting Content", 239 | } 240 | } 241 | 242 | finalres = string(body) 243 | 244 | if len(encoding) > 0 { 245 | if encoding[0] == "gzip" { 246 | unz, err := gUnzipData(body) 247 | if err != nil { 248 | panic(err) 249 | } 250 | finalres = string(unz) 251 | } else if encoding[0] == "deflate" { 252 | unz, err := enflateData(body) 253 | if err != nil { 254 | panic(err) 255 | } 256 | finalres = string(unz) 257 | } else if encoding[0] == "br" { 258 | unz, err := unBrotliData(body) 259 | if err != nil { 260 | panic(err) 261 | } 262 | finalres = string(unz) 263 | } else { 264 | finalres = string(body) 265 | } 266 | } else { 267 | finalres = string(body) 268 | } 269 | 270 | responseData.Success = true 271 | responseData.Body = finalres 272 | 273 | log("success", requestData, "Request Successfully Proxied") 274 | 275 | return responseData 276 | } 277 | 278 | func gUnzipData(data []byte) (resData []byte, err error) { 279 | gz, _ := gzip.NewReader(bytes.NewReader(data)) 280 | defer gz.Close() 281 | respBody, err := ioutil.ReadAll(gz) 282 | return respBody, err 283 | } 284 | func enflateData(data []byte) (resData []byte, err error) { 285 | zr, _ := zlib.NewReader(bytes.NewReader(data)) 286 | defer zr.Close() 287 | enflated, err := ioutil.ReadAll(zr) 288 | return enflated, err 289 | } 290 | func unBrotliData(data []byte) (resData []byte, err error) { 291 | br := brotli.NewReader(bytes.NewReader(data)) 292 | respBody, err := ioutil.ReadAll(br) 293 | return respBody, err 294 | } 295 | -------------------------------------------------------------------------------- /got-tls-proxy/proxyService/interface.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | type RequestData struct { 4 | ID string `json:"id"` 5 | Debug bool `json:"debug"` 6 | Method string `json:"method"` 7 | URL string `json:"url"` 8 | Proxy string `json:"proxy"` 9 | Headers map[string]string `json:"headers"` 10 | Body string `json:"body"` 11 | Timeout string `json:"timeout"` 12 | Redirect bool `json:"redirect"` 13 | } 14 | 15 | type ResponseData struct { 16 | ID string `json:"id"` 17 | Method string `json:"method"` 18 | StatusCode int `json:"statusCode"` 19 | URL string `json:"url"` 20 | Headers map[string][]interface{} `json:"headers"` 21 | Body string `json:"body"` 22 | Success bool `json:"success"` 23 | Message string `json:"message"` 24 | } 25 | 26 | var Masterheaderorder = []string{ 27 | "host", 28 | "connection", 29 | "cache-control", 30 | "device-memory", 31 | "viewport-width", 32 | "rtt", 33 | "downlink", 34 | "ect", 35 | "sec-ch-ua", 36 | "sec-ch-ua-mobile", 37 | "sec-ch-ua-full-version", 38 | "sec-ch-ua-arch", 39 | "sec-ch-ua-platform", 40 | "sec-ch-ua-platform-version", 41 | "sec-ch-ua-model", 42 | "upgrade-insecure-requests", 43 | "user-agent", 44 | "accept", 45 | "sec-fetch-site", 46 | "sec-fetch-mode", 47 | "sec-fetch-user", 48 | "sec-fetch-dest", 49 | "referer", 50 | "accept-encoding", 51 | "accept-language", 52 | "cookie", 53 | } 54 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "got-tls", 3 | "version": "1.0.6", 4 | "description": "A node websocket api version of https://github.com/Carcraftz/TLS-Fingerprint-API to spoof TLS fingerprint to prevent your requests from being fingerprinted. \r > im fairly new to golang / websockets pull requests are welcome", 5 | "main": "dist/index.js", 6 | "files": [ 7 | "/dist" 8 | ], 9 | "scripts": { 10 | "build": "tsc", 11 | "start": "ts-node src/index.ts", 12 | "test": "jest" 13 | }, 14 | "keywords": [ 15 | "tls", 16 | "utls", 17 | "ja3", 18 | "antibot", 19 | "fingerprint", 20 | "spoof", 21 | "bot", 22 | "sneakers" 23 | ], 24 | "repository": "evade99/got-tls", 25 | "homepage": "https://github.com/evade99/got-tls#readme", 26 | "bugs": { 27 | "url": "https://github.com/evade99/got-tls/issues" 28 | }, 29 | "license": "MIT", 30 | "devDependencies": { 31 | "@types/jest": "^27.0.2", 32 | "@types/node": "^16.10.1", 33 | "@types/pubsub-js": "^1.8.3", 34 | "husky": "^7.0.2", 35 | "jest": "^27.2.4", 36 | "prettier": "^2.4.1", 37 | "pretty-quick": "^3.1.1", 38 | "ts-jest": "^27.0.5", 39 | "ts-node": "^10.2.1", 40 | "typescript": "^4.4.3" 41 | }, 42 | "dependencies": { 43 | "@types/tough-cookie": "^4.0.1", 44 | "form-data-encoder": "^1.6.0", 45 | "get-port": "^5.1.1", 46 | "pubsub-js": "^1.9.3", 47 | "tough-cookie": "^4.0.0", 48 | "uuid": "^8.3.2", 49 | "uuidv4": "^6.2.12", 50 | "websocket": "^1.0.34" 51 | }, 52 | "directories": { 53 | "test": "tests" 54 | }, 55 | "author": "evade99" 56 | } 57 | -------------------------------------------------------------------------------- /src/examples/getRequest.ts: -------------------------------------------------------------------------------- 1 | import { Server, got } from "../index"; 2 | 3 | export const getRequest = async() => { 4 | Server.connect() 5 | let response = await got("https://httpbin.org/anything", "GET", { 6 | headers: { 7 | "User-Agent": "test" 8 | } 9 | }) 10 | console.log(response?.statusCode) 11 | console.log(response?.headers) 12 | } 13 | 14 | getRequest() -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { sleep, getBaseUrl, startServer, CONNECTED, BACKEND } from "./util/main"; 2 | import type { 3 | RequestOptions, 4 | ResponseData, 5 | RequestData, 6 | PromiseCookieJar, 7 | } from "./interface"; 8 | import { EventEmitter } from "events"; 9 | import { FormDataEncoder, isFormDataLike } from "form-data-encoder"; 10 | import { v4 as uuidv4 } from "uuid"; 11 | import PubSub from "pubsub-js"; 12 | import { URLSearchParams } from "url"; 13 | 14 | export const Server = { 15 | connect: startServer, 16 | }; 17 | 18 | const validMethods = [ 19 | "GET", 20 | "POST", 21 | "PUT", 22 | "PATCH", 23 | "HEAD", 24 | "DELETE", 25 | "OPTIONS", 26 | "TRACE", 27 | "get", 28 | "post", 29 | "put", 30 | "patch", 31 | "head", 32 | "delete", 33 | "options", 34 | "trace", 35 | ]; 36 | 37 | export const got = async ( 38 | method: string, 39 | url: string, 40 | options: RequestOptions, 41 | responseEmitter: EventEmitter | null = null, 42 | redirects: number = 0 43 | ) => { 44 | method = method.toUpperCase(); 45 | 46 | /** 47 | * Only create an event emitter for the initial request 48 | * redirect request will get the initial emitter passed in 49 | */ 50 | 51 | let init = false; 52 | 53 | if (!responseEmitter) { 54 | init = true; 55 | responseEmitter = new EventEmitter(); 56 | } 57 | 58 | /** 59 | * Time waited for websocket connection if not already connected 60 | * return error if not connected within 10 seconds 61 | */ 62 | 63 | let timeWaited = 0; 64 | 65 | while (!CONNECTED) { 66 | (await sleep(100)) && (timeWaited += 100); 67 | if (timeWaited > 10000) { 68 | responseEmitter.emit("error", `Proxy Client Took Too Long To Connect!`); 69 | } 70 | } 71 | 72 | /** 73 | * Check if the request method is valid 74 | */ 75 | 76 | if (!validMethods.includes(method)) { 77 | responseEmitter.emit("error", `Request Method ${method} Is Not Supported`); 78 | } 79 | 80 | /** 81 | * Check if the request url is valid 82 | */ 83 | 84 | if (typeof url === "string" && url === "") { 85 | responseEmitter.emit("error", `Request Url ${url} Is Not Valid`); 86 | } 87 | 88 | if (!url.includes("https://") && !url.includes("http://")) { 89 | responseEmitter.emit( 90 | "error", 91 | `Request Protocol Not Found! e.g http:// https://` 92 | ); 93 | } 94 | 95 | /** 96 | * Get Base Url For Cookie Jar Handling 97 | */ 98 | 99 | let baseUrl = getBaseUrl(url); 100 | 101 | if (options.cookieJar && typeof baseUrl === "string" && baseUrl === "") { 102 | responseEmitter.emit( 103 | "error", 104 | `Cookie Domain Cannot Be Resolved With Url ${url}` 105 | ); 106 | } 107 | 108 | const id = uuidv4(); 109 | 110 | let request: RequestData = { 111 | id: id, 112 | method: method, 113 | url: url, 114 | headers: options.headers, 115 | debug: options.debug, 116 | }; 117 | 118 | let hasContentType = 119 | options.headers["content-type"] || options.headers["Content-Type"]; 120 | 121 | if (options.json) { 122 | if (!hasContentType) { 123 | options.headers["content-type"] = "application/json"; 124 | } 125 | request.body = JSON.stringify(options.json); 126 | } else if (options.body) { 127 | if (isFormDataLike(options.body)) { 128 | const encoder = new FormDataEncoder(options.body); 129 | if (!hasContentType) { 130 | options.headers["content-type"] = encoder.headers["Content-Type"]; 131 | } 132 | request.body = encoder.encode().toString(); 133 | }else { 134 | request.body = options.body 135 | } 136 | } else if (options.form) { 137 | if (!hasContentType) { 138 | options.headers["content-type"] = "application/x-www-form-urlencoded"; 139 | } 140 | request.body = new URLSearchParams( 141 | options.form as Record 142 | ).toString(); 143 | } 144 | 145 | if (options.redirect) { 146 | request.redirect = options.redirect; 147 | } 148 | 149 | if (options.timeout) { 150 | request.timeout = options.timeout; 151 | } 152 | 153 | if (options.proxy) { 154 | request.proxy = options.proxy; 155 | } 156 | 157 | /** 158 | * If a cookie jar is passed in get the cookies for the domain 159 | * and set the cookie header for the request 160 | */ 161 | 162 | if (options.cookieJar) { 163 | const cookieString: string = await options.cookieJar.getCookieString( 164 | baseUrl 165 | ); 166 | if (cookieString != "") { 167 | request.headers.cookie = cookieString; 168 | } 169 | } 170 | 171 | PubSub.subscribe(id, async (msg: any, data: ResponseData) => { 172 | if (data.success) { 173 | if (data.headers) { 174 | /** 175 | * the proxy client returns headers as a string keys and array values 176 | * here we turn then into strings 177 | */ 178 | let finalHeaders: { [key: string]: string } = {}; 179 | 180 | for (const header in data.headers) { 181 | /** 182 | * the proxy client returns cookies as an object array and we loop and set them here 183 | */ 184 | if ( 185 | header === "Set-Cookie" && 186 | typeof data.headers["Set-Cookie"] === "object" && 187 | Array.isArray(data.headers["Set-Cookie"]) 188 | ) { 189 | console.log(data.headers) 190 | if (options.cookieJar) { 191 | let promises: Array> = data.headers[ 192 | "Set-Cookie" 193 | ].map(async (rawCookie: string) => 194 | (options.cookieJar as PromiseCookieJar).setCookie( 195 | rawCookie, 196 | url!.toString() 197 | ) 198 | ); 199 | try { 200 | await Promise.all(promises); 201 | } catch (error: any) {} 202 | } 203 | finalHeaders["Set-Cookie"] = data.headers["Set-Cookie"].join(", "); 204 | } else { 205 | finalHeaders[header] = data.headers[header][0]; 206 | } 207 | } 208 | data.headers = finalHeaders; 209 | } 210 | 211 | /** 212 | * Here we handle the response if the status code is a redirect one 213 | * and there is a location header we redirect else we return the response 214 | */ 215 | 216 | if ( 217 | data.statusCode >= 300 && 218 | data.statusCode < 400 && 219 | data.headers["Location"] 220 | ) { 221 | responseEmitter?.emit("redirect", data.headers["Location"]); 222 | } else { 223 | responseEmitter?.emit("end", data); 224 | } 225 | } else { 226 | responseEmitter?.emit("error", data.message); 227 | } 228 | }); 229 | 230 | BACKEND.send(JSON.stringify(request)); 231 | 232 | /** 233 | * handle redirect and pass in the initial response emitter and 234 | * redirect count to enforce max redirect limit 235 | */ 236 | 237 | responseEmitter.on("redirect", async (data) => { 238 | redirects = redirects + 1; 239 | if (redirects >= 20) { 240 | responseEmitter?.emit("error", "Too Many Redirects Error"); 241 | } else { 242 | options.body = undefined 243 | options.json = undefined 244 | options.form = undefined 245 | got("GET", data, options, responseEmitter, redirects); 246 | } 247 | }); 248 | 249 | if (init) { 250 | return new Promise((resolve, reject) => { 251 | responseEmitter?.on("end", (data: ResponseData) => { 252 | PubSub.unsubscribe(data.id); 253 | resolve(data); 254 | }); 255 | responseEmitter?.on("error", (err) => { 256 | reject(new Error(err)); 257 | }); 258 | }); 259 | } else { 260 | return { 261 | id: "", 262 | method: "", 263 | statusCode: 0, 264 | url: "", 265 | headers: {}, 266 | body: "", 267 | success: false, 268 | message: "", 269 | }; 270 | } 271 | }; 272 | 273 | got.head = (url: string, options: RequestOptions): Promise => { 274 | return got("head", url, options); 275 | }; 276 | got.get = (url: string, options: RequestOptions): Promise => { 277 | return got("get", url, options); 278 | }; 279 | got.post = (url: string, options: RequestOptions): Promise => { 280 | return got("post", url, options); 281 | }; 282 | got.put = (url: string, options: RequestOptions): Promise => { 283 | return got("put", url, options); 284 | }; 285 | got.delete = (url: string, options: RequestOptions): Promise => { 286 | return got("delete", url, options); 287 | }; 288 | got.trace = (url: string, options: RequestOptions): Promise => { 289 | return got("trace", url, options); 290 | }; 291 | got.options = (url: string, options: RequestOptions): Promise => { 292 | return got("options", url, options); 293 | }; 294 | got.connect = (url: string, options: RequestOptions): Promise => { 295 | return got("options", url, options); 296 | }; 297 | got.patch = (url: string, options: RequestOptions): Promise => { 298 | return got("patch", url, options); 299 | }; 300 | -------------------------------------------------------------------------------- /src/interface.ts: -------------------------------------------------------------------------------- 1 | import type {CookieJar} from 'tough-cookie' 2 | 3 | export interface ResponseData { 4 | id: string; 5 | method: string; 6 | statusCode: number; 7 | url: string; 8 | headers: Headers; 9 | body: string; 10 | success: boolean; 11 | message: string; 12 | } 13 | 14 | export interface RequestData { 15 | id: string; 16 | debug?: boolean; 17 | method: string; 18 | url: string; 19 | proxy?: string; 20 | headers: Headers; 21 | body?: string; 22 | timeout?: string; 23 | redirect?: boolean; 24 | } 25 | 26 | export interface ResponseObject { 27 | statusCode: number; 28 | headers: Record; 29 | body: string; 30 | } 31 | 32 | export interface Headers { 33 | [key: string]: any; 34 | } 35 | 36 | export interface RequestOptions { 37 | headers: Record; 38 | json?: Record; 39 | body?: string; 40 | form?: Record; 41 | redirect?: boolean; 42 | timeout?: string; 43 | debug?: boolean 44 | cookieJar?: CookieJar 45 | proxy?: string 46 | } 47 | 48 | export interface ToughCookieJar { 49 | getCookieString: ((currentUrl: string, options: Record, cb: (error: Error | null, cookies: string) => void) => void) 50 | & ((url: string, callback: (error: Error | null, cookieHeader: string) => void) => void); 51 | setCookie: ((cookieOrString: unknown, currentUrl: string, options: Record, cb: (error: Error | null, cookie: unknown) => void) => void) 52 | & ((rawCookie: string, url: string, callback: (error: Error | null, result: unknown) => void) => void); 53 | } 54 | 55 | export interface PromiseCookieJar { 56 | getCookieString: (url: string) => Promise; 57 | setCookie: (rawCookie: string, url: string) => Promise; 58 | } 59 | -------------------------------------------------------------------------------- /src/resources/got-tls-proxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/depicts/got-tls/95685e1937ef3aa399b54dfacc23c8e730dec1e3/src/resources/got-tls-proxy -------------------------------------------------------------------------------- /src/resources/got-tls-proxy-linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/depicts/got-tls/95685e1937ef3aa399b54dfacc23c8e730dec1e3/src/resources/got-tls-proxy-linux -------------------------------------------------------------------------------- /src/resources/got-tls-proxy.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/depicts/got-tls/95685e1937ef3aa399b54dfacc23c8e730dec1e3/src/resources/got-tls-proxy.exe -------------------------------------------------------------------------------- /src/util/main.ts: -------------------------------------------------------------------------------- 1 | import { spawn, exec, ChildProcessWithoutNullStreams } from "child_process"; 2 | import type { ResponseData } from "../interface"; 3 | import { join } from "path"; 4 | import PubSub from "pubsub-js"; 5 | import getPort from "get-port"; 6 | const W3CWebSocket = require("websocket").w3cwebsocket; 7 | 8 | let child: ChildProcessWithoutNullStreams; 9 | 10 | const cleanExit = async (message?: string | Error) => { 11 | if (!child) return; 12 | if (message) { 13 | console.log(message); 14 | } 15 | if (process.platform == "win32") { 16 | new Promise((resolve, reject) => { 17 | exec( 18 | "taskkill /pid " + child.pid + " /T /F", 19 | (error: any, stdout: any, stderr: any) => { 20 | if (error) { 21 | console.warn(error); 22 | } 23 | process.exit(); 24 | resolve(stdout ? stdout : stderr); 25 | } 26 | ); 27 | }); 28 | } else { 29 | new Promise((resolve, reject) => { 30 | if (child.pid) { 31 | process.kill(-child.pid); 32 | process.exit(); 33 | } 34 | }); 35 | } 36 | }; 37 | 38 | process.on("SIGINT", () => cleanExit()); 39 | 40 | process.on("SIGTERM", () => cleanExit()); 41 | 42 | export let BACKEND: any 43 | let PORT: number 44 | export let CONNECTED = false 45 | 46 | const connectToServer = async () => { 47 | try { 48 | await sleep(500) 49 | 50 | BACKEND = new W3CWebSocket(`ws://localhost:${PORT}/client`) 51 | 52 | BACKEND.onopen = function () { 53 | console.log('Successfully Connnected To Backend Proxy') 54 | CONNECTED = true 55 | } 56 | 57 | BACKEND.onmessage = function (e: any) { 58 | if (typeof e.data === "string") { 59 | let responseData: ResponseData = JSON.parse(e.data); 60 | PubSub.publish(responseData.id, responseData); 61 | } 62 | } 63 | 64 | BACKEND.onclose = function () { 65 | console.log('Backend Proxy Client Closed Error! Retrying Connection...') 66 | CONNECTED = false 67 | connectToServer() 68 | } 69 | 70 | BACKEND.onerror = function () { 71 | console.log('Backend Proxy Connection Error! Retrying Connection...') 72 | CONNECTED = false 73 | // connectToServer() 74 | } 75 | } catch (e) {} 76 | } 77 | 78 | export const startServer = async () => { 79 | try { 80 | PORT = await getPort() 81 | 82 | console.log('Starting Server...') 83 | 84 | let executableFilename = ""; 85 | if (process.platform == "win32") { 86 | executableFilename = "got-tls-proxy.exe"; 87 | } else if (process.platform == "linux") { 88 | executableFilename = "got-tls-proxy-linux"; 89 | } else if (process.platform == "darwin") { 90 | executableFilename = "got-tls-proxy"; 91 | } else { 92 | throw new Error("Operating system not supported"); 93 | } 94 | 95 | child = spawn(join(__dirname, `../resources/${executableFilename}`), { 96 | env: { PROXY_PORT: PORT.toString() }, 97 | shell: true, 98 | windowsHide: true, 99 | detached: process.platform !== "win32", 100 | }); 101 | 102 | await connectToServer() 103 | } catch (e) { 104 | console.log(e) 105 | } 106 | } 107 | 108 | 109 | const dir = "/"; 110 | 111 | export function getBaseUrl(url: string, prefix?: string) { 112 | const urlAsArray = url.split(dir); 113 | const doubleSlashIndex = url.indexOf("://"); 114 | if (doubleSlashIndex !== -1 && doubleSlashIndex === url.indexOf(dir) - 1) { 115 | urlAsArray.length = 3; 116 | let url = urlAsArray.join(dir); 117 | if (prefix !== undefined) url = url.replace(/http:\/\/|https:\/\//, prefix); 118 | return url; 119 | } else { 120 | let pointIndex = url.indexOf("."); 121 | if (pointIndex !== -1 && pointIndex !== 0) { 122 | return (prefix !== undefined ? prefix : "https://") + urlAsArray[0]; 123 | } 124 | } 125 | return ""; 126 | } 127 | 128 | export function sleep(ms: number) { 129 | return new Promise((resolve) => setTimeout(resolve, ms)); 130 | } 131 | -------------------------------------------------------------------------------- /tests/main.test.ts: -------------------------------------------------------------------------------- 1 | const { Server } = require("../src"); 2 | const { sleep } = require("../src/util/main"); 3 | 4 | test("Should Start And Connect To Proxy Client", async () => { 5 | let didConnect = await (async () => { 6 | let maxWaitTime = 10000; 7 | let timeWaited = 0; 8 | 9 | Server.connect(); 10 | 11 | while (!Server.isConnected) { 12 | (await sleep(100)) && (timeWaited += 100); 13 | if (timeWaited > maxWaitTime) { 14 | return false; 15 | } 16 | } 17 | 18 | return true; 19 | })(); 20 | 21 | expect(didConnect).toEqual(true); 22 | }); 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "dist", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true 11 | }, 12 | "include": ["src"], 13 | "exclude": ["node_modules", "tests"] 14 | } 15 | --------------------------------------------------------------------------------