npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cycletls

v1.0.26

Published

Spoof TLS/JA3 fingerprint in JS with help from Go

Downloads

12,479

Readme

CycleTLS

Accepting Community Support and PR's

build GoDoc license Go Report Card npm version

If you have a API change or feature request feel free to open an Issue

🚀 Features

  • High-performance Built-in goroutine pool used for handling asynchronous requests
  • Custom header ordering via fhttp
  • Proxy support | Socks4, Socks5, Socks5h
  • Ja3 Token configuration

Table of contents

Dependencies

node ^v16.0
golang ^v1.21x

Installation

Node Js

$ npm install cycletls

Golang

$ go get github.com/Danny-Dasilva/CycleTLS/cycletls 

Usage

Example CycleTLS Request for Typescript and Javascript

You can run this test in tests/simple.test.ts


const initCycleTLS = require('cycletls');
// Typescript: import initCycleTLS from 'cycletls';

(async () => {
  // Initiate CycleTLS
  const cycleTLS = await initCycleTLS();

  // Send request
  const response = await cycleTLS('https://ja3er.com/json', {
    body: '',
    ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0',
    userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0',
    proxy: 'http://username:[email protected]:443', 
  }, 'get');

  console.log(response);

  // Cleanly exit CycleTLS
  cycleTLS.exit();

})();

Example CycleTLS Request for Golang

package main

import (
	"log"
	"github.com/Danny-Dasilva/CycleTLS/cycletls"
)

func main() {

	client := cycletls.Init()

	response, err := client.Do("https://ja3er.com/json", cycletls.Options{
		Body : "",
		Ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0",
		UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0",
	  }, "GET");
	if err != nil {
		log.Print("Request Failed: " + err.Error())
	}
	log.Println(response)
}

Example using your own custom http.Client

import (
	"github.com/Danny-Dasilva/CycleTLS/cycletls"
	http "github.com/Danny-Dasilva/fhttp" // note this is a drop-in replacement for net/http
)

func main() {
	ja3 := "771,52393-52392-52244-52243-49195-49199-49196-49200-49171-49172-156-157-47-53-10,65281-0-23-35-13-5-18-16-30032-11-10,29-23-24,0"
	ua := "Chrome Version 57.0.2987.110 (64-bit) Linux"

 	 cycleClient := &http.Client{
 		Transport:     cycletls.NewTransport(ja3, ua),
 	 }

	resp, err := cycleClient.Get("https://tls.peet.ws/")
	...
}

Creating an instance

In order to create a cycleTLS instance, you can run the following:

JavaScript

// The initCycleTLS function spawns a Golang process that handles all requests concurrently via goroutine loops. 
const initCycleTLS = require('cycletls');
// import initCycleTLS from 'cycletls';

// Async/Await method
const cycleTLS = await initCycleTLS();
// .then method
initCycleTLS().then((cycleTLS) => {});

Golang

import (
	"github.com/Danny-Dasilva/CycleTLS/cycletls"
)

//The `Init` function initializes golang channels to process requests. 
client := cycletls.Init()

CycleTLS Alias Methods

The following methods exist in CycleTLS

cycleTLS(url, [config])

cycleTLS.get(url, [config])

cycleTLS.delete(url, [config])

cycleTLS.head(url, [config])

cycleTLS.options(url, [config])

cycleTLS.post(url, [config])

cycleTLS.put(url, config)

cycleTLS.patch(url, [config])

Url is not optional, config is optional

CycleTLS Request Config

