using System;
using System.Net;
using Newtonsoft.Json;
using Twilio.Exceptions;
using Twilio.AuthStrategies;
using System.Collections.Generic;
#if !NET35
using System.Threading.Tasks;
#endif
using Twilio.Http;
#if NET35
using Twilio.Http.Net35;
#endif
namespace Twilio.Clients
{
///
/// Implementation of a TwilioRestClient.
///
public class TwilioRestClient : ITwilioRestClient
{
///
/// Client to make HTTP requests
///
public HttpClient HttpClient { get; }
///
/// Account SID to use for requests
///
public string AccountSid { get; }
///
/// Twilio region to make requests to
///
public string Region { get; }
///
/// Twilio edge to make requests to
///
public string Edge { get; set; }
///
/// Additions to the user agent string
///
public string[] UserAgentExtensions { get; set; }
///
/// Log level for logging
///
public string LogLevel { get; set; } = Environment.GetEnvironmentVariable("TWILIO_LOG_LEVEL");
private readonly string _username;
private readonly string _password;
private readonly AuthStrategy _authstrategy;
///
/// Constructor for a TwilioRestClient
///
///
/// username for requests
/// password for requests
/// account sid to make requests for
/// region to make requests for
/// http client used to make the requests
/// edge to make requests for
public TwilioRestClient(
string username,
string password,
string accountSid = null,
string region = null,
HttpClient httpClient = null,
string edge = null
)
{
_username = username;
_password = password;
AccountSid = accountSid ?? username;
HttpClient = httpClient ?? DefaultClient();
Region = region;
Edge = edge;
}
///
/// Constructor for a TwilioRestClient
///
///
/// username for requests
/// password for requests
/// account sid to make requests for
/// region to make requests for
/// http client used to make the requests
/// edge to make requests for
/// authentication strategy that will be used to make requests for
public TwilioRestClient(
string username,
string password,
AuthStrategy authstrategy,
string accountSid = null,
string region = null,
HttpClient httpClient = null,
string edge = null
)
{
_username = username;
_password = password;
_authstrategy = authstrategy;
AccountSid = accountSid ?? username;
HttpClient = httpClient ?? DefaultClient();
Region = region;
Edge = edge;
}
///
/// Make a request to the Twilio API
///
///
/// request to make
/// response of the request
public Response Request(Request request)
{
if(_username != null && _password != null){
request.SetAuth(_username, _password);
}
else if(_authstrategy != null){
request.SetAuth(_authstrategy);
}
if (LogLevel == "debug")
LogRequest(request);
if (Region != null)
request.Region = Region;
if (Edge != null)
request.Edge = Edge;
if (UserAgentExtensions != null)
request.UserAgentExtensions = UserAgentExtensions;
Response response;
try
{
response = HttpClient.MakeRequest(request);
if (LogLevel == "debug")
{
Console.WriteLine("response.status: " + response.StatusCode);
Console.WriteLine("response.headers: " + response.Headers);
}
}
catch (Exception clientException)
{
throw new ApiConnectionException(
"Connection Error: " + request.Method + request.ConstructUrl(),
clientException
);
}
return ProcessResponse(response);
}
#if !NET35
///
/// Make a request to the Twilio API
///
///
/// request to make
/// Task that resolves to the response of the request
public async Task RequestAsync(Request request)
{
if(_username != null && _password != null){
request.SetAuth(_username, _password);
}
else if(_authstrategy != null){
request.SetAuth(_authstrategy);
}
if (Region != null)
request.Region = Region;
if (Edge != null)
request.Edge = Edge;
if (UserAgentExtensions != null)
request.UserAgentExtensions = UserAgentExtensions;
Response response;
try
{
response = await HttpClient.MakeRequestAsync(request);
}
catch (Exception clientException)
{
throw new ApiConnectionException(
"Connection Error: " + request.Method + request.ConstructUrl(),
clientException
);
}
return ProcessResponse(response);
}
private static HttpClient DefaultClient()
{
return new SystemNetHttpClient();
}
#else
private static HttpClient DefaultClient()
{
return new WebRequestClient();
}
#endif
private static Response ProcessResponse(Response response)
{
if (response == null)
{
throw new ApiConnectionException("Connection Error: No response received.");
}
if (response.StatusCode >= HttpStatusCode.OK && response.StatusCode < HttpStatusCode.BadRequest)
{
return response;
}
// Deserialize and throw exception
RestException restException = null;
try
{
restException = RestException.FromJson(response.Content);
}
catch (JsonReaderException) { /* Allow null check below to handle */ }
if (restException != null)
{
throw new ApiException(
restException.Code,
(int)response.StatusCode,
restException.Message ?? "Unable to make request, " + response.StatusCode,
restException.MoreInfo,
restException.Details
);
}
// Try to deserialize as RFC 9457 format first (RestApiStandardException)
RestApiStandardException restApiStandardException = null;
try
{
restApiStandardException = RestApiStandardException.FromJson(response.Content);
}
catch (JsonReaderException) { /* Allow fallback to legacy format */ }
// Check if it's a valid RFC 9457 response (has 'type' field)
if (restApiStandardException != null)
{
throw new ApiStandardException(
restApiStandardException.Code,
(int)response.StatusCode,
restApiStandardException.Type,
restApiStandardException.Title,
restApiStandardException.Detail,
restApiStandardException.Instance,
restApiStandardException.Errors
);
}
// If both RestException and RestApiStandardException are null and throw default exception
throw new ApiException("Api Error: " + response.StatusCode + " - " + (response.Content ?? "[no content]"));
}
///
/// Test if your environment is impacted by a TLS or certificate change
/// by sending an HTTP request to the test endpoint tls-test.twilio.com:443
/// It's a bit easier to call this method from TwilioClient.ValidateSslCertificate().
///
public static void ValidateSslCertificate()
{
ValidateSslCertificate(DefaultClient());
}
///
/// Test that this application can use updated SSL certificates on
/// tls-test.twilio.com:443. Generally, you'll want to use the version of this
/// function that takes no parameters unless you have a reason not to.
///
///
/// HTTP Client to use for testing the request
public static void ValidateSslCertificate(HttpClient client)
{
Request request = new Request("GET", "tls-test", ":443/", null);
try
{
Response response = client.MakeRequest(request);
if (!response.StatusCode.Equals(HttpStatusCode.OK))
{
throw new CertificateValidationException(
"Unexpected response from certificate endpoint",
request,
response
);
}
}
catch (CertificateValidationException e)
{
throw e;
}
catch (Exception e)
{
throw new CertificateValidationException(
"Connection to tls-test.twilio.com:443 failed",
e,
request
);
}
}
///
/// Format request information when LogLevel is set to debug
///
///
/// HTTP request
private static void LogRequest(Request request)
{
Console.WriteLine("-- BEGIN Twilio API Request --");
Console.WriteLine("request.method: " + request.Method);
Console.WriteLine("request.URI: " + request.Uri);
if (request.QueryParams != null)
{
request.QueryParams.ForEach(parameter => Console.WriteLine(parameter.Key + ":" + parameter.Value));
}
if (request.HeaderParams != null)
{
for (int i = 0; i < request.HeaderParams.Count; i++)
{
var lowercaseHeader = request.HeaderParams[i].Key.ToLower();
if (lowercaseHeader.Contains("authorization") == false)
{
Console.WriteLine(request.HeaderParams[i].Key + ":" + request.HeaderParams[i].Value);
}
}
}
Console.WriteLine("-- END Twilio API Request --");
}
}
}