OpenBullet2 Enhanced Part1 With Color
OpenBullet2 Enhanced Part1 With Color
-----------------------------------
File: /mnt/data/RuriLib_Http_Tests_Documentation.pdf
ProxyClientHandlerTests.cs
using Newtonsoft.Json.Linq;
using RuriLib.Proxies;
using RuriLib.Proxies.Clients;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
namespace RuriLib.Http.Tests
[Fact]
var userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60
Method = HttpMethod.Get,
};
message.Headers.Add("User-Agent", userAgent);
Assert.NotEmpty(userAgentActual);
Assert.Equal(userAgent, userAgentActual);
[Fact]
Method = HttpMethod.Get,
};
Assert.True(actual.ContainsKey(key));
Assert.True(actual.ContainsValue(value));
[Fact]
Method = HttpMethod.Get,
};
Assert.Contains(expected, actual);
[Fact]
Method = HttpMethod.Get,
};
Assert.NotNull(content);
var headers = response.Content.Headers;
Assert.NotNull(headers);
Assert.NotNull(headers.ContentLength);
Assert.Equal(expectedLength, headers.ContentLength.Value);
Assert.NotNull(headers.ContentType);
Assert.Equal(contentType, headers.ContentType.MediaType);
Assert.Equal(charSet, headers.ContentType.CharSet);
[Fact]
Method = HttpMethod.Get,
};
Assert.NotNull(response);
Assert.NotNull(source);
[Fact]
Method = HttpMethod.Get,
};
Assert.NotNull(response);
Assert.NotNull(source);
[Fact]
Method = HttpMethod.Get,
};
message.Headers.TryAddWithoutValidation("Accept-Encoding", expected);
Assert.Equal(expected, actual["Accept-Encoding"]);
[Fact]
public async Task SendAsync_Get_Cookies()
Method = HttpMethod.Get,
};
CookieContainer = cookieContainer
};
Assert.Single(cookies);
Assert.Equal(name, cookie.Name);
Assert.Equal(value, cookie.Value);
client.Dispose();
}
[Fact]
Method = HttpMethod.Get,
};
Assert.NotNull(response);
Assert.Equal(expected, response.StatusCode.ToString());
[Fact]
message.Headers.Host = "httpbin.org";
Assert.NotNull(response);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
private static async Task<HttpResponseMessage> RequestAsync(HttpRequestMessage request
};
return result
? token.Value<string>()
: string.Empty;
return result
? token.ToObject<Dictionary<string, string>>()
: null;
RLHttpClientTests.cs
using Newtonsoft.Json.Linq;
using RuriLib.Http.Models;
using RuriLib.Proxies;
using RuriLib.Proxies.Clients;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
namespace RuriLib.Http.Tests
[Fact]
var userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, lik
var message = new HttpRequest
Method = HttpMethod.Get,
};
message.Headers.Add("User-Agent", userAgent);
Assert.NotEmpty(userAgentActual);
Assert.Equal(userAgent, userAgentActual);
[Fact]
Method = HttpMethod.Get,
};
Assert.True(actual.ContainsKey(key));
Assert.True(actual.ContainsValue(value));
[Fact]
Method = HttpMethod.Get,
};
Assert.Contains(expected, actual);
[Fact]
{
Method = HttpMethod.Get,
};
Assert.NotNull(content);
Assert.NotNull(headers);
Assert.NotNull(headers.ContentLength);
Assert.Equal(expectedLength, headers.ContentLength.Value);
Assert.NotNull(headers.ContentType);
Assert.Equal(contentType, headers.ContentType.MediaType);
Assert.Equal(charSet, headers.ContentType.CharSet);
[Fact]
Method = HttpMethod.Get,
};
Assert.NotNull(source);
[Fact]
Method = HttpMethod.Get,
};
Assert.NotNull(response);
Assert.NotNull(source);
[Fact]
Method = HttpMethod.Get,
};
message.Headers["Accept-Encoding"] = expected;
Assert.Equal(expected, actual["Accept-Encoding"]);
[Fact]
Method = HttpMethod.Get,
Cookies = cookies
};
Assert.Single(cookies);
Assert.Equal(value, cookies[name]);
}
[Fact]
Method = HttpMethod.Get,
};
Assert.NotNull(response);
Assert.Equal(expected, response.StatusCode.ToString());
[Fact]
Method = HttpMethod.Get,
};
message.Headers["Host"] = "httpbin.org";
var response = await RequestAsync(message);
Assert.NotNull(response);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
[Fact]
Method = HttpMethod.Get,
};
Assert.True(actual);
[Fact]
Method = HttpMethod.Get,
};
Assert.True(actual);
return result
? token.Value<T>()
: default;
return result
? token.ToObject<Dictionary<string, string>>()
: null;
RuriLib.Http.Tests.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
</ItemGroup>
</Project>
File: /mnt/data/RuriLib_Http_Documentation.pdf
HttpRequestMessageBuilder.cs
using System.Text;
using System.Net.Http;
using System.Collections.Generic;
using System.Net;
using System.Linq;
using RuriLib.Http.Extensions;
using System;
namespace RuriLib.Http
// Host: example.com
// Connection: Close
public static string BuildHeaders(HttpRequestMessage request, CookieContainer cookies = null)
// on Unix-like systems.
if (string.IsNullOrEmpty(request.Headers.Host))
headers.Add("Host", request.RequestUri.Host);
if (request.Headers.Connection.Count == 0)
headers.Add("Connection", "Close");
headers.Add(header.Key, GetHeaderValue(header));
if (cookies != null)
cookieBuilder
.Append(cookie)
.Append("; ");
if (cookieBuilder.Length > 2)
cookieBuilder.Remove(cookieBuilder.Length - 2, 2);
headers.Add("Cookie", cookieBuilder);
if (request.Content != null)
headers.Add(header.Key, GetHeaderValue(header));
headers.Add("Content-Length", contentLength);
sb
.Append(header.Key)
.Append(": ")
.Append(header.Value)
.Append(newLine);
sb.Append(newLine);
return sb.ToString();
0 => string.Empty,
1 => values[0],
};
HttpResponseBuilder.cs
using RuriLib.Http.Helpers;
using RuriLib.Http.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO.Pipelines;
using System.Buffers;
using System.Runtime.CompilerServices;
namespace RuriLib.Http
{
internal HttpResponseBuilder()
/// <summary>
/// </summary>
[MethodImpl(methodImplOptions: MethodImplOptions.AggressiveOptimization)]
reader = PipeReader.Create(stream);
{
Request = request
};
try
await ReceiveFirstLineAsync(cancellationToken).ConfigureAwait(false);
await ReceiveHeadersAsync(cancellationToken).ConfigureAwait(false);
if (request.Method != HttpMethod.Head)
catch
response.Dispose();
throw;
return response;
// HTTP/1.1 200 OK
while (true)
try
response.Version = Version.Parse(fields[0].Trim()[5..]);
break;
catch
else
if (res.IsCanceled || res.IsCompleted)
reader.Complete();
cancellationToken.ThrowIfCancellationRequested();
break;
while (true)
if (buff.IsSingleSegment)
if (ReadHeadersFastPath(ref buff))
reader.AdvanceTo(buff.Start);
break;
else
{
if (ReadHeadersSlowerPath(ref buff))
reader.AdvanceTo(buff.Start);
break;
reader.AdvanceTo(buff.Start, buff.End); // not adding this line might result in infinit loop.
if (res.IsCanceled || res.IsCompleted)
reader.Complete();
cancellationToken.ThrowIfCancellationRequested();
break;
/// <summary>
/// Reads all Header Lines using <see cref="Span{T}"/> For High Perfromace Parsing.
/// </summary>
int endofheadersindex;
var Lines = spanLines.SplitLines();// we use spanHelper class here to make a for each loop.
foreach (var Line in Lines)
ProcessHeaderLine(Line);
buff = buff.Slice(endofheadersindex + 4); // add 4 bytes for \r\n\r\n and to advance the pipe b
return true;
return false;
/// <summary>
/// </summary>
buff = buff.Slice(reader.Position);
ProcessHeaderLine(Line);
}
buff = buff.Slice(reader.Position);
if (header.Length == 0)
return;
// changed to use span directly to decrease the number of strings allocated (less GC activity)
// Sometimes it can happen that the first line e.g. HTTP/1.1 200 OK is read as a header (maybe
if (separatorPos == -1)
return;
if (headerName.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase) ||
headerName.Equals("Set-Cookie2", StringComparison.OrdinalIgnoreCase))
{
SetCookie(response, headerValue);
else if (ContentHelper.IsContentHeader(headerName))
values.Add(headerValue);
else
headerValue
};
contentHeaders.Add(headerName, values);
else
response.Headers[headerName] = headerValue;
if (value.Length == 0)
{
return;
if (separatorPos == -1)
return;
string cookieValue;
if (endCookiePos == -1)
else
response.Request.Cookies[cookieName] = cookieValue;
{
// If there are content headers
if (contentHeaders.Count != 0)
contentLength = GetContentLength();
if (readResponseContent)
// Rewind the stream and set the content of the response and its headers
finaleResponceStream.Seek(0, SeekOrigin.Begin);
else
response.Content.Headers.TryAddWithoutValidation(pair.Key, pair.Value);
if (response.Headers.ContainsKey("Transfer-Encoding"))
{
if (contentHeaders.ContainsKey("Content-Encoding"))
return GetChunkedDecompressedStream(cancellationToken);
else
return ReceiveMessageBodyChunked(cancellationToken);
if (contentHeaders.ContainsKey("Content-Encoding"))
return GetContentLengthDecompressedStream(cancellationToken);
else
return ReciveContentLength(cancellationToken);
else // handle the case where sever never sent chunked encoding nor content-length headrs (th
if (contentHeaders.ContainsKey("Content-Encoding"))
return GetResponcestreamUntilCloseDecompressed(cancellationToken);
}
else
return GetResponcestreamUntilClose(cancellationToken);
while (true)
if (res.IsCanceled)
cancellationToken.ThrowIfCancellationRequested();
if (buff.IsSingleSegment)
responcestream.Write(buff.FirstSpan);
else
{
responcestream.Write(seg.Span);
reader.AdvanceTo(buff.End);
if (res.IsCompleted || res.Buffer.Length == 0)// here the pipe will be complete if the server clo
break;
return responcestream;
return decompressedStream;
return decompressedStream;
}
private async Task<Stream> GetResponcestreamUntilCloseDecompressed(CancellationToken c
return decompressedStream;
if (contentLength == 0)
return contentlenghtStream;
while (true)
if (buff.IsSingleSegment)
contentlenghtStream.Write(buff.FirstSpan);
else
{
contentlenghtStream.Write(seg.Span);
reader.AdvanceTo(buff.End);
return contentlenghtStream;
if (res.IsCanceled || res.IsCompleted)
reader.Complete();
cancellationToken.ThrowIfCancellationRequested();
break;
return contentlenghtStream;
return length;
return -1;
}
encoding = values[0];
return encoding;
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
while (true)
chunkedDecoder.Decode(ref buff);
reader.AdvanceTo(buff.Start, buff.End);
if (chunkedDecoder.Finished)
return chunkedDecoder.DecodedStream;
if (res.IsCanceled || res.IsCompleted)
{
reader.Complete();
cancellationToken.ThrowIfCancellationRequested();
break;
return chunkedDecoder.DecodedStream;
stream.Seek(0, SeekOrigin.Begin);
};
HttpResponseMessageBuilder.cs
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Net.Http;
using System.Threading;
using System.Net.Sockets;
using System.IO.Compression;
using System.Threading.Tasks;
using System.Collections.Generic;
using RuriLib.Http.Helpers;
using RuriLib.Http;
using System.IO.Pipelines;
using System.Buffers;
namespace RuriLib.Http
// this.bufferSize = bufferSize;
this.cookies = cookies;
this.uri = uri;
reader = PipeReader.Create(stream);
response.RequestMessage = request;
try
await ReceiveFirstLineAsync(cancellationToken).ConfigureAwait(false);
await ReceiveHeadersAsync(cancellationToken).ConfigureAwait(false);
catch
response.Dispose();
throw;
return response;
// HTTP/1.1 200 OK
while (true)
try
break;
catch
else
if (res.IsCanceled || res.IsCompleted)
reader.Complete();
cancellationToken.ThrowIfCancellationRequested();
{
while (true)
if (buff.IsSingleSegment)
if (ReadHeadersFastPath(ref buff))
reader.AdvanceTo(buff.Start);
break;
else
if (ReadHeadersSlowerPath(ref buff))
reader.AdvanceTo(buff.Start);
break;
reader.AdvanceTo(buff.Start, buff.End);// not adding ghis linw might result in infinit loop
if (res.IsCanceled || res.IsCompleted)
reader.Complete();
cancellationToken.ThrowIfCancellationRequested();
}
}
/// <summary>
/// Reads all Header Lines using <see cref="Span{T}"/> For High Perfromace Parsing.
/// </summary>
int endofheadersindex;
var Lines = spanLines.SplitLines();// we use spanHelper class here to make a for each loop.
ProcessHeaderLine(Line);
buff = buff.Slice(endofheadersindex + 4); // add 4 bytes for \r\n\r\n and to advance the pipe b
return true;
return false;
/// <summary>
/// </summary>
buff = buff.Slice(reader.Position);
ProcessHeaderLine(Line);
buff = buff.Slice(reader.Position);
if (header.Length == 0)
return;
// changed to use span directly to decrease the number of strings allocated (less GC activity)
// Sometimes it can happen that the first line e.g. HTTP/1.1 200 OK is read as a header (maybe
// is not advanced properly) so it can cause an exception.
if (separatorPos == -1)
return;
if (headerName.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase) ||
headerName.Equals("Set-Cookie2", StringComparison.OrdinalIgnoreCase))
SetCookie(headerValue);
else if (ContentHelper.IsContentHeader(headerName))
values.Add(headerValue);
else
headerValue
};
contentHeaders.Add(headerName, values);
else
response.Headers.TryAddWithoutValidation(headerName, headerValue);
if (value.Length == 0)
return;
if (separatorPos == -1)
return;
string cookieValue;
if (endCookiePos == -1)
{
else
if (expiresPos != -1)
string expiresStr;
expiresPos += 8;
if (endExpiresPos == -1)
expiresStr = value[expiresPos..];
else
expiresStr = value[expiresPos..endExpiresPos];
{
var collection = cookies.GetCookies(uri);
if (collection[cookieName] != null)
collection[cookieName].Expired = true;
#endregion
if (cookieValue.Length == 0 ||
cookieValue.Equals("deleted", StringComparison.OrdinalIgnoreCase))
if (collection[cookieName] != null)
collection[cookieName].Expired = true;
else
if (contentHeaders.Count != 0)
contentLength = GetContentLength();
if (readResponseContent)
// Rewind the stream and set the content of the response and its headers
finaleResponceStream.Seek(0, SeekOrigin.Begin);
else
response.Content.Headers.TryAddWithoutValidation(pair.Key, pair.Value);
if (response.Headers.Contains("Transfer-Encoding"))
if (contentHeaders.ContainsKey("Content-Encoding"))
return GetChunkedDecompressedStream(cancellationToken);
}
else
return ReceiveMessageBodyChunked(cancellationToken);
if (contentHeaders.ContainsKey("Content-Encoding"))
return GetContentLengthDecompressedStream(cancellationToken);
else
return ReciveContentLength(cancellationToken);
else // handle the case where sever never sent chunked encoding nor content-length headrs (th
if (contentHeaders.ContainsKey("Content-Encoding"))
return GetResponcestreamUntilCloseDecompressed(cancellationToken);
else
return GetResponcestreamUntilClose(cancellationToken);
}
}
while (true)
if (res.IsCanceled)
cancellationToken.ThrowIfCancellationRequested();
if (buff.IsSingleSegment)
responcestream.Write(buff.FirstSpan);
else
responcestream.Write(seg.Span);
reader.AdvanceTo(buff.End);
if (res.IsCompleted || res.Buffer.Length == 0)// here the pipe will be complete if the server clo
{
break;
return responcestream;
return decompressedStream;
return decompressedStream;
return decompressedStream;
if (contentLength == 0)
return contentlenghtStream;
while (true)
if (buff.IsSingleSegment)
contentlenghtStream.Write(buff.FirstSpan);
else
contentlenghtStream.Write(seg.Span);
reader.AdvanceTo(buff.End);
if (contentlenghtStream.Length >= contentLength)
return contentlenghtStream;
if (res.IsCanceled || res.IsCompleted)
reader.Complete();
cancellationToken.ThrowIfCancellationRequested();
return length;
return -1;
encoding = values[0];
return encoding;
while (true)
chunkedDecoder.Decode(ref buff);
reader.AdvanceTo(buff.Start, buff.End);
if (chunkedDecoder.Finished)
return chunkedDecoder.DecodedStream;
if (res.IsCanceled || res.IsCompleted)
{
reader.Complete();
cancellationToken.ThrowIfCancellationRequested();
stream.Seek(0, SeekOrigin.Begin);
};
LICENSE
Some portions of the code were the work of Ruslan Khuduev and Artem Dontsov, to which
I am grateful, all rights are reserved to them. Their work is under the MIT license.
-----------------------------------------------------------------------------
The above copyright notice and this permission notice shall be included in
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
THE SOFTWARE.
ProxyClientHandler.cs
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using RuriLib.Proxies;
using System.Text;
using RuriLib.Proxies.Exceptions;
using System.Collections.Generic;
namespace RuriLib.Http
/// <summary>
/// </summary>
#region Properties
/// <summary>
/// </summary>
/// <summary>
/// Gets the raw bytes of the last request that was sent.
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// Whether to read the content of the response. Set to false if you're only interested
/// in headers.
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// If true, <see cref="AllowedCipherSuites"/> will be used instead of the default ones.
/// </summary>
/// <summary>
/// The cipher suites to send to the server during the TLS handshake, in order.
/// The default value of this property contains the cipher suites sent by Firefox as of 21 Dec 2020.
/// </summary>
TlsCipherSuite.TLS_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TlsCipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
TlsCipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
TlsCipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA
};
/// <summary>
/// Gets the type of decompression method used by the handler for automatic
/// <remarks>
/// </remarks>
/// <summary>
/// Gets or sets a value that indicates whether the handler uses the CookieContainer
/// property to store server cookies and uses these cookies when sending requests.
/// </summary>
/// <summary>
/// Gets or sets the cookie container used to store server cookies by the handler.
/// </summary>
/// <summary>
/// Gets or sets delegate to verifies the remote Secure Sockets Layer (SSL)
/// </summary>
/// <summary>
/// </summary>
#endregion
/// <summary>
/// Creates a new instance of <see cref="ProxyClientHandler"/> given a <paramref name="proxyC
/// </summary>
/// <summary>
/// </summary>
if (request == null)
}
if (UseCookies && CookieContainer == null)
try
if (!responseMessage.Headers.Contains("Location"))
? responseMessage.Headers.Location
if (responseMessage.StatusCode != HttpStatusCode.RedirectKeepVerb)
request.Method = HttpMethod.Get;
request.Content = null;
if (request.RequestUri.Host != redirectUri.Host)
request.Headers.Host = string.Empty;
request.Headers.Remove("Origin");
request.RequestUri = redirectUri;
responseMessage.Dispose();
}
catch
responseMessage.Dispose();
throw;
return responseMessage;
byte[] buffer;
buffer = Encoding.ASCII.GetBytes(HttpRequestMessageBuilder.BuildFirstLine(request));
ms.Write(buffer);
ms.Write(buffer);
if (request.Content != null)
ms.Write(buffer);
ms.Seek(0, SeekOrigin.Begin);
RawRequests.Add(ms.ToArray());
CancellationToken cancellationToken)
tcpClient?.Close();
connectionCommonStream?.Dispose();
connectionNetworkStream?.Dispose();
connectionNetworkStream = tcpClient.GetStream();
if (uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
try
{
var sslStream = new SslStream(connectionNetworkStream, false, ServerCertificateCustom
TargetHost = uri.Host,
EnabledSslProtocols = SslProtocols,
CertificateRevocationCheckMode = CertRevocationMode,
};
if (CertRevocationMode != X509RevocationMode.Online)
sslOptions.RemoteCertificateValidationCallback =
if (UseCustomCipherSuites)
connectionCommonStream = sslStream;
}
throw;
else
connectionCommonStream = connectionNetworkStream;
/// <inheritdoc/>
if (disposing)
tcpClient?.Dispose();
connectionCommonStream?.Dispose();
connectionNetworkStream?.Dispose();
base.Dispose(disposing);
README.md
# RuriLib.Http
This is a library that provides a custom HTTP client, in addition to an `HttpMessageHandler` to be used
# Installation
```csharp
using RuriLib.Http;
using RuriLib.Http.Models;
using RuriLib.Proxies;
using RuriLib.Proxies.Clients;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace HttpDemo
class Program
_ = MainAsync(args);
Console.ReadLine();
Method = HttpMethod.Post,
},
{ "PHPSESSID", "12345" }
},
};
// Send the request and get the response (this can fail so make sure to wrap it in a try/catch blo
Console.WriteLine(content);
}
}
```
# Example (HttpClient)
This example is equivalent to the one above, but it uses the default `HttpClient` so you can take advan
```csharp
using RuriLib.Http;
using RuriLib.Proxies;
using RuriLib.Proxies.Clients;
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace HttpDemo
class Program
_ = MainAsync(args);
Console.ReadLine();
};
Method = HttpMethod.Post,
};
// Send the request and get the response (this can fail so make sure to wrap it in a try/catch blo
Console.WriteLine(content);
```
# Credits
Some portions of the code were the work of Ruslan Khuduev and Artem Dontsov, to which I am gratef
RLHttpClient.cs
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using RuriLib.Proxies;
using RuriLib.Proxies.Clients;
using RuriLib.Proxies.Exceptions;
using System.Collections.Generic;
using RuriLib.Http.Models;
using System.Linq;
using System.Runtime.InteropServices;
namespace RuriLib.Http
/// <summary>
/// </summary>
#region Properties
/// <summary>
/// </summary>
/// <summary>
/// Gets the raw bytes of all the requests that were sent.
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// The maximum number of times a request will be redirected.
/// </summary>
/// <summary>
/// Whether to read the content of the response. Set to false if you're only interested
/// in headers.
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// If true, <see cref="AllowedCipherSuites"/> will be used instead of the default ones.
/// </summary>
/// <summary>
/// The cipher suites to send to the server during the TLS handshake, in order.
/// The default value of this property contains the cipher suites sent by Firefox as of 21 Dec 2020.
/// </summary>
TlsCipherSuite.TLS_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TlsCipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
TlsCipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
TlsCipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA
};
/// <summary>
/// Gets the type of decompression method used by the handler for automatic
/// </summary>
/// <remarks>
/// </remarks>
/// <summary>
/// Gets or sets delegate to verifies the remote Secure Sockets Layer (SSL)
/// <summary>
/// </summary>
#endregion
/// <summary>
/// </summary>
/// <summary>
/// </summary>
{
if (redirects > MaxNumberOfRedirects)
if (request == null)
try
if (locationHeaderName is null)
Uri.TryCreate(responseMessage.Headers[locationHeaderName], UriKind.RelativeOrAbso
var redirectUri = newLocation.IsAbsoluteUri
? newLocation
if (responseMessage.StatusCode != HttpStatusCode.RedirectKeepVerb)
request.Method = HttpMethod.Get;
request.Content = null;
if (request.Uri.Host != redirectUri.Host)
request.Headers.Remove(hostHeaderName);
request.Headers.Remove("Origin");
request.Uri = redirectUri;
catch
responseMessage.Dispose();
throw;
return responseMessage;
RawRequests.Add(buffer);
tcpClient?.Close();
if (connectionCommonStream is not null)
await connectionCommonStream.DisposeAsync().ConfigureAwait(false);
await connectionNetworkStream.DisposeAsync().ConfigureAwait(false);
connectionNetworkStream = tcpClient.GetStream();
if (uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
try
TargetHost = uri.Host,
EnabledSslProtocols = SslProtocols,
CertificateRevocationCheckMode = CertRevocationMode
};
if (CertRevocationMode != X509RevocationMode.Online)
sslOptions.RemoteCertificateValidationCallback =
connectionCommonStream = sslStream;
throw;
else
connectionCommonStream = connectionNetworkStream;
}
/// <inheritdoc/>
tcpClient?.Dispose();
connectionCommonStream?.Dispose();
connectionNetworkStream?.Dispose();
RuriLib.Http.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<Authors>Ruri</Authors>
<Version>1.0.1</Version>
<Copyright>Ruri 2022</Copyright>
<RepositoryUrl>https://github.com/openbullet/OpenBullet2/tree/master/RuriLib.Http</RepositoryUrl>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RuriLib.Proxies\RuriLib.Proxies.csproj" />
</ItemGroup>
</Project>
Extensions/IListExtensions.cs
using System.Collections.Generic;
namespace RuriLib.Http.Extensions
public static void Add(this IList<KeyValuePair<string, string>> list, string key, object value)
Helpers/ChunkedDecoderOptimized.cs
using System;
using System.Buffers;
using System.IO;
using System.Text;
namespace RuriLib.Http.Helpers
public ChunkedDecoderOptimized()
if (Isnewchunk)
Isnewchunk = false;
Finished = true;
return;
Isnewchunk = true;
return;
WritetoStream(chunk);
Isnewchunk = true;
buff = buff.Slice(chunk.End);
ParseNewChunk(ref buff);
if (buff.IsSingleSegment)
index = span.IndexOf(CRLF_Bytes);
if (index != -1)
if (pos != -1)
}
buff = buff.Slice(index + 2);
else
return -1;
else
if (pos > 0)
buff = buff.Slice(reader.Position);
else
return -1;
}
}
if (buff.IsSingleSegment)
DecodedStream.Write(buff.FirstSpan);
else
DecodedStream.Write(seg.Span);
Helpers/ContentHelper.cs
using System;
using System.Linq;
namespace RuriLib.Http.Helpers
{
//https://github.com/dotnet/corefx/blob/3e72ee5971db5d0bd46606fa672969adde29e307/src/Syste
"Last-Modified",
"Expires",
"Content-Type",
"Content-Range",
"Content-MD5",
"Content-Location",
"Content-Length",
"Content-Language",
"Content-Encoding",
"Allow"
};
public static bool IsContentHeader(string name) => contentHeaders.Any(h => h.Equals(name, Str
Helpers/SpanHelpers.cs
using System;
namespace RuriLib.Http.Helpers
_span = span;
Current = default;
return false;
return true;
else
return true;
Line = line;
Separator = separator;
}
public ReadOnlySpan<byte> Line { get; }
// This method allow to deconstruct the type, so you can write any of the following code
// https://docs.microsoft.com/en-us/dotnet/csharp/deconstruct#deconstructing-user-defined-type
line = Line;
separator = Separator;
// This method allow to implicitly cast the type into a ReadOnlySpan<byte>, so you can write th
Models/HttpRequest.cs
using RuriLib.Http.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace RuriLib.Http.Models
/// <summary>
/// </summary>
/// <summary>
/// Whether to write the absolute URI in the first line of the request instead of
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// The cookies to send inside the Cookie header of this request.
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// Gets the raw bytes that will be sent on the network stream.
/// </summary>
ms.Write(Encoding.ASCII.GetBytes(BuildFirstLine()));
ms.Write(Encoding.ASCII.GetBytes(BuildHeaders()));
if (Content != null)
ms.Write(await Content.ReadAsByteArrayAsync(cancellationToken));
}
return ms.ToArray();
/// <summary>
/// </summary>
// Make sure Host is written properly otherwise it won't get picked up below
if (name.Equals("Host", StringComparison.OrdinalIgnoreCase))
Headers["Host"] = value;
else
Headers[name] = value;
}
// Builds the headers, for example
// Host: example.com
// Connection: Close
// on Unix-like systems.
finalHeaders.Add("Host", Uri.Host);
finalHeaders.Add("Connection", "Close");
finalHeaders.Add(header);
// Add the Cookie header if not set manually and container not null
if (!HeaderExists("Cookie", out _) && Cookies.Any())
cookieBuilder
.Append($"{cookie.Key}={cookie.Value}; ");
if (cookieBuilder.Length > 2)
cookieBuilder.Remove(cookieBuilder.Length - 2, 2);
finalHeaders.Add("Cookie", cookieBuilder);
if (Content != null)
}
}
finalHeaders.Add("Content-Length", contentLength);
sb
.Append(header.Key)
.Append(": ")
.Append(header.Value)
.Append(newLine);
sb.Append(newLine);
return sb.ToString();
/// <summary>
/// Checks whether a header that matches a given <paramref name="name"/> exists. If it exists,
/// </summary>
actualName = key;
/// <inheritdoc/>
Models/HttpResponse.cs
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
namespace RuriLib.Http.Models
/// <summary>
/// </summary>
/// <summary>
/// The request that retrieved this response.
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <inheritdoc/>
}
File: /mnt/data/RuriLib_Tests_Documentation.pdf
ExportDescriptors.cs
using Newtonsoft.Json;
using System.IO;
using Xunit;
namespace RuriLib.Tests
[Fact]
Formatting = Formatting.Indented
};
Globals.DescriptorsRepository.Descriptors, settings);
File.WriteAllText("descriptors.json", json);
RuriLib.Tests.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
</ItemGroup>
</Project>
Extensions/StringExtensionsTests.cs
using System.Runtime.InteropServices;
using RuriLib.Extensions;
using Xunit;
namespace RuriLib.Tests.Extensions
{
public class StringExtensionsTests
[Fact]
Assert.Equal("EEEEABCD", padded);
[Fact]
Assert.Equal("hello", "hel".WithEnding("llo"));
[Fact]
Assert.Equal("llo", "hello".RightMostCharacters(3));
[Theory]
[InlineData("C:/test/dir/file.txt")]
[InlineData("C:\\test\\dir\\file.txt")]
[InlineData("file.txt")]
[InlineData("./file.txt")]
[InlineData("Test/file.txt")]
{
Assert.True(path.IsSubPathOf("C:/test/"));
[Theory]
[InlineData("/home/user/file.txt")]
[InlineData("file.txt")]
[InlineData("./file.txt")]
[InlineData("Test/file.txt")]
Assert.True(path.IsSubPathOf("/home/user/"));
[Theory]
[InlineData("C:/windows/system32/cmd.exe")]
[InlineData("C:/test/../windows/system32/cmd.exe")]
[InlineData("../../windows/system32/cmd.exe")]
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
Assert.False(path.IsSubPathOf("C:/test/"));
[Theory]
[InlineData("/opt/test")]
[InlineData("/home/user/../../root")]
[InlineData("../../root")]
Assert.False(path.IsSubPathOf("/home/user/"));
Functions/Conversion/Base64ConverterTests.cs
using RuriLib.Functions.Conversion;
using System.Text;
using Xunit;
namespace RuriLib.Tests.Functions.Conversion
[Fact]
Functions/Conversion/BinaryConverterTests.cs
using RuriLib.Functions.Conversion;
using Xunit;
namespace RuriLib.Tests.Functions.Conversion
[Fact]
[Fact]
Functions/Conversion/HexConverterTests.cs
using RuriLib.Functions.Conversion;
using Xunit;
namespace RuriLib.Tests.Functions.Conversion
[Fact]
[Fact]
Functions/Conversion/SizeConverterTests.cs
using RuriLib.Functions.Conversion;
using Xunit;
namespace RuriLib.Tests.Functions.Conversion
[Fact]
[Fact]
[Fact]
[Fact]
}
Functions/Crypto/AWS4SignatureTests.cs
using RuriLib.Functions.Conversion;
using System;
using System.Security.Cryptography;
using System.Text;
using Xunit;
namespace RuriLib.Tests.Functions.Crypto
[Fact]
Assert.Equal("f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d", H
Functions/Crypto/AesTests.cs
using System;
using System.Security.Cryptography;
using System.Text;
using Xunit;
namespace RuriLib.Tests.Functions.Crypto
[Fact]
var iv = Convert.FromBase64String("bjVKSk0wNEZONGtRMTc4ZA==");
Assert.Equal("Yu5IU/XZS3tMjw2m5p4Pgw==", Convert.ToBase64String(encrypted));
Functions/Files/FileUtilsTests.cs
using RuriLib.Functions.Files;
using System.IO;
using Xunit;
namespace RuriLib.Tests.Functions.Files
[Fact]
Assert.Equal(file, FileUtils.GetFirstAvailableFileName(file));
[Fact]
Assert.Equal(fileWithOne, FileUtils.GetFirstAvailableFileName(file));
[Fact]
File.Create(fileWithOne);
Assert.Equal(fileWithTwo, FileUtils.GetFirstAvailableFileName(file));
Functions/Http/HttpTests.cs
using RuriLib.Functions.Http;
using RuriLib.Logging;
using RuriLib.Models.Bots;
using RuriLib.Models.Configs;
using RuriLib.Models.Data;
using RuriLib.Models.Environment;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;
using RuriLib.Blocks.Requests.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RuriLib.Tests.Utils;
using RuriLib.Models.Blocks.Custom.HttpRequest.Multipart;
using System.IO;
using RuriLib.Tests.Utils.Mockup;
using RuriLib.Functions.Http.Options;
namespace RuriLib.Tests.Functions.Http
new(null)
},
new ConfigSettings(),
new BotLogger(),
null,
false);
[Theory]
[InlineData(HttpLibrary.RuriLibHttp)]
[InlineData(HttpLibrary.SystemNet)]
{ "name1", "value1" },
{ "name2", "value2" }
};
{ "Custom", "value" }
};
Url = httpBin,
Method = HttpMethod.GET,
HttpLibrary = library,
CustomHeaders = headers,
CustomCookies = cookies
};
Assert.Equal("value", response.Headers["Custom"]);
Assert.Equal("httpbin.org", response.Headers["Host"]);
Assert.Equal("GET", response.Method);
Assert.Equal(httpBin, response.Url);
[Theory]
[InlineData(HttpLibrary.RuriLibHttp)]
[InlineData(HttpLibrary.SystemNet)]
Url = httpBin,
Method = HttpMethod.POST,
HttpLibrary = library,
Content = "name1=value1&name2=value2",
ContentType = "application/x-www-form-urlencoded"
};
Assert.Equal("POST", response.Method);
Assert.Equal("value1", response.Form["name1"]);
Assert.Equal("value2", response.Form["name2"]);
Assert.Equal("application/x-www-form-urlencoded", response.Headers["Content-Type"]);
[Theory]
[InlineData(HttpLibrary.RuriLibHttp)]
[InlineData(HttpLibrary.SystemNet)]
Url = httpBin,
Method = HttpMethod.POST,
HttpLibrary = library,
Content = Encoding.UTF8.GetBytes("name1=value1&name2=value2"),
ContentType = "application/x-www-form-urlencoded"
};
Assert.Equal("POST", response.Method);
Assert.Equal("value1", response.Form["name1"]);
Assert.Equal("value2", response.Form["name2"]);
Assert.Equal("application/x-www-form-urlencoded", response.Headers["Content-Type"]);
[Theory]
[InlineData(HttpLibrary.RuriLibHttp)]
[InlineData(HttpLibrary.SystemNet)]
Url = httpBin,
Method = HttpMethod.GET,
HttpLibrary = library,
Username = "myUsername",
Password = "myPassword"
};
Assert.Equal("GET", response.Method);
[Theory]
[InlineData(HttpLibrary.RuriLibHttp)]
[InlineData(HttpLibrary.SystemNet)]
File.WriteAllText(tempFile, "fileContent");
};
Url = httpBin,
Method = HttpMethod.POST,
HttpLibrary = library,
Boundary = "myBoundary",
Contents = contents
};
Assert.Equal("POST", response.Method);
Assert.Equal("stringContent", response.Form["stringName"]);
Assert.Equal("rawContent", response.Form["rawName"]);
Assert.Equal("fileContent", response.Files["fileName"]);
/*
[Fact]
Url = "https://http2.golang.org/reqinfo",
Method = HttpMethod.GET,
HttpVersion = "2.0"
};
*/
Functions/Interop/IronPyTests.cs
using IronPython.Compiler;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace RuriLib.Tests.Functions.Interop
public IronPyTests()
engine = runtime.GetEngine("py");
[Fact]
scope.SetVariable("x", 3);
scope.SetVariable("y", 5);
code.Execute(scope);
Assert.Equal(8, scope.GetVariable<int>("result"));
}
[Fact]
scope.SetVariable("x", 3.5f);
scope.SetVariable("y", 5.2f);
code.Execute(scope);
Assert.Equal(8.7f, scope.GetVariable<float>("result"));
[Fact]
scope.SetVariable("x", "my");
scope.SetVariable("y", "string");
code.Execute(scope);
Assert.Equal("mystring", scope.GetVariable<string>("result"));
[Fact]
scope.SetVariable("x", "a");
scope.SetVariable("y", "b");
var code = engine.CreateScriptSourceFromString("result = [ x, y ]");
code.Execute(scope);
Assert.Equal(2, outputList.Count);
Assert.Equal("a", outputList[0]);
Assert.Equal("b", outputList[1]);
[Fact]
code.Execute(scope);
Assert.Equal("a", scope.GetVariable<string>("result"));
[Fact]
scope.SetVariable("x", 1);
code.Execute(scope);
}
}
Functions/Interop/JintTests.cs
using Jint;
using RuriLib.Extensions;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace RuriLib.Tests.Functions.Interop
[Fact]
engine.SetValue("x", 3);
engine.SetValue("y", 5);
Assert.Equal(8, engine.Global.GetProperty("result").Value.AsNumber().ToInt());
[Fact]
engine.SetValue("x", 3.5f);
engine.SetValue("y", 5.2f);
engine.Execute("var result = x + y;");
Assert.Equal(8.7f, engine.Global.GetProperty("result").Value.AsNumber().ToSingle());
[Fact]
engine.SetValue("x", "my");
engine.SetValue("y", "string");
Assert.Equal("mystring", engine.Global.GetProperty("result").Value.AsString());
[Fact]
engine.SetValue("x", "a");
engine.SetValue("y", "b");
Assert.Equal(2, outputList.Count);
Assert.Equal("a", outputList[0]);
Assert.Equal("b", outputList[1]);
[Fact]
Assert.Equal("a", engine.Global.GetProperty("result").Value.AsString());
[Fact]
engine.SetValue("x", 1);
engine.Execute("var y = x + 1;");
Assert.Null(engine.Global.GetProperty("result").Value);
Functions/Interop/NodeJSTests.cs
using Jering.Javascript.NodeJS;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Xunit;
namespace RuriLib.Tests.Functions.Interop
{
public class NodeJsTests
{innerScript}
var noderesult = {{
{MakeNodeObject(outputs)}
}};
callback(null, noderesult);
}}";
[Fact]
Assert.Equal(8, result.GetProperty("result").GetInt32());
[Fact]
{
var script = BuildScript("var result = x + y;", ["x", "y"], ["result"]);
Assert.Equal(8.7f, result.GetProperty("result").GetSingle());
[Fact]
Assert.False(result.GetProperty("result").GetBoolean());
[Fact]
Assert.Equal("mystring", result.GetProperty("result").GetString());
[Fact]
Assert.Equal(2, outputList.Count);
Assert.Equal("a", outputList[0]);
Assert.Equal("b", outputList[1]);
[Fact]
Assert.Equal("a", result.GetProperty("result").GetString());
[Fact]
Assert.Equal("hello", result.GetProperty("result").GetString());
[Fact]
}
Functions/Networking/DnsLookupTests.cs
using RuriLib.Functions.Networking;
using System.Threading.Tasks;
using Xunit;
namespace RuriLib.Tests.Functions.Networking
[Fact]
Assert.Contains("mx00.mail.com", entries);
Assert.Contains("mx01.mail.com", entries);
Functions/Parsing/HtmlParserTests.cs
using RuriLib.Functions.Parsing;
using System.Linq;
using Xunit;
namespace RuriLib.Tests.Functions.Parsing
{
private readonly string htmlPage = @"
<html>
<body>
<p>text <strong>123</strong></p>
<ul>
<li><p>innertext2</p></li>
<ul/>
</body>
</html>
";
// CSS Selector
[Fact]
Assert.Equal("innertext1", match);
[Fact]
Assert.Equal("<strong>123</strong>", match);
[Fact]
[Fact]
Assert.Null(match);
[Fact]
Assert.Equal("testvalue", match);
[Fact]
// XPATH
[Fact]
Assert.Equal("innertext1", match);
[Fact]
Assert.Equal("<strong>123</strong>", match);
[Fact]
[Fact]
Assert.Null(match);
[Fact]
[Fact]
Functions/Parsing/JsonParserTests.cs
using RuriLib.Functions.Parsing;
using System.Linq;
using Xunit;
namespace RuriLib.Tests.Functions.Parsing
[Fact]
Assert.Equal("value", match);
}
[Fact]
Assert.Null(match);
[Fact]
Functions/Parsing/LRParserTests.cs
using RuriLib.Functions.Parsing;
using System.Linq;
using Xunit;
namespace RuriLib.Tests.Functions.Parsing
private readonly string oneLineTwoMatchesString = "The cat is fat, the dog is lazy";
[Fact]
Assert.Equal("cat", match);
[Fact]
Assert.Null(match);
[Fact]
Assert.Equal("cat", match);
[Fact]
[Fact]
[Fact]
[Fact]
Assert.Equal(oneLineString, match);
[Fact]
Assert.Null(match);
[Fact]
{
string match = LRParser.ParseBetween(oneLineString, "cat", "John").FirstOrDefault();
Assert.Null(match);
[Fact]
Assert.Null(match);
Functions/Parsing/RegexParserTests.cs
using RuriLib.Functions.Parsing;
using System.Linq;
using Xunit;
namespace RuriLib.Tests.Functions.Parsing
[Fact]
}
[Fact]
[Fact]
Assert.Equal(string.Empty, match);
[Fact]
Assert.Null(match);
Functions/Time/TimeConverterTests.cs
using RuriLib.Functions.Time;
using System;
using Xunit;
namespace RuriLib.Tests.Functions.Time
{
[Fact]
Assert.Equal(recentUnixTimeSeconds, unixTime);
[Fact]
[Fact]
Assert.Equal(recentDate.ToUniversalTime(), dateTime);
[Fact]
public void ToDateTimeUtc_Milliseconds_OutputCorrectDate()
Assert.Equal(recentDate.ToUniversalTime(), dateTime);
[Fact]
Assert.Equal(recentDate, dateTime);
[Fact]
Assert.Equal(recentISO8601Date, iso);
Helpers/PauseTokenSourceTests.cs
using RuriLib.Helpers;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;
namespace RuriLib.Tests.Helpers
{
// This test is temporarily disabled until I figure out why it works perfectly fine on a
// [Fact]
sw.Start();
await Task.Delay(200);
await pts.Token.PauseIfRequestedAsync();
});
// Wait 300 ms, then pause the task for 1 second, then resume
await Task.Delay(300);
await pts.PauseAsync();
await Task.Delay(1000);
await pts.ResumeAsync();
// Wait until the task completes
await task.WaitAsync(TimeSpan.FromSeconds(5));
sw.Stop();
Helpers/StepperTests.cs
using RuriLib.Helpers;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;
namespace RuriLib.Tests.Helpers
// This test is temporarily disabled until I figure out why it works perfectly fine on a
// [Fact]
sw.Start();
await stepper.WaitForStepAsync();
await Task.Delay(500);
await stepper.WaitForStepAsync();
await Task.Delay(500);
});
await Task.Delay(300);
Assert.True(tookStep);
await Task.Delay(800);
tookStep = stepper.TryTakeStep();
Assert.True(tookStep);
await task.WaitAsync(TimeSpan.FromSeconds(5));
sw.Stop();
Helpers/VariableNamesTests.cs
using RuriLib.Helpers;
using System;
using Xunit;
namespace RuriLib.Tests.Helpers
[Fact]
[Fact]
Assert.True(VariableNames.IsValid(VariableNames.MakeValid(string.Empty)));
[Fact]
{
Assert.True(VariableNames.IsValid(VariableNames.MakeValid("$<&/")));
[Fact]
Assert.Equal("hello", VariableNames.MakeValid("!h£ello?"));
[Fact]
Assert.Equal("_2pac", VariableNames.MakeValid("2pac"));
[Fact]
Assert.Equal("data.SOURCE", VariableNames.MakeValid("data.SOURCE"));
Helpers/CSharp/CSharpWriterTests.cs
using RuriLib.Helpers.CSharp;
using Xunit;
namespace RuriLib.Tests.Helpers.CSharp
[Fact]
Assert.Equal("$\"{value}\"", CSharpWriter.SerializeInterpString("<value>"));
[Fact]
[Fact]
Helpers/LoliCode/LoliCodeParserTests.cs
using RuriLib.Helpers.Blocks;
using RuriLib.Helpers.LoliCode;
using RuriLib.Models.Blocks;
using RuriLib.Models.Blocks.Settings;
using RuriLib.Models.Blocks.Settings.Interpolated;
using Xunit;
namespace RuriLib.Tests.Helpers.LoliCode
[Fact]
[Fact]
[Fact]
[Fact]
{
var input = "42 is the answer";
[Fact]
[Fact]
Assert.Empty(LineParser.ParseList(ref input));
[Fact]
Assert.Empty(LineParser.ParseList(ref input));
[Fact]
[Fact]
[Fact]
[Fact]
Assert.True(LineParser.ParseBool(ref input));
Assert.Equal("indeed", input);
[Fact]
public void ParseDictionary_EmptyDictionary_Parse()
Assert.Empty(LineParser.ParseDictionary(ref input));
[Fact]
Assert.Empty(LineParser.ParseDictionary(ref input));
[Fact]
Assert.Equal("value1", dict["key1"]);
[Fact]
Assert.Equal("value1", dict["key1"]);
Assert.Equal("value2", dict["key2"]);
}
[Fact]
Assert.Equal(SettingInputMode.Fixed, valueSetting.InputMode);
Assert.IsType<StringSetting>(valueSetting.FixedSetting);
[Fact]
Assert.Equal(SettingInputMode.Variable, valueSetting.InputMode);
Assert.Equal("myVariable", valueSetting.InputVariableName);
[Fact]
{
var block = BlockFactory.GetBlock<AutoBlockInstance>("ConstantString");
Assert.Equal(SettingInputMode.Interpolated, valueSetting.InputMode);
Assert.IsType<InterpolatedStringSetting>(valueSetting.InterpolatedSetting);
Helpers/Transpilers/TranspilerTests.cs
using System;
using RuriLib.Helpers.Transpilers;
using Xunit;
namespace RuriLib.Tests.Helpers.Transpilers;
[Fact]
var nl = Environment.NewLine;
Assert.Equal(script, newScript);
}
Models/Blocks/AutoBlockInstanceTests.cs
using System;
using RuriLib.Helpers.Blocks;
using RuriLib.Models.Blocks;
using RuriLib.Models.Blocks.Settings;
using RuriLib.Models.Blocks.Settings.Interpolated;
using RuriLib.Models.Configs;
using Xunit;
namespace RuriLib.Tests.Models.Blocks;
[Fact]
block.OutputVariable = "myOutput";
block.IsCapture = false;
block.Disabled = true;
input.InputVariableName = "myInput";
index.InputMode = SettingInputMode.Fixed;
(index.FixedSetting as IntSetting)!.Value = 3;
length.InputMode = SettingInputMode.Fixed;
Assert.Equal(expected, block.ToLC());
[Fact]
int lineNumber = 0;
Assert.True(block.Disabled);
Assert.Equal("myOutput", block.OutputVariable);
Assert.True(block.IsCapture);
Assert.Equal(SettingInputMode.Variable, input.InputMode);
Assert.Equal("myInput", input.InputVariableName);
Assert.Equal(SettingInputMode.Fixed, index.InputMode);
Assert.Equal(3, (index.FixedSetting as IntSetting)!.Value);
[Fact]
block.OutputVariable = "myOutput";
input.InputMode = SettingInputMode.Variable;
input.InputVariableName = "myInput";
index.InputMode = SettingInputMode.Fixed;
(index.FixedSetting as IntSetting)!.Value = 3;
length.InputMode = SettingInputMode.Fixed;
(length.FixedSetting as IntSetting)!.Value = 5;
[Fact]
block.OutputVariable = "myOutput";
block.IsCapture = true;
input.InputMode = SettingInputMode.Variable;
input.InputVariableName = "myInput";
index.InputMode = SettingInputMode.Fixed;
(index.FixedSetting as IntSetting)!.Value = 3;
length.InputMode = SettingInputMode.Fixed;
(length.FixedSetting as IntSetting)!.Value = 5;
[Fact]
[Fact]
block.OutputVariable = "myOutput";
input.InputMode = SettingInputMode.Interpolated;
index.InputMode = SettingInputMode.Fixed;
(index.FixedSetting as IntSetting)!.Value = 3;
length.InputMode = SettingInputMode.Variable;
length.InputVariableName = "myLength";
Models/Blocks/Custom/HttpRequestBlockInstanceTests.cs
using RuriLib.Helpers.Blocks;
using RuriLib.Models.Blocks.Custom;
using RuriLib.Models.Blocks.Custom.HttpRequest;
using RuriLib.Models.Blocks.Custom.HttpRequest.Multipart;
using RuriLib.Models.Blocks.Settings;
using RuriLib.Models.Configs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
namespace RuriLib.Tests.Models.Blocks.Custom
/*
[Fact]
url.InputMode = SettingInputMode.Fixed;
method.InputMode = SettingInputMode.Fixed;
(method.FixedSetting as EnumSetting).Value = "POST";
};
Assert.Equal(expected, block.ToLC());
[Fact]
url.InputMode = SettingInputMode.Fixed;
method.InputMode = SettingInputMode.Fixed;
new StringHttpContentSettingsGroup
},
new FileHttpContentSettingsGroup
},
};
Assert.Equal(expected, block.ToLC());
[Fact]
int lineNumber = 0;
Assert.IsType<MultipartRequestParams>(block.RequestParams);
Assert.Equal(SettingInputMode.Variable, multipart.Boundary.InputMode);
Assert.Equal("myBoundary", multipart.Boundary.InputVariableName);
Assert.IsType<StringHttpContentSettingsGroup>(content);
content = multipart.Contents[1];
Assert.IsType<FileHttpContentSettingsGroup>(content);
[Fact]
int lineNumber = 0;
(headers.FixedSetting as DictionaryOfStringsSetting).Value.Clear();
*/
Models/Blocks/Custom/KeycheckBlockInstanceTests.cs
using RuriLib.Helpers.Blocks;
using RuriLib.Models.Blocks.Custom;
using RuriLib.Models.Blocks.Custom.Keycheck;
using RuriLib.Models.Blocks.Settings;
using RuriLib.Models.Conditions.Comparisons;
using RuriLib.Models.Configs;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace RuriLib.Tests.Models.Blocks.Custom
/*
[Fact]
banIfNoMatch.InputMode = SettingInputMode.Fixed;
block.Disabled = true;
new Keychain
ResultStatus = "SUCCESS",
Mode = KeychainMode.OR,
new StringKey
InputMode = SettingInputMode.Variable,
InputVariableName = "myString"
},
Comparison = StrComparison.Contains,
},
new FloatKey
},
Comparison = NumComparison.GreaterThan,
},
new Keychain
ResultStatus = "FAIL",
Mode = KeychainMode.AND,
new ListKey
{
InputMode = SettingInputMode.Variable,
InputVariableName = "myList"
},
Comparison = ListComparison.Contains,
},
new DictionaryKey
InputMode = SettingInputMode.Variable,
InputVariableName = "myDict"
},
Comparison = DictComparison.HasKey,
};
var expected = "DISABLED\r\nLABEL:My Label\r\n banIfNoMatch = False\r\n KEYCHAIN SUC
Assert.Equal(expected, block.ToLC());
[Fact]
int lineNumber = 0;
Assert.True(block.Disabled);
Assert.False((banIfNoMatch.FixedSetting as BoolSetting).Value);
Assert.Equal("SUCCESS", kc1.ResultStatus);
Assert.Equal(KeychainMode.OR, kc1.Mode);
left = k1.Left;
right = k1.Right;
Assert.Equal(SettingInputMode.Variable, left.InputMode);
Assert.Equal("myString", left.InputVariableName);
Assert.Equal(StrComparison.Contains, k1.Comparison);
Assert.Equal(SettingInputMode.Fixed, right.InputMode);
left = k2.Left;
right = k2.Right;
Assert.Equal(SettingInputMode.Fixed, left.InputMode);
Assert.Equal(NumComparison.GreaterThan, k2.Comparison);
Assert.Equal(SettingInputMode.Fixed, right.InputMode);
Assert.Equal("FAIL", kc2.ResultStatus);
Assert.Equal(KeychainMode.AND, kc2.Mode);
left = k3.Left;
right = k3.Right;
Assert.Equal(SettingInputMode.Variable, left.InputMode);
Assert.Equal("myList", left.InputVariableName);
Assert.Equal(ListComparison.Contains, k3.Comparison);
Assert.Equal(SettingInputMode.Fixed, right.InputMode);
left = k4.Left;
right = k4.Right;
Assert.Equal(SettingInputMode.Variable, left.InputMode);
Assert.Equal("myDict", left.InputVariableName);
Assert.Equal(DictComparison.HasKey, k4.Comparison);
Assert.Equal(SettingInputMode.Fixed, right.InputMode);
[Fact]
banIfNoMatch.InputMode = SettingInputMode.Variable;
banIfNoMatch.InputVariableName = "myBool";
block.Disabled = true;
new Keychain
ResultStatus = "SUCCESS",
Mode = KeychainMode.OR,
{
new StringKey
Comparison = StrComparison.Contains,
},
new FloatKey
Comparison = NumComparison.GreaterThan,
},
new Keychain
ResultStatus = "FAIL",
Mode = KeychainMode.AND,
new ListKey
Comparison = ListComparison.Contains,
},
new DictionaryKey
{
Comparison = DictComparison.HasKey,
};
[Fact]
banIfNoMatch.InputMode = SettingInputMode.Variable;
banIfNoMatch.InputVariableName = "myBool";
block.Disabled = true;
*/
Models/Data/DataLineTests.cs
using RuriLib.Models.Data;
using RuriLib.Models.Environment;
using RuriLib.Models.Variables;
using System.Collections.Generic;
using Xunit;
namespace RuriLib.Tests.Models.Data
Name = "Default",
Separator = ","
};
[Fact]
Assert.Equal(2, variables.Count);
Assert.Equal("ONE", variables[0].Name);
Assert.Equal("TWO", variables[1].Name);
Assert.Equal("good", variables[0].AsString());
Assert.Equal("day", variables[1].AsString());
Models/Data/DataPoolTests.cs
using RuriLib.Models.Data;
using RuriLib.Models.Data.DataPools;
using System.Linq;
using Xunit;
namespace RuriLib.Tests.Models.Data
[Fact]
[Fact]
[Fact]
Assert.Equal(100, pool.DataList.Take(100).Count());
Models/Data/DataRuleTests.cs
using RuriLib.Models.Data.Rules;
using Xunit;
namespace RuriLib.Tests.Models.Data
[Fact]
Comparison = StringRule.Contains,
StringToCompare = "abc",
Invert = false
};
Assert.True(rule.IsSatisfied("abcdef"));
[Fact]
Comparison = StringRule.Contains,
StringToCompare = "abc",
Invert = true
};
Assert.False(rule.IsSatisfied("abcdef"));
[Fact]
Comparison = StringRule.Contains,
StringToCompare = "ABC",
CaseSensitive = false,
Invert = false
};
Assert.True(rule.IsSatisfied("abcdef"));
}
[Fact]
Comparison = StringRule.ContainsAll,
StringToCompare = "acf",
Invert = false
};
Assert.True(rule.IsSatisfied("abcdef"));
[Fact]
Comparison = StringRule.ContainsAny,
StringToCompare = "a78",
Invert = false
};
Assert.True(rule.IsSatisfied("abcdef"));
Models/Proxies/ProxyPoolTests.cs
using RuriLib.Models.Proxies;
using RuriLib.Models.Proxies.ProxySources;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace RuriLib.Tests.Models.Proxies
[Fact]
});
await pool.ReloadAllAsync();
pool.RemoveDuplicates();
Assert.Single(pool.Proxies);
}
[Fact]
});
await pool.ReloadAllAsync();
Assert.NotNull(pool.GetProxy());
[Fact]
});
await pool.ReloadAllAsync();
Assert.Null(pool.GetProxy());
[Fact]
{
ListProxySource source = new(new Proxy[]
});
await pool.ReloadAllAsync();
Assert.NotNull(pool.GetProxy(true));
[Fact]
});
await pool.ReloadAllAsync();
Assert.Null(pool.GetProxy(true, 3));
[Fact(Timeout = 10000)]
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Well, Only Windows contains Powershell.
return;
echo 127.0.0.1:1111
echo 127.0.0.1:2222
echo (Socks5)127.0.0.1:3333
", Encoding.UTF8);
await pool.ReloadAllAsync(false);
File.Delete(tmpBatchFilePath);
Assert.Equal(3, pool.Proxies.Count());
Assert.NotNull(proxy);
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(1111, proxy.Port);
proxy = pool.GetProxy();
Assert.NotNull(proxy);
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(2222, proxy.Port);
proxy = pool.GetProxy();
Assert.NotNull(proxy);
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(3333, proxy.Port);
Assert.Equal(ProxyType.Socks5, proxy.Type);
[Fact(Timeout = 10000)]
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
// Setting Execution Policy is needed both in the test and real-world use cases of the functional
// users can use "Set-ExecutionPolicy unrestricted -Scope CurrentUser" apply for all scripts.
System.Diagnostics.Process.Start("cmd.exe", command);
Write-Output 127.0.0.1:1111
Write-Output 127.0.0.1:2222
Write-Output ""(Socks5)127.0.0.1:3333""
", Encoding.UTF8);
await pool.ReloadAllAsync(false);
File.Delete(tmpBatchFilePath);
Assert.Equal(3, pool.Proxies.Count());
var proxy = pool.GetProxy();
Assert.NotNull(proxy);
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(1111, proxy.Port);
proxy = pool.GetProxy();
Assert.NotNull(proxy);
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(2222, proxy.Port);
proxy = pool.GetProxy();
Assert.NotNull(proxy);
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(3333, proxy.Port);
Assert.Equal(ProxyType.Socks5, proxy.Type);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;
echo 127.0.0.1:1111
echo 127.0.0.1:2222
echo ""(Socks5)127.0.0.1:3333""
", Encoding.UTF8);
await pool.ReloadAllAsync(false);
File.Delete(tmpBashFilePath);
Assert.Equal(3, pool.Proxies.Count());
Assert.NotNull(proxy);
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(1111, proxy.Port);
proxy = pool.GetProxy();
Assert.NotNull(proxy);
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(2222, proxy.Port);
proxy = pool.GetProxy();
Assert.NotNull(proxy);
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(3333, proxy.Port);
Assert.Equal(ProxyType.Socks5, proxy.Type);
Models/Proxies/ProxyTests.cs
using RuriLib.Models.Proxies;
using Xunit;
namespace RuriLib.Tests.Models.Proxies
{
[Fact]
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(8000, proxy.Port);
[Fact]
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(8000, proxy.Port);
Assert.Equal(ProxyType.Socks5, proxy.Type);
[Fact]
Assert.Equal("127.0.0.1", proxy.Host);
Assert.Equal(8000, proxy.Port);
Assert.Equal("user", proxy.Username);
Assert.Equal("pass", proxy.Password);
}
Utils/CurrentOS.cs
using System.Runtime.InteropServices;
namespace RuriLib.Tests.Utils
Utils/HttpBinResponse.cs
using System.Collections.Generic;
namespace RuriLib.Tests.Utils
/// <summary>
/// </summary>
Utils/Mockup/MockedGeneralSettingsProvider.cs
using RuriLib.Providers.Proxies;
namespace RuriLib.Tests.Utils.Mockup
Utils/Mockup/MockedProxySettingsProvider.cs
using RuriLib.Providers.Proxies;
using System;
namespace RuriLib.Tests.Utils.Mockup
public bool ContainsBanKey(string text, out string matchedKey, bool caseSensitive = false)
{
matchedKey = null;
return false;
public bool ContainsRetryKey(string text, out string matchedKey, bool caseSensitive = false)
matchedKey = null;
return false;
Utils/Mockup/MockedSecurityProvider.cs
using RuriLib.Providers.Security;
using System.Security.Cryptography.X509Certificates;
namespace RuriLib.Tests.Utils.Mockup
}
Detailed Documentation and Explanations
#### LICENSE
- **Content:** Contains the license information for the project, specifying the terms under which the code can be used, mod
#### ProxyClient.cs
- **Purpose:** Abstract class representing a proxy client. Provides the blueprint for creating a connection through different t
- **Key Methods:**
- `CreateConnectionAsync`: Abstract method to create a connection to a specified host and port.
- `ValidateEndpoint`: Validates the host and port.
- `ConnectSocketAsync`: Connects a socket to a specified host and port.
#### ProxySettings.cs
- **Purpose:** Class representing the settings for a proxy, including credentials, host, and port.
#### README.md
- **Content:** Markdown file describing the library, listing the available proxy clients and helper classes.
#### RuriLib.Proxies.csproj
- **Purpose:** Project file for the `RuriLib.Proxies` library, specifying the target framework and package references.
### Clients
#### HttpProxyClient.cs
- **Purpose:** Represents an HTTP proxy client.
- **Key Methods:**
- `CreateConnectionAsync`: Creates a connection through an HTTP proxy.
#### NoProxyClient.cs
- **Purpose:** Represents a direct connection without using a proxy.
- **Key Methods:**
- `CreateConnectionAsync`: Creates a direct connection to the host and port.
#### Socks4ProxyClient.cs
- **Purpose:** Represents a SOCKS4 proxy client.
- **Key Methods:**
- `CreateConnectionAsync`: Creates a connection through a SOCKS4 proxy.
#### Socks4aProxyClient.cs
- **Purpose:** Represents a SOCKS4a proxy client.
- **Key Methods:**
- `CreateConnectionAsync`: Creates a connection through a SOCKS4a proxy.
#### Socks5ProxyClient.cs
- **Purpose:** Represents a SOCKS5 proxy client.
- **Key Methods:**
- `CreateConnectionAsync`: Creates a connection through a SOCKS5 proxy.
### Exceptions
#### ProxyException.cs
- **Purpose:** Custom exception class for proxy-related errors.
- **Key Methods:**
- `ProxyException`: Constructors for creating instances of `ProxyException`.
### Helpers
#### HostHelper.cs
- **Purpose:** Helper class for handling host-related operations.
- **Key Methods:**
- `GetPortBytes`: Converts a port number to its byte representation.
- `GetIPAddressBytesAsync`: Gets the byte representation of an IP address.
#### PortHelper.cs
- **Purpose:** Helper class for handling port-related operations.
- **Key Methods:**
- `ValidateTcpPort`: Validates if a port number is within the valid range for TCP connections.
Block Descriptions
Block: Keycheck
Description: Modifies the bot's status by checking conditions
Category: Conditions
Parameters:
banIfNoMatch: Ban If No Match - None
Block: Parse
Description: Parses text from a string
Category: Parsing
Parameters:
input: Input - None
prefix: Prefix - None
suffix: Suffix - None
urlEncodeOutput: Url Encode Output - None
leftDelim: Left Delim - None
rightDelim: Right Delim - None
caseSensitive: Case Sensitive - None
cssSelector: Css Selector - None
attributeName: Attribute Name - None
xPath: X Path - None
jToken: J Token - None
pattern: Pattern - None
outputFormat: Output Format - None
multiLine: Multi Line - None
Block: Script
Description: This block can invoke a script in a different language, pass some variables and return some results.
Category: Interop
Parameters:
Block: Clear Cookies
Description: Clears the cookie jar used for HTTP requests
Category: Utility
Parameters:
Block: Delay
Description: Sleeps for a specified amount of milliseconds
Category: Utility
Parameters:
milliseconds: Milliseconds - None
Block: Scroll by
Description: Scrolls the page by a given amount of pixels
Category: Page
Parameters:
x: X - None
y: Y - None
Block: Execute JS
Description: Evaluates a js expression in the current page and returns a json response
Category: Page
Parameters:
expression: Expression - None
Block: Click
Description: Clicks an element
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
Block: Submit
Description: Submits a form
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
Block: Select
Description: Selects a value in a select element
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
value: Value - None
Block: Is Displayed
Description: Checks if an element is currently being displayed on the page
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
Block: Is Enabled
Description: Checks if an element is currently enabled
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
Block: Exists
Description: Checks if an element exists on the page
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
Block: Reload
Description: Reloads the current page
Category: Browser
Parameters:
Block: Go Back
Description: Goes back to the previously visited page
Category: Browser
Parameters:
Block: Go Forward
Description: Goes forward to the next visited page
Category: Browser
Parameters:
Block: Minimize
Description: Minimizes the browser window
Category: Browser
Parameters:
Block: Maximize
Description: Maximizes the browser window
Category: Browser
Parameters:
Block: Full Screen
Description: Makes the browser window full screen
Category: Browser
Parameters:
Block: Navigate To
Description: Navigates to a given URL in the current page
Category: Page
Parameters:
url: Url - None
loadedEvent: Loaded Event - None
referer: Referer - None
timeout: Timeout - None
Block: Scroll by
Description: Scrolls the page by a certain amount horizontally and vertically
Category: Page
Parameters:
horizontalScroll: Horizontal Scroll - None
verticalScroll: Vertical Scroll - None
Block: Execute JS
Description: Evaluates a js expression in the current page and returns a json response
Category: Page
Parameters:
expression: Expression - None
Block: Type
Description: Types text in an input field
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
text: Text - None
timeBetweenKeystrokes: Time Between Keystrokes - None
Block: Click
Description: Clicks an element
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
mouseButton: Mouse Button - None
clickCount: Click Count - None
timeBetweenClicks: Time Between Clicks - None
Block: Submit
Description: Submits a form
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
Block: Select
Description: Selects a value in a select element
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
value: Value - None
Block: Is Displayed
Description: Checks if an element is currently being displayed on the page
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
Block: Exists
Description: Checks if an element exists on the page
Category: Elements
Parameters:
findBy: Find By - None
identifier: Identifier - None
index: Index - None
Block: Reload
Description: Reloads the current page
Category: Browser
Parameters:
Block: Go Back
Description: Goes back to the previously visited page
Category: Browser
Parameters:
Block: Go Forward
Description: Goes forward to the next visited page
Category: Browser
Parameters:
Block: Substring
Description: Retrieves a piece of an input string
Category: String Functions
Parameters:
input: Input - None
index: Index - None
length: Length - None
Block: Reverse
Description: Reverses the characters in the input string
Category: String Functions
Parameters:
input: Input - None
Block: Trim
Description: Removes leading or trailing whitespace from the input string
Category: String Functions
Parameters:
input: Input - None
Block: Length
Description: Gets the length of a string
Category: String Functions
Parameters:
input: Input - None
Block: To Uppercase
Description: Changes all letters of a string to uppercase
Category: String Functions
Parameters:
input: Input - None
Block: To Lowercase
Description: Changes all letters of a string to lowercase
Category: String Functions
Parameters:
input: Input - None
Block: Replace
Description: Replaces all occurrences of some text in a string
Category: String Functions
Parameters:
original: Original - None
toReplace: To Replace - None
replacement: Replacement - None
Block: Translate
Description: Translates text in a string basing on a dictionary
Category: String Functions
Parameters:
input: Input - None
translations: Translations - None
replaceOne: Replace One - None
Block: Url Encode
Description: URL encodes a string
Category: String Functions
Parameters:
input: Input - None
Block: Unescape
Description: Unescapes characters in a string
Category: String Functions
Parameters:
input: Input - None
Block: Split
Description: Splits a string into a list
Category: String Functions
Parameters:
input: Input - None
separator: Separator - None
Block: Char At
Description: Gets the character at a specific index
Category: String Functions
Parameters:
input: Input - None
index: Index - None
Block: Shuffle
Description: Shuffles the items of a list
Category: List Functions
Parameters:
list: List - None
Block: To Dictionary
Description: Splits the items of a list to create a dictionary
Category: List Functions
Parameters:
list: List - None
separator: Separator - None
autoTrim: Auto Trim - None
Block: Index Of
Description: Gets the index of an element of a list
Category: List Functions
Parameters:
list: List - None
item: Item - None
exactMatch: Exact Match - None
caseSensitive: Case Sensitive - None
Block: Ceil
Description: Rounds the value up to the nearest integer
Category: Float Functions
Parameters:
input: Input - None
Block: Floor
Description: Rounds the value down to the nearest integer
Category: Float Functions
Parameters:
input: Input - None
Block: Round
Description: Rounds the value to the given decimal places
Category: Float Functions
Parameters:
input: Input - None
decimalPlaces: Decimal Places - None
Block: Compute
Description: Computes the value of a given mathematical expression
Category: Float Functions
Parameters:
input: Input - None
Block: XOR
Description: XOR En-/Decryption on byte arrays
Category: Crypto
Parameters:
bytes: Bytes - None
key: Key - None
Block: Hash
Description: Hashes data using the specified hashing function
Category: Crypto
Parameters:
input: Input - None
hashFunction: Hash Function - None
Block: Hmac
Description: Computes the HMAC signature of some data using the specified secret key and hashing
Category: Crypto
Parameters:
input: Input - None
key: Key - None
hashFunction: Hash Function - None
Block: PBKDF2PKCS5
Description: Generates a PKCS v5 #2.0 key using a Password-Based Key Derivation Function
Category: Crypto
Parameters:
password: Password - None
salt: Salt - None
saltSize: Salt Size - None
iterations: Iterations - None
keyLength: Key Length - None
type: Type - None