Installation
No additional dependencies are required beyond the standard .NET libraries. You’ll also need the Newtonsoft.Json package for JSON serialization:dotnet add package Newtonsoft.Json
Loading the library
Download the appropriate shared library for your platform:- macOS:
tls-client-darwin-amd64-1.7.2.dylib - Linux:
tls-client-xgo-1.7.2-linux-amd64.so - Windows:
tls-client-windows-64-1.7.2.dll
using System;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
[DllImport("../dist/tls-client-windows-64-1.7.2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr request(byte[] requestPayload, string sessionID);
[DllImport("../dist/tls-client-windows-64-1.7.2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void freeMemory(string sessionID);
Creating a TLS session wrapper
Here’s a complete wrapper class that simplifies working with the TLS Client:using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Newtonsoft.Json;
class RequestResult
{
public string Id { get; set; }
public string Body { get; set; }
public object Cookies { get; set; }
public Dictionary<string, List<string>> Headers { get; set; }
public int Status { get; set; }
public string Target { get; set; }
public string UsedProtocol { get; set; }
}
class RequestPayload
{
public string TlsClientIdentifier { get; set; } = "FireFox110";
public bool FollowRedirects { get; set; } = true;
public bool InsecureSkipVerify { get; set; } = false;
public bool WithoutCookieJar { get; set; } = false;
public bool WithCustomCookieJar { get; set; } = false;
public bool IsByteRequest { get; set; } = false;
public bool ForceHttp1 { get; set; } = false;
public bool WithDebug { get; set; } = false;
public bool CatchPanics { get; set; } = false;
public bool WithRandomTLSExtensionOrder { get; set; } = false;
public string sessionId { get; set; } = "Nada";
public int TimeoutSeconds { get; set; } = 30;
public int TimeoutMilliseconds { get; set; } = 0;
public Dictionary<string, string> CertificatePinningHosts { get; set; } = new Dictionary<string, string>();
public string ProxyUrl { get; set; } = "";
public bool IsRotatingProxy { get; set; } = false;
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
public List<string> HeaderOrder { get; set; }
public string RequestUrl { get; set; }
public string RequestMethod { get; set; }
public string RequestBody { get; set; }
}
class TLSSession
{
[DllImport("../dist/tls-client-windows-64-1.7.2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr request(byte[] requestPayload, string sessionID);
[DllImport("../dist/tls-client-windows-64-1.7.2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void freeMemory(string sessionID);
private string sessionID;
private RequestPayload sessionPayload;
public TLSSession(
Dictionary<string, string> headers = null,
string TlsClientIdentifier = "FireFox110",
int TimeoutSeconds = 30,
bool FollowRedirects = true,
string proxy = null)
{
this.sessionID = Guid.NewGuid().ToString();
this.sessionPayload = new RequestPayload
{
TlsClientIdentifier = TlsClientIdentifier,
FollowRedirects = FollowRedirects,
InsecureSkipVerify = false,
IsByteRequest = false,
ForceHttp1 = false,
WithDebug = false,
CatchPanics = false,
WithRandomTLSExtensionOrder = true,
sessionId = this.sessionID,
TimeoutSeconds = TimeoutSeconds,
TimeoutMilliseconds = 0,
CertificatePinningHosts = new Dictionary<string, string>(),
ProxyUrl = "",
IsRotatingProxy = false,
Headers = headers ?? new Dictionary<string, string>(),
HeaderOrder = headers != null ? new List<string>(headers.Keys) : new List<string>(),
RequestUrl = "",
RequestMethod = "",
RequestBody = "",
};
if (proxy != null)
{
Console.WriteLine(proxy);
this.sessionPayload.ProxyUrl = proxy;
}
}
public RequestResult Get(string url, Dictionary<string, string> additionalHeaders = null)
{
return this.MakeRequest("GET", url, MergeHeaders(this.sessionPayload.Headers, additionalHeaders));
}
public RequestResult Post(string url, Dictionary<string, string> additionalHeaders = null, string body = "")
{
return this.MakeRequest("POST", url, MergeHeaders(this.sessionPayload.Headers, additionalHeaders), body);
}
public RequestResult Patch(string url, Dictionary<string, string> additionalHeaders = null, string body = "")
{
return this.MakeRequest("PATCH", url, MergeHeaders(this.sessionPayload.Headers, additionalHeaders), body);
}
public RequestResult Put(string url, Dictionary<string, string> additionalHeaders = null, string body = "")
{
return this.MakeRequest("PUT", url, MergeHeaders(this.sessionPayload.Headers, additionalHeaders), body);
}
public RequestResult Delete(string url, Dictionary<string, string> additionalHeaders = null)
{
return this.MakeRequest("DELETE", url, MergeHeaders(this.sessionPayload.Headers, additionalHeaders));
}
private RequestResult MakeRequest(string method, string url, Dictionary<string, string> headers, string body = "")
{
this.sessionPayload.RequestMethod = method;
this.sessionPayload.RequestUrl = url;
this.sessionPayload.Headers = headers;
this.sessionPayload.RequestBody = body;
string requestJson = JsonConvert.SerializeObject(this.sessionPayload);
byte[] requestBytes = Encoding.UTF8.GetBytes(requestJson);
IntPtr responsePtr = request(requestBytes, this.sessionID);
string responseJson = Marshal.PtrToStringAnsi(responsePtr);
RequestResult result = JsonConvert.DeserializeObject<RequestResult>(responseJson);
freeMemory(result.Id);
return result;
}
private Dictionary<string, string> MergeHeaders(Dictionary<string, string> originalHeaders, Dictionary<string, string> additionalHeaders)
{
if (additionalHeaders == null) return originalHeaders;
foreach (var header in additionalHeaders)
{
originalHeaders[header.Key] = header.Value;
}
return originalHeaders;
}
}
Making requests
Once you have the wrapper class, making requests is straightforward:GET request
var headers = new Dictionary<string, string>
{
{ "accept", "text/html,application/xhtml+xml,application/xml;q=0.9" },
{ "user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" },
{ "accept-encoding", "gzip, deflate, br" },
{ "accept-language", "en-US,en;q=0.9" }
};
var session = new TLSSession(
headers: headers,
TlsClientIdentifier: "chrome_103",
TimeoutSeconds: 30,
FollowRedirects: true
);
var result = session.Get("https://example.com");
if (result.Status == 200)
{
Console.WriteLine("Success:");
Console.WriteLine(result.Body);
}
else if (result.Status == 0)
{
Console.WriteLine("Error:");
Console.WriteLine(result.Body);
}
else
{
Console.WriteLine($"Status: {result.Status}");
Console.WriteLine(result.Body);
}
POST request
var headers = new Dictionary<string, string>
{
{ "accept", "application/json" },
{ "user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" },
{ "content-type", "application/json" },
{ "accept-encoding", "gzip, deflate, br" }
};
var session = new TLSSession(
headers: headers,
TlsClientIdentifier: "chrome_103"
);
string jsonBody = JsonConvert.SerializeObject(new
{
username = "user",
password = "pass"
});
var result = session.Post(
"https://api.example.com/login",
additionalHeaders: null,
body: jsonBody
);
Console.WriteLine($"Status: {result.Status}");
Console.WriteLine(result.Body);
Using a proxy
var session = new TLSSession(
headers: headers,
TlsClientIdentifier: "chrome_103",
proxy: "http://proxy.example.com:8080"
);
var result = session.Get("https://example.com");
HTTP methods
TheTLSSession class supports all common HTTP methods:
- GET
- POST
- PUT
- PATCH
- DELETE
var result = session.Get("https://example.com");
var result = session.Post(
"https://example.com/api",
additionalHeaders: null,
body: "key=value"
);
var result = session.Put(
"https://example.com/api/resource",
additionalHeaders: null,
body: jsonBody
);
var result = session.Patch(
"https://example.com/api/resource",
additionalHeaders: null,
body: jsonBody
);
var result = session.Delete("https://example.com/api/resource");
Working with headers
You can set default headers when creating a session and override them per request:// Default headers for all requests
var defaultHeaders = new Dictionary<string, string>
{
{ "accept", "application/json" },
{ "user-agent", "MyApp/1.0" }
};
var session = new TLSSession(headers: defaultHeaders);
// Override headers for a specific request
var customHeaders = new Dictionary<string, string>
{
{ "authorization", "Bearer token123" },
{ "x-api-key", "key123" }
};
var result = session.Get("https://api.example.com", customHeaders);
Error handling
Check the status code to handle errors:var result = session.Get("https://example.com");
if (result.Status == 0)
{
// Request failed - error message in Body
Console.WriteLine($"Request failed: {result.Body}");
}
else if (result.Status >= 200 && result.Status < 300)
{
// Success
Console.WriteLine($"Success: {result.Body}");
}
else
{
// HTTP error status
Console.WriteLine($"HTTP {result.Status}: {result.Body}");
}
Response structure
TheRequestResult class contains all response information:
var result = session.Get("https://example.com");
Console.WriteLine($"ID: {result.Id}");
Console.WriteLine($"Status: {result.Status}");
Console.WriteLine($"Body: {result.Body}");
Console.WriteLine($"Target: {result.Target}");
Console.WriteLine($"Protocol: {result.UsedProtocol}");
foreach (var header in result.Headers)
{
Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
}
Platform-specific DllImport
Adjust the library path based on your platform:- Windows
- Linux
- macOS
[DllImport("tls-client-windows-64-1.7.2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr request(byte[] requestPayload, string sessionID);
[DllImport("tls-client-xgo-1.7.2-linux-amd64.so", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr request(byte[] requestPayload, string sessionID);
[DllImport("tls-client-darwin-amd64-1.7.2.dylib", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr request(byte[] requestPayload, string sessionID);