{
  // URL for the request (required if not specified as an argument)
  url: "https://example.com"
  // Method for the request ("head" | "get" | "post" | "put" | "delete" | "trace" | "options" | "connect" | "patch")
  method: "get" // Default method
  // Custom headers to send
  headers: { "Authorization": "Bearer someexampletoken" }
  // Custom cookies to send
  Cookies: [{
    "name": "key",
    "value": "val",
    "path":  "/docs",
    "domain":  "google.com",
                "expires": "Mon, 02-Jan-2022 15:04:05 EST"
    "maxAge": 90,
    "secure": false,
    "httpOnly": true,
    "sameSite": "Lax"		
  }],
  // Body to send with request (must be a string - cannot pass an object)
  body: '',
  // JA3 token to send with request
  ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0',
  // User agent for request
  userAgent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0',
  // Proxy to send request through (must be in the same format)
  proxy: 'http://username:[email protected]:443',
  // Amount of seconds before request timeout (default: 7)
  timeout: 2,
  // Toggle if CycleTLS should follow redirects
  disableRedirect: true,
  // Custom header order to send with request (This value will overwrite default header order)
  headerOrder: ["cache-control", "connection", "host"],
  // Toggle if CycleTLS should skip verify certificate (If InsecureSkipVerify is true, TLS accepts any certificate presented by the server and any host name in that certificate.)
  insecureSkipVerify: false	
  // Forces CycleTLS to do a http1 handshake
  forceHTTP1: false	
}

CycleTLS Response Schema

{
  // Status code returned from server (Number)
  status: 200,
  // Body returned from the server (String)
  body: "",
  // Headers returned from the server (Object)
  headers: {
    "some": "header",
    ...
  },
  // FinalUrl returned from the server (String). This field is useful when redirection is active.
  finalUrl: "https://final.url/"	
}

Multiple Requests Example for Typescript and Javascript

If CycleTLS is being used by in a JavaScript environment, CycleTLS will spawn a Golang process to handle requests. This Golang process handles requests concurrently in a worker pool. Due to this, CycleTLS returns response objects as soon as they are made available (in other terms, CycleTLS processes requests as they are received, but responses are returned asynchronously so they will NOT be returned in the order requested)

If you are using CycleTLS in JavaScript, it is necessary to exit out of the instance to prevent zombie processes. The example below shows one way to approach cleanly exiting CycleTLS if you need to process multiple requests (note: keep in mind that calling the exit() function will kill any requests in progress). If your workflow requires requests running the entire time the process runs, modules such as exit-hook could serve as an alternative solution to cleanly exiting CycleTLS.

const initCycleTLS = require("cycletls");
// Typescript: import initCycleTLS from 'cycletls';

// Defining custom JA3 token and user agenton multiple requests,
  "https://httpbin.org/user-agent": {
    ja3: ja3,
    userAgent: userAgent,
  },
  "http://httpbin.org/post": {
    body: '{"field":"POST-VAL"}',
    method: "POST",
  },
  "http://httpbin.org/cookies": {
    cookies: [
      {
        name: "example1",
        value: "aaaaaaa",
        expires: "Mon, 02-Jan-2022 15:04:05 EST",
      },
    ],
  },
};

// Promises array of requests
const promises = [];

// Anonymous async function
(async () => {
  // Initiate CycleTLS
  const cycleTLS = await initCycleTLS();

  // Loop through requestDict (Object) defined above
  for (const url in requestDict) {
    // Fetch configs from requestDict (Object)
    const params = requestDict[url];

    // Send request (note: no waiting)
    const response = cycleTLS(
      url, {
        body: params.body ?? "", //?? is just setting defaults in this case
        ja3: params.ja3 ?? ja3,
        userAgent: params.userAgent ?? userAgent,
        headers: params.headers,
        cookies: params.cookies,
      }, params.method ?? "GET");

    // console.log the response object
    response.then((out) => {
      console.log(url, out);
    });

    // Push request to promise array
    promises.push(response);
  }

  // Wait for all requests to execute successfully
  Promise.all(promises).then(() => {
    // Cleanly exit CycleTLS one all requests have been received
    cycleTLS.exit();
  });
})();

Multiple Requests Example for Golang

The general expectation for golang packages is to expect the user to implement a worker pool or any other form of goroutine/asynchronous processing. This package includes a built in Queue method that leverages a worker pool/channels for long running asynchronous requests against a set of urls.

package main

import (
	"log"

	cycletls "github.com/Danny-Dasilva/CycleTLS/cycletls"
)

// Static variables
var (
	ja3       = "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0"
	userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"
)

// RequestConfig holds the configuration for each request.
type RequestConfig struct {
	URL     string
	Method  string
	Options cycletls.Options
}

