Skip to content

Instantly share code, notes, and snippets.

@sappho192
Created August 30, 2024 07:42
Show Gist options
  • Save sappho192/6ca7c067d4eb4a7fdda1197de23fc813 to your computer and use it in GitHub Desktop.
Save sappho192/6ca7c067d4eb4a7fdda1197de23fc813 to your computer and use it in GitHub Desktop.
C# client toward Cloudflare worker
using Npgsql;
using NpgsqlTypes;
var connString = "Host=database.example.com:5432;Username=postgres;Password=PASSWORD;Database=DATABASE_NAME";
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString);
var dataSource = dataSourceBuilder.Build();
var conn = await dataSource.OpenConnectionAsync();
// Insert some data
await using (var cmd = new NpgsqlCommand(
"INSERT INTO sentences.raw VALUES (@input_sentence,@input_language,@output_sentence,@output_language,@timestamp,@comment)",
conn))
{
// Specify the id as null to let the database generate the default value
cmd.Parameters.Add(new NpgsqlParameter("input_sentence", NpgsqlDbType.Text) { Value = "안녕하세요." });
cmd.Parameters.Add(new NpgsqlParameter("input_language", NpgsqlDbType.Text) { Value = "" });
cmd.Parameters.Add(new NpgsqlParameter("output_sentence", NpgsqlDbType.Text) { Value = "" });
cmd.Parameters.Add(new NpgsqlParameter("output_language", NpgsqlDbType.Text) { Value = "" });
cmd.Parameters.Add(new NpgsqlParameter("timestamp", NpgsqlDbType.Timestamp) { Value = DateTime.UtcNow });
cmd.Parameters.Add(new NpgsqlParameter("comment", NpgsqlDbType.Text) { Value = "테스트 데이터" });
await cmd.ExecuteNonQueryAsync();
}
// Retrieve all rows
await using (var cmd = new NpgsqlCommand("SELECT * FROM schema_name.table_name", conn))
await using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
Console.WriteLine(reader.GetString(0));
}
using System.Net.Http;
using System.Threading.Tasks;
namespace test
{
public class Program
{
public static async Task Main(string[] args)
{
var httpClient = new HttpClient();
var cloudflareClient = new CloudflareWorkerHttpClient(httpClient);
try
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.worker.dev");
var response = await cloudflareClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
// Process successful response
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
else
{
// Handle error
Console.WriteLine($"Error: {response.StatusCode}");
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}
catch (CloudflareWorkerException ex)
{
Console.WriteLine($"Cloudflare Worker Error: Code {ex.ErrorCode}");
Console.WriteLine($"Error Message: {ex.ErrorMessage}");
// from https://developers.cloudflare.com/workers/observability/errors/#_top
switch (ex.ErrorCode)
{
case 1101:
Console.WriteLine("Worker threw a JavaScript exception.");
break;
case 1102:
Console.WriteLine("Worker exceeded CPU time limit.");
break;
case 1103:
Console.WriteLine("The owner of this worker needs to contact Cloudflare Support");
break;
case 1015:
Console.WriteLine("Worker hit the burst rate limit.");
break;
case 1019:
Console.WriteLine("Worker hit loop limit.");
break;
case 1021:
Console.WriteLine("Worker has requested a host it cannot access.");
break;
case 1022:
Console.WriteLine("Cloudflare has failed to route the request to the Worker.");
break;
case 1024:
Console.WriteLine("Worker cannot make a subrequest to a Cloudflare-owned IP address.");
break;
case 1027:
Console.WriteLine("Worker exceeded free tier daily request limit.");
break;
case 1042:
Console.WriteLine("Worker tried to fetch from another Worker on the same zone, which is unsupported.");
break;
default:
Console.WriteLine("Unknown Cloudflare Worker error");
break;
}
}
}
}
public class CloudflareWorkerException : Exception
{
public int ErrorCode { get; }
public string ErrorMessage { get; }
public CloudflareWorkerException(int errorCode, string errorMessage)
: base($"Cloudflare Worker Error {errorCode}: {errorMessage}")
{
ErrorCode = errorCode;
ErrorMessage = errorMessage;
}
}
public class CloudflareWorkerHttpClient
{
private readonly HttpClient _httpClient;
public CloudflareWorkerHttpClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
var response = await _httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode && IsCloudflareWorkerError(response))
{
throw new CloudflareWorkerException(
GetErrorCodeFromResponse(response).GetAwaiter().GetResult(),
GetErrorMessageFromResponse(response).GetAwaiter().GetResult());
}
return response;
}
private bool IsCloudflareWorkerError(HttpResponseMessage response)
{
// Check if the response contains Cloudflare-specific headers
return response.Headers.Contains("CF-RAY") &&
response.Content.Headers.ContentType.MediaType == "text/html";
}
private async Task<int> GetErrorCodeFromResponse(HttpResponseMessage response)
{
// Parse the HTML content to extract the error code
var htmlContent = await response.Content.ReadAsStringAsync();
var errorCodeMatch = System.Text.RegularExpressions.Regex.Match(htmlContent, @"<title>Error (\d+)</title>");
if (errorCodeMatch.Success && int.TryParse(errorCodeMatch.Groups[1].Value, out var errorCode))
{
return errorCode;
}
throw new Exception("Unable to parse error code from Cloudflare Worker response");
}
private async Task<string> GetErrorMessageFromResponse(HttpResponseMessage response)
{
// Parse the HTML content to extract the error message
var htmlContent = await response.Content.ReadAsStringAsync();
var errorMessageMatch = System.Text.RegularExpressions.Regex.Match(htmlContent, @"<p class=""error-message"">(.*)</p>");
if (errorMessageMatch.Success)
{
return errorMessageMatch.Groups[1].Value.Trim();
}
throw new Exception("Unable to parse error message from Cloudflare Worker response");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment