using System;
using System.Collections.Generic;
using System.Threading;
using Best.HTTP.Request.Authentication;
using Best.HTTP.Shared.Logger;
using Best.HTTP.Shared.Streams;
namespace Best.HTTP.Proxies
{
///
/// Represents parameters used when connecting through a proxy server.
///
///
/// The ProxyConnectParameters struct defines the parameters required when initiating a connection
/// through a proxy server. It includes information about the proxy, target URI, and callbacks for success and error handling.
/// This struct is commonly used during the negotiation steps in the class.
///
public struct ProxyConnectParameters
{
///
/// The maximum number of authentication attempts allowed during proxy connection.
///
public const int MaxAuthenticationAttempts = 1;
///
/// The proxy server through which the connection is established.
///
public Proxy proxy;
///
/// The stream used for communication with the proxy server.
///
public PeekableContentProviderStream stream;
///
/// The target URI to reach through the proxy server.
///
public Uri uri;
///
/// A cancellation token that allows canceling the proxy connection operation.
///
public CancellationToken token;
///
/// The number of authentication attempts made during proxy connection.
///
public int AuthenticationAttempts;
///
/// Gets or sets a value indicating whether to create a proxy tunnel.
///
///
/// A proxy tunnel, also known as a TCP tunnel, is established when communication between the client and the target server
/// needs to be relayed through the proxy without modification. Setting this field to true indicates the intention
/// to create a tunnel, allowing the data to pass through the proxy without interpretation or alteration by the proxy.
/// This is typically used for protocols like HTTPS, where end-to-end encryption is desired, and the proxy should act as a
/// pass-through conduit.
///
public bool createTunel;
///
/// The logging context for debugging purposes.
///
public LoggingContext context;
///
/// A callback to be executed upon successful proxy connection.
///
public Action OnSuccess;
///
/// A callback to be executed upon encountering an error during proxy connection.
///
///
/// The callback includes parameters for the current connection parameters, the encountered exception,
/// and a flag indicating whether the connection should be retried for authentication.
///
public Action OnError;
}
///
/// Base class for proxy implementations, providing common proxy configuration and behavior.
///
///
/// The Proxy class serves as the base class for various proxy client implementations,
/// such as and . It provides a foundation for configuring proxy settings and handling
/// proxy-related functionality common to all proxy types, like connecting to a proxy, setting up a request to go through the proxy
/// and deciding whether an address is usable with the proxy or the plugin must connect directly.
///
public abstract class Proxy
{
///
/// Address of the proxy server. It has to be in the http://proxyaddress:port form.
///
public Uri Address { get; set; }
///
/// Credentials for authenticating with the proxy server.
///
public Credentials Credentials { get; set; }
///
/// List of exceptions for which the proxy should not be used. Elements of this list are compared to the Host (DNS or IP address) part of the uri.
///
public List Exceptions { get; set; }
///
/// Initializes a new instance of the Proxy class with the specified proxy address and credentials.
///
/// The address of the proxy server.
/// The credentials for proxy authentication.
internal Proxy(Uri address, Credentials credentials)
{
this.Address = address;
this.Credentials = credentials;
}
///
/// Initiates a connection through the proxy server. Used during the negotiation steps.
///
/// Parameters for the proxy connection.
internal abstract void BeginConnect(ProxyConnectParameters parameters);
///
/// Gets the request path to be used for proxy communication. In some cases with HTTPProxy, the request must send the whole uri as the request path.
///
/// The target URI.
/// The request path for proxy communication.
public abstract string GetRequestPath(Uri uri);
///
/// Sets up an HTTP request to use the proxy as needed.
///
/// The HTTP request to set up.
/// true if the request should use the proxy; otherwise, false.
internal abstract bool SetupRequest(HTTPRequest request);
///
/// Determines whether the proxy should be used for a specific address based on the configured exceptions.
///
/// The address to check for proxy usage.
/// true if the proxy should be used for the address; otherwise, false.
public bool UseProxyForAddress(Uri address)
{
if (this.Exceptions == null)
return true;
string host = address.Host;
// https://github.com/httplib2/httplib2/issues/94
// If domain starts with a dot (example: .example.com):
// 1. Use endswith to match any subdomain (foo.example.com should match)
// 2. Remove the dot and do an exact match (example.com should also match)
//
// If domain does not start with a dot (example: example.com):
// 1. It should be an exact match.
for (int i = 0; i < this.Exceptions.Count; ++i)
{
var exception = this.Exceptions[i];
if (exception == "*")
return false;
if (exception.StartsWith("."))
{
// Use EndsWith to match any subdomain
if (host.EndsWith(exception))
return false;
// Remove the dot and
exception = exception.Substring(1);
}
// do an exact match
if (host.Equals(exception))
return false;
}
return true;
}
}
}