func main() {
	client := cycletls.Init(true) // Initialize with worker pool

	// Define the requests
	requests := []RequestConfig{
		{
			URL:    "http://httpbin.org/delay/4",
			Method: "GET",
			Options: cycletls.Options{
				Ja3:       ja3,
				UserAgent: userAgent,
			},
		},
		{
			URL:    "http://httpbin.org/post",
			Method: "POST",
			Options: cycletls.Options{
				Body:      `{"field":"POST-VAL"}`,
				Ja3:       ja3,
				UserAgent: userAgent,
			},
		},
		{
			URL:    "http://httpbin.org/cookies",
			Method: "GET",
			Options: cycletls.Options{
				Ja3:       ja3,
				UserAgent: userAgent,
				Cookies: []cycletls.Cookie{
					{
						Name:  "example1",
						Value: "aaaaaaa",
					},
				},
			},
		},
	}

	// Queue the requests
	for _, req := range requests {
		client.Queue(req.URL, req.Options, req.Method)
	}

	// Asynchronously read responses as soon as they are available
	// They will return as soon as they are processed
	// e.g. Delay 3 will be returned last
	for i := 0; i < len(requests); i++ {
		response := <-client.RespChan
		log.Println("Response:", response)
	}

	// Close the client
	client.Close()
}

Dev Setup

If you would like to compile CycleTLS on your own, use the following commands:

Set module-aware mode go env -w GO111MODULE=off

Install golang dependencies go get github.com/Danny-Dasilva/CycleTLS/cycletls

install npm packages (this command handles the above)

npm install

To recompile index.ts in the src folder

npm run build

To recompile Golang files in the golang folder

All

npm run build:go

Windows

npm run build:go:windows:amd64

Linux

npm run build:go:linux:amd64

Mac

npm run build:go:mac:arm64

You can view the available compile options within the package.json

Questions

How do I set Cookies

There are two simple ways to interface with cookies

Javascript Simple Cookie Configuration

const initCycleTLS = require("cycletls");
(async () => {
  // Initiate cycleTLS
  const cycleTLS = await initCycleTLS();
  const response = await cycleTLS("https://httpbin.org/cookies", {
    cookies: {
      cookie1: "value1",
      cookie2: "value2",
    },
  });
  console.log(response.body);
  /* Expected
  {
    "cookies": {
      "cookie1": "value1",
      "cookie2": "value2"
    }
  }
  */
  cycleTLS.exit();
})();

In this simple example you can set the cookie name and value within an object

Javascript Complex Cookie Configuration

If you wish to have more fine grained control over cookie parameters you have access to the full underlying Go struct

here are the following values you can set

export interface Cookie {
  name: string;
  value: string;
  path?: string;
  domain?: string;
  expires?: string;
  rawExpires?: string;
  maxAge?: number;
  secure?: boolean;
  httpOnly?: boolean;
  sameSite?: string;
  unparsed?: string;
}

you can use them in a request as follows

const initCycleTLS = require("cycletls");
(async () => {
  // Initiate cycleTLS
  const cycleTLS = await initCycleTLS();
  const complexCookies = [
    {
      name: "cookie1",
      value: "value1",
      domain: "httpbin.org",
    },
    {
      name: "cookie2",
      value: "value2",
      domain: "httpbin.org",
    },
  ];

  const response = await cycleTLS("https://httpbin.org/cookies", {
    cookies: complexCookies,
  });

  console.log(response.body);
  /* Expected
  {
    "cookies": {
      "cookie1": "value1",
      "cookie2": "value2"
    }
  }
  */
  cycleTLS.exit();
})();

Golang Configure Cookies

package main

import (
    "github.com/Danny-Dasilva/CycleTLS/cycletls"
)

func main() {
    resp, err := client.Do("https://httpbin.org/cookies", cycletls.Options{
		Body:      "",
		Ja3:       "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0",
		UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0",
		Cookies: []cycletls.Cookie{{Name: "cookie1", Value: "value1"},
			{Name: "cookie2", Value: "value2"}},
    }, "GET")
    if err != nil {
      log.Print("Request Failed: " + err.Error())
    }
    log.Println(resp.Body)
    /* Expected
    {
      "cookies": {
        "cookie1": "value1", 
        "cookie2": "value2"
      }
      }
    */
    
    //Altenatively if you want access to values within a map
    log.Println(resp.JSONBody())
    /* Expected
    map[cookies:map[cookie1:value1 cookie2:value2]]
    */
}

Feel free to open an Issue with a feature request for specific file type support.

How do I use CookieJar in CycleTLS?

CookieJar in JS

const initCycleTLS = require("cycletls");

const tough = require("tough-cookie");
const Cookie = tough.Cookie;

(async () => {
  // Initiate cycleTLS and CookieJar
  const cycleTLS = await initCycleTLS();
  const cookieJar = new tough.CookieJar();

  // Capture a set cookie
  const firstResponse = await cycleTLS.get(
    "https://httpbin.org/cookies/set?freeform=test",
    {
      disableRedirect: true,
    }
  );
  
  // Now use the processCookies function to add the cookies from the response headers to the cookie jar
  await processCookies(
    firstResponse,
    "https://httpbin.org/cookies/set?freeform=test",
    cookieJar
  );
  // Now send a second to verify we have our cookies
  const secondResponse = await cycleTLS.get("https://httpbin.org/cookies", {
    headers: {
      cookie: await cookieJar.getCookieString("https://httpbin.org/cookies"),
    },
  });
  
  //verify cookies were set
  console.log(secondResponse.body)
  /* Expected
  {
    "cookies": {
      "freeform": "test"
    }
  }
  */
  cycleTLS.exit();
})();

async function processCookies(response, url, cookieJar) {
  if (response.headers["Set-Cookie"] instanceof Array) {
    response.headers["Set-Cookie"].map(
      async (cookieString) => await cookieJar.setCookie(cookieString, url)
    );
  } else {
    await cookieJar.setCookie(response.headers["Set-Cookie"], url);
  }
}

CookieJar in Golang

package main

import (
	"github.com/Danny-Dasilva/CycleTLS/cycletls"
	"log"
	"net/http/cookiejar"
	"net/url"
	"strings"
)

func main() {
	client := cycletls.Init()
	jar, err := cookiejar.New(nil)
  if err != nil {
      log.Fatal(err)
  }
	// First request to set cookie
	firstResponse, err := client.Do("https://httpbin.org/cookies/set?a=1&b=2&c=3", cycletls.Options{
		Body: "",
		Ja3:       "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0",
		UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36",
		DisableRedirect: true,
	},
		 "GET")
	if err != nil {
		log.Fatal(err)
	}
	firstURL, _ := url.Parse(firstResponse.FinalUrl)
  jar.SetCookies( firstURL, firstResponse.Cookies)


	// Second request to verify cookies, including the cookies from the first response
	secondResponse, err := client.Do("https://httpbin.org/cookies", cycletls.Options{
    Body: "",
    Ja3:       "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0",
    UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36",
    Headers: map[string]string{
        "Cookie": getHeadersFromJar(jar, firstURL),
    },
	}, "GET")
	if err != nil {
	    log.Fatal(err)
	}

	log.Println("Second Response body:", secondResponse.Body)
}


func getHeadersFromJar(jar *cookiejar.Jar, url *url.URL) string {
    cookies := jar.Cookies(url)
    var cookieStrs []string
    for _, cookie := range cookies {
        cookieStrs = append(cookieStrs, cookie.Name+"="+cookie.Value)
    }
    return strings.Join(cookieStrs, "; ")
}

How do I send multipart/form-data in CycleTLS

Javascript Text form-data

const initCycleTLS = require("cycletls");
const FormData = require('form-data');

(async () => {
  const cycleTLS = await initCycleTLS();

  const formData = new FormData();
  formData.append("key1", "value1");
  formData.append("key2", "value2");
  
  const response = await cycleTLS('http://httpbin.org/post', {
      body: formData,
      headers: {
          'Content-Type': 'multipart/form-data',
      },
  }, 'post');

  console.log(response);

  cycleTLS.exit();
})();

Javascript File form-data

const initCycleTLS = require("cycletls");
const FormData = require('form-data');
const fs = require('fs');

(async () => {
  const cycleTLS = await initCycleTLS();

  const formData = new FormData();
  const fileStream = fs.createReadStream("../go.mod");
  formData.append('file', fileStream);

  
  const response = await cycleTLS('http://httpbin.org/post', {
      body: formData,
      headers: {
          'Content-Type': 'multipart/form-data',
      },
  }, 'post');

  console.log(response);

  cycleTLS.exit();
})();

Golang Text form-data

package main

import (
	"bytes"
	"github.com/Danny-Dasilva/CycleTLS/cycletls"
	"log"
	"mime/multipart"
)

func main() {
	client := cycletls.Init()

	// Prepare a buffer to write our multipart form
	var requestBody bytes.Buffer
	multipartWriter := multipart.NewWriter(&requestBody)

	// Add form fields
	multipartWriter.WriteField("key1", "value1")
	multipartWriter.WriteField("key2", "value2")

	contentType := multipartWriter.FormDataContentType()
	// Close the writer before making the request
	multipartWriter.Close()

	response, err := client.Do("http://httpbin.org/post", cycletls.Options{
		Body: requestBody.String(),
		Headers: map[string]string{
			"Content-Type": contentType,
		},
	}, "POST")

	if err != nil {
		log.Print("Request Failed: " + err.Error())
	}

	log.Println(response.Body)
}

Golang file upload form-data

package main

import (
	"github.com/Danny-Dasilva/CycleTLS/cycletls"
	"bytes"
	"io"
	"log"
	"mime/multipart"
	"os"
)

func main() {
  client := cycletls.Init()

  // Prepare a buffer to write our multipart form
  var requestBody bytes.Buffer
  multipartWriter := multipart.NewWriter(&requestBody)

  // Add a file
  fileWriter, err := multipartWriter.CreateFormFile("fieldname", "filename")
  if err != nil {
      log.Fatal("CreateFormFile Error: ", err)
  }

  // Open the file that you want to upload
  file, err := os.Open("path/to/your/file")
  if err != nil {
      log.Fatal("File Open Error: ", err)
  }
  defer file.Close()

  // Copy the file to the multipart writer
  _, err = io.Copy(fileWriter, file)
  if err != nil {
      log.Fatal("File Copy Error: ", err)
  }

  // Close the writer before making the request
  contentType := multipartWriter.FormDataContentType()
  multipartWriter.Close()

  response, err := client.Do("http://httpbin.org/post", cycletls.Options{
      Body: requestBody.String(),
      Headers: map[string]string{
          "Content-Type": contentType,
      },
  }, "POST")

  if err != nil {
      log.Print("Request Failed: " + err.Error())
  }

  log.Println(response.Body)
}

If requested encoding helpers can be added to the repo for golang

How do I send a application/x-www-form-urlencoded Post request

Javascript application/x-www-form-urlencoded form

const initCycleTLS = require("cycletls");
(async () => {
  const cycleTLS = await initCycleTLS();

  const urlEncodedData = new URLSearchParams();
  urlEncodedData.append('key1', 'value1');
  urlEncodedData.append('key2', 'value2');

  const response = await cycleTLS('http://httpbin.org/post', {
      body: urlEncodedData,
      headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
      },
  }, 'post');

  console.log(response);

  cycleTLS.exit();
})();

Golang application/x-www-form-urlencoded form

package main

import (
    "log"
	  "net/url"
    "github.com/Danny-Dasilva/CycleTLS/cycletls"
)

func main() {

	client := cycletls.Init()

	// Prepare form data
	form := url.Values{}
	form.Add("key1", "value1")
	form.Add("key2", "value2")

	response, err := client.Do("http://httpbin.org/post", cycletls.Options{
		Body: form.Encode(),
		Headers: map[string]string{
			"Content-Type": "application/x-www-form-urlencoded",
		},
	}, "POST")
	if err != nil {
		log.Print("Request Failed: " + err.Error())
	}
	log.Println(response.Body)
}

How do I download images?

Images with a Content-Type header of the following types are base 64 encoded.

Supported Image Types

  • image/svg+xml
  • image/webp
  • image/jpeg
  • image/png
  • application/pdf

To write them to a file you can use the below methods

Javascript Image Write to File

const initCycleTLS = require("cycletls");
var fs = require("fs");

//Function to write image to a file
const writeImage = (filename, data) => {
  let writeStream = fs.createWriteStream(filename);

  // write some data with a base64 encoding
  writeStream.write(data, "base64");
  writeStream.on("finish", () => {
    console.log(`wrote to file ${filename}`);
  });
  
  // close the stream
  writeStream.end();
};

(async () => {
  const cycleTLS = await initCycleTLS();
  // try {

  const jpegImage = await cycleTLS("http://httpbin.org/image/jpeg", {
    ja3: "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0",
    userAgent:
      "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0",
  });
  //Write Image
  writeImage("test.jpeg", jpegImage.body);

  cycleTLS.exit();
})();

Golang Image Write to File

package main

import (
    "encoding/base64"
    "os"
    "github.com/Danny-Dasilva/CycleTLS/cycletls"
)

func main() {

    client := cycletls.Init()
    response, err := client.Do("http://httpbin.org/image/jpeg", cycletls.Options{
      Body:      "",
      Ja3:       "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21,29-23-24,0",
      UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36",
    }, "GET")
    // Decode Base64
    dec, err := base64.StdEncoding.DecodeString(response.Body)
    if err != nil {
        panic(err)
    }
    //create file to write
    f, err := os.Create("test.jpeg")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    //write b64 to file
    if _, err := f.Write(dec); err != nil {
        panic(err)
    }
    if err := f.Sync(); err != nil {
        panic(err)
    }
}

Additional file type support is planned.

Feel free to open an Issue with a feature request for specific file type support.

How do I set/force HTTP1

In golang set ForceHTTP1 in Options

package main

import (
	"github.com/Danny-Dasilva/CycleTLS/cycletls"
	"log"
)

func main() {
	client := cycletls.Init()
	response, err := client.Do("https://tls.peet.ws/api/all", cycletls.Options{
		ForceHTTP1: true,
	}, "GET")
	if err != nil {
		log.Print("Request Failed: " + err.Error())
	}
	log.Println(response.Body,) //You can verify the HTTP_Version in the response

}

In JS/TS set forceHTTP1 in Options

const initCycleTLS = require('cycletls');
// Typescript: import initCycleTLS from 'cycletls';

(async () => {
  const cycleTLS = await initCycleTLS();

  const response = await cycleTLS('https://ja3er.com/json', {
     body: '',
    ja3: '771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-51-57-47-53-10,0-23-65281-10-11-35-16-5-51-43-13-45-28-21,29-23-24-25-256-257,0',
    userAgent:
      'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0',
    forceHTTP1: false, //Set this field
  });

  console.log(response);
  //You can verify the HTTP_Version in the response
  cycleTLS.exit();

})();

Cross Compiling for other platforms

Natively the 3 Operating System types linux, darwin , windows should cover most use cases.

You can use the built in Golang cross compiling commands go build to compile for another operating system.

As an example for linux arm you need to pass in the GOOS and GOARCH arguments

$ GOOS=linux GOARCH=arm go build -o ./dist/index ./golang && chmod +x ./dist/index

With the above command you can simply run ./index and CycleTLS should function as intended.

Use this gist for different Operating Systems that support cross-compilation and feel free to open an Issue with a feature request for your specific operating system use case.

LICENSE

GPL3 LICENSE SYNOPSIS

TL;DR* Here's what the GPL3 license entails:

1. Anyone can copy, modify and distribute this software.
2. You have to include the license and copyright notice with each and every distribution.
3. You can use this software privately.
4. You can use this software for commercial purposes.
5. Source code MUST be made available when the software is distributed.
6. Any modifications of this code base MUST be distributed with the same license, GPLv3.
7. This software is provided without warranty.
8. The software author or license can not be held liable for any damages inflicted by the software.

More information on about the LICENSE can be found here