diff --git a/README.md b/README.md index 52a1e2686..0081ef80a 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ A lightweight HTTP(S) proxy server written in C#. Report bugs or raise issues here. For programming help use [StackOverflow](http://stackoverflow.com/questions/tagged/titanium-web-proxy) with the tag Titanium-Web-Proxy. -* [API Documentation](https://justcoding121.github.io/Titanium-Web-Proxy/docs/api/Titanium.Web.Proxy.ProxyServer.html) +* [API Documentation](https://justcoding121.github.io/titanium-web-proxy/docs/api/Titanium.Web.Proxy.ProxyServer.html) * [Wiki & Contribution guidelines](https://github.com/justcoding121/Titanium-Web-Proxy/wiki) ### Features -* Multi-threaded fully asynchronous proxy employing server connection pooling, certificate cache, and buffer pooling -* View/modify/redirect/block requests and responses +* Multithreaded and asynchronous proxy employing server connection pooling, certificate cache, and buffer pooling +* View, modify, redirect and block requests or responses * Supports mutual SSL authentication, proxy authentication & automatic upstream proxy detection -* Kerberos/NTLM authentication over HTTP protocols for windows domain +* Supports kerberos, NTLM authentication over HTTP protocols on windows domain controlled networks * SOCKS4/5 Proxy support ### Installation @@ -32,6 +32,21 @@ Supports * .NET Standard 2.0 or above * .NET Framework 4.5 or above + +### Note to contributors + +#### Road map + +* Fix [outstanding bugs](https://github.com/justcoding121/Titanium-Web-Proxy/issues?q=is%3Aopen+is%3Aissue+label%3Abug) +* Support reading request and response body as stream [#823](https://github.com/justcoding121/Titanium-Web-Proxy/issues/823) +* Stop throwing new exceptions [#634](https://github.com/justcoding121/Titanium-Web-Proxy/issues/634) +* Support HTTP 2.0 + +#### Collaborators + +The owner of this project, [justcoding121](https://github.com/justcoding121), is considered to be inactive from this project due to his busy work schedule. However, we have a collaborator listed below who time and again shows up to maintain this project. Please create pull requests prioritizing bug fixes for the attention of collaborators. + +* [honfika](https://github.com/honfika) ### Development environment @@ -57,7 +72,7 @@ Setup HTTP proxy: var proxyServer = new ProxyServer(); // locally trust root certificate used by this proxy -proxyServer.CertificateManager.TrustRootCertificate = true; +proxyServer.CertificateManager.TrustRootCertificate(true); // optionally set the Certificate Engine // Under Mono only BouncyCastle will be supported @@ -150,11 +165,11 @@ public async Task OnRequest(object sender, SessionEventArgs e) { // Get/Set request body bytes byte[] bodyBytes = await e.GetRequestBody(); - await e.SetRequestBody(bodyBytes); + e.SetRequestBody(bodyBytes); // Get/Set request body as string string bodyString = await e.GetRequestBodyAsString(); - await e.SetRequestBodyString(bodyString); + e.SetRequestBodyString(bodyString); // store request // so that you can find it from response handler @@ -195,10 +210,10 @@ public async Task OnResponse(object sender, SessionEventArgs e) if (e.HttpClient.Response.ContentType != null && e.HttpClient.Response.ContentType.Trim().ToLower().Contains("text/html")) { byte[] bodyBytes = await e.GetResponseBody(); - await e.SetResponseBody(bodyBytes); + e.SetResponseBody(bodyBytes); string body = await e.GetResponseBodyAsString(); - await e.SetResponseBodyString(body); + e.SetResponseBodyString(body); } } } @@ -227,16 +242,6 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs return Task.CompletedTask; } ``` -### Note to contributors - -#### Road map - -* Support HTTP 2.0 - -#### Collaborators - -* [honfika](https://github.com/honfika) - **Console example application screenshot** diff --git a/appveyor.yml b/appveyor.yml index 4d4c9500d..71cbcb40a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -68,7 +68,7 @@ environment: github_email: secure: iBJZGqxyiHVNeYI0uIW+MdGd3I3pg8brJtETNRkKe/A= nuget_access_token: - secure: 65rklofkoUWJzPPja621kXlxrruHemmbREnIX7QgXK0cydW412yYMZJQsN42Js27 + secure: OO7/GRKfrScfh1Z92UxFEuBuXUM0WlPv2RMzCL6WnYAMByhiyWC2e1ezy34r/Lc6 deploy: - provider: GitHub auth_token: $(github_access_token) diff --git a/docs/api/Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html b/docs/api/Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html index 3d0eb82b9..fec0dbc38 100644 --- a/docs/api/Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html +++ b/docs/api/Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html @@ -158,7 +158,7 @@
Type Parameters
Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html b/docs/api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html index 5a305146e..328b53484 100644 --- a/docs/api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html +++ b/docs/api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html @@ -163,6 +163,9 @@
    Inherited Members
    SessionEventArgsBase.Exception
    +
    + SessionEventArgsBase.Dispose() +
    SessionEventArgsBase.DataSent
    @@ -365,22 +368,53 @@

    Methods

    | - Improve this Doc + Improve this Doc - View Source + View Source -

    Dispose()

    -

    Implement any cleanup here

    -
    +

    Dispose(Boolean)

    +
    Declaration
    -
    public override void Dispose()
    +
    protected override void Dispose(bool disposing)
    +
    Parameters
    + + + + + + + + + + + + + + + +
    TypeNameDescription
    Booleandisposing
    Overrides
    -
    SessionEventArgsBase.Dispose()
    +
    SessionEventArgsBase.Dispose(Boolean)
    + + | + Improve this Doc + + + View Source + + +

    Finalize()

    +
    +
    +
    Declaration
    +
    +
    protected void Finalize()
    +
    | Improve this Doc diff --git a/docs/api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html b/docs/api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html index 968420f21..4f8c02e5f 100644 --- a/docs/api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html +++ b/docs/api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html @@ -765,23 +765,69 @@

    Methods Improve this Doc - View Source + View Source

    Dispose()

    -

    Implements cleanup here.

    -
    +
    +
    +
    Declaration
    +
    +
    public void Dispose()
    +
    + + | + Improve this Doc + + + View Source + + +

    Dispose(Boolean)

    +
    +
    +
    Declaration
    +
    +
    protected virtual void Dispose(bool disposing)
    +
    +
    Parameters
    + + + + + + + + + + + + + + + +
    TypeNameDescription
    Booleandisposing
    + + | + Improve this Doc + + + View Source + + +

    Finalize()

    +
    Declaration
    -
    public virtual void Dispose()
    +
    protected void Finalize()
    | Improve this Doc - View Source + View Source

    TerminateSession()

    @@ -799,7 +845,7 @@

    Events Improve this Doc - View Source + View Source

    DataReceived

    Fired when data is received within this session from client/server.

    @@ -829,7 +875,7 @@
    Event Type
    Improve this Doc - View Source + View Source

    DataSent

    Fired when data is sent within this session to server/client.

    diff --git a/docs/api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html b/docs/api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html index fd5b029b8..4255e29a3 100644 --- a/docs/api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html +++ b/docs/api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html @@ -160,6 +160,9 @@
    Inherited Members
    SessionEventArgsBase.Exception
    +
    + SessionEventArgsBase.Dispose(Boolean) +
    SessionEventArgsBase.Dispose()
    diff --git a/docs/api/Titanium.Web.Proxy.Http.HttpWebClient.html b/docs/api/Titanium.Web.Proxy.Http.HttpWebClient.html index f258285d1..c5cdd59ef 100644 --- a/docs/api/Titanium.Web.Proxy.Http.HttpWebClient.html +++ b/docs/api/Titanium.Web.Proxy.Http.HttpWebClient.html @@ -128,7 +128,7 @@

    Properties Improve this Doc - View Source + View Source

    ConnectRequest

    @@ -159,7 +159,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    IsHttps

    @@ -190,7 +190,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    ProcessId

    @@ -222,7 +222,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Request

    @@ -253,7 +253,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Response

    @@ -284,7 +284,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    UpStreamEndPoint

    @@ -315,7 +315,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    UserData

    @@ -352,7 +352,7 @@
    Property Value
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.Network.Certificate.AlternativeNameType.html b/docs/api/Titanium.Web.Proxy.Network.Certificate.AlternativeNameType.html index c32bae68a..471647f0f 100644 --- a/docs/api/Titanium.Web.Proxy.Network.Certificate.AlternativeNameType.html +++ b/docs/api/Titanium.Web.Proxy.Network.Certificate.AlternativeNameType.html @@ -154,7 +154,7 @@

    Fields Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.Network.Certificate.EncodingType.html b/docs/api/Titanium.Web.Proxy.Network.Certificate.EncodingType.html index ee1f5027c..13817d3d0 100644 --- a/docs/api/Titanium.Web.Proxy.Network.Certificate.EncodingType.html +++ b/docs/api/Titanium.Web.Proxy.Network.Certificate.EncodingType.html @@ -202,7 +202,7 @@

    Fields Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.Network.CertificateManager.html b/docs/api/Titanium.Web.Proxy.Network.CertificateManager.html index 22527e76a..3a7ea52f4 100644 --- a/docs/api/Titanium.Web.Proxy.Network.CertificateManager.html +++ b/docs/api/Titanium.Web.Proxy.Network.CertificateManager.html @@ -132,7 +132,7 @@

    Properties Improve this Doc - View Source + View Source

    CertificateCacheTimeOutMinutes

    @@ -163,7 +163,7 @@
    Property Value
    Improve this Doc
    - View Source + View Source

    CertificateEngine

    @@ -196,7 +196,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    CertificateStorage

    @@ -229,7 +229,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    CertificateValidDays

    @@ -261,7 +261,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    OverwritePfxFile

    @@ -293,7 +293,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    PfxFilePath

    @@ -328,7 +328,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    PfxPassword

    @@ -360,7 +360,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    RootCertificate

    @@ -391,7 +391,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    RootCertificateIssuerName

    @@ -423,7 +423,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    RootCertificateName

    @@ -458,7 +458,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    SaveFakeCertificates

    @@ -490,7 +490,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    StorageFlag

    @@ -523,7 +523,7 @@

    Methods Improve this Doc - View Source + View Source

    ClearRootCertificate()

    @@ -539,7 +539,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    CreateRootCertificate(Boolean)

    @@ -589,7 +589,7 @@
    Returns
    Improve this Doc - View Source + View Source

    CreateServerCertificate(String)

    @@ -637,12 +637,11 @@
    Returns
    Improve this Doc - View Source + View Source

    Dispose()

    -

    Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

    -
    +
    Declaration
    @@ -653,7 +652,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    EnsureRootCertificate()

    @@ -670,7 +669,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    EnsureRootCertificate(Boolean, Boolean, Boolean)

    @@ -715,12 +714,27 @@
    Parameters
    + + | + Improve this Doc + + + View Source + + +

    Finalize()

    +
    +
    +
    Declaration
    +
    +
    protected void Finalize()
    +
    | Improve this Doc - View Source + View Source

    IsRootCertificateMachineTrusted()

    @@ -751,7 +765,7 @@
    Returns
    Improve this Doc - View Source + View Source

    IsRootCertificateUserTrusted()

    @@ -782,7 +796,7 @@
    Returns
    Improve this Doc - View Source + View Source

    LoadRootCertificate()

    @@ -813,7 +827,7 @@
    Returns
    Improve this Doc - View Source + View Source

    LoadRootCertificate(String, String, Boolean, X509KeyStorageFlags)

    @@ -882,7 +896,7 @@
    Returns
    Improve this Doc - View Source + View Source

    RemoveTrustedRootCertificate(Boolean)

    @@ -917,7 +931,7 @@
    Parameters
    Improve this Doc - View Source + View Source

    RemoveTrustedRootCertificateAsAdmin(Boolean)

    @@ -966,7 +980,7 @@
    Returns
    Improve this Doc - View Source + View Source

    TrustRootCertificate(Boolean)

    @@ -1000,7 +1014,7 @@
    Parameters
    Improve this Doc - View Source + View Source

    TrustRootCertificateAsAdmin(Boolean)

    diff --git a/docs/api/Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html b/docs/api/Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html index e1f4a633a..920440e4a 100644 --- a/docs/api/Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html +++ b/docs/api/Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html @@ -131,7 +131,7 @@

    Methods Improve this Doc - View Source + View Source

    Clear()

    @@ -146,7 +146,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    LoadCertificate(String, X509KeyStorageFlags)

    @@ -199,7 +199,7 @@
    Returns
    Improve this Doc - View Source + View Source

    LoadRootCertificate(String, String, X509KeyStorageFlags)

    @@ -256,7 +256,7 @@
    Returns
    Improve this Doc - View Source + View Source

    SaveCertificate(String, X509Certificate2)

    @@ -294,7 +294,7 @@
    Parameters
    Improve this Doc - View Source + View Source

    SaveRootCertificate(String, String, X509Certificate2)

    @@ -346,7 +346,7 @@

    Implements

    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.Network.ICertificateCache.html b/docs/api/Titanium.Web.Proxy.Network.ICertificateCache.html index d1b7b2aba..02446e45b 100644 --- a/docs/api/Titanium.Web.Proxy.Network.ICertificateCache.html +++ b/docs/api/Titanium.Web.Proxy.Network.ICertificateCache.html @@ -98,7 +98,7 @@

    Methods Improve this Doc - View Source + View Source

    Clear()

    @@ -114,7 +114,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    LoadCertificate(String, X509KeyStorageFlags)

    @@ -167,7 +167,7 @@
    Returns
    Improve this Doc - View Source + View Source

    LoadRootCertificate(String, String, X509KeyStorageFlags)

    @@ -225,7 +225,7 @@
    Returns
    Improve this Doc - View Source + View Source

    SaveCertificate(String, X509Certificate2)

    @@ -263,7 +263,7 @@
    Parameters
    Improve this Doc - View Source + View Source

    SaveRootCertificate(String, String, X509Certificate2)

    @@ -312,7 +312,7 @@
    Parameters
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.ProxyServer.html b/docs/api/Titanium.Web.Proxy.ProxyServer.html index b92f3a0f2..986680694 100644 --- a/docs/api/Titanium.Web.Proxy.ProxyServer.html +++ b/docs/api/Titanium.Web.Proxy.ProxyServer.html @@ -1121,7 +1121,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    ThreadPoolWorkerThread

    @@ -1248,7 +1248,7 @@

    Methods Improve this Doc - View Source + View Source

    AddEndPoint(ProxyEndPoint)

    @@ -1282,7 +1282,7 @@
    Parameters
    Improve this Doc - View Source + View Source

    DisableAllSystemProxies()

    @@ -1298,7 +1298,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    DisableSystemHttpProxy()

    @@ -1314,7 +1314,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    DisableSystemHttpsProxy()

    @@ -1330,7 +1330,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    DisableSystemProxy(ProxyProtocolType)

    @@ -1363,23 +1363,69 @@
    Parameters
    Improve this Doc - View Source + View Source

    Dispose()

    -

    Dispose the Proxy instance.

    -
    +
    Declaration
    public void Dispose()
    + + | + Improve this Doc + + + View Source + + +

    Dispose(Boolean)

    +
    +
    +
    Declaration
    +
    +
    protected virtual void Dispose(bool disposing)
    +
    +
    Parameters
    + + + + + + + + + + + + + + + +
    TypeNameDescription
    Booleandisposing
    + + | + Improve this Doc + + + View Source + + +

    Finalize()

    +
    +
    +
    Declaration
    +
    +
    protected void Finalize()
    +
    | Improve this Doc - View Source + View Source

    RemoveEndPoint(ProxyEndPoint)

    @@ -1414,7 +1460,7 @@
    Parameters
    Improve this Doc - View Source + View Source

    RestoreOriginalProxySettings()

    @@ -1430,7 +1476,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    SetAsSystemHttpProxy(ExplicitProxyEndPoint)

    @@ -1464,7 +1510,7 @@
    Parameters
    Improve this Doc - View Source + View Source

    SetAsSystemHttpsProxy(ExplicitProxyEndPoint)

    @@ -1498,7 +1544,7 @@
    Parameters
    Improve this Doc - View Source + View Source

    SetAsSystemProxy(ExplicitProxyEndPoint, ProxyProtocolType)

    @@ -1538,7 +1584,7 @@
    Parameters
    Improve this Doc - View Source + View Source

    Start()

    @@ -1554,7 +1600,7 @@
    Declaration
    Improve this Doc - View Source + View Source

    Stop()

    @@ -1572,7 +1618,7 @@

    Events Improve this Doc - View Source + View Source

    AfterResponse

    Intercept after response event from server.

    @@ -1632,7 +1678,7 @@
    Event Type
    Improve this Doc - View Source + View Source

    BeforeResponse

    Intercept response event from server.

    @@ -1722,7 +1768,7 @@
    Event Type
    Improve this Doc - View Source + View Source

    OnClientConnectionCreate

    Customize TcpClient used for client connection upon create.

    @@ -1752,7 +1798,7 @@
    Event Type
    Improve this Doc - View Source + View Source

    OnServerConnectionCreate

    Customize TcpClient used for server connection upon create.

    diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool.html b/docs/api/Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool.html index 40a553c87..67ae240cb 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool.html @@ -106,7 +106,7 @@

    Properties Improve this Doc - View Source + View Source

    BufferSize

    @@ -138,7 +138,7 @@

    Methods Improve this Doc - View Source + View Source

    GetBuffer()

    @@ -168,7 +168,7 @@
    Returns
    Improve this Doc - View Source + View Source

    GetBuffer(Int32)

    @@ -215,7 +215,7 @@
    Returns
    Improve this Doc - View Source + View Source

    ReturnBuffer(Byte[])

    @@ -253,7 +253,7 @@
    Parameters
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.ClientHelloInfo.html b/docs/api/Titanium.Web.Proxy.StreamExtended.ClientHelloInfo.html index 21e4c7724..35bd109c6 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.ClientHelloInfo.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.ClientHelloInfo.html @@ -125,7 +125,7 @@

    Properties Improve this Doc - View Source + View Source

    Ciphers

    @@ -155,7 +155,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    CompressionData

    @@ -185,7 +185,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Extensions

    @@ -215,7 +215,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    HandshakeVersion

    @@ -245,7 +245,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    MajorVersion

    @@ -275,7 +275,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    MinorVersion

    @@ -305,7 +305,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Random

    @@ -335,7 +335,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    SessionId

    @@ -365,7 +365,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    SslProtocol

    @@ -395,7 +395,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Time

    @@ -427,7 +427,7 @@

    Methods Improve this Doc - View Source + View Source

    ToString()

    @@ -467,7 +467,7 @@
    Overrides
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.Models.SslExtension.html b/docs/api/Titanium.Web.Proxy.StreamExtended.Models.SslExtension.html index 50f0e8a06..d8e9492e8 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.Models.SslExtension.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.Models.SslExtension.html @@ -128,7 +128,7 @@

    Constructors Improve this Doc - View Source + View Source

    SslExtension(Int32, String, String, Int32)

    @@ -182,7 +182,7 @@

    Properties Improve this Doc - View Source + View Source

    Data

    @@ -214,7 +214,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Name

    @@ -246,7 +246,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Position

    @@ -278,7 +278,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Value

    @@ -316,7 +316,7 @@
    Property Value
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs.html b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs.html index 563c6acb1..5d3789ad7 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs.html @@ -132,7 +132,7 @@

    Constructors Improve this Doc - View Source + View Source

    DataEventArgs(Byte[], Int32, Int32)

    @@ -176,7 +176,7 @@

    Properties Improve this Doc - View Source + View Source

    Buffer

    @@ -207,7 +207,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Count

    @@ -238,7 +238,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Offset

    @@ -275,7 +275,7 @@
    Property Value
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html index 4e2a4d0c7..f34a871e7 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html @@ -110,18 +110,18 @@

    Methods

    | - Improve this Doc + Improve this Doc - View Source + View Source -

    CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Action<Byte[], Int32, Int32>, CancellationToken)

    +

    CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Boolean, SessionEventArgs, CancellationToken)

    Declaration
    -
    Task CopyBodyAsync(IHttpStreamWriter writer, bool isChunked, long contentLength, Action<byte[], int, int> onCopy, CancellationToken cancellationToken)
    +
    Task CopyBodyAsync(IHttpStreamWriter writer, bool isChunked, long contentLength, bool isRequest, SessionEventArgs args, CancellationToken cancellationToken)
    Parameters
    @@ -149,8 +149,13 @@
    Parameters
    - - + + + + + + + @@ -180,7 +185,7 @@
    Returns
    Improve this Doc - View Source + View Source

    Read(Byte[], Int32, Int32)

    @@ -237,7 +242,7 @@
    Returns
    Improve this Doc - View Source + View Source

    ReadAsync(Byte[], Int32, Int32, CancellationToken)

    @@ -305,7 +310,7 @@
    Returns
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html index fa38d2dfa..430e75b3a 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html @@ -92,6 +92,38 @@
    Synt
    public interface IHttpStreamWriter
    +

    Properties +

    + + | + Improve this Doc + + + View Source + + +

    IsNetworkStream

    +
    +
    +
    Declaration
    +
    +
    bool IsNetworkStream { get; }
    +
    +
    Property Value
    +
    Action<Byte[], Int32, Int32>onCopyBooleanisRequest
    SessionEventArgsargs
    + + + + + + + + + + + + +
    TypeDescription
    Boolean

    Methods

    @@ -99,7 +131,7 @@

    Methods Improve this Doc - View Source + View Source

    Write(Byte[], Int32, Int32)

    @@ -141,7 +173,7 @@
    Parameters
    Improve this Doc
    - View Source + View Source

    WriteAsync(Byte[], Int32, Int32, CancellationToken)

    @@ -203,7 +235,7 @@
    Returns
    Improve this Doc - View Source + View Source

    WriteLineAsync(String, CancellationToken)

    @@ -255,7 +287,7 @@
    Returns
    Improve this Doc - View Source + View Source

    WriteLineAsync(CancellationToken)

    @@ -308,7 +340,7 @@
    Returns
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html index dcdbd4c30..1241e63e7 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html @@ -98,7 +98,7 @@

    Properties Improve this Doc - View Source + View Source

    DataAvailable

    @@ -130,7 +130,7 @@

    Methods Improve this Doc - View Source + View Source

    FillBufferAsync(CancellationToken)

    @@ -178,7 +178,7 @@
    Returns
    Improve this Doc - View Source + View Source

    ReadByteFromBuffer()

    @@ -208,7 +208,7 @@
    Returns
    Improve this Doc - View Source + View Source

    ReadLineAsync(CancellationToken)

    @@ -262,7 +262,7 @@
    Returns
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IPeekStream.html b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IPeekStream.html index 01337ea97..839136e1b 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IPeekStream.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.IPeekStream.html @@ -98,7 +98,7 @@

    Methods Improve this Doc - View Source + View Source

    PeekByteAsync(Int32, CancellationToken)

    @@ -153,7 +153,7 @@
    Returns
    Improve this Doc - View Source + View Source

    PeekByteFromBuffer(Int32)

    @@ -218,7 +218,7 @@
    Exceptions
    Improve this Doc - View Source + View Source

    PeekBytesAsync(Byte[], Int32, Int32, Int32, CancellationToken)

    @@ -297,7 +297,7 @@
    Returns
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.TaskResult-1.html b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.TaskResult-1.html index 7dbcf91a9..516985027 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.TaskResult-1.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.TaskResult-1.html @@ -147,7 +147,7 @@

    Constructors Improve this Doc - View Source + View Source

    TaskResult(Task<T>, Object)

    @@ -186,7 +186,7 @@

    Properties Improve this Doc - View Source + View Source

    AsyncState

    @@ -216,7 +216,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    AsyncWaitHandle

    @@ -246,7 +246,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    CompletedSynchronously

    @@ -276,7 +276,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    IsCompleted

    @@ -306,7 +306,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Result

    @@ -346,7 +346,7 @@

    Implements

    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.TaskResult.html b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.TaskResult.html index ecbf81a7b..f6a0d9acb 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.Network.TaskResult.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.Network.TaskResult.html @@ -132,7 +132,7 @@

    Constructors Improve this Doc - View Source + View Source

    TaskResult(Task, Object)

    @@ -171,7 +171,7 @@

    Properties Improve this Doc - View Source + View Source

    AsyncState

    @@ -201,7 +201,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    AsyncWaitHandle

    @@ -231,7 +231,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    CompletedSynchronously

    @@ -261,7 +261,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    IsCompleted

    @@ -293,7 +293,7 @@

    Methods Improve this Doc - View Source + View Source

    GetResult()

    @@ -318,7 +318,7 @@

    Implements

    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/api/Titanium.Web.Proxy.StreamExtended.ServerHelloInfo.html b/docs/api/Titanium.Web.Proxy.StreamExtended.ServerHelloInfo.html index 4a407c721..1bdaf39de 100644 --- a/docs/api/Titanium.Web.Proxy.StreamExtended.ServerHelloInfo.html +++ b/docs/api/Titanium.Web.Proxy.StreamExtended.ServerHelloInfo.html @@ -125,7 +125,7 @@

    Constructors Improve this Doc - View Source + View Source

    ServerHelloInfo(Int32, Int32, Int32, Byte[], Byte[], Int32, Int32)

    @@ -189,7 +189,7 @@

    Properties Improve this Doc - View Source + View Source

    CipherSuite

    @@ -219,7 +219,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    CompressionMethod

    @@ -249,7 +249,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Extensions

    @@ -279,7 +279,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    HandshakeVersion

    @@ -309,7 +309,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    MajorVersion

    @@ -339,7 +339,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    MinorVersion

    @@ -369,7 +369,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Random

    @@ -399,7 +399,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    SessionId

    @@ -429,7 +429,7 @@
    Property Value
    Improve this Doc - View Source + View Source

    Time

    @@ -461,7 +461,7 @@

    Methods Improve this Doc - View Source + View Source

    ToString()

    @@ -501,7 +501,7 @@
    Overrides
    Improve this Doc
  • - View Source + View Source
  • diff --git a/docs/index.json b/docs/index.json index 71a21e55e..fc49c5949 100644 --- a/docs/index.json +++ b/docs/index.json @@ -42,17 +42,17 @@ "api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html": { "href": "api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html", "title": "Class SessionEventArgs | Titanium Web Proxy", - "keywords": "Class SessionEventArgs Holds info related to a single proxy session (single request/response sequence). A proxy session is bounded to a single connection from client. A proxy session ends when client terminates connection to proxy or when server terminates connection from proxy. Inheritance Object EventArgs ProxyEventArgsBase SessionEventArgsBase SessionEventArgs Implements IDisposable Inherited Members SessionEventArgsBase.ClientConnectionId SessionEventArgsBase.ServerConnectionId SessionEventArgsBase.BufferPool SessionEventArgsBase.ExceptionFunc SessionEventArgsBase.TimeLine SessionEventArgsBase.UserData SessionEventArgsBase.EnableWinAuth SessionEventArgsBase.IsHttps SessionEventArgsBase.ClientLocalEndPoint SessionEventArgsBase.ClientRemoteEndPoint SessionEventArgsBase.ClientEndPoint SessionEventArgsBase.HttpClient SessionEventArgsBase.WebSession SessionEventArgsBase.CustomUpStreamProxy SessionEventArgsBase.CustomUpStreamProxyUsed SessionEventArgsBase.ProxyEndPoint SessionEventArgsBase.LocalEndPoint SessionEventArgsBase.IsTransparent SessionEventArgsBase.IsSocks SessionEventArgsBase.Exception SessionEventArgsBase.DataSent SessionEventArgsBase.DataReceived SessionEventArgsBase.TerminateSession() ProxyEventArgsBase.ClientUserData EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public class SessionEventArgs : SessionEventArgsBase, IDisposable Properties | Improve this Doc View Source IsPromise Is this session a HTTP/2 promise? Declaration public bool IsPromise { get; } Property Value Type Description Boolean | Improve this Doc View Source ReRequest Should we send the request again ? Declaration public bool ReRequest { get; set; } Property Value Type Description Boolean | Improve this Doc View Source WebSocketDecoder Declaration [Obsolete(\"Use [WebSocketDecoderReceive] instead\")] public WebSocketDecoder WebSocketDecoder { get; } Property Value Type Description WebSocketDecoder | Improve this Doc View Source WebSocketDecoderReceive Declaration public WebSocketDecoder WebSocketDecoderReceive { get; } Property Value Type Description WebSocketDecoder | Improve this Doc View Source WebSocketDecoderSend Declaration public WebSocketDecoder WebSocketDecoderSend { get; } Property Value Type Description WebSocketDecoder Methods | Improve this Doc View Source Dispose() Implement any cleanup here Declaration public override void Dispose() Overrides SessionEventArgsBase.Dispose() | Improve this Doc View Source GenericResponse(Byte[], HttpStatusCode, IDictionary, Boolean) Before request is made to server respond with the specified byte[], the specified status to client. And then ignore the request. Declaration public void GenericResponse(byte[] result, HttpStatusCode status, IDictionary headers, bool closeServerConnection = false) Parameters Type Name Description Byte [] result The bytes to sent. HttpStatusCode status The HTTP status code. IDictionary < String , HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source GenericResponse(Byte[], HttpStatusCode, IEnumerable, Boolean) Before request is made to server respond with the specified byte[], the specified status to client. And then ignore the request. Declaration public void GenericResponse(byte[] result, HttpStatusCode status, IEnumerable headers, bool closeServerConnection = false) Parameters Type Name Description Byte [] result The bytes to sent. HttpStatusCode status The HTTP status code. IEnumerable < HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source GenericResponse(String, HttpStatusCode, IDictionary, Boolean) Before request is made to server respond with the specified HTML string and the specified status to client. And then ignore the request. Declaration public void GenericResponse(string html, HttpStatusCode status, IDictionary headers, bool closeServerConnection = false) Parameters Type Name Description String html The html content. HttpStatusCode status The HTTP status code. IDictionary < String , HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source GenericResponse(String, HttpStatusCode, IEnumerable, Boolean) Before request is made to server respond with the specified HTML string and the specified status to client. And then ignore the request. Declaration public void GenericResponse(string html, HttpStatusCode status, IEnumerable headers = null, bool closeServerConnection = false) Parameters Type Name Description String html The html content. HttpStatusCode status The HTTP status code. IEnumerable < HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source GetRequestBody(CancellationToken) Gets the request body as bytes. Declaration public Task GetRequestBody(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < Byte []> The body as bytes. | Improve this Doc View Source GetRequestBodyAsString(CancellationToken) Gets the request body as string. Declaration public Task GetRequestBodyAsString(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < String > The body as string. | Improve this Doc View Source GetResponseBody(CancellationToken) Gets the response body as bytes. Declaration public Task GetResponseBody(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < Byte []> The resulting bytes. | Improve this Doc View Source GetResponseBodyAsString(CancellationToken) Gets the response body as string. Declaration public Task GetResponseBodyAsString(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < String > The string body. | Improve this Doc View Source Ok(Byte[], IDictionary, Boolean) Before request is made to server respond with the specified byte[] to client and ignore the request. Declaration public void Ok(byte[] result, IDictionary headers, bool closeServerConnection = false) Parameters Type Name Description Byte [] result The html content bytes. IDictionary < String , HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Ok(Byte[], IEnumerable, Boolean) Before request is made to server respond with the specified byte[] to client and ignore the request. Declaration public void Ok(byte[] result, IEnumerable headers = null, bool closeServerConnection = false) Parameters Type Name Description Byte [] result The html content bytes. IEnumerable < HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Ok(String, IDictionary, Boolean) Before request is made to server respond with the specified HTML string to client and ignore the request. Declaration public void Ok(string html, IDictionary headers, bool closeServerConnection = false) Parameters Type Name Description String html HTML content to sent. IDictionary < String , HttpHeader > headers HTTP response headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Ok(String, IEnumerable, Boolean) Before request is made to server respond with the specified HTML string to client and ignore the request. Declaration public void Ok(string html, IEnumerable headers = null, bool closeServerConnection = false) Parameters Type Name Description String html HTML content to sent. IEnumerable < HttpHeader > headers HTTP response headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Redirect(String, Boolean) Redirect to provided URL. Declaration public void Redirect(string url, bool closeServerConnection = false) Parameters Type Name Description String url The URL to redirect. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Respond(Response, Boolean) Respond with given response object to client. Declaration public void Respond(Response response, bool closeServerConnection = false) Parameters Type Name Description Response response The response object. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source SetRequestBody(Byte[]) Sets the request body. Declaration public void SetRequestBody(byte[] body) Parameters Type Name Description Byte [] body The request body bytes. | Improve this Doc View Source SetRequestBodyString(String) Sets the body with the specified string. Declaration public void SetRequestBodyString(string body) Parameters Type Name Description String body The request body string to set. | Improve this Doc View Source SetResponseBody(Byte[]) Set the response body bytes. Declaration public void SetResponseBody(byte[] body) Parameters Type Name Description Byte [] body The body bytes to set. | Improve this Doc View Source SetResponseBodyString(String) Replace the response body with the specified string. Declaration public void SetResponseBodyString(string body) Parameters Type Name Description String body The body string to set. | Improve this Doc View Source TerminateServerConnection() Terminate the connection to server at the end of this HTTP request/response session. Declaration public void TerminateServerConnection() Events | Improve this Doc View Source MultipartRequestPartSent Occurs when multipart request part sent. Declaration public event EventHandler MultipartRequestPartSent Event Type Type Description EventHandler < MultipartRequestPartSentEventArgs > Implements System.IDisposable" + "keywords": "Class SessionEventArgs Holds info related to a single proxy session (single request/response sequence). A proxy session is bounded to a single connection from client. A proxy session ends when client terminates connection to proxy or when server terminates connection from proxy. Inheritance Object EventArgs ProxyEventArgsBase SessionEventArgsBase SessionEventArgs Implements IDisposable Inherited Members SessionEventArgsBase.ClientConnectionId SessionEventArgsBase.ServerConnectionId SessionEventArgsBase.BufferPool SessionEventArgsBase.ExceptionFunc SessionEventArgsBase.TimeLine SessionEventArgsBase.UserData SessionEventArgsBase.EnableWinAuth SessionEventArgsBase.IsHttps SessionEventArgsBase.ClientLocalEndPoint SessionEventArgsBase.ClientRemoteEndPoint SessionEventArgsBase.ClientEndPoint SessionEventArgsBase.HttpClient SessionEventArgsBase.WebSession SessionEventArgsBase.CustomUpStreamProxy SessionEventArgsBase.CustomUpStreamProxyUsed SessionEventArgsBase.ProxyEndPoint SessionEventArgsBase.LocalEndPoint SessionEventArgsBase.IsTransparent SessionEventArgsBase.IsSocks SessionEventArgsBase.Exception SessionEventArgsBase.Dispose() SessionEventArgsBase.DataSent SessionEventArgsBase.DataReceived SessionEventArgsBase.TerminateSession() ProxyEventArgsBase.ClientUserData EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public class SessionEventArgs : SessionEventArgsBase, IDisposable Properties | Improve this Doc View Source IsPromise Is this session a HTTP/2 promise? Declaration public bool IsPromise { get; } Property Value Type Description Boolean | Improve this Doc View Source ReRequest Should we send the request again ? Declaration public bool ReRequest { get; set; } Property Value Type Description Boolean | Improve this Doc View Source WebSocketDecoder Declaration [Obsolete(\"Use [WebSocketDecoderReceive] instead\")] public WebSocketDecoder WebSocketDecoder { get; } Property Value Type Description WebSocketDecoder | Improve this Doc View Source WebSocketDecoderReceive Declaration public WebSocketDecoder WebSocketDecoderReceive { get; } Property Value Type Description WebSocketDecoder | Improve this Doc View Source WebSocketDecoderSend Declaration public WebSocketDecoder WebSocketDecoderSend { get; } Property Value Type Description WebSocketDecoder Methods | Improve this Doc View Source Dispose(Boolean) Declaration protected override void Dispose(bool disposing) Parameters Type Name Description Boolean disposing Overrides SessionEventArgsBase.Dispose(Boolean) | Improve this Doc View Source Finalize() Declaration protected void Finalize() | Improve this Doc View Source GenericResponse(Byte[], HttpStatusCode, IDictionary, Boolean) Before request is made to server respond with the specified byte[], the specified status to client. And then ignore the request. Declaration public void GenericResponse(byte[] result, HttpStatusCode status, IDictionary headers, bool closeServerConnection = false) Parameters Type Name Description Byte [] result The bytes to sent. HttpStatusCode status The HTTP status code. IDictionary < String , HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source GenericResponse(Byte[], HttpStatusCode, IEnumerable, Boolean) Before request is made to server respond with the specified byte[], the specified status to client. And then ignore the request. Declaration public void GenericResponse(byte[] result, HttpStatusCode status, IEnumerable headers, bool closeServerConnection = false) Parameters Type Name Description Byte [] result The bytes to sent. HttpStatusCode status The HTTP status code. IEnumerable < HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source GenericResponse(String, HttpStatusCode, IDictionary, Boolean) Before request is made to server respond with the specified HTML string and the specified status to client. And then ignore the request. Declaration public void GenericResponse(string html, HttpStatusCode status, IDictionary headers, bool closeServerConnection = false) Parameters Type Name Description String html The html content. HttpStatusCode status The HTTP status code. IDictionary < String , HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source GenericResponse(String, HttpStatusCode, IEnumerable, Boolean) Before request is made to server respond with the specified HTML string and the specified status to client. And then ignore the request. Declaration public void GenericResponse(string html, HttpStatusCode status, IEnumerable headers = null, bool closeServerConnection = false) Parameters Type Name Description String html The html content. HttpStatusCode status The HTTP status code. IEnumerable < HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source GetRequestBody(CancellationToken) Gets the request body as bytes. Declaration public Task GetRequestBody(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < Byte []> The body as bytes. | Improve this Doc View Source GetRequestBodyAsString(CancellationToken) Gets the request body as string. Declaration public Task GetRequestBodyAsString(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < String > The body as string. | Improve this Doc View Source GetResponseBody(CancellationToken) Gets the response body as bytes. Declaration public Task GetResponseBody(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < Byte []> The resulting bytes. | Improve this Doc View Source GetResponseBodyAsString(CancellationToken) Gets the response body as string. Declaration public Task GetResponseBodyAsString(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < String > The string body. | Improve this Doc View Source Ok(Byte[], IDictionary, Boolean) Before request is made to server respond with the specified byte[] to client and ignore the request. Declaration public void Ok(byte[] result, IDictionary headers, bool closeServerConnection = false) Parameters Type Name Description Byte [] result The html content bytes. IDictionary < String , HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Ok(Byte[], IEnumerable, Boolean) Before request is made to server respond with the specified byte[] to client and ignore the request. Declaration public void Ok(byte[] result, IEnumerable headers = null, bool closeServerConnection = false) Parameters Type Name Description Byte [] result The html content bytes. IEnumerable < HttpHeader > headers The HTTP headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Ok(String, IDictionary, Boolean) Before request is made to server respond with the specified HTML string to client and ignore the request. Declaration public void Ok(string html, IDictionary headers, bool closeServerConnection = false) Parameters Type Name Description String html HTML content to sent. IDictionary < String , HttpHeader > headers HTTP response headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Ok(String, IEnumerable, Boolean) Before request is made to server respond with the specified HTML string to client and ignore the request. Declaration public void Ok(string html, IEnumerable headers = null, bool closeServerConnection = false) Parameters Type Name Description String html HTML content to sent. IEnumerable < HttpHeader > headers HTTP response headers. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Redirect(String, Boolean) Redirect to provided URL. Declaration public void Redirect(string url, bool closeServerConnection = false) Parameters Type Name Description String url The URL to redirect. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source Respond(Response, Boolean) Respond with given response object to client. Declaration public void Respond(Response response, bool closeServerConnection = false) Parameters Type Name Description Response response The response object. Boolean closeServerConnection Close the server connection used by request if any? | Improve this Doc View Source SetRequestBody(Byte[]) Sets the request body. Declaration public void SetRequestBody(byte[] body) Parameters Type Name Description Byte [] body The request body bytes. | Improve this Doc View Source SetRequestBodyString(String) Sets the body with the specified string. Declaration public void SetRequestBodyString(string body) Parameters Type Name Description String body The request body string to set. | Improve this Doc View Source SetResponseBody(Byte[]) Set the response body bytes. Declaration public void SetResponseBody(byte[] body) Parameters Type Name Description Byte [] body The body bytes to set. | Improve this Doc View Source SetResponseBodyString(String) Replace the response body with the specified string. Declaration public void SetResponseBodyString(string body) Parameters Type Name Description String body The body string to set. | Improve this Doc View Source TerminateServerConnection() Terminate the connection to server at the end of this HTTP request/response session. Declaration public void TerminateServerConnection() Events | Improve this Doc View Source MultipartRequestPartSent Occurs when multipart request part sent. Declaration public event EventHandler MultipartRequestPartSent Event Type Type Description EventHandler < MultipartRequestPartSentEventArgs > Implements System.IDisposable" }, "api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html": { "href": "api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html", "title": "Class SessionEventArgsBase | Titanium Web Proxy", - "keywords": "Class SessionEventArgsBase Holds info related to a single proxy session (single request/response sequence). A proxy session is bounded to a single connection from client. A proxy session ends when client terminates connection to proxy or when server terminates connection from proxy. Inheritance Object EventArgs ProxyEventArgsBase SessionEventArgsBase SessionEventArgs TunnelConnectSessionEventArgs Implements IDisposable Inherited Members ProxyEventArgsBase.ClientUserData EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public abstract class SessionEventArgsBase : ProxyEventArgsBase, IDisposable Fields | Improve this Doc View Source BufferPool Declaration protected readonly IBufferPool BufferPool Field Value Type Description IBufferPool | Improve this Doc View Source ExceptionFunc Declaration protected readonly ExceptionHandler ExceptionFunc Field Value Type Description ExceptionHandler Properties | Improve this Doc View Source ClientConnectionId Declaration public Guid ClientConnectionId { get; } Property Value Type Description Guid | Improve this Doc View Source ClientEndPoint Declaration [Obsolete(\"Use ClientRemoteEndPoint instead.\")] public IPEndPoint ClientEndPoint { get; } Property Value Type Description IPEndPoint | Improve this Doc View Source ClientLocalEndPoint Client Local End Point. Declaration public IPEndPoint ClientLocalEndPoint { get; } Property Value Type Description IPEndPoint | Improve this Doc View Source ClientRemoteEndPoint Client Remote End Point. Declaration public IPEndPoint ClientRemoteEndPoint { get; } Property Value Type Description IPEndPoint | Improve this Doc View Source CustomUpStreamProxy Gets or sets the custom up stream proxy. Declaration public IExternalProxy CustomUpStreamProxy { get; set; } Property Value Type Description IExternalProxy The custom up stream proxy. | Improve this Doc View Source CustomUpStreamProxyUsed Are we using a custom upstream HTTP(S) proxy? Declaration public IExternalProxy CustomUpStreamProxyUsed { get; } Property Value Type Description IExternalProxy | Improve this Doc View Source EnableWinAuth Enable/disable Windows Authentication (NTLM/Kerberos) for the current session. Declaration public bool EnableWinAuth { get; set; } Property Value Type Description Boolean | Improve this Doc View Source Exception The last exception that happened. Declaration public Exception Exception { get; } Property Value Type Description Exception | Improve this Doc View Source HttpClient The web client used to communicate with server for this session. Declaration public HttpWebClient HttpClient { get; } Property Value Type Description HttpWebClient | Improve this Doc View Source IsHttps Does this session uses SSL? Declaration public bool IsHttps { get; } Property Value Type Description Boolean | Improve this Doc View Source IsSocks Is this a SOCKS endpoint? Declaration public bool IsSocks { get; } Property Value Type Description Boolean | Improve this Doc View Source IsTransparent Is this a transparent endpoint? Declaration public bool IsTransparent { get; } Property Value Type Description Boolean | Improve this Doc View Source LocalEndPoint Declaration [Obsolete(\"Use ProxyEndPoint instead.\")] public ProxyEndPoint LocalEndPoint { get; } Property Value Type Description ProxyEndPoint | Improve this Doc View Source ProxyEndPoint Local endpoint via which we make the request. Declaration public ProxyEndPoint ProxyEndPoint { get; } Property Value Type Description ProxyEndPoint | Improve this Doc View Source ServerConnectionId Declaration public Guid ServerConnectionId { get; } Property Value Type Description Guid | Improve this Doc View Source TimeLine Relative milliseconds for various events. Declaration public Dictionary TimeLine { get; } Property Value Type Description Dictionary < String , DateTime > | Improve this Doc View Source UserData Returns a user data for this request/response session which is same as the user data of HttpClient. Declaration public object UserData { get; set; } Property Value Type Description Object | Improve this Doc View Source WebSession Declaration [Obsolete(\"Use HttpClient instead.\")] public HttpWebClient WebSession { get; } Property Value Type Description HttpWebClient Methods | Improve this Doc View Source Dispose() Implements cleanup here. Declaration public virtual void Dispose() | Improve this Doc View Source TerminateSession() Terminates the session abruptly by terminating client/server connections. Declaration public void TerminateSession() Events | Improve this Doc View Source DataReceived Fired when data is received within this session from client/server. Declaration public event EventHandler DataReceived Event Type Type Description EventHandler < DataEventArgs > | Improve this Doc View Source DataSent Fired when data is sent within this session to server/client. Declaration public event EventHandler DataSent Event Type Type Description EventHandler < DataEventArgs > Implements System.IDisposable" + "keywords": "Class SessionEventArgsBase Holds info related to a single proxy session (single request/response sequence). A proxy session is bounded to a single connection from client. A proxy session ends when client terminates connection to proxy or when server terminates connection from proxy. Inheritance Object EventArgs ProxyEventArgsBase SessionEventArgsBase SessionEventArgs TunnelConnectSessionEventArgs Implements IDisposable Inherited Members ProxyEventArgsBase.ClientUserData EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public abstract class SessionEventArgsBase : ProxyEventArgsBase, IDisposable Fields | Improve this Doc View Source BufferPool Declaration protected readonly IBufferPool BufferPool Field Value Type Description IBufferPool | Improve this Doc View Source ExceptionFunc Declaration protected readonly ExceptionHandler ExceptionFunc Field Value Type Description ExceptionHandler Properties | Improve this Doc View Source ClientConnectionId Declaration public Guid ClientConnectionId { get; } Property Value Type Description Guid | Improve this Doc View Source ClientEndPoint Declaration [Obsolete(\"Use ClientRemoteEndPoint instead.\")] public IPEndPoint ClientEndPoint { get; } Property Value Type Description IPEndPoint | Improve this Doc View Source ClientLocalEndPoint Client Local End Point. Declaration public IPEndPoint ClientLocalEndPoint { get; } Property Value Type Description IPEndPoint | Improve this Doc View Source ClientRemoteEndPoint Client Remote End Point. Declaration public IPEndPoint ClientRemoteEndPoint { get; } Property Value Type Description IPEndPoint | Improve this Doc View Source CustomUpStreamProxy Gets or sets the custom up stream proxy. Declaration public IExternalProxy CustomUpStreamProxy { get; set; } Property Value Type Description IExternalProxy The custom up stream proxy. | Improve this Doc View Source CustomUpStreamProxyUsed Are we using a custom upstream HTTP(S) proxy? Declaration public IExternalProxy CustomUpStreamProxyUsed { get; } Property Value Type Description IExternalProxy | Improve this Doc View Source EnableWinAuth Enable/disable Windows Authentication (NTLM/Kerberos) for the current session. Declaration public bool EnableWinAuth { get; set; } Property Value Type Description Boolean | Improve this Doc View Source Exception The last exception that happened. Declaration public Exception Exception { get; } Property Value Type Description Exception | Improve this Doc View Source HttpClient The web client used to communicate with server for this session. Declaration public HttpWebClient HttpClient { get; } Property Value Type Description HttpWebClient | Improve this Doc View Source IsHttps Does this session uses SSL? Declaration public bool IsHttps { get; } Property Value Type Description Boolean | Improve this Doc View Source IsSocks Is this a SOCKS endpoint? Declaration public bool IsSocks { get; } Property Value Type Description Boolean | Improve this Doc View Source IsTransparent Is this a transparent endpoint? Declaration public bool IsTransparent { get; } Property Value Type Description Boolean | Improve this Doc View Source LocalEndPoint Declaration [Obsolete(\"Use ProxyEndPoint instead.\")] public ProxyEndPoint LocalEndPoint { get; } Property Value Type Description ProxyEndPoint | Improve this Doc View Source ProxyEndPoint Local endpoint via which we make the request. Declaration public ProxyEndPoint ProxyEndPoint { get; } Property Value Type Description ProxyEndPoint | Improve this Doc View Source ServerConnectionId Declaration public Guid ServerConnectionId { get; } Property Value Type Description Guid | Improve this Doc View Source TimeLine Relative milliseconds for various events. Declaration public Dictionary TimeLine { get; } Property Value Type Description Dictionary < String , DateTime > | Improve this Doc View Source UserData Returns a user data for this request/response session which is same as the user data of HttpClient. Declaration public object UserData { get; set; } Property Value Type Description Object | Improve this Doc View Source WebSession Declaration [Obsolete(\"Use HttpClient instead.\")] public HttpWebClient WebSession { get; } Property Value Type Description HttpWebClient Methods | Improve this Doc View Source Dispose() Declaration public void Dispose() | Improve this Doc View Source Dispose(Boolean) Declaration protected virtual void Dispose(bool disposing) Parameters Type Name Description Boolean disposing | Improve this Doc View Source Finalize() Declaration protected void Finalize() | Improve this Doc View Source TerminateSession() Terminates the session abruptly by terminating client/server connections. Declaration public void TerminateSession() Events | Improve this Doc View Source DataReceived Fired when data is received within this session from client/server. Declaration public event EventHandler DataReceived Event Type Type Description EventHandler < DataEventArgs > | Improve this Doc View Source DataSent Fired when data is sent within this session to server/client. Declaration public event EventHandler DataSent Event Type Type Description EventHandler < DataEventArgs > Implements System.IDisposable" }, "api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html": { "href": "api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html", "title": "Class TunnelConnectSessionEventArgs | Titanium Web Proxy", - "keywords": "Class TunnelConnectSessionEventArgs A class that wraps the state when a tunnel connect event happen for Explicit endpoints. Inheritance Object EventArgs ProxyEventArgsBase SessionEventArgsBase TunnelConnectSessionEventArgs Implements IDisposable Inherited Members SessionEventArgsBase.ClientConnectionId SessionEventArgsBase.ServerConnectionId SessionEventArgsBase.BufferPool SessionEventArgsBase.ExceptionFunc SessionEventArgsBase.TimeLine SessionEventArgsBase.UserData SessionEventArgsBase.EnableWinAuth SessionEventArgsBase.IsHttps SessionEventArgsBase.ClientLocalEndPoint SessionEventArgsBase.ClientRemoteEndPoint SessionEventArgsBase.ClientEndPoint SessionEventArgsBase.HttpClient SessionEventArgsBase.WebSession SessionEventArgsBase.CustomUpStreamProxy SessionEventArgsBase.CustomUpStreamProxyUsed SessionEventArgsBase.ProxyEndPoint SessionEventArgsBase.LocalEndPoint SessionEventArgsBase.IsTransparent SessionEventArgsBase.IsSocks SessionEventArgsBase.Exception SessionEventArgsBase.Dispose() SessionEventArgsBase.DataSent SessionEventArgsBase.DataReceived SessionEventArgsBase.TerminateSession() ProxyEventArgsBase.ClientUserData EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public class TunnelConnectSessionEventArgs : SessionEventArgsBase, IDisposable Properties | Improve this Doc View Source DecryptSsl Should we decrypt the Ssl or relay it to server? Default is true. Declaration public bool DecryptSsl { get; set; } Property Value Type Description Boolean | Improve this Doc View Source DenyConnect When set to true it denies the connect request with a Forbidden status. Declaration public bool DenyConnect { get; set; } Property Value Type Description Boolean | Improve this Doc View Source IsHttpsConnect Is this a connect request to secure HTTP server? Or is it to some other protocol. Declaration public bool IsHttpsConnect { get; } Property Value Type Description Boolean Events | Improve this Doc View Source DecryptedDataReceived Fired when decrypted data is received within this session from client/server. Declaration public event EventHandler DecryptedDataReceived Event Type Type Description EventHandler < DataEventArgs > | Improve this Doc View Source DecryptedDataSent Fired when decrypted data is sent within this session to server/client. Declaration public event EventHandler DecryptedDataSent Event Type Type Description EventHandler < DataEventArgs > Implements System.IDisposable" + "keywords": "Class TunnelConnectSessionEventArgs A class that wraps the state when a tunnel connect event happen for Explicit endpoints. Inheritance Object EventArgs ProxyEventArgsBase SessionEventArgsBase TunnelConnectSessionEventArgs Implements IDisposable Inherited Members SessionEventArgsBase.ClientConnectionId SessionEventArgsBase.ServerConnectionId SessionEventArgsBase.BufferPool SessionEventArgsBase.ExceptionFunc SessionEventArgsBase.TimeLine SessionEventArgsBase.UserData SessionEventArgsBase.EnableWinAuth SessionEventArgsBase.IsHttps SessionEventArgsBase.ClientLocalEndPoint SessionEventArgsBase.ClientRemoteEndPoint SessionEventArgsBase.ClientEndPoint SessionEventArgsBase.HttpClient SessionEventArgsBase.WebSession SessionEventArgsBase.CustomUpStreamProxy SessionEventArgsBase.CustomUpStreamProxyUsed SessionEventArgsBase.ProxyEndPoint SessionEventArgsBase.LocalEndPoint SessionEventArgsBase.IsTransparent SessionEventArgsBase.IsSocks SessionEventArgsBase.Exception SessionEventArgsBase.Dispose(Boolean) SessionEventArgsBase.Dispose() SessionEventArgsBase.DataSent SessionEventArgsBase.DataReceived SessionEventArgsBase.TerminateSession() ProxyEventArgsBase.ClientUserData EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public class TunnelConnectSessionEventArgs : SessionEventArgsBase, IDisposable Properties | Improve this Doc View Source DecryptSsl Should we decrypt the Ssl or relay it to server? Default is true. Declaration public bool DecryptSsl { get; set; } Property Value Type Description Boolean | Improve this Doc View Source DenyConnect When set to true it denies the connect request with a Forbidden status. Declaration public bool DenyConnect { get; set; } Property Value Type Description Boolean | Improve this Doc View Source IsHttpsConnect Is this a connect request to secure HTTP server? Or is it to some other protocol. Declaration public bool IsHttpsConnect { get; } Property Value Type Description Boolean Events | Improve this Doc View Source DecryptedDataReceived Fired when decrypted data is received within this session from client/server. Declaration public event EventHandler DecryptedDataReceived Event Type Type Description EventHandler < DataEventArgs > | Improve this Doc View Source DecryptedDataSent Fired when decrypted data is sent within this session to server/client. Declaration public event EventHandler DecryptedDataSent Event Type Type Description EventHandler < DataEventArgs > Implements System.IDisposable" }, "api/Titanium.Web.Proxy.ExceptionHandler.html": { "href": "api/Titanium.Web.Proxy.ExceptionHandler.html", @@ -297,7 +297,7 @@ "api/Titanium.Web.Proxy.Network.CertificateManager.html": { "href": "api/Titanium.Web.Proxy.Network.CertificateManager.html", "title": "Class CertificateManager | Titanium Web Proxy", - "keywords": "Class CertificateManager A class to manage SSL certificates used by this proxy server. Inheritance Object CertificateManager Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.Network Assembly : Titanium.Web.Proxy.dll Syntax public sealed class CertificateManager : IDisposable Properties | Improve this Doc View Source CertificateCacheTimeOutMinutes Minutes certificates should be kept in cache when not used. Declaration public int CertificateCacheTimeOutMinutes { get; set; } Property Value Type Description Int32 | Improve this Doc View Source CertificateEngine Select Certificate Engine. Optionally set to BouncyCastle. Mono only support BouncyCastle and it is the default. Declaration public CertificateEngine CertificateEngine { get; set; } Property Value Type Description CertificateEngine | Improve this Doc View Source CertificateStorage The fake certificate cache storage. The default cache storage implementation saves certificates in folder \"crts\" (will be created in proxy dll directory). Implement ICertificateCache interface and assign concrete class here to customize. Declaration public ICertificateCache CertificateStorage { get; set; } Property Value Type Description ICertificateCache | Improve this Doc View Source CertificateValidDays Number of Days generated HTTPS certificates are valid for. Maximum allowed on iOS 13 is 825 days and it is the default. Declaration public int CertificateValidDays { get; set; } Property Value Type Description Int32 | Improve this Doc View Source OverwritePfxFile Overwrite Root certificate file. true : replace an existing .pfx file if password is incorrect or if RootCertificate = null. Declaration public bool OverwritePfxFile { get; set; } Property Value Type Description Boolean | Improve this Doc View Source PfxFilePath Name(path) of the Root certificate file. Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be named as \"rootCert.pfx\" (and will be saved in proxy dll directory) Declaration public string PfxFilePath { get; set; } Property Value Type Description String | Improve this Doc View Source PfxPassword Password of the Root certificate file. Set a password for the .pfx file Declaration public string PfxPassword { get; set; } Property Value Type Description String | Improve this Doc View Source RootCertificate The root certificate. Declaration public X509Certificate2 RootCertificate { get; set; } Property Value Type Description X509Certificate2 | Improve this Doc View Source RootCertificateIssuerName Name of the root certificate issuer. (This is valid only when RootCertificate property is not set.) Declaration public string RootCertificateIssuerName { get; set; } Property Value Type Description String | Improve this Doc View Source RootCertificateName Name of the root certificate. (This is valid only when RootCertificate property is not set.) If no certificate is provided then a default Root Certificate will be created and used. The provided root certificate will be stored in proxy exe directory with the private key. Root certificate file will be named as \"rootCert.pfx\". Declaration public string RootCertificateName { get; set; } Property Value Type Description String | Improve this Doc View Source SaveFakeCertificates Save all fake certificates using CertificateStorage . for can load the certificate and not make new certificate every time. Declaration public bool SaveFakeCertificates { get; set; } Property Value Type Description Boolean | Improve this Doc View Source StorageFlag Adjust behaviour when certificates are saved to filesystem. Declaration public X509KeyStorageFlags StorageFlag { get; set; } Property Value Type Description X509KeyStorageFlags Methods | Improve this Doc View Source ClearRootCertificate() Clear the root certificate and cache. Declaration public void ClearRootCertificate() | Improve this Doc View Source CreateRootCertificate(Boolean) Attempts to create a RootCertificate. Declaration public bool CreateRootCertificate(bool persistToFile = true) Parameters Type Name Description Boolean persistToFile if set to true try to load/save the certificate from rootCert.pfx. Returns Type Description Boolean true if succeeded, else false. | Improve this Doc View Source CreateServerCertificate(String) Creates a server certificate signed by the root certificate. Declaration public Task CreateServerCertificate(string certificateName) Parameters Type Name Description String certificateName Returns Type Description Task < X509Certificate2 > | Improve this Doc View Source Dispose() Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. Declaration public void Dispose() | Improve this Doc View Source EnsureRootCertificate() Ensure certificates are setup (creates root if required). Also makes root certificate trusted based on initial setup from proxy constructor for user/machine trust. Declaration public void EnsureRootCertificate() | Improve this Doc View Source EnsureRootCertificate(Boolean, Boolean, Boolean) Ensure certificates are setup (creates root if required). Also makes root certificate trusted based on provided parameters. Note:setting machineTrustRootCertificate to true will force userTrustRootCertificate to true. Declaration public void EnsureRootCertificate(bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? | Improve this Doc View Source IsRootCertificateMachineTrusted() Determines whether the root certificate is machine trusted. Declaration public bool IsRootCertificateMachineTrusted() Returns Type Description Boolean | Improve this Doc View Source IsRootCertificateUserTrusted() Determines whether the root certificate is trusted. Declaration public bool IsRootCertificateUserTrusted() Returns Type Description Boolean | Improve this Doc View Source LoadRootCertificate() Loads root certificate from current executing assembly location with expected name rootCert.pfx. Declaration public X509Certificate2 LoadRootCertificate() Returns Type Description X509Certificate2 | Improve this Doc View Source LoadRootCertificate(String, String, Boolean, X509KeyStorageFlags) Manually load a Root certificate file from give path (.pfx file). Declaration public bool LoadRootCertificate(string pfxFilePath, string password, bool overwritePfXFile = true, X509KeyStorageFlags storageFlag = X509KeyStorageFlags.Exportable) Parameters Type Name Description String pfxFilePath Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be named as \"rootCert.pfx\" (and will be saved in proxy dll directory). String password Set a password for the .pfx file. Boolean overwritePfXFile true : replace an existing .pfx file if password is incorrect or if RootCertificate==null. X509KeyStorageFlags storageFlag Returns Type Description Boolean true if succeeded, else false. | Improve this Doc View Source RemoveTrustedRootCertificate(Boolean) Removes the trusted certificates from user store, optionally also from machine store. To remove from machine store elevated permissions are required (will fail silently otherwise). Declaration public void RemoveTrustedRootCertificate(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Should also remove from machine store? | Improve this Doc View Source RemoveTrustedRootCertificateAsAdmin(Boolean) Removes the trusted certificates from user store, optionally also from machine store Declaration public bool RemoveTrustedRootCertificateAsAdmin(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Returns Type Description Boolean Should also remove from machine store? | Improve this Doc View Source TrustRootCertificate(Boolean) Trusts the root certificate in user store, optionally also in machine store. Machine trust would require elevated permissions (will silently fail otherwise). Declaration public void TrustRootCertificate(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted | Improve this Doc View Source TrustRootCertificateAsAdmin(Boolean) Puts the certificate to the user store, optionally also to machine store. Prompts with UAC if elevated permissions are required. Works only on Windows. Declaration public bool TrustRootCertificateAsAdmin(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Returns Type Description Boolean True if success. Implements System.IDisposable" + "keywords": "Class CertificateManager A class to manage SSL certificates used by this proxy server. Inheritance Object CertificateManager Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.Network Assembly : Titanium.Web.Proxy.dll Syntax public sealed class CertificateManager : IDisposable Properties | Improve this Doc View Source CertificateCacheTimeOutMinutes Minutes certificates should be kept in cache when not used. Declaration public int CertificateCacheTimeOutMinutes { get; set; } Property Value Type Description Int32 | Improve this Doc View Source CertificateEngine Select Certificate Engine. Optionally set to BouncyCastle. Mono only support BouncyCastle and it is the default. Declaration public CertificateEngine CertificateEngine { get; set; } Property Value Type Description CertificateEngine | Improve this Doc View Source CertificateStorage The fake certificate cache storage. The default cache storage implementation saves certificates in folder \"crts\" (will be created in proxy dll directory). Implement ICertificateCache interface and assign concrete class here to customize. Declaration public ICertificateCache CertificateStorage { get; set; } Property Value Type Description ICertificateCache | Improve this Doc View Source CertificateValidDays Number of Days generated HTTPS certificates are valid for. Maximum allowed on iOS 13 is 825 days and it is the default. Declaration public int CertificateValidDays { get; set; } Property Value Type Description Int32 | Improve this Doc View Source OverwritePfxFile Overwrite Root certificate file. true : replace an existing .pfx file if password is incorrect or if RootCertificate = null. Declaration public bool OverwritePfxFile { get; set; } Property Value Type Description Boolean | Improve this Doc View Source PfxFilePath Name(path) of the Root certificate file. Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be named as \"rootCert.pfx\" (and will be saved in proxy dll directory) Declaration public string PfxFilePath { get; set; } Property Value Type Description String | Improve this Doc View Source PfxPassword Password of the Root certificate file. Set a password for the .pfx file Declaration public string PfxPassword { get; set; } Property Value Type Description String | Improve this Doc View Source RootCertificate The root certificate. Declaration public X509Certificate2 RootCertificate { get; set; } Property Value Type Description X509Certificate2 | Improve this Doc View Source RootCertificateIssuerName Name of the root certificate issuer. (This is valid only when RootCertificate property is not set.) Declaration public string RootCertificateIssuerName { get; set; } Property Value Type Description String | Improve this Doc View Source RootCertificateName Name of the root certificate. (This is valid only when RootCertificate property is not set.) If no certificate is provided then a default Root Certificate will be created and used. The provided root certificate will be stored in proxy exe directory with the private key. Root certificate file will be named as \"rootCert.pfx\". Declaration public string RootCertificateName { get; set; } Property Value Type Description String | Improve this Doc View Source SaveFakeCertificates Save all fake certificates using CertificateStorage . for can load the certificate and not make new certificate every time. Declaration public bool SaveFakeCertificates { get; set; } Property Value Type Description Boolean | Improve this Doc View Source StorageFlag Adjust behaviour when certificates are saved to filesystem. Declaration public X509KeyStorageFlags StorageFlag { get; set; } Property Value Type Description X509KeyStorageFlags Methods | Improve this Doc View Source ClearRootCertificate() Clear the root certificate and cache. Declaration public void ClearRootCertificate() | Improve this Doc View Source CreateRootCertificate(Boolean) Attempts to create a RootCertificate. Declaration public bool CreateRootCertificate(bool persistToFile = true) Parameters Type Name Description Boolean persistToFile if set to true try to load/save the certificate from rootCert.pfx. Returns Type Description Boolean true if succeeded, else false. | Improve this Doc View Source CreateServerCertificate(String) Creates a server certificate signed by the root certificate. Declaration public Task CreateServerCertificate(string certificateName) Parameters Type Name Description String certificateName Returns Type Description Task < X509Certificate2 > | Improve this Doc View Source Dispose() Declaration public void Dispose() | Improve this Doc View Source EnsureRootCertificate() Ensure certificates are setup (creates root if required). Also makes root certificate trusted based on initial setup from proxy constructor for user/machine trust. Declaration public void EnsureRootCertificate() | Improve this Doc View Source EnsureRootCertificate(Boolean, Boolean, Boolean) Ensure certificates are setup (creates root if required). Also makes root certificate trusted based on provided parameters. Note:setting machineTrustRootCertificate to true will force userTrustRootCertificate to true. Declaration public void EnsureRootCertificate(bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? | Improve this Doc View Source Finalize() Declaration protected void Finalize() | Improve this Doc View Source IsRootCertificateMachineTrusted() Determines whether the root certificate is machine trusted. Declaration public bool IsRootCertificateMachineTrusted() Returns Type Description Boolean | Improve this Doc View Source IsRootCertificateUserTrusted() Determines whether the root certificate is trusted. Declaration public bool IsRootCertificateUserTrusted() Returns Type Description Boolean | Improve this Doc View Source LoadRootCertificate() Loads root certificate from current executing assembly location with expected name rootCert.pfx. Declaration public X509Certificate2 LoadRootCertificate() Returns Type Description X509Certificate2 | Improve this Doc View Source LoadRootCertificate(String, String, Boolean, X509KeyStorageFlags) Manually load a Root certificate file from give path (.pfx file). Declaration public bool LoadRootCertificate(string pfxFilePath, string password, bool overwritePfXFile = true, X509KeyStorageFlags storageFlag = X509KeyStorageFlags.Exportable) Parameters Type Name Description String pfxFilePath Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be named as \"rootCert.pfx\" (and will be saved in proxy dll directory). String password Set a password for the .pfx file. Boolean overwritePfXFile true : replace an existing .pfx file if password is incorrect or if RootCertificate==null. X509KeyStorageFlags storageFlag Returns Type Description Boolean true if succeeded, else false. | Improve this Doc View Source RemoveTrustedRootCertificate(Boolean) Removes the trusted certificates from user store, optionally also from machine store. To remove from machine store elevated permissions are required (will fail silently otherwise). Declaration public void RemoveTrustedRootCertificate(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Should also remove from machine store? | Improve this Doc View Source RemoveTrustedRootCertificateAsAdmin(Boolean) Removes the trusted certificates from user store, optionally also from machine store Declaration public bool RemoveTrustedRootCertificateAsAdmin(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Returns Type Description Boolean Should also remove from machine store? | Improve this Doc View Source TrustRootCertificate(Boolean) Trusts the root certificate in user store, optionally also in machine store. Machine trust would require elevated permissions (will silently fail otherwise). Declaration public void TrustRootCertificate(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted | Improve this Doc View Source TrustRootCertificateAsAdmin(Boolean) Puts the certificate to the user store, optionally also to machine store. Prompts with UAC if elevated permissions are required. Works only on Windows. Declaration public bool TrustRootCertificateAsAdmin(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Returns Type Description Boolean True if success. Implements System.IDisposable" }, "api/Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html": { "href": "api/Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html", @@ -317,7 +317,7 @@ "api/Titanium.Web.Proxy.ProxyServer.html": { "href": "api/Titanium.Web.Proxy.ProxyServer.html", "title": "Class ProxyServer | Titanium Web Proxy", - "keywords": "Class ProxyServer This class is the backbone of proxy. One can create as many instances as needed. However care should be taken to avoid using the same listening ports across multiple instances. Inheritance Object ProxyServer Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy Assembly : Titanium.Web.Proxy.dll Syntax public class ProxyServer : IDisposable Constructors | Improve this Doc View Source ProxyServer(Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? | Improve this Doc View Source ProxyServer(String, String, Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(string rootCertificateName, string rootCertificateIssuerName, bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description String rootCertificateName Name of the root certificate. String rootCertificateIssuerName Name of the root certificate issuer. Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? Properties | Improve this Doc View Source BufferPool The buffer pool used throughout this proxy instance. Set custom implementations by implementing this interface. By default this uses DefaultBufferPool implementation available in StreamExtended library package. Buffer size should be at least 10 bytes. Declaration public IBufferPool BufferPool { get; set; } Property Value Type Description IBufferPool | Improve this Doc View Source CertificateManager Manages certificates used by this proxy. Declaration public CertificateManager CertificateManager { get; } Property Value Type Description CertificateManager | Improve this Doc View Source CheckCertificateRevocation Should we check for certificate revocation during SSL authentication to servers Note: If enabled can reduce performance. Defaults to false. Declaration public X509RevocationMode CheckCertificateRevocation { get; set; } Property Value Type Description X509RevocationMode | Improve this Doc View Source ClientConnectionCount Total number of active client connections. Declaration public int ClientConnectionCount { get; } Property Value Type Description Int32 | Improve this Doc View Source ConnectionTimeOutSeconds Seconds client/server connection are to be kept alive when waiting for read/write to complete. This will also determine the pool eviction time when connection pool is enabled. Default value is 60 seconds. Declaration public int ConnectionTimeOutSeconds { get; set; } Property Value Type Description Int32 | Improve this Doc View Source ConnectTimeOutSeconds Seconds server connection are to wait for connection to be established. Default value is 20 seconds. Declaration public int ConnectTimeOutSeconds { get; set; } Property Value Type Description Int32 | Improve this Doc View Source CustomUpStreamProxyFailureFunc A callback to provide a chance for an upstream proxy failure to be handled by a new upstream proxy. User should return the ExternalProxy object with valid credentials or null. Declaration public Func> CustomUpStreamProxyFailureFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , Task < IExternalProxy >> | Improve this Doc View Source Enable100ContinueBehaviour Does this proxy uses the HTTP protocol 100 continue behaviour strictly? Broken 100 continue implementations on server/client may cause problems if enabled. Defaults to false. Declaration public bool Enable100ContinueBehaviour { get; set; } Property Value Type Description Boolean | Improve this Doc View Source EnableConnectionPool Should we enable server connection pool. Defaults to true. When you enable connection pooling, instead of creating a new TCP connection to server for each client TCP connection, we check if a server connection is available in our cached pool. If it is available in our pool, created from earlier requests to the same server, we will reuse those idle connections. There is also a ConnectionTimeOutSeconds parameter, which determine the eviction time for inactive server connections. This will help to reduce TCP connection establishment cost, both the wall clock time and CPU cycles. Declaration public bool EnableConnectionPool { get; set; } Property Value Type Description Boolean | Improve this Doc View Source EnableHttp2 Enable disable HTTP/2 support. Warning: HTTP/2 support is very limited only enabled when both client and server supports it (no protocol changing in proxy) cannot modify the request/response (e.g header modifications in BeforeRequest/Response events are ignored) Declaration public bool EnableHttp2 { get; set; } Property Value Type Description Boolean | Improve this Doc View Source EnableTcpServerConnectionPrefetch Should we enable tcp server connection prefetching? When enabled, as soon as we receive a client connection we concurrently initiate corresponding server connection process using CONNECT hostname or SNI hostname on a separate task so that after parsing client request we will have the server connection immediately ready or in the process of getting ready. If a server connection is available in cache then this prefetch task will immediately return with the available connection from cache. Defaults to true. Declaration public bool EnableTcpServerConnectionPrefetch { get; set; } Property Value Type Description Boolean | Improve this Doc View Source EnableWinAuth Enable disable Windows Authentication (NTLM/Kerberos). Note: NTLM/Kerberos will always send local credentials of current user running the proxy process. This is because a man in middle attack with Windows domain authentication is not currently supported. Defaults to false. Declaration public bool EnableWinAuth { get; set; } Property Value Type Description Boolean | Improve this Doc View Source ExceptionFunc Callback for error events in this proxy instance. Declaration public ExceptionHandler ExceptionFunc { get; set; } Property Value Type Description ExceptionHandler | Improve this Doc View Source ForwardToUpstreamGateway Gets or sets a value indicating whether requests will be chained to upstream gateway. Defaults to false. Declaration public bool ForwardToUpstreamGateway { get; set; } Property Value Type Description Boolean | Improve this Doc View Source GetCustomUpStreamProxyFunc A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests. User should return the ExternalProxy object with valid credentials. Declaration public Func> GetCustomUpStreamProxyFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , Task < IExternalProxy >> | Improve this Doc View Source MaxCachedConnections Maximum number of concurrent connections per remote host in cache. Only valid when connection pooling is enabled. Default value is 2. Declaration public int MaxCachedConnections { get; set; } Property Value Type Description Int32 | Improve this Doc View Source NoDelay Gets or sets a Boolean value that specifies whether server and client stream Sockets are using the Nagle algorithm. Defaults to true, no nagle algorithm is used. Declaration public bool NoDelay { get; set; } Property Value Type Description Boolean | Improve this Doc View Source ProxyAuthenticationRealm Realm used during Proxy Basic Authentication. Declaration public string ProxyAuthenticationRealm { get; set; } Property Value Type Description String | Improve this Doc View Source ProxyAuthenticationSchemes A collection of scheme types, e.g. basic, NTLM, Kerberos, Negotiate, to return if scheme authentication is required. Works in relation with ProxySchemeAuthenticateFunc. Declaration public IEnumerable ProxyAuthenticationSchemes { get; set; } Property Value Type Description IEnumerable < String > | Improve this Doc View Source ProxyBasicAuthenticateFunc A callback to authenticate proxy clients via basic authentication. Parameters are username and password as provided by client. Should return true for successful authentication. Declaration public Func> ProxyBasicAuthenticateFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , String , String , Task < Boolean >> | Improve this Doc View Source ProxyEndPoints A list of IpAddress and port this proxy is listening to. Declaration public List ProxyEndPoints { get; set; } Property Value Type Description List < ProxyEndPoint > | Improve this Doc View Source ProxyRunning Is the proxy currently running? Declaration public bool ProxyRunning { get; } Property Value Type Description Boolean | Improve this Doc View Source ProxySchemeAuthenticateFunc A pluggable callback to authenticate clients by scheme instead of requiring basic authentication through ProxyBasicAuthenticateFunc. Parameters are current working session, schemeType, and token as provided by a calling client. Should return success for successful authentication, continuation if the package requests, or failure. Declaration public Func> ProxySchemeAuthenticateFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , String , String , Task < ProxyAuthenticationContext >> | Improve this Doc View Source ReuseSocket Should we reuse client/server tcp sockets. Default is true (disabled for linux/macOS due to bug in .Net core). Declaration public bool ReuseSocket { get; set; } Property Value Type Description Boolean | Improve this Doc View Source ServerConnectionCount Total number of active server connections. Declaration public int ServerConnectionCount { get; } Property Value Type Description Int32 | Improve this Doc View Source SupportedSslProtocols List of supported Ssl versions. Declaration public SslProtocols SupportedSslProtocols { get; set; } Property Value Type Description SslProtocols | Improve this Doc View Source TcpTimeWaitSeconds Number of seconds to linger when Tcp connection is in TIME_WAIT state. Default value is 30. Declaration public int TcpTimeWaitSeconds { get; set; } Property Value Type Description Int32 | Improve this Doc View Source ThreadPoolWorkerThread Customize the minimum ThreadPool size (increase it on a server) Declaration public int ThreadPoolWorkerThread { get; set; } Property Value Type Description Int32 | Improve this Doc View Source UpStreamEndPoint Local adapter/NIC endpoint where proxy makes request via. Defaults via any IP addresses of this machine. Declaration public IPEndPoint UpStreamEndPoint { get; set; } Property Value Type Description IPEndPoint | Improve this Doc View Source UpStreamHttpProxy External proxy used for Http requests. Declaration public IExternalProxy UpStreamHttpProxy { get; set; } Property Value Type Description IExternalProxy | Improve this Doc View Source UpStreamHttpsProxy External proxy used for Https requests. Declaration public IExternalProxy UpStreamHttpsProxy { get; set; } Property Value Type Description IExternalProxy Methods | Improve this Doc View Source AddEndPoint(ProxyEndPoint) Add a proxy end point. Declaration public void AddEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The proxy endpoint. | Improve this Doc View Source DisableAllSystemProxies() Clear all proxy settings for current machine. Declaration public void DisableAllSystemProxies() | Improve this Doc View Source DisableSystemHttpProxy() Clear HTTP proxy settings of current machine. Declaration public void DisableSystemHttpProxy() | Improve this Doc View Source DisableSystemHttpsProxy() Clear HTTPS proxy settings of current machine. Declaration public void DisableSystemHttpsProxy() | Improve this Doc View Source DisableSystemProxy(ProxyProtocolType) Clear the specified proxy setting for current machine. Declaration public void DisableSystemProxy(ProxyProtocolType protocolType) Parameters Type Name Description ProxyProtocolType protocolType | Improve this Doc View Source Dispose() Dispose the Proxy instance. Declaration public void Dispose() | Improve this Doc View Source RemoveEndPoint(ProxyEndPoint) Remove a proxy end point. Will throw error if the end point doesn't exist. Declaration public void RemoveEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The existing endpoint to remove. | Improve this Doc View Source RestoreOriginalProxySettings() Restores the original proxy settings. Declaration public void RestoreOriginalProxySettings() | Improve this Doc View Source SetAsSystemHttpProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. | Improve this Doc View Source SetAsSystemHttpsProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. | Improve this Doc View Source SetAsSystemProxy(ExplicitProxyEndPoint, ProxyProtocolType) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. ProxyProtocolType protocolType The proxy protocol type. | Improve this Doc View Source Start() Start this proxy server instance. Declaration public void Start() | Improve this Doc View Source Stop() Stop this proxy server instance. Declaration public void Stop() Events | Improve this Doc View Source AfterResponse Intercept after response event from server. Declaration public event AsyncEventHandler AfterResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > | Improve this Doc View Source BeforeRequest Intercept request event to server. Declaration public event AsyncEventHandler BeforeRequest Event Type Type Description AsyncEventHandler < SessionEventArgs > | Improve this Doc View Source BeforeResponse Intercept response event from server. Declaration public event AsyncEventHandler BeforeResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > | Improve this Doc View Source ClientCertificateSelectionCallback Event to override client certificate selection during mutual SSL authentication. Declaration public event AsyncEventHandler ClientCertificateSelectionCallback Event Type Type Description AsyncEventHandler < CertificateSelectionEventArgs > | Improve this Doc View Source ClientConnectionCountChanged Event occurs when client connection count changed. Declaration public event EventHandler ClientConnectionCountChanged Event Type Type Description EventHandler | Improve this Doc View Source OnClientConnectionCreate Customize TcpClient used for client connection upon create. Declaration public event AsyncEventHandler OnClientConnectionCreate Event Type Type Description AsyncEventHandler < Socket > | Improve this Doc View Source OnServerConnectionCreate Customize TcpClient used for server connection upon create. Declaration public event AsyncEventHandler OnServerConnectionCreate Event Type Type Description AsyncEventHandler < Socket > | Improve this Doc View Source ServerCertificateValidationCallback Event to override the default verification logic of remote SSL certificate received during authentication. Declaration public event AsyncEventHandler ServerCertificateValidationCallback Event Type Type Description AsyncEventHandler < CertificateValidationEventArgs > | Improve this Doc View Source ServerConnectionCountChanged Event occurs when server connection count changed. Declaration public event EventHandler ServerConnectionCountChanged Event Type Type Description EventHandler Implements System.IDisposable" + "keywords": "Class ProxyServer This class is the backbone of proxy. One can create as many instances as needed. However care should be taken to avoid using the same listening ports across multiple instances. Inheritance Object ProxyServer Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy Assembly : Titanium.Web.Proxy.dll Syntax public class ProxyServer : IDisposable Constructors | Improve this Doc View Source ProxyServer(Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? | Improve this Doc View Source ProxyServer(String, String, Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(string rootCertificateName, string rootCertificateIssuerName, bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description String rootCertificateName Name of the root certificate. String rootCertificateIssuerName Name of the root certificate issuer. Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? Properties | Improve this Doc View Source BufferPool The buffer pool used throughout this proxy instance. Set custom implementations by implementing this interface. By default this uses DefaultBufferPool implementation available in StreamExtended library package. Buffer size should be at least 10 bytes. Declaration public IBufferPool BufferPool { get; set; } Property Value Type Description IBufferPool | Improve this Doc View Source CertificateManager Manages certificates used by this proxy. Declaration public CertificateManager CertificateManager { get; } Property Value Type Description CertificateManager | Improve this Doc View Source CheckCertificateRevocation Should we check for certificate revocation during SSL authentication to servers Note: If enabled can reduce performance. Defaults to false. Declaration public X509RevocationMode CheckCertificateRevocation { get; set; } Property Value Type Description X509RevocationMode | Improve this Doc View Source ClientConnectionCount Total number of active client connections. Declaration public int ClientConnectionCount { get; } Property Value Type Description Int32 | Improve this Doc View Source ConnectionTimeOutSeconds Seconds client/server connection are to be kept alive when waiting for read/write to complete. This will also determine the pool eviction time when connection pool is enabled. Default value is 60 seconds. Declaration public int ConnectionTimeOutSeconds { get; set; } Property Value Type Description Int32 | Improve this Doc View Source ConnectTimeOutSeconds Seconds server connection are to wait for connection to be established. Default value is 20 seconds. Declaration public int ConnectTimeOutSeconds { get; set; } Property Value Type Description Int32 | Improve this Doc View Source CustomUpStreamProxyFailureFunc A callback to provide a chance for an upstream proxy failure to be handled by a new upstream proxy. User should return the ExternalProxy object with valid credentials or null. Declaration public Func> CustomUpStreamProxyFailureFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , Task < IExternalProxy >> | Improve this Doc View Source Enable100ContinueBehaviour Does this proxy uses the HTTP protocol 100 continue behaviour strictly? Broken 100 continue implementations on server/client may cause problems if enabled. Defaults to false. Declaration public bool Enable100ContinueBehaviour { get; set; } Property Value Type Description Boolean | Improve this Doc View Source EnableConnectionPool Should we enable server connection pool. Defaults to true. When you enable connection pooling, instead of creating a new TCP connection to server for each client TCP connection, we check if a server connection is available in our cached pool. If it is available in our pool, created from earlier requests to the same server, we will reuse those idle connections. There is also a ConnectionTimeOutSeconds parameter, which determine the eviction time for inactive server connections. This will help to reduce TCP connection establishment cost, both the wall clock time and CPU cycles. Declaration public bool EnableConnectionPool { get; set; } Property Value Type Description Boolean | Improve this Doc View Source EnableHttp2 Enable disable HTTP/2 support. Warning: HTTP/2 support is very limited only enabled when both client and server supports it (no protocol changing in proxy) cannot modify the request/response (e.g header modifications in BeforeRequest/Response events are ignored) Declaration public bool EnableHttp2 { get; set; } Property Value Type Description Boolean | Improve this Doc View Source EnableTcpServerConnectionPrefetch Should we enable tcp server connection prefetching? When enabled, as soon as we receive a client connection we concurrently initiate corresponding server connection process using CONNECT hostname or SNI hostname on a separate task so that after parsing client request we will have the server connection immediately ready or in the process of getting ready. If a server connection is available in cache then this prefetch task will immediately return with the available connection from cache. Defaults to true. Declaration public bool EnableTcpServerConnectionPrefetch { get; set; } Property Value Type Description Boolean | Improve this Doc View Source EnableWinAuth Enable disable Windows Authentication (NTLM/Kerberos). Note: NTLM/Kerberos will always send local credentials of current user running the proxy process. This is because a man in middle attack with Windows domain authentication is not currently supported. Defaults to false. Declaration public bool EnableWinAuth { get; set; } Property Value Type Description Boolean | Improve this Doc View Source ExceptionFunc Callback for error events in this proxy instance. Declaration public ExceptionHandler ExceptionFunc { get; set; } Property Value Type Description ExceptionHandler | Improve this Doc View Source ForwardToUpstreamGateway Gets or sets a value indicating whether requests will be chained to upstream gateway. Defaults to false. Declaration public bool ForwardToUpstreamGateway { get; set; } Property Value Type Description Boolean | Improve this Doc View Source GetCustomUpStreamProxyFunc A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests. User should return the ExternalProxy object with valid credentials. Declaration public Func> GetCustomUpStreamProxyFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , Task < IExternalProxy >> | Improve this Doc View Source MaxCachedConnections Maximum number of concurrent connections per remote host in cache. Only valid when connection pooling is enabled. Default value is 2. Declaration public int MaxCachedConnections { get; set; } Property Value Type Description Int32 | Improve this Doc View Source NoDelay Gets or sets a Boolean value that specifies whether server and client stream Sockets are using the Nagle algorithm. Defaults to true, no nagle algorithm is used. Declaration public bool NoDelay { get; set; } Property Value Type Description Boolean | Improve this Doc View Source ProxyAuthenticationRealm Realm used during Proxy Basic Authentication. Declaration public string ProxyAuthenticationRealm { get; set; } Property Value Type Description String | Improve this Doc View Source ProxyAuthenticationSchemes A collection of scheme types, e.g. basic, NTLM, Kerberos, Negotiate, to return if scheme authentication is required. Works in relation with ProxySchemeAuthenticateFunc. Declaration public IEnumerable ProxyAuthenticationSchemes { get; set; } Property Value Type Description IEnumerable < String > | Improve this Doc View Source ProxyBasicAuthenticateFunc A callback to authenticate proxy clients via basic authentication. Parameters are username and password as provided by client. Should return true for successful authentication. Declaration public Func> ProxyBasicAuthenticateFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , String , String , Task < Boolean >> | Improve this Doc View Source ProxyEndPoints A list of IpAddress and port this proxy is listening to. Declaration public List ProxyEndPoints { get; set; } Property Value Type Description List < ProxyEndPoint > | Improve this Doc View Source ProxyRunning Is the proxy currently running? Declaration public bool ProxyRunning { get; } Property Value Type Description Boolean | Improve this Doc View Source ProxySchemeAuthenticateFunc A pluggable callback to authenticate clients by scheme instead of requiring basic authentication through ProxyBasicAuthenticateFunc. Parameters are current working session, schemeType, and token as provided by a calling client. Should return success for successful authentication, continuation if the package requests, or failure. Declaration public Func> ProxySchemeAuthenticateFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , String , String , Task < ProxyAuthenticationContext >> | Improve this Doc View Source ReuseSocket Should we reuse client/server tcp sockets. Default is true (disabled for linux/macOS due to bug in .Net core). Declaration public bool ReuseSocket { get; set; } Property Value Type Description Boolean | Improve this Doc View Source ServerConnectionCount Total number of active server connections. Declaration public int ServerConnectionCount { get; } Property Value Type Description Int32 | Improve this Doc View Source SupportedSslProtocols List of supported Ssl versions. Declaration public SslProtocols SupportedSslProtocols { get; set; } Property Value Type Description SslProtocols | Improve this Doc View Source TcpTimeWaitSeconds Number of seconds to linger when Tcp connection is in TIME_WAIT state. Default value is 30. Declaration public int TcpTimeWaitSeconds { get; set; } Property Value Type Description Int32 | Improve this Doc View Source ThreadPoolWorkerThread Customize the minimum ThreadPool size (increase it on a server) Declaration public int ThreadPoolWorkerThread { get; set; } Property Value Type Description Int32 | Improve this Doc View Source UpStreamEndPoint Local adapter/NIC endpoint where proxy makes request via. Defaults via any IP addresses of this machine. Declaration public IPEndPoint UpStreamEndPoint { get; set; } Property Value Type Description IPEndPoint | Improve this Doc View Source UpStreamHttpProxy External proxy used for Http requests. Declaration public IExternalProxy UpStreamHttpProxy { get; set; } Property Value Type Description IExternalProxy | Improve this Doc View Source UpStreamHttpsProxy External proxy used for Https requests. Declaration public IExternalProxy UpStreamHttpsProxy { get; set; } Property Value Type Description IExternalProxy Methods | Improve this Doc View Source AddEndPoint(ProxyEndPoint) Add a proxy end point. Declaration public void AddEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The proxy endpoint. | Improve this Doc View Source DisableAllSystemProxies() Clear all proxy settings for current machine. Declaration public void DisableAllSystemProxies() | Improve this Doc View Source DisableSystemHttpProxy() Clear HTTP proxy settings of current machine. Declaration public void DisableSystemHttpProxy() | Improve this Doc View Source DisableSystemHttpsProxy() Clear HTTPS proxy settings of current machine. Declaration public void DisableSystemHttpsProxy() | Improve this Doc View Source DisableSystemProxy(ProxyProtocolType) Clear the specified proxy setting for current machine. Declaration public void DisableSystemProxy(ProxyProtocolType protocolType) Parameters Type Name Description ProxyProtocolType protocolType | Improve this Doc View Source Dispose() Declaration public void Dispose() | Improve this Doc View Source Dispose(Boolean) Declaration protected virtual void Dispose(bool disposing) Parameters Type Name Description Boolean disposing | Improve this Doc View Source Finalize() Declaration protected void Finalize() | Improve this Doc View Source RemoveEndPoint(ProxyEndPoint) Remove a proxy end point. Will throw error if the end point doesn't exist. Declaration public void RemoveEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The existing endpoint to remove. | Improve this Doc View Source RestoreOriginalProxySettings() Restores the original proxy settings. Declaration public void RestoreOriginalProxySettings() | Improve this Doc View Source SetAsSystemHttpProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. | Improve this Doc View Source SetAsSystemHttpsProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. | Improve this Doc View Source SetAsSystemProxy(ExplicitProxyEndPoint, ProxyProtocolType) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. ProxyProtocolType protocolType The proxy protocol type. | Improve this Doc View Source Start() Start this proxy server instance. Declaration public void Start() | Improve this Doc View Source Stop() Stop this proxy server instance. Declaration public void Stop() Events | Improve this Doc View Source AfterResponse Intercept after response event from server. Declaration public event AsyncEventHandler AfterResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > | Improve this Doc View Source BeforeRequest Intercept request event to server. Declaration public event AsyncEventHandler BeforeRequest Event Type Type Description AsyncEventHandler < SessionEventArgs > | Improve this Doc View Source BeforeResponse Intercept response event from server. Declaration public event AsyncEventHandler BeforeResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > | Improve this Doc View Source ClientCertificateSelectionCallback Event to override client certificate selection during mutual SSL authentication. Declaration public event AsyncEventHandler ClientCertificateSelectionCallback Event Type Type Description AsyncEventHandler < CertificateSelectionEventArgs > | Improve this Doc View Source ClientConnectionCountChanged Event occurs when client connection count changed. Declaration public event EventHandler ClientConnectionCountChanged Event Type Type Description EventHandler | Improve this Doc View Source OnClientConnectionCreate Customize TcpClient used for client connection upon create. Declaration public event AsyncEventHandler OnClientConnectionCreate Event Type Type Description AsyncEventHandler < Socket > | Improve this Doc View Source OnServerConnectionCreate Customize TcpClient used for server connection upon create. Declaration public event AsyncEventHandler OnServerConnectionCreate Event Type Type Description AsyncEventHandler < Socket > | Improve this Doc View Source ServerCertificateValidationCallback Event to override the default verification logic of remote SSL certificate received during authentication. Declaration public event AsyncEventHandler ServerCertificateValidationCallback Event Type Type Description AsyncEventHandler < CertificateValidationEventArgs > | Improve this Doc View Source ServerConnectionCountChanged Event occurs when server connection count changed. Declaration public event EventHandler ServerConnectionCountChanged Event Type Type Description EventHandler Implements System.IDisposable" }, "api/Titanium.Web.Proxy.StreamExtended.BufferPool.html": { "href": "api/Titanium.Web.Proxy.StreamExtended.BufferPool.html", @@ -362,12 +362,12 @@ "api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html": { "href": "api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html", "title": "Interface IHttpStreamReader | Titanium Web Proxy", - "keywords": "Interface IHttpStreamReader Inherited Members ILineStream.DataAvailable ILineStream.FillBufferAsync(CancellationToken) ILineStream.ReadByteFromBuffer() ILineStream.ReadLineAsync(CancellationToken) Namespace : Titanium.Web.Proxy.StreamExtended.Network Assembly : Titanium.Web.Proxy.dll Syntax public interface IHttpStreamReader : ILineStream Methods | Improve this Doc View Source CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Action, CancellationToken) Declaration Task CopyBodyAsync(IHttpStreamWriter writer, bool isChunked, long contentLength, Action onCopy, CancellationToken cancellationToken) Parameters Type Name Description IHttpStreamWriter writer Boolean isChunked Int64 contentLength Action < Byte [], Int32 , Int32 > onCopy CancellationToken cancellationToken Returns Type Description Task | Improve this Doc View Source Read(Byte[], Int32, Int32) Declaration int Read(byte[] buffer, int offset, int count) Parameters Type Name Description Byte [] buffer Int32 offset Int32 count Returns Type Description Int32 | Improve this Doc View Source ReadAsync(Byte[], Int32, Int32, CancellationToken) Declaration Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) Parameters Type Name Description Byte [] buffer Int32 offset Int32 count CancellationToken cancellationToken Returns Type Description Task < Int32 >" + "keywords": "Interface IHttpStreamReader Inherited Members ILineStream.DataAvailable ILineStream.FillBufferAsync(CancellationToken) ILineStream.ReadByteFromBuffer() ILineStream.ReadLineAsync(CancellationToken) Namespace : Titanium.Web.Proxy.StreamExtended.Network Assembly : Titanium.Web.Proxy.dll Syntax public interface IHttpStreamReader : ILineStream Methods | Improve this Doc View Source CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Boolean, SessionEventArgs, CancellationToken) Declaration Task CopyBodyAsync(IHttpStreamWriter writer, bool isChunked, long contentLength, bool isRequest, SessionEventArgs args, CancellationToken cancellationToken) Parameters Type Name Description IHttpStreamWriter writer Boolean isChunked Int64 contentLength Boolean isRequest SessionEventArgs args CancellationToken cancellationToken Returns Type Description Task | Improve this Doc View Source Read(Byte[], Int32, Int32) Declaration int Read(byte[] buffer, int offset, int count) Parameters Type Name Description Byte [] buffer Int32 offset Int32 count Returns Type Description Int32 | Improve this Doc View Source ReadAsync(Byte[], Int32, Int32, CancellationToken) Declaration Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) Parameters Type Name Description Byte [] buffer Int32 offset Int32 count CancellationToken cancellationToken Returns Type Description Task < Int32 >" }, "api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html": { "href": "api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html", "title": "Interface IHttpStreamWriter | Titanium Web Proxy", - "keywords": "Interface IHttpStreamWriter A concrete implementation of this interface is required when calling CopyStream. Namespace : Titanium.Web.Proxy.StreamExtended.Network Assembly : Titanium.Web.Proxy.dll Syntax public interface IHttpStreamWriter Methods | Improve this Doc View Source Write(Byte[], Int32, Int32) Declaration void Write(byte[] buffer, int offset, int count) Parameters Type Name Description Byte [] buffer Int32 offset Int32 count | Improve this Doc View Source WriteAsync(Byte[], Int32, Int32, CancellationToken) Declaration Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) Parameters Type Name Description Byte [] buffer Int32 offset Int32 count CancellationToken cancellationToken Returns Type Description Task | Improve this Doc View Source WriteLineAsync(String, CancellationToken) Declaration ValueTask WriteLineAsync(string value, CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description String value CancellationToken cancellationToken Returns Type Description ValueTask | Improve this Doc View Source WriteLineAsync(CancellationToken) Declaration ValueTask WriteLineAsync(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Returns Type Description ValueTask" + "keywords": "Interface IHttpStreamWriter A concrete implementation of this interface is required when calling CopyStream. Namespace : Titanium.Web.Proxy.StreamExtended.Network Assembly : Titanium.Web.Proxy.dll Syntax public interface IHttpStreamWriter Properties | Improve this Doc View Source IsNetworkStream Declaration bool IsNetworkStream { get; } Property Value Type Description Boolean Methods | Improve this Doc View Source Write(Byte[], Int32, Int32) Declaration void Write(byte[] buffer, int offset, int count) Parameters Type Name Description Byte [] buffer Int32 offset Int32 count | Improve this Doc View Source WriteAsync(Byte[], Int32, Int32, CancellationToken) Declaration Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) Parameters Type Name Description Byte [] buffer Int32 offset Int32 count CancellationToken cancellationToken Returns Type Description Task | Improve this Doc View Source WriteLineAsync(String, CancellationToken) Declaration ValueTask WriteLineAsync(string value, CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description String value CancellationToken cancellationToken Returns Type Description ValueTask | Improve this Doc View Source WriteLineAsync(CancellationToken) Declaration ValueTask WriteLineAsync(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Returns Type Description ValueTask" }, "api/Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html": { "href": "api/Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html", diff --git a/docs/xrefmap.yml b/docs/xrefmap.yml index dbc897cee..2ce3ccc1e 100644 --- a/docs/xrefmap.yml +++ b/docs/xrefmap.yml @@ -327,12 +327,12 @@ references: commentId: T:Titanium.Web.Proxy.EventArguments.SessionEventArgs fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs nameWithType: SessionEventArgs -- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Dispose - name: Dispose() - href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs_Dispose - commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgs.Dispose - fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Dispose() - nameWithType: SessionEventArgs.Dispose() +- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Dispose(System.Boolean) + name: Dispose(Boolean) + href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs_Dispose_System_Boolean_ + commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgs.Dispose(System.Boolean) + fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Dispose(System.Boolean) + nameWithType: SessionEventArgs.Dispose(Boolean) - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Dispose* name: Dispose href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs_Dispose_ @@ -340,6 +340,19 @@ references: isSpec: "True" fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Dispose nameWithType: SessionEventArgs.Dispose +- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Finalize + name: Finalize() + href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs_Finalize + commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgs.Finalize + fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Finalize() + nameWithType: SessionEventArgs.Finalize() +- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Finalize* + name: Finalize + href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs_Finalize_ + commentId: Overload:Titanium.Web.Proxy.EventArguments.SessionEventArgs.Finalize + isSpec: "True" + fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs.Finalize + nameWithType: SessionEventArgs.Finalize - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.GenericResponse(System.Byte[],System.Net.HttpStatusCode,System.Collections.Generic.IDictionary{System.String,Titanium.Web.Proxy.Models.HttpHeader},System.Boolean) name: GenericResponse(Byte[], HttpStatusCode, IDictionary, Boolean) href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs_GenericResponse_System_Byte___System_Net_HttpStatusCode_System_Collections_Generic_IDictionary_System_String_Titanium_Web_Proxy_Models_HttpHeader__System_Boolean_ @@ -754,6 +767,12 @@ references: commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Dispose fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Dispose() nameWithType: SessionEventArgsBase.Dispose() +- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Dispose(System.Boolean) + name: Dispose(Boolean) + href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_Dispose_System_Boolean_ + commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Dispose(System.Boolean) + fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Dispose(System.Boolean) + nameWithType: SessionEventArgsBase.Dispose(Boolean) - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Dispose* name: Dispose href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_Dispose_ @@ -793,6 +812,19 @@ references: commentId: F:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.ExceptionFunc fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.ExceptionFunc nameWithType: SessionEventArgsBase.ExceptionFunc +- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Finalize + name: Finalize() + href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_Finalize + commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Finalize + fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Finalize() + nameWithType: SessionEventArgsBase.Finalize() +- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Finalize* + name: Finalize + href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_Finalize_ + commentId: Overload:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Finalize + isSpec: "True" + fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Finalize + nameWithType: SessionEventArgsBase.Finalize - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.HttpClient name: HttpClient href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_HttpClient @@ -3631,6 +3663,19 @@ references: isSpec: "True" fullName: Titanium.Web.Proxy.Network.CertificateManager.EnsureRootCertificate nameWithType: CertificateManager.EnsureRootCertificate +- uid: Titanium.Web.Proxy.Network.CertificateManager.Finalize + name: Finalize() + href: api/Titanium.Web.Proxy.Network.CertificateManager.html#Titanium_Web_Proxy_Network_CertificateManager_Finalize + commentId: M:Titanium.Web.Proxy.Network.CertificateManager.Finalize + fullName: Titanium.Web.Proxy.Network.CertificateManager.Finalize() + nameWithType: CertificateManager.Finalize() +- uid: Titanium.Web.Proxy.Network.CertificateManager.Finalize* + name: Finalize + href: api/Titanium.Web.Proxy.Network.CertificateManager.html#Titanium_Web_Proxy_Network_CertificateManager_Finalize_ + commentId: Overload:Titanium.Web.Proxy.Network.CertificateManager.Finalize + isSpec: "True" + fullName: Titanium.Web.Proxy.Network.CertificateManager.Finalize + nameWithType: CertificateManager.Finalize - uid: Titanium.Web.Proxy.Network.CertificateManager.IsRootCertificateMachineTrusted name: IsRootCertificateMachineTrusted() href: api/Titanium.Web.Proxy.Network.CertificateManager.html#Titanium_Web_Proxy_Network_CertificateManager_IsRootCertificateMachineTrusted @@ -4191,6 +4236,12 @@ references: commentId: M:Titanium.Web.Proxy.ProxyServer.Dispose fullName: Titanium.Web.Proxy.ProxyServer.Dispose() nameWithType: ProxyServer.Dispose() +- uid: Titanium.Web.Proxy.ProxyServer.Dispose(System.Boolean) + name: Dispose(Boolean) + href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_Dispose_System_Boolean_ + commentId: M:Titanium.Web.Proxy.ProxyServer.Dispose(System.Boolean) + fullName: Titanium.Web.Proxy.ProxyServer.Dispose(System.Boolean) + nameWithType: ProxyServer.Dispose(Boolean) - uid: Titanium.Web.Proxy.ProxyServer.Dispose* name: Dispose href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_Dispose_ @@ -4276,6 +4327,19 @@ references: isSpec: "True" fullName: Titanium.Web.Proxy.ProxyServer.ExceptionFunc nameWithType: ProxyServer.ExceptionFunc +- uid: Titanium.Web.Proxy.ProxyServer.Finalize + name: Finalize() + href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_Finalize + commentId: M:Titanium.Web.Proxy.ProxyServer.Finalize + fullName: Titanium.Web.Proxy.ProxyServer.Finalize() + nameWithType: ProxyServer.Finalize() +- uid: Titanium.Web.Proxy.ProxyServer.Finalize* + name: Finalize + href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_Finalize_ + commentId: Overload:Titanium.Web.Proxy.ProxyServer.Finalize + isSpec: "True" + fullName: Titanium.Web.Proxy.ProxyServer.Finalize + nameWithType: ProxyServer.Finalize - uid: Titanium.Web.Proxy.ProxyServer.ForwardToUpstreamGateway name: ForwardToUpstreamGateway href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_ForwardToUpstreamGateway @@ -4990,15 +5054,12 @@ references: commentId: T:Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader fullName: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader nameWithType: IHttpStreamReader -- uid: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.CopyBodyAsync(Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter,System.Boolean,System.Int64,System.Action{System.Byte[],System.Int32,System.Int32},System.Threading.CancellationToken) - name: CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Action, CancellationToken) - href: api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html#Titanium_Web_Proxy_StreamExtended_Network_IHttpStreamReader_CopyBodyAsync_Titanium_Web_Proxy_StreamExtended_Network_IHttpStreamWriter_System_Boolean_System_Int64_System_Action_System_Byte___System_Int32_System_Int32__System_Threading_CancellationToken_ - commentId: M:Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.CopyBodyAsync(Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter,System.Boolean,System.Int64,System.Action{System.Byte[],System.Int32,System.Int32},System.Threading.CancellationToken) - name.vb: CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Action(Of Byte(), Int32, Int32), CancellationToken) - fullName: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.CopyBodyAsync(Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter, System.Boolean, System.Int64, System.Action, System.Threading.CancellationToken) - fullName.vb: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.CopyBodyAsync(Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter, System.Boolean, System.Int64, System.Action(Of System.Byte(), System.Int32, System.Int32), System.Threading.CancellationToken) - nameWithType: IHttpStreamReader.CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Action, CancellationToken) - nameWithType.vb: IHttpStreamReader.CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Action(Of Byte(), Int32, Int32), CancellationToken) +- uid: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.CopyBodyAsync(Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter,System.Boolean,System.Int64,System.Boolean,Titanium.Web.Proxy.EventArguments.SessionEventArgs,System.Threading.CancellationToken) + name: CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Boolean, SessionEventArgs, CancellationToken) + href: api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html#Titanium_Web_Proxy_StreamExtended_Network_IHttpStreamReader_CopyBodyAsync_Titanium_Web_Proxy_StreamExtended_Network_IHttpStreamWriter_System_Boolean_System_Int64_System_Boolean_Titanium_Web_Proxy_EventArguments_SessionEventArgs_System_Threading_CancellationToken_ + commentId: M:Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.CopyBodyAsync(Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter,System.Boolean,System.Int64,System.Boolean,Titanium.Web.Proxy.EventArguments.SessionEventArgs,System.Threading.CancellationToken) + fullName: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.CopyBodyAsync(Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter, System.Boolean, System.Int64, System.Boolean, Titanium.Web.Proxy.EventArguments.SessionEventArgs, System.Threading.CancellationToken) + nameWithType: IHttpStreamReader.CopyBodyAsync(IHttpStreamWriter, Boolean, Int64, Boolean, SessionEventArgs, CancellationToken) - uid: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.CopyBodyAsync* name: CopyBodyAsync href: api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html#Titanium_Web_Proxy_StreamExtended_Network_IHttpStreamReader_CopyBodyAsync_ @@ -5044,6 +5105,19 @@ references: commentId: T:Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter fullName: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter nameWithType: IHttpStreamWriter +- uid: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.IsNetworkStream + name: IsNetworkStream + href: api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html#Titanium_Web_Proxy_StreamExtended_Network_IHttpStreamWriter_IsNetworkStream + commentId: P:Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.IsNetworkStream + fullName: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.IsNetworkStream + nameWithType: IHttpStreamWriter.IsNetworkStream +- uid: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.IsNetworkStream* + name: IsNetworkStream + href: api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html#Titanium_Web_Proxy_StreamExtended_Network_IHttpStreamWriter_IsNetworkStream_ + commentId: Overload:Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.IsNetworkStream + isSpec: "True" + fullName: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.IsNetworkStream + nameWithType: IHttpStreamWriter.IsNetworkStream - uid: Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.Write(System.Byte[],System.Int32,System.Int32) name: Write(Byte[], Int32, Int32) href: api/Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html#Titanium_Web_Proxy_StreamExtended_Network_IHttpStreamWriter_Write_System_Byte___System_Int32_System_Int32_ diff --git a/examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index 45481912f..047b2ed51 100644 --- a/examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -27,7 +27,7 @@ public class ProxyTestController : IDisposable public ProxyTestController() { - Task.Run(()=> listenToConsole()); + Task.Run(() => listenToConsole()); proxyServer = new ProxyServer(); @@ -97,13 +97,13 @@ public void StartProxy() proxyServer.Start(); // Transparent endpoint is useful for reverse proxy (client is not aware of the existence of proxy) - // A transparent endpoint usually requires a network router port forwarding HTTP(S) packets or DNS - // to send data to this endPoint + // A transparent endpoint usually requires a network router port forwarding HTTP(S) packets + // or by DNS to send data to this endPoint. //var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 443, true) - //{ + //{ // // Generic Certificate hostname to use // // When SNI is disabled by client - // GenericCertificateName = "google.com" + // GenericCertificateName = "localhost" //}; //proxyServer.AddEndPoint(transparentEndPoint); diff --git a/src/Titanium.Web.Proxy/EventArguments/AsyncEventHandler.cs b/src/Titanium.Web.Proxy/AsyncEventHandler.cs similarity index 100% rename from src/Titanium.Web.Proxy/EventArguments/AsyncEventHandler.cs rename to src/Titanium.Web.Proxy/AsyncEventHandler.cs diff --git a/src/Titanium.Web.Proxy/CertificateHandler.cs b/src/Titanium.Web.Proxy/CertificateHandler.cs index c409746c8..1ca07c93b 100644 --- a/src/Titanium.Web.Proxy/CertificateHandler.cs +++ b/src/Titanium.Web.Proxy/CertificateHandler.cs @@ -56,6 +56,7 @@ internal bool ValidateServerCertificate(object sender, SessionEventArgsBase sess { X509Certificate? clientCertificate = null; + //fallback to the first client certificate from proxy machine certificate store if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null && localCertificates.Count > 0) { @@ -69,7 +70,9 @@ internal bool ValidateServerCertificate(object sender, SessionEventArgsBase sess } } - if (localCertificates != null && localCertificates.Count > 0) + //fallback to the first client certificate from proxy machine certificate store + if (clientCertificate == null + && localCertificates != null && localCertificates.Count > 0) { clientCertificate = localCertificates[0]; } @@ -82,7 +85,7 @@ internal bool ValidateServerCertificate(object sender, SessionEventArgsBase sess ClientCertificate = clientCertificate }; - // why is the sender null? + ClientCertificateSelectionCallback.InvokeAsync(this, args, ExceptionFunc).Wait(); return args.ClientCertificate; } diff --git a/src/Titanium.Web.Proxy/Certificates/CachedCertificate.cs b/src/Titanium.Web.Proxy/Certificates/Cache/CachedCertificate.cs similarity index 100% rename from src/Titanium.Web.Proxy/Certificates/CachedCertificate.cs rename to src/Titanium.Web.Proxy/Certificates/Cache/CachedCertificate.cs diff --git a/src/Titanium.Web.Proxy/Certificates/DefaultCertificateDiskCache.cs b/src/Titanium.Web.Proxy/Certificates/Cache/DefaultCertificateDiskCache.cs similarity index 100% rename from src/Titanium.Web.Proxy/Certificates/DefaultCertificateDiskCache.cs rename to src/Titanium.Web.Proxy/Certificates/Cache/DefaultCertificateDiskCache.cs diff --git a/src/Titanium.Web.Proxy/Certificates/ICertificateCache.cs b/src/Titanium.Web.Proxy/Certificates/Cache/ICertificateCache.cs similarity index 100% rename from src/Titanium.Web.Proxy/Certificates/ICertificateCache.cs rename to src/Titanium.Web.Proxy/Certificates/Cache/ICertificateCache.cs diff --git a/src/Titanium.Web.Proxy/Certificates/CertificateManager.cs b/src/Titanium.Web.Proxy/Certificates/CertificateManager.cs index 242ccbdf2..d4992c91a 100644 --- a/src/Titanium.Web.Proxy/Certificates/CertificateManager.cs +++ b/src/Titanium.Web.Proxy/Certificates/CertificateManager.cs @@ -50,12 +50,16 @@ private readonly ConcurrentDictionary cachedCertifica = new ConcurrentDictionary(); /// - /// A list of pending certificate creation tasks. - /// Useful to prevent multiple threads working on same certificate generation + /// Used to prevent multiple threads working on same certificate generation /// when burst certificate generation requests happen for same certificate. /// - private readonly ConcurrentDictionary> pendingCertificateCreationTasks - = new ConcurrentDictionary>(); + private readonly SemaphoreSlim pendingCertificateCreationTaskLock = new SemaphoreSlim(1); + + /// + /// A list of pending certificate creation tasks. + /// + private readonly Dictionary> pendingCertificateCreationTasks + = new Dictionary>(); private readonly CancellationTokenSource clearCertificatesTokenSource = new CancellationTokenSource(); @@ -279,14 +283,6 @@ public ICertificateCache CertificateStorage /// public X509KeyStorageFlags StorageFlag { get; set; } = X509KeyStorageFlags.Exportable; - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - clearCertificatesTokenSource.Dispose(); - } - /// /// For CertificateEngine.DefaultWindows to work we need to also check in personal store /// @@ -412,6 +408,9 @@ private X509Certificate2 makeCertificate(string certificateName, bool isRootCert return certificate; } + private static ConcurrentDictionary saveCertificateLocks + = new ConcurrentDictionary(); + /// /// Create an SSL certificate /// @@ -432,6 +431,12 @@ private X509Certificate2 makeCertificate(string certificateName, bool isRootCert try { certificate = certificateCache.LoadCertificate(subjectName, StorageFlag); + + if (certificate != null && certificate.NotAfter <= DateTime.Now) + { + ExceptionFunc(new Exception($"Cached certificate for {subjectName} has expired.")); + certificate = null; + } } catch (Exception e) { @@ -443,14 +448,33 @@ private X509Certificate2 makeCertificate(string certificateName, bool isRootCert { certificate = makeCertificate(certificateName, false); - try + //Don't need to wait for save to complete + _ = Task.Run(() => { - certificateCache.SaveCertificate(subjectName, certificate); - } - catch (Exception e) - { - ExceptionFunc(new Exception("Failed to save fake certificate.", e)); - } + try + { + var lockKey = subjectName.ToLower(); + //acquire lock by subjectName + //Async lock is not needed. Since this is a rare race-condition + lock (saveCertificateLocks.GetOrAdd(lockKey, new object())) + { + try + { + //no two tasks with same subject name should together enter here + certificateCache.SaveCertificate(subjectName, certificate); + } + finally + { + //save operation is complete. Free lock from memory. + saveCertificateLocks.TryRemove(lockKey, out var _); + } + } + } + catch (Exception e) + { + ExceptionFunc(new Exception("Failed to save fake certificate.", e)); + } + }); } } else @@ -481,29 +505,58 @@ private X509Certificate2 makeCertificate(string certificateName, bool isRootCert return cached.Certificate; } - // handle burst requests with same certificate name - // by checking for existing task for same certificate name - if (pendingCertificateCreationTasks.TryGetValue(certificateName, out var task)) + var createdTask = false; + Task createCertificateTask; + await pendingCertificateCreationTaskLock.WaitAsync(); + try { - return await task; - } + // check in cache first + if (cachedCertificates.TryGetValue(certificateName, out cached)) + { + cached.LastAccess = DateTime.UtcNow; + return cached.Certificate; + } - // run certificate creation task & add it to pending tasks - task = Task.Run(() => - { - var result = CreateCertificate(certificateName, false); - if (result != null) + // handle burst requests with same certificate name + // by checking for existing task for same certificate name + if (!pendingCertificateCreationTasks.TryGetValue(certificateName, out createCertificateTask)) { - cachedCertificates.TryAdd(certificateName, new CachedCertificate(result)); + // run certificate creation task & add it to pending tasks + createCertificateTask = Task.Run(() => + { + var result = CreateCertificate(certificateName, false); + if (result != null) + { + cachedCertificates.TryAdd(certificateName, new CachedCertificate(result)); + } + + return result; + }); + + pendingCertificateCreationTasks[certificateName] = createCertificateTask; + createdTask = true; } + } + finally + { + pendingCertificateCreationTaskLock.Release(); + } - return result; - }); - pendingCertificateCreationTasks.TryAdd(certificateName, task); + var certificate = await createCertificateTask; - // cleanup pending tasks & return result - var certificate = await task; - pendingCertificateCreationTasks.TryRemove(certificateName, out task); + if (createdTask) + { + // cleanup pending task + await pendingCertificateCreationTaskLock.WaitAsync(); + try + { + pendingCertificateCreationTasks.Remove(certificateName); + } + finally + { + pendingCertificateCreationTaskLock.Release(); + } + } return certificate; } @@ -573,14 +626,22 @@ public bool CreateRootCertificate(bool persistToFile = true) var rootCert = certificateCache.LoadRootCertificate(PfxFilePath, PfxPassword, X509KeyStorageFlags.Exportable); - if (rootCert != null) + if (rootCert != null && rootCert.NotAfter <= DateTime.Now) { + ExceptionFunc(new Exception("Loaded root certificate has expired.")); return false; } + + if (rootCert != null) + { + RootCertificate = rootCert; + return true; + } } - catch + catch (Exception e) { // root cert cannot be loaded + ExceptionFunc(new Exception("Root cert cannot be loaded.", e)); } } @@ -601,9 +662,9 @@ public bool CreateRootCertificate(bool persistToFile = true) { certificateCache.Clear(); } - catch + catch (Exception e) { - // ignore + ExceptionFunc(new Exception("An error happened when clearing certificate cache.", e)); } certificateCache.SaveRootCertificate(PfxFilePath, PfxPassword, RootCertificate); @@ -626,7 +687,15 @@ public bool CreateRootCertificate(bool persistToFile = true) { try { - return certificateCache.LoadRootCertificate(PfxFilePath, PfxPassword, X509KeyStorageFlags.Exportable); + var rootCert = certificateCache.LoadRootCertificate(PfxFilePath, PfxPassword, X509KeyStorageFlags.Exportable); + + if (rootCert != null && rootCert.NotAfter <= DateTime.Now) + { + ExceptionFunc(new ArgumentException("Loaded root certificate has expired.")); + return null; + } + + return rootCert; } catch (Exception e) { @@ -923,5 +992,29 @@ public void ClearRootCertificate() cachedCertificates.Clear(); rootCertificate = null; } + + private bool disposed = false; + + void dispose(bool disposing) + { + if (disposed) + { + return; + } + + clearCertificatesTokenSource.Dispose(); + disposed = true; + } + + public void Dispose() + { + dispose(true); + GC.SuppressFinalize(this); + } + + ~CertificateManager() + { + dispose(false); + } } } diff --git a/src/Titanium.Web.Proxy/Certificates/BCCertificateMaker.cs b/src/Titanium.Web.Proxy/Certificates/Makers/BCCertificateMaker.cs similarity index 97% rename from src/Titanium.Web.Proxy/Certificates/BCCertificateMaker.cs rename to src/Titanium.Web.Proxy/Certificates/Makers/BCCertificateMaker.cs index f8b3d4ba7..c4ad9e65c 100644 --- a/src/Titanium.Web.Proxy/Certificates/BCCertificateMaker.cs +++ b/src/Titanium.Web.Proxy/Certificates/Makers/BCCertificateMaker.cs @@ -1,237 +1,237 @@ -using System; -using System.IO; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Generators; -using Org.BouncyCastle.Crypto.Operators; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Crypto.Prng; -using Org.BouncyCastle.Math; -using Org.BouncyCastle.OpenSsl; -using Org.BouncyCastle.Pkcs; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Utilities; -using Org.BouncyCastle.X509; -using Titanium.Web.Proxy.Helpers; -using Titanium.Web.Proxy.Shared; -using X509Certificate = Org.BouncyCastle.X509.X509Certificate; - -namespace Titanium.Web.Proxy.Network.Certificate -{ - /// - /// Implements certificate generation operations. - /// - internal class BCCertificateMaker : ICertificateMaker - { - private readonly int certificateValidDays; - private const int certificateGraceDays = 366; - - // The FriendlyName value cannot be set on Unix. - // Set this flag to true when exception detected to avoid further exceptions - private static bool doNotSetFriendlyName; - - private readonly ExceptionHandler exceptionFunc; - - internal BCCertificateMaker(ExceptionHandler exceptionFunc, int certificateValidDays) - { - this.certificateValidDays = certificateValidDays; - this.exceptionFunc = exceptionFunc; - } - - /// - /// Makes the certificate. - /// - /// The s subject cn. - /// The signing cert. - /// X509Certificate2 instance. - public X509Certificate2 MakeCertificate(string sSubjectCn, X509Certificate2? signingCert = null) - { - return makeCertificateInternal(sSubjectCn, true, signingCert); - } - - /// - /// Generates the certificate. - /// - /// Name of the subject. - /// Name of the issuer. - /// The valid from. - /// The valid to. - /// The key strength. - /// The signature algorithm. - /// The issuer private key. - /// The host name - /// X509Certificate2 instance. - /// Malformed sequence in RSA private key - private static X509Certificate2 generateCertificate(string? hostName, - string subjectName, - string issuerName, DateTime validFrom, - DateTime validTo, int keyStrength = 2048, - string signatureAlgorithm = "SHA256WithRSA", - AsymmetricKeyParameter? issuerPrivateKey = null) - { - // Generating Random Numbers - var randomGenerator = new CryptoApiRandomGenerator(); - var secureRandom = new SecureRandom(randomGenerator); - - // The Certificate Generator - var certificateGenerator = new X509V3CertificateGenerator(); - - // Serial Number - var serialNumber = - BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), secureRandom); - certificateGenerator.SetSerialNumber(serialNumber); - - // Issuer and Subject Name - var subjectDn = new X509Name(subjectName); - var issuerDn = new X509Name(issuerName); - certificateGenerator.SetIssuerDN(issuerDn); - certificateGenerator.SetSubjectDN(subjectDn); - - certificateGenerator.SetNotBefore(validFrom); - certificateGenerator.SetNotAfter(validTo); - - if (hostName != null) - { - // add subject alternative names - var nameType = GeneralName.DnsName; - if (IPAddress.TryParse(hostName, out _)) - { - nameType = GeneralName.IPAddress; - } - - var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(nameType, hostName) }; - - var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames); - certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, - subjectAlternativeNamesExtension); - } - - // Subject Public Key - var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyStrength); - var keyPairGenerator = new RsaKeyPairGenerator(); - keyPairGenerator.Init(keyGenerationParameters); - var subjectKeyPair = keyPairGenerator.GenerateKeyPair(); - - certificateGenerator.SetPublicKey(subjectKeyPair.Public); - - // Set certificate intended purposes to only Server Authentication - certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, - new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth)); - if (issuerPrivateKey == null) - { - certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(true)); - } - - var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, - issuerPrivateKey ?? subjectKeyPair.Private, secureRandom); - - // Self-sign the certificate - var certificate = certificateGenerator.Generate(signatureFactory); - - // Corresponding private key - var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private); - - var seq = (Asn1Sequence)Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded()); - - if (seq.Count != 9) - { - throw new PemException("Malformed sequence in RSA private key"); - } - - var rsa = RsaPrivateKeyStructure.GetInstance(seq); - var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, - rsa.Prime1, rsa.Prime2, rsa.Exponent1, - rsa.Exponent2, rsa.Coefficient); - - // Set private key onto certificate instance - var x509Certificate = withPrivateKey(certificate, rsaparams); - - if (!doNotSetFriendlyName) - { - try - { - x509Certificate.FriendlyName = ProxyConstants.CNRemoverRegex.Replace(subjectName, string.Empty); - } - catch (PlatformNotSupportedException) - { - doNotSetFriendlyName = true; - } - } - - return x509Certificate; - } - - private static X509Certificate2 withPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey) - { - const string password = "password"; - Pkcs12Store store; - - if (RunTime.IsRunningOnMono) - { - var builder = new Pkcs12StoreBuilder(); - builder.SetUseDerEncoding(true); - store = builder.Build(); - } - else - { - store = new Pkcs12Store(); - } - - var entry = new X509CertificateEntry(certificate); - store.SetCertificateEntry(certificate.SubjectDN.ToString(), entry); - - store.SetKeyEntry(certificate.SubjectDN.ToString(), new AsymmetricKeyEntry(privateKey), new[] { entry }); - using (var ms = new MemoryStream()) - { - store.Save(ms, password.ToCharArray(), new SecureRandom(new CryptoApiRandomGenerator())); - - return new X509Certificate2(ms.ToArray(), password, X509KeyStorageFlags.Exportable); - } - } - - /// - /// Makes the certificate internal. - /// - /// hostname for certificate - /// The full subject. - /// The valid from. - /// The valid to. - /// The signing certificate. - /// X509Certificate2 instance. - /// - /// You must specify a Signing Certificate if and only if you are not creating a - /// root. - /// - private X509Certificate2 makeCertificateInternal(string hostName, string subjectName, - DateTime validFrom, DateTime validTo, X509Certificate2? signingCertificate) - { - if (signingCertificate == null) - { - return generateCertificate(null, subjectName, subjectName, validFrom, validTo); - } - - var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey); - return generateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, - issuerPrivateKey: kp.Private); - } - - /// - /// Makes the certificate internal. - /// - /// The s subject cn. - /// if set to true [switch to MTA if needed]. - /// The signing cert. - /// X509Certificate2. - private X509Certificate2 makeCertificateInternal(string subject, - bool switchToMtaIfNeeded, X509Certificate2? signingCert = null) - { - return makeCertificateInternal(subject, $"CN={subject}", - DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), - signingCert); - } - } -} +using System; +using System.IO; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Titanium.Web.Proxy.Helpers; +using Titanium.Web.Proxy.Shared; +using X509Certificate = Org.BouncyCastle.X509.X509Certificate; + +namespace Titanium.Web.Proxy.Network.Certificate +{ + /// + /// Implements certificate generation operations. + /// + internal class BCCertificateMaker : ICertificateMaker + { + private readonly int certificateValidDays; + private const int certificateGraceDays = 366; + + // The FriendlyName value cannot be set on Unix. + // Set this flag to true when exception detected to avoid further exceptions + private static bool doNotSetFriendlyName; + + private readonly ExceptionHandler exceptionFunc; + + internal BCCertificateMaker(ExceptionHandler exceptionFunc, int certificateValidDays) + { + this.certificateValidDays = certificateValidDays; + this.exceptionFunc = exceptionFunc; + } + + /// + /// Makes the certificate. + /// + /// The s subject cn. + /// The signing cert. + /// X509Certificate2 instance. + public X509Certificate2 MakeCertificate(string sSubjectCn, X509Certificate2? signingCert = null) + { + return makeCertificateInternal(sSubjectCn, true, signingCert); + } + + /// + /// Generates the certificate. + /// + /// Name of the subject. + /// Name of the issuer. + /// The valid from. + /// The valid to. + /// The key strength. + /// The signature algorithm. + /// The issuer private key. + /// The host name + /// X509Certificate2 instance. + /// Malformed sequence in RSA private key + private static X509Certificate2 generateCertificate(string? hostName, + string subjectName, + string issuerName, DateTime validFrom, + DateTime validTo, int keyStrength = 2048, + string signatureAlgorithm = "SHA256WithRSA", + AsymmetricKeyParameter? issuerPrivateKey = null) + { + // Generating Random Numbers + var randomGenerator = new CryptoApiRandomGenerator(); + var secureRandom = new SecureRandom(randomGenerator); + + // The Certificate Generator + var certificateGenerator = new X509V3CertificateGenerator(); + + // Serial Number + var serialNumber = + BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), secureRandom); + certificateGenerator.SetSerialNumber(serialNumber); + + // Issuer and Subject Name + var subjectDn = new X509Name(subjectName); + var issuerDn = new X509Name(issuerName); + certificateGenerator.SetIssuerDN(issuerDn); + certificateGenerator.SetSubjectDN(subjectDn); + + certificateGenerator.SetNotBefore(validFrom); + certificateGenerator.SetNotAfter(validTo); + + if (hostName != null) + { + // add subject alternative names + var nameType = GeneralName.DnsName; + if (IPAddress.TryParse(hostName, out _)) + { + nameType = GeneralName.IPAddress; + } + + var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(nameType, hostName) }; + + var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames); + certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, + subjectAlternativeNamesExtension); + } + + // Subject Public Key + var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyStrength); + var keyPairGenerator = new RsaKeyPairGenerator(); + keyPairGenerator.Init(keyGenerationParameters); + var subjectKeyPair = keyPairGenerator.GenerateKeyPair(); + + certificateGenerator.SetPublicKey(subjectKeyPair.Public); + + // Set certificate intended purposes to only Server Authentication + certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, + new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth)); + if (issuerPrivateKey == null) + { + certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(true)); + } + + var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, + issuerPrivateKey ?? subjectKeyPair.Private, secureRandom); + + // Self-sign the certificate + var certificate = certificateGenerator.Generate(signatureFactory); + + // Corresponding private key + var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private); + + var seq = (Asn1Sequence)Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded()); + + if (seq.Count != 9) + { + throw new PemException("Malformed sequence in RSA private key"); + } + + var rsa = RsaPrivateKeyStructure.GetInstance(seq); + var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, + rsa.Prime1, rsa.Prime2, rsa.Exponent1, + rsa.Exponent2, rsa.Coefficient); + + // Set private key onto certificate instance + var x509Certificate = withPrivateKey(certificate, rsaparams); + + if (!doNotSetFriendlyName) + { + try + { + x509Certificate.FriendlyName = ProxyConstants.CNRemoverRegex.Replace(subjectName, string.Empty); + } + catch (PlatformNotSupportedException) + { + doNotSetFriendlyName = true; + } + } + + return x509Certificate; + } + + private static X509Certificate2 withPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey) + { + const string password = "password"; + Pkcs12Store store; + + if (RunTime.IsRunningOnMono) + { + var builder = new Pkcs12StoreBuilder(); + builder.SetUseDerEncoding(true); + store = builder.Build(); + } + else + { + store = new Pkcs12Store(); + } + + var entry = new X509CertificateEntry(certificate); + store.SetCertificateEntry(certificate.SubjectDN.ToString(), entry); + + store.SetKeyEntry(certificate.SubjectDN.ToString(), new AsymmetricKeyEntry(privateKey), new[] { entry }); + using (var ms = new MemoryStream()) + { + store.Save(ms, password.ToCharArray(), new SecureRandom(new CryptoApiRandomGenerator())); + + return new X509Certificate2(ms.ToArray(), password, X509KeyStorageFlags.Exportable); + } + } + + /// + /// Makes the certificate internal. + /// + /// hostname for certificate + /// The full subject. + /// The valid from. + /// The valid to. + /// The signing certificate. + /// X509Certificate2 instance. + /// + /// You must specify a Signing Certificate if and only if you are not creating a + /// root. + /// + private X509Certificate2 makeCertificateInternal(string hostName, string subjectName, + DateTime validFrom, DateTime validTo, X509Certificate2? signingCertificate) + { + if (signingCertificate == null) + { + return generateCertificate(null, subjectName, subjectName, validFrom, validTo); + } + + var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey); + return generateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, + issuerPrivateKey: kp.Private); + } + + /// + /// Makes the certificate internal. + /// + /// The s subject cn. + /// if set to true [switch to MTA if needed]. + /// The signing cert. + /// X509Certificate2. + private X509Certificate2 makeCertificateInternal(string subject, + bool switchToMtaIfNeeded, X509Certificate2? signingCert = null) + { + return makeCertificateInternal(subject, $"CN={subject}", + DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), + signingCert); + } + } +} diff --git a/src/Titanium.Web.Proxy/Certificates/BCCertificateMakerFast.cs b/src/Titanium.Web.Proxy/Certificates/Makers/BCCertificateMakerFast.cs similarity index 100% rename from src/Titanium.Web.Proxy/Certificates/BCCertificateMakerFast.cs rename to src/Titanium.Web.Proxy/Certificates/Makers/BCCertificateMakerFast.cs diff --git a/src/Titanium.Web.Proxy/Certificates/ICertificateMaker.cs b/src/Titanium.Web.Proxy/Certificates/Makers/ICertificateMaker.cs similarity index 100% rename from src/Titanium.Web.Proxy/Certificates/ICertificateMaker.cs rename to src/Titanium.Web.Proxy/Certificates/Makers/ICertificateMaker.cs diff --git a/src/Titanium.Web.Proxy/Certificates/WinCertificateMaker.cs b/src/Titanium.Web.Proxy/Certificates/Makers/WinCertificateMaker.cs similarity index 99% rename from src/Titanium.Web.Proxy/Certificates/WinCertificateMaker.cs rename to src/Titanium.Web.Proxy/Certificates/Makers/WinCertificateMaker.cs index 45f832e65..01fe8ec39 100644 --- a/src/Titanium.Web.Proxy/Certificates/WinCertificateMaker.cs +++ b/src/Titanium.Web.Proxy/Certificates/Makers/WinCertificateMaker.cs @@ -1,5 +1,5 @@ using System; -using System.Net; +using System.Net; using System.Reflection; using System.Security.Cryptography.X509Certificates; using System.Threading; @@ -221,9 +221,9 @@ private X509Certificate2 makeCertificate(string subject, string fullSubject, var altNameCollection = Activator.CreateInstance(typeAltNamesCollection); var extNames = Activator.CreateInstance(typeExtNames); - var altDnsNames = Activator.CreateInstance(typeCAlternativeName); - - IPAddress ip; + var altDnsNames = Activator.CreateInstance(typeCAlternativeName); + + IPAddress ip; if (IPAddress.TryParse(subject, out ip)) { String ipBase64 = Convert.ToBase64String(ip.GetAddressBytes()); @@ -308,45 +308,45 @@ private X509Certificate2 makeCertificate(string subject, string fullSubject, return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable); } - } - - public enum EncodingType - { - XCN_CRYPT_STRING_ANY = 7, - XCN_CRYPT_STRING_BASE64 = 1, - XCN_CRYPT_STRING_BASE64_ANY = 6, - XCN_CRYPT_STRING_BASE64HEADER = 0, - XCN_CRYPT_STRING_BASE64REQUESTHEADER = 3, - XCN_CRYPT_STRING_BASE64URI = 13, - XCN_CRYPT_STRING_BASE64X509CRLHEADER = 9, - XCN_CRYPT_STRING_BINARY = 2, - XCN_CRYPT_STRING_CHAIN = 0x100, - XCN_CRYPT_STRING_ENCODEMASK = 0xff, - XCN_CRYPT_STRING_HASHDATA = 0x10000000, - XCN_CRYPT_STRING_HEX = 4, - XCN_CRYPT_STRING_HEX_ANY = 8, - XCN_CRYPT_STRING_HEXADDR = 10, - XCN_CRYPT_STRING_HEXASCII = 5, - XCN_CRYPT_STRING_HEXASCIIADDR = 11, - XCN_CRYPT_STRING_HEXRAW = 12, - XCN_CRYPT_STRING_NOCR = -2147483648, - XCN_CRYPT_STRING_NOCRLF = 0x40000000, - XCN_CRYPT_STRING_PERCENTESCAPE = 0x8000000, - XCN_CRYPT_STRING_STRICT = 0x20000000, - XCN_CRYPT_STRING_TEXT = 0x200 - } - - public enum AlternativeNameType - { - XCN_CERT_ALT_NAME_DIRECTORY_NAME = 5, - XCN_CERT_ALT_NAME_DNS_NAME = 3, - XCN_CERT_ALT_NAME_GUID = 10, - XCN_CERT_ALT_NAME_IP_ADDRESS = 8, - XCN_CERT_ALT_NAME_OTHER_NAME = 1, - XCN_CERT_ALT_NAME_REGISTERED_ID = 9, - XCN_CERT_ALT_NAME_RFC822_NAME = 2, - XCN_CERT_ALT_NAME_UNKNOWN = 0, - XCN_CERT_ALT_NAME_URL = 7, - XCN_CERT_ALT_NAME_USER_PRINCIPLE_NAME = 11 + } + + public enum EncodingType + { + XCN_CRYPT_STRING_ANY = 7, + XCN_CRYPT_STRING_BASE64 = 1, + XCN_CRYPT_STRING_BASE64_ANY = 6, + XCN_CRYPT_STRING_BASE64HEADER = 0, + XCN_CRYPT_STRING_BASE64REQUESTHEADER = 3, + XCN_CRYPT_STRING_BASE64URI = 13, + XCN_CRYPT_STRING_BASE64X509CRLHEADER = 9, + XCN_CRYPT_STRING_BINARY = 2, + XCN_CRYPT_STRING_CHAIN = 0x100, + XCN_CRYPT_STRING_ENCODEMASK = 0xff, + XCN_CRYPT_STRING_HASHDATA = 0x10000000, + XCN_CRYPT_STRING_HEX = 4, + XCN_CRYPT_STRING_HEX_ANY = 8, + XCN_CRYPT_STRING_HEXADDR = 10, + XCN_CRYPT_STRING_HEXASCII = 5, + XCN_CRYPT_STRING_HEXASCIIADDR = 11, + XCN_CRYPT_STRING_HEXRAW = 12, + XCN_CRYPT_STRING_NOCR = -2147483648, + XCN_CRYPT_STRING_NOCRLF = 0x40000000, + XCN_CRYPT_STRING_PERCENTESCAPE = 0x8000000, + XCN_CRYPT_STRING_STRICT = 0x20000000, + XCN_CRYPT_STRING_TEXT = 0x200 + } + + public enum AlternativeNameType + { + XCN_CERT_ALT_NAME_DIRECTORY_NAME = 5, + XCN_CERT_ALT_NAME_DNS_NAME = 3, + XCN_CERT_ALT_NAME_GUID = 10, + XCN_CERT_ALT_NAME_IP_ADDRESS = 8, + XCN_CERT_ALT_NAME_OTHER_NAME = 1, + XCN_CERT_ALT_NAME_REGISTERED_ID = 9, + XCN_CERT_ALT_NAME_RFC822_NAME = 2, + XCN_CERT_ALT_NAME_UNKNOWN = 0, + XCN_CERT_ALT_NAME_URL = 7, + XCN_CERT_ALT_NAME_USER_PRINCIPLE_NAME = 11 } } diff --git a/src/Titanium.Web.Proxy/EventArguments/BeforeBodyWriteEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/BeforeBodyWriteEventArgs.cs new file mode 100644 index 000000000..e0788e934 --- /dev/null +++ b/src/Titanium.Web.Proxy/EventArguments/BeforeBodyWriteEventArgs.cs @@ -0,0 +1,46 @@ +#if DEBUG +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Titanium.Web.Proxy.EventArguments +{ + + public class BeforeBodyWriteEventArgs : ProxyEventArgsBase + { + internal BeforeBodyWriteEventArgs(SessionEventArgs session, byte[] bodyBytes, bool isChunked, bool isLastChunk) : base(session.Server, session.ClientConnection) + { + Session = session; + BodyBytes = bodyBytes; + IsChunked = isChunked; + IsLastChunk = isLastChunk; + } + + + /// + /// The session arguments. + /// + public SessionEventArgs Session { get; } + + /// + /// Indicates whether body is written chunked stream. + /// If this is true, BeforeRequestBodySend or BeforeResponseBodySend will be called until IsLastChunk is false. + /// + public bool IsChunked { get; } + + /// + /// Indicates if this is the last chunk from client or server stream, when request is chunked. + /// Override this property to true if there are more bytes to write. + /// + public bool IsLastChunk { get; set; } + + /// + /// The bytes about to be written. If IsChunked is true, this will be a chunk of the bytes to be written. + /// Override this property with custom bytes if needed, and adjust IsLastChunk accordingly. + /// + public byte[] BodyBytes { get; set; } + } +} +#endif diff --git a/src/Titanium.Web.Proxy/EventArguments/BeforeSslAuthenticateEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/BeforeSslAuthenticateEventArgs.cs index 3482061db..554cf3c48 100644 --- a/src/Titanium.Web.Proxy/EventArguments/BeforeSslAuthenticateEventArgs.cs +++ b/src/Titanium.Web.Proxy/EventArguments/BeforeSslAuthenticateEventArgs.cs @@ -10,15 +10,16 @@ public class BeforeSslAuthenticateEventArgs : ProxyEventArgsBase { internal readonly CancellationTokenSource TaskCancellationSource; - internal BeforeSslAuthenticateEventArgs(TcpClientConnection clientConnection, CancellationTokenSource taskCancellationSource, string sniHostName) : base(clientConnection) + internal BeforeSslAuthenticateEventArgs(ProxyServer server, TcpClientConnection clientConnection, CancellationTokenSource taskCancellationSource, string sniHostName) : base(server, clientConnection) { TaskCancellationSource = taskCancellationSource; SniHostName = sniHostName; + ForwardHttpsHostName = sniHostName; } /// - /// The server name indication hostname if available. Otherwise the generic certificate hostname of - /// TransparentEndPoint. + /// The server name indication hostname if available. + /// Otherwise the GenericCertificateName property of TransparentEndPoint. /// public string SniHostName { get; } @@ -29,6 +30,25 @@ internal BeforeSslAuthenticateEventArgs(TcpClientConnection clientConnection, Ca /// public bool DecryptSsl { get; set; } = true; + /// + /// We need to know the server hostname we are forwarding the request to. + /// By default its the SNI hostname indicated in SSL handshake, when SNI is available. + /// When SNI is not available, it will use the GenericCertificateName of TransparentEndPoint. + /// This property is used only when DecryptSsl or when BeforeSslAuthenticateEventArgs.DecryptSsl is false. + /// When DecryptSsl is true, we need to explicitly set the Forwarded host and port by setting + /// e.HttpClient.Request.Url inside BeforeRequest event handler. + /// + public string ForwardHttpsHostName { get; set; } + + /// + /// We need to know the server port we are forwarding the request to. + /// By default its the standard https port, 443. + /// This property is used only when DecryptSsl or when BeforeSslAuthenticateEventArgs.DecryptSsl is false. + /// When DecryptSsl is true, we need to explicitly set the Forwarded host and port by setting + /// e.HttpClient.Request.Url inside BeforeRequest event handler. + /// + public int ForwardHttpsPort { get; set; } = 443; + /// /// Terminate the request abruptly by closing client/server connections. /// diff --git a/src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs index 3da92e710..47dc0023c 100644 --- a/src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs +++ b/src/Titanium.Web.Proxy/EventArguments/CertificateSelectionEventArgs.cs @@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.EventArguments public class CertificateSelectionEventArgs : ProxyEventArgsBase { public CertificateSelectionEventArgs(SessionEventArgsBase session, string targetHost, - X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) : base(session.ClientConnection) + X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) : base(session.Server, session.ClientConnection) { Session = session; TargetHost = targetHost; diff --git a/src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs index 50d2a7d53..064eb4d7c 100644 --- a/src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs +++ b/src/Titanium.Web.Proxy/EventArguments/CertificateValidationEventArgs.cs @@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.EventArguments /// public class CertificateValidationEventArgs : ProxyEventArgsBase { - public CertificateValidationEventArgs(SessionEventArgsBase session, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) : base(session.ClientConnection) + public CertificateValidationEventArgs(SessionEventArgsBase session, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) : base(session.Server, session.ClientConnection) { Session = session; Certificate = certificate; diff --git a/src/Titanium.Web.Proxy/StreamExtended/Network/DataEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/DataEventArgs.cs similarity index 100% rename from src/Titanium.Web.Proxy/StreamExtended/Network/DataEventArgs.cs rename to src/Titanium.Web.Proxy/EventArguments/DataEventArgs.cs diff --git a/src/Titanium.Web.Proxy/EventArguments/EmptyProxyEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/EmptyProxyEventArgs.cs index 364c61285..67f6bf295 100644 --- a/src/Titanium.Web.Proxy/EventArguments/EmptyProxyEventArgs.cs +++ b/src/Titanium.Web.Proxy/EventArguments/EmptyProxyEventArgs.cs @@ -4,7 +4,7 @@ namespace Titanium.Web.Proxy.EventArguments { public class EmptyProxyEventArgs : ProxyEventArgsBase { - internal EmptyProxyEventArgs(TcpClientConnection clientConnection) : base(clientConnection) + internal EmptyProxyEventArgs(ProxyServer server, TcpClientConnection clientConnection) : base(server, clientConnection) { } } diff --git a/src/Titanium.Web.Proxy/EventArguments/MultipartRequestPartSentEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/MultipartRequestPartSentEventArgs.cs index 22d4ead8a..bd8b174e6 100644 --- a/src/Titanium.Web.Proxy/EventArguments/MultipartRequestPartSentEventArgs.cs +++ b/src/Titanium.Web.Proxy/EventArguments/MultipartRequestPartSentEventArgs.cs @@ -7,7 +7,7 @@ namespace Titanium.Web.Proxy.EventArguments /// public class MultipartRequestPartSentEventArgs : ProxyEventArgsBase { - internal MultipartRequestPartSentEventArgs(SessionEventArgs session, string boundary, HeaderCollection headers) : base(session.ClientConnection) + internal MultipartRequestPartSentEventArgs(SessionEventArgs session, string boundary, HeaderCollection headers) : base(session.Server, session.ClientConnection) { Session = session; Boundary = boundary; diff --git a/src/Titanium.Web.Proxy/EventArguments/ProxyEventArgsBase.cs b/src/Titanium.Web.Proxy/EventArguments/ProxyEventArgsBase.cs index f115896f1..42bad2d4c 100644 --- a/src/Titanium.Web.Proxy/EventArguments/ProxyEventArgsBase.cs +++ b/src/Titanium.Web.Proxy/EventArguments/ProxyEventArgsBase.cs @@ -10,16 +10,17 @@ namespace Titanium.Web.Proxy.EventArguments public abstract class ProxyEventArgsBase : EventArgs { private readonly TcpClientConnection clientConnection; - + internal readonly ProxyServer Server; public object ClientUserData { get => clientConnection.ClientUserData; set => clientConnection.ClientUserData = value; } - internal ProxyEventArgsBase(TcpClientConnection clientConnection) + internal ProxyEventArgsBase(ProxyServer server, TcpClientConnection clientConnection) { this.clientConnection = clientConnection; + this.Server = server; } } } diff --git a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs index 9da967765..f13ba0453 100644 --- a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs +++ b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs @@ -197,7 +197,7 @@ private async Task readResponseBodyAsync(CancellationToken cancellationToken) private async Task readBodyAsync(bool isRequest, CancellationToken cancellationToken) { using var bodyStream = new MemoryStream(); - using var writer = new HttpStream(bodyStream, BufferPool, cancellationToken); + using var writer = new HttpStream(Server, bodyStream, BufferPool, cancellationToken); if (isRequest) { @@ -228,7 +228,7 @@ internal async Task SyphonOutBodyAsync(bool isRequest, CancellationToken cancell var reader = isRequest ? (HttpStream)ClientStream : HttpClient.Connection.Stream; - await reader.CopyBodyAsync(requestResponse, true, NullWriter.Instance, TransformationMode.None, null, cancellationToken); + await reader.CopyBodyAsync(requestResponse, true, NullWriter.Instance, TransformationMode.None, isRequest, this, cancellationToken); } /// @@ -270,13 +270,13 @@ internal async Task CopyRequestBodyAsync(IHttpStreamWriter writer, Transformatio } else { - await reader.CopyBodyAsync(request, false, writer, transformation, OnDataSent, cancellationToken); + await reader.CopyBodyAsync(request, false, writer, transformation, true, this, cancellationToken); } } private async Task copyResponseBodyAsync(IHttpStreamWriter writer, TransformationMode transformation, CancellationToken cancellationToken) { - await HttpClient.Connection.Stream.CopyBodyAsync(HttpClient.Response, false, writer, transformation, OnDataReceived, cancellationToken); + await HttpClient.Connection.Stream.CopyBodyAsync(HttpClient.Response, false, writer, transformation, false, this, cancellationToken); } /// @@ -665,13 +665,23 @@ public void TerminateServerConnection() HttpClient.CloseServerConnection = true; } - /// - /// Implement any cleanup here - /// - public override void Dispose() + private bool disposed = false; + protected override void Dispose(bool disposing) { + if (disposed) + { + return; + } + MultipartRequestPartSent = null; - base.Dispose(); + disposed = true; + + base.Dispose(disposing); + } + + ~SessionEventArgs() + { + Dispose(false); } } } diff --git a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs index 88d82ac05..8828999c8 100644 --- a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs +++ b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs @@ -49,7 +49,7 @@ public abstract class SessionEventArgsBase : ProxyEventArgsBase, IDisposable /// Initializes a new instance of the class. /// private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint, - HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource) : base(clientStream.Connection) + HttpClientStream clientStream, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource) : base(server, clientStream.Connection) { BufferPool = server.BufferPool; ExceptionFunc = server.ExceptionFunc; @@ -150,18 +150,39 @@ public bool EnableWinAuth /// public Exception? Exception { get; internal set; } - /// - /// Implements cleanup here. - /// - public virtual void Dispose() + private bool disposed = false; + + protected virtual void Dispose(bool disposing) { - CustomUpStreamProxyUsed = null; + if (disposed) + { + return; + } + + disposed = true; + + if (disposing) + { + CustomUpStreamProxyUsed = null; - DataSent = null; - DataReceived = null; - Exception = null; + DataSent = null; + DataReceived = null; + Exception = null; + } HttpClient.FinishSession(); + + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~SessionEventArgsBase() + { + Dispose(false); } /// diff --git a/src/Titanium.Web.Proxy/Exceptions/ServerConnectionException.cs b/src/Titanium.Web.Proxy/Exceptions/RetryableServerConnectionException.cs similarity index 51% rename from src/Titanium.Web.Proxy/Exceptions/ServerConnectionException.cs rename to src/Titanium.Web.Proxy/Exceptions/RetryableServerConnectionException.cs index d885c74e7..ad07df990 100644 --- a/src/Titanium.Web.Proxy/Exceptions/ServerConnectionException.cs +++ b/src/Titanium.Web.Proxy/Exceptions/RetryableServerConnectionException.cs @@ -3,12 +3,12 @@ namespace Titanium.Web.Proxy.Exceptions { /// - /// The server connection was closed upon first read with the new connection from pool. + /// The server connection was closed upon first write with the new connection from pool. /// Should retry the request with a new connection. /// - public class ServerConnectionException : ProxyException + public class RetryableServerConnectionException : ProxyException { - internal ServerConnectionException(string message) : base(message) + internal RetryableServerConnectionException(string message) : base(message) { } @@ -17,9 +17,8 @@ internal ServerConnectionException(string message) : base(message) /// /// /// - internal ServerConnectionException(string message, Exception e) : base(message, e) + internal RetryableServerConnectionException(string message, Exception e) : base(message, e) { } - } } diff --git a/src/Titanium.Web.Proxy/ExplicitClientHandler.cs b/src/Titanium.Web.Proxy/ExplicitClientHandler.cs index fb58a6c47..b40e80e2f 100644 --- a/src/Titanium.Web.Proxy/ExplicitClientHandler.cs +++ b/src/Titanium.Web.Proxy/ExplicitClientHandler.cs @@ -33,7 +33,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; - var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken); + var clientStream = new HttpClientStream(this, clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken); Task? prefetchConnectionTask = null; bool closeServerConnection = false; @@ -211,7 +211,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect #endif // HTTPS server created - we can now decrypt the client's traffic - clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool, cancellationToken); + clientStream = new HttpClientStream(this, clientStream.Connection, sslStream, BufferPool, cancellationToken); sslStream = null; // clientStream was created, no need to keep SSL stream reference clientStream.DataRead += (o, args) => connectArgs.OnDecryptedDataSent(args.Buffer, args.Offset, args.Count); diff --git a/src/Titanium.Web.Proxy/Compression/CompressionUtil.cs b/src/Titanium.Web.Proxy/Helpers/CompressionUtil.cs similarity index 100% rename from src/Titanium.Web.Proxy/Compression/CompressionUtil.cs rename to src/Titanium.Web.Proxy/Helpers/CompressionUtil.cs diff --git a/src/Titanium.Web.Proxy/Helpers/HttpServerStream.cs b/src/Titanium.Web.Proxy/Helpers/HttpServerStream.cs deleted file mode 100644 index 8376f19fe..000000000 --- a/src/Titanium.Web.Proxy/Helpers/HttpServerStream.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Titanium.Web.Proxy.Exceptions; -using Titanium.Web.Proxy.Http; -using Titanium.Web.Proxy.StreamExtended.BufferPool; - -namespace Titanium.Web.Proxy.Helpers -{ - internal sealed class HttpServerStream : HttpStream - { - internal HttpServerStream(Stream stream, IBufferPool bufferPool, CancellationToken cancellationToken) - : base(stream, bufferPool, cancellationToken) - { - } - - /// - /// Writes the request. - /// - /// The request object. - /// Optional cancellation token for this async task. - /// - internal async ValueTask WriteRequestAsync(Request request, CancellationToken cancellationToken = default) - { - var headerBuilder = new HeaderBuilder(); - headerBuilder.WriteRequestLine(request.Method, request.RequestUriString, request.HttpVersion); - await WriteAsync(request, headerBuilder, cancellationToken); - } - - internal async ValueTask ReadResponseStatus(CancellationToken cancellationToken = default) - { - try - { - string httpStatus = await ReadLineAsync(cancellationToken) ?? - throw new ServerConnectionException("Server connection was closed."); - - if (httpStatus == string.Empty) - { - // is this really possible? - httpStatus = await ReadLineAsync(cancellationToken) ?? - throw new ServerConnectionException("Server connection was closed. Response status is empty."); - } - - Response.ParseResponseLine(httpStatus, out var version, out int statusCode, out string description); - return new ResponseStatusInfo { Version = version, StatusCode = statusCode, Description = description }; - } - catch (Exception e) when (!(e is ServerConnectionException)) - { - throw new ServerConnectionException("Server connection was closed. Exception while reading the response status.", e); - } - } - } -} diff --git a/src/Titanium.Web.Proxy/Helpers/Network.cs b/src/Titanium.Web.Proxy/Helpers/Network.cs index 8402ccd1e..1cb6f7acb 100644 --- a/src/Titanium.Web.Proxy/Helpers/Network.cs +++ b/src/Titanium.Web.Proxy/Helpers/Network.cs @@ -27,7 +27,7 @@ internal static bool IsLocalIpAddress(IPAddress address) return localhostEntry.AddressList.Contains(address); } - internal static bool IsLocalIpAddress(string hostName) + internal static bool IsLocalIpAddress(string hostName, bool proxyDnsRequests = false) { if (IPAddress.TryParse(hostName, out var ipAddress) && IsLocalIpAddress(ipAddress)) @@ -52,20 +52,23 @@ internal static bool IsLocalIpAddress(string hostName) return true; } - try + if (!proxyDnsRequests) { - // do reverse DNS lookup even if hostName is an IP address - var hostEntry = Dns.GetHostEntry(hostName); - // if DNS resolved hostname matches local DNS name, - // or if host IP address list contains any local IP address - if (hostEntry.HostName.Equals(localhostEntry.HostName, StringComparison.OrdinalIgnoreCase) - || hostEntry.AddressList.Any(hostIP => localhostEntry.AddressList.Contains(hostIP))) + try + { + // do reverse DNS lookup even if hostName is an IP address + var hostEntry = Dns.GetHostEntry(hostName); + // if DNS resolved hostname matches local DNS name, + // or if host IP address list contains any local IP address + if (hostEntry.HostName.Equals(localhostEntry.HostName, StringComparison.OrdinalIgnoreCase) + || hostEntry.AddressList.Any(hostIP => localhostEntry.AddressList.Contains(hostIP))) + { + return true; + } + } + catch (SocketException) { - return true; } - } - catch (SocketException) - { } return false; diff --git a/src/Titanium.Web.Proxy/Helpers/WinHttp/WinHttpWebProxyFinder.cs b/src/Titanium.Web.Proxy/Helpers/WinHttp/WinHttpWebProxyFinder.cs index 1aed46492..e20a7c677 100644 --- a/src/Titanium.Web.Proxy/Helpers/WinHttp/WinHttpWebProxyFinder.cs +++ b/src/Titanium.Web.Proxy/Helpers/WinHttp/WinHttpWebProxyFinder.cs @@ -48,11 +48,6 @@ public WinHttpWebProxyFinder() private WebProxy? proxy { get; set; } - public void Dispose() - { - dispose(true); - } - public bool GetAutoProxies(Uri destination, out IList? proxyList) { proxyList = null; @@ -191,16 +186,6 @@ public void Reset() autoDetectFailed = false; } - private void dispose(bool disposing) - { - if (!disposing || session == null || session.IsInvalid) - { - return; - } - - session.Close(); - } - private int getAutoProxies(Uri destination, Uri? scriptLocation, out string? proxyListString) { int num = 0; @@ -352,5 +337,35 @@ private enum AutoWebProxyState UnrecognizedScheme, Completed } + + private bool disposed = false; + + void dispose(bool disposing) + { + if (disposed) + { + return; + } + + disposed = true; + + if (session == null || session.IsInvalid) + { + return; + } + + session.Close(); + } + + public void Dispose() + { + dispose(true); + GC.SuppressFinalize(this); + } + + ~WinHttpWebProxyFinder() + { + dispose(false); + } } } diff --git a/src/Titanium.Web.Proxy/Compression/HttpCompression.cs b/src/Titanium.Web.Proxy/Models/HttpCompression.cs similarity index 100% rename from src/Titanium.Web.Proxy/Compression/HttpCompression.cs rename to src/Titanium.Web.Proxy/Models/HttpCompression.cs diff --git a/src/Titanium.Web.Proxy/Helpers/KnownMethod.cs b/src/Titanium.Web.Proxy/Models/KnownMethod.cs similarity index 100% rename from src/Titanium.Web.Proxy/Helpers/KnownMethod.cs rename to src/Titanium.Web.Proxy/Models/KnownMethod.cs diff --git a/src/Titanium.Web.Proxy/Helpers/RequestStatusInfo.cs b/src/Titanium.Web.Proxy/Models/RequestStatusInfo.cs similarity index 100% rename from src/Titanium.Web.Proxy/Helpers/RequestStatusInfo.cs rename to src/Titanium.Web.Proxy/Models/RequestStatusInfo.cs diff --git a/src/Titanium.Web.Proxy/Helpers/ResponseStatusInfo.cs b/src/Titanium.Web.Proxy/Models/ResponseStatusInfo.cs similarity index 100% rename from src/Titanium.Web.Proxy/Helpers/ResponseStatusInfo.cs rename to src/Titanium.Web.Proxy/Models/ResponseStatusInfo.cs diff --git a/src/Titanium.Web.Proxy/Models/TransparentBaseProxyEndPoint.cs b/src/Titanium.Web.Proxy/Models/TransparentBaseProxyEndPoint.cs index 70a7153a3..9ebca387f 100644 --- a/src/Titanium.Web.Proxy/Models/TransparentBaseProxyEndPoint.cs +++ b/src/Titanium.Web.Proxy/Models/TransparentBaseProxyEndPoint.cs @@ -5,7 +5,12 @@ namespace Titanium.Web.Proxy.Models { public abstract class TransparentBaseProxyEndPoint : ProxyEndPoint - { + { + /// + /// The hostname of the generic certificate to negotiate SSL. + /// This will be only used when Sever Name Indication (SNI) is not supported by client, + /// or when it does not indicate any host name. + /// public abstract string GenericCertificateName { get; set; } protected TransparentBaseProxyEndPoint(IPAddress ipAddress, int port, bool decryptSsl) : base(ipAddress, port, decryptSsl) diff --git a/src/Titanium.Web.Proxy/StreamExtended/BufferPool/DefaultBufferPool.cs b/src/Titanium.Web.Proxy/Network/BufferPool/DefaultBufferPool.cs similarity index 96% rename from src/Titanium.Web.Proxy/StreamExtended/BufferPool/DefaultBufferPool.cs rename to src/Titanium.Web.Proxy/Network/BufferPool/DefaultBufferPool.cs index a1b169d09..96cdf10ff 100644 --- a/src/Titanium.Web.Proxy/StreamExtended/BufferPool/DefaultBufferPool.cs +++ b/src/Titanium.Web.Proxy/Network/BufferPool/DefaultBufferPool.cs @@ -46,6 +46,7 @@ public void ReturnBuffer(byte[] buffer) public void Dispose() { + //Nothing to dispose. But need for the interface } } } diff --git a/src/Titanium.Web.Proxy/StreamExtended/BufferPool/IBufferPool.cs b/src/Titanium.Web.Proxy/Network/BufferPool/IBufferPool.cs similarity index 100% rename from src/Titanium.Web.Proxy/StreamExtended/BufferPool/IBufferPool.cs rename to src/Titanium.Web.Proxy/Network/BufferPool/IBufferPool.cs diff --git a/src/Titanium.Web.Proxy/Http/HttpWebClient.cs b/src/Titanium.Web.Proxy/Network/HttpWebClient.cs similarity index 100% rename from src/Titanium.Web.Proxy/Http/HttpWebClient.cs rename to src/Titanium.Web.Proxy/Network/HttpWebClient.cs diff --git a/src/Titanium.Web.Proxy/StreamExtended/Models/SslCiphers.cs b/src/Titanium.Web.Proxy/Network/Models/SslCiphers.cs similarity index 100% rename from src/Titanium.Web.Proxy/StreamExtended/Models/SslCiphers.cs rename to src/Titanium.Web.Proxy/Network/Models/SslCiphers.cs diff --git a/src/Titanium.Web.Proxy/StreamExtended/Models/SslExtension.cs b/src/Titanium.Web.Proxy/Network/Models/SslExtension.cs similarity index 100% rename from src/Titanium.Web.Proxy/StreamExtended/Models/SslExtension.cs rename to src/Titanium.Web.Proxy/Network/Models/SslExtension.cs diff --git a/src/Titanium.Web.Proxy/StreamExtended/Network/TaskResult.cs b/src/Titanium.Web.Proxy/Network/Models/TaskResult.cs similarity index 100% rename from src/Titanium.Web.Proxy/StreamExtended/Network/TaskResult.cs rename to src/Titanium.Web.Proxy/Network/Models/TaskResult.cs diff --git a/src/Titanium.Web.Proxy/StreamExtended/Network/IHttpStreamReader.cs b/src/Titanium.Web.Proxy/Network/Readers/IHttpStreamReader.cs similarity index 77% rename from src/Titanium.Web.Proxy/StreamExtended/Network/IHttpStreamReader.cs rename to src/Titanium.Web.Proxy/Network/Readers/IHttpStreamReader.cs index 3bb6eed21..bcee2686b 100644 --- a/src/Titanium.Web.Proxy/StreamExtended/Network/IHttpStreamReader.cs +++ b/src/Titanium.Web.Proxy/Network/Readers/IHttpStreamReader.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Titanium.Web.Proxy.EventArguments; namespace Titanium.Web.Proxy.StreamExtended.Network { @@ -11,6 +12,6 @@ public interface IHttpStreamReader : ILineStream Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken); Task CopyBodyAsync(IHttpStreamWriter writer, bool isChunked, long contentLength, - Action? onCopy, CancellationToken cancellationToken); + bool isRequest, SessionEventArgs args, CancellationToken cancellationToken); } } diff --git a/src/Titanium.Web.Proxy/StreamExtended/Network/PeekStreamReader.cs b/src/Titanium.Web.Proxy/Network/Readers/PeekStreamReader.cs similarity index 100% rename from src/Titanium.Web.Proxy/StreamExtended/Network/PeekStreamReader.cs rename to src/Titanium.Web.Proxy/Network/Readers/PeekStreamReader.cs diff --git a/src/Titanium.Web.Proxy/Network/RetryPolicy.cs b/src/Titanium.Web.Proxy/Network/RetryPolicy.cs index a2f724554..19863cba0 100644 --- a/src/Titanium.Web.Proxy/Network/RetryPolicy.cs +++ b/src/Titanium.Web.Proxy/Network/RetryPolicy.cs @@ -35,14 +35,12 @@ internal async Task ExecuteAsync(Func reader.DataAvailable; public long ReadBytes { get; private set; } @@ -65,13 +63,28 @@ public byte ReadByteFromBuffer() return HttpStream.ReadLineInternalAsync(this, bufferPool, cancellationToken); } - public void Dispose() + private bool disposed = false; + + protected virtual void Dispose(bool disposing) { - if (!disposed) + if (disposed) { - disposed = true; - bufferPool.ReturnBuffer(buffer); + return; } + + disposed = true; + bufferPool.ReturnBuffer(buffer); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~CopyStream() + { + Dispose(false); } } } diff --git a/src/Titanium.Web.Proxy/Helpers/HttpClientStream.cs b/src/Titanium.Web.Proxy/Network/Streams/HttpClientStream.cs similarity index 88% rename from src/Titanium.Web.Proxy/Helpers/HttpClientStream.cs rename to src/Titanium.Web.Proxy/Network/Streams/HttpClientStream.cs index 7a13157e8..57dc88d0a 100644 --- a/src/Titanium.Web.Proxy/Helpers/HttpClientStream.cs +++ b/src/Titanium.Web.Proxy/Network/Streams/HttpClientStream.cs @@ -11,8 +11,8 @@ internal sealed class HttpClientStream : HttpStream { public TcpClientConnection Connection { get; } - internal HttpClientStream(TcpClientConnection connection, Stream stream, IBufferPool bufferPool, CancellationToken cancellationToken) - : base(stream, bufferPool, cancellationToken) + internal HttpClientStream(ProxyServer server, TcpClientConnection connection, Stream stream, IBufferPool bufferPool, CancellationToken cancellationToken) + : base(server, stream, bufferPool, cancellationToken) { Connection = connection; } diff --git a/src/Titanium.Web.Proxy/Network/Streams/HttpServerStream.cs b/src/Titanium.Web.Proxy/Network/Streams/HttpServerStream.cs new file mode 100644 index 000000000..a646d64b8 --- /dev/null +++ b/src/Titanium.Web.Proxy/Network/Streams/HttpServerStream.cs @@ -0,0 +1,48 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Titanium.Web.Proxy.Exceptions; +using Titanium.Web.Proxy.Http; +using Titanium.Web.Proxy.StreamExtended.BufferPool; + +namespace Titanium.Web.Proxy.Helpers +{ + internal sealed class HttpServerStream : HttpStream + { + internal HttpServerStream(ProxyServer server, Stream stream, IBufferPool bufferPool, CancellationToken cancellationToken) + : base(server, stream, bufferPool, cancellationToken) + { + } + + /// + /// Writes the request. + /// + /// The request object. + /// Optional cancellation token for this async task. + /// + internal async ValueTask WriteRequestAsync(Request request, CancellationToken cancellationToken = default) + { + var headerBuilder = new HeaderBuilder(); + headerBuilder.WriteRequestLine(request.Method, request.RequestUriString, request.HttpVersion); + await WriteAsync(request, headerBuilder, cancellationToken); + } + + internal async ValueTask ReadResponseStatus(CancellationToken cancellationToken = default) + { + string httpStatus = await ReadLineAsync(cancellationToken) ?? + throw new IOException("Invalid http status code."); + + if (httpStatus == string.Empty) + { + // is this really possible? + httpStatus = await ReadLineAsync(cancellationToken) ?? + throw new IOException("Response status is empty."); + } + + Response.ParseResponseLine(httpStatus, out var version, out int statusCode, out string description); + return new ResponseStatusInfo { Version = version, StatusCode = statusCode, Description = description }; + + } + } +} diff --git a/src/Titanium.Web.Proxy/Helpers/HttpStream.cs b/src/Titanium.Web.Proxy/Network/Streams/HttpStream.cs similarity index 91% rename from src/Titanium.Web.Proxy/Helpers/HttpStream.cs rename to src/Titanium.Web.Proxy/Network/Streams/HttpStream.cs index 06c7aa844..9e38996ee 100644 --- a/src/Titanium.Web.Proxy/Helpers/HttpStream.cs +++ b/src/Titanium.Web.Proxy/Network/Streams/HttpStream.cs @@ -41,6 +41,8 @@ internal class HttpStream : Stream, IHttpStreamWriter, IHttpStreamReader, IPeekS private readonly IBufferPool bufferPool; private readonly CancellationToken cancellationToken; + public bool IsNetworkStream => isNetworkStream; + public event EventHandler? DataRead; public event EventHandler? DataWrite; @@ -68,7 +70,7 @@ static HttpStream() } private static readonly byte[] newLine = ProxyConstants.NewLineBytes; - + private readonly ProxyServer server; /// /// Initializes a new instance of the class. /// @@ -76,8 +78,10 @@ static HttpStream() /// Bufferpool. /// The cancellation token. /// to leave the stream open after disposing the object; otherwise, . - internal HttpStream(Stream baseStream, IBufferPool bufferPool, CancellationToken cancellationToken, bool leaveOpen = false) + internal HttpStream(ProxyServer server, Stream baseStream, IBufferPool bufferPool, CancellationToken cancellationToken, bool leaveOpen = false) { + this.server = server; + if (baseStream is NetworkStream) { isNetworkStream = true; @@ -928,7 +932,21 @@ public ValueTask WriteLineAsync(string value, CancellationToken cancellationToke internal async Task WriteHeadersAsync(HeaderBuilder headerBuilder, CancellationToken cancellationToken = default) { var buffer = headerBuilder.GetBuffer(); - await WriteAsync(buffer.Array, buffer.Offset, buffer.Count, true, cancellationToken); + + try + { + await WriteAsync(buffer.Array, buffer.Offset, buffer.Count, true, cancellationToken); + } + catch (IOException e) + { + //throw this as ServerConnectionException so that RetryPolicy can retry with a new server connection. + if (this is HttpServerStream) + { + throw new RetryableServerConnectionException("Server connection was closed. Exception while sending request line and headers.", e); + } + + throw; + } } /// @@ -1001,14 +1019,14 @@ internal ValueTask WriteBodyAsync(byte[] data, bool isChunked, CancellationToken return WriteAsync(data, cancellationToken: cancellationToken); } - public async Task CopyBodyAsync(RequestResponseBase requestResponse, bool useOriginalHeaderValues, IHttpStreamWriter writer, TransformationMode transformation, Action? onCopy, CancellationToken cancellationToken) + public async Task CopyBodyAsync(RequestResponseBase requestResponse, bool useOriginalHeaderValues, IHttpStreamWriter writer, TransformationMode transformation, bool isRequest, SessionEventArgs args, CancellationToken cancellationToken) { bool isChunked = useOriginalHeaderValues ? requestResponse.OriginalIsChunked : requestResponse.IsChunked; long contentLength = useOriginalHeaderValues ? requestResponse.OriginalContentLength : requestResponse.ContentLength; if (transformation == TransformationMode.None) { - await CopyBodyAsync(writer, isChunked, contentLength, onCopy, cancellationToken); + await CopyBodyAsync(writer, isChunked, contentLength, isRequest, args, cancellationToken); return; } @@ -1026,8 +1044,8 @@ public async Task CopyBodyAsync(RequestResponseBase requestResponse, bool useOri try { - var http = new HttpStream(s, bufferPool, cancellationToken, true); - await http.CopyBodyAsync(writer, false, -1, onCopy, cancellationToken); + var http = new HttpStream(server, s, bufferPool, cancellationToken, true); + await http.CopyBodyAsync(writer, false, -1, isRequest, args, cancellationToken); } finally { @@ -1049,12 +1067,24 @@ public async Task CopyBodyAsync(RequestResponseBase requestResponse, bool useOri /// /// public Task CopyBodyAsync(IHttpStreamWriter writer, bool isChunked, long contentLength, - Action? onCopy, CancellationToken cancellationToken) + bool isRequest, + SessionEventArgs args, CancellationToken cancellationToken) { + +#if DEBUG + var isResponse = !isRequest; + + if (isNetworkStream && writer.IsNetworkStream && + (isRequest && args.HttpClient.Request.OriginalHasBody && !args.HttpClient.Request.IsBodyRead && server.ShouldCallBeforeRequestBodyWrite()) || + (isResponse && args.HttpClient.Response.OriginalHasBody && !args.HttpClient.Response.IsBodyRead && server.ShouldCallBeforeResponseBodyWrite())) + { + return handleBodyWrite(writer, isChunked, contentLength, isRequest, args, cancellationToken); + } +#endif // For chunked request we need to read data as they arrive, until we reach a chunk end symbol if (isChunked) { - return copyBodyChunkedAsync(writer, onCopy, cancellationToken); + return copyBodyChunkedAsync(writer, isRequest, args, cancellationToken); } // http 1.0 or the stream reader limits the stream @@ -1064,7 +1094,27 @@ public Task CopyBodyAsync(IHttpStreamWriter writer, bool isChunked, long content } // If not chunked then its easy just read the amount of bytes mentioned in content length header - return copyBytesToStream(writer, contentLength, onCopy, cancellationToken); + return copyBytesToStream(writer, contentLength, isRequest, args, cancellationToken); + } + + private Task handleBodyWrite(IHttpStreamWriter writer, bool isChunked, long contentLength, + bool isRequest, SessionEventArgs args, CancellationToken cancellationToken) + { + var originalContentLength = isRequest ? args.HttpClient.Request.OriginalContentLength : args.HttpClient.Response.OriginalContentLength; + var originalIsChunked = isRequest ? args.HttpClient.Request.OriginalIsChunked : args.HttpClient.Response.OriginalIsChunked; + + //TODO + //create a new decompression stream to wrap this source HttpStream based on original content encoding if needed. + //create a new compression stream to wrap target writer stream based on content encoding if needed. + + //1. Begin while(true) loop + //2. Parse chunk if chunked, and read bytes from original stream. Max length of bytes read will be equal to bufferPool.BufferSize. + //3. Call BeforeBodyWrite event handler with BeforeBodyWriteEventArgs.BodyBytes set to the bytes read from original stream (pass null if original stream reached its end). + //4. Write BeforeBodyWriteEventArgs.BodyBytes to the target stream when BeforeBodyWriteEventArgs.BodyBytes is not null or empty. + //5. Stop writing to target stream when 'long contentLength' parameter number of bytes are written (when not chunked) or + //when BeforeBodyWriteEventArgs.IsLastChunk is true after callback (when chunked). + //6. Exit loop when original stream reaches its end AND when writing in step 5 has stopped. + throw new NotImplementedException(); } /// @@ -1093,7 +1143,7 @@ private async ValueTask writeBodyChunkedAsync(byte[] data, CancellationToken can /// /// /// - private async Task copyBodyChunkedAsync(IHttpStreamWriter writer, Action? onCopy, CancellationToken cancellationToken) + private async Task copyBodyChunkedAsync(IHttpStreamWriter writer, bool isRequest, SessionEventArgs args, CancellationToken cancellationToken) { while (true) { @@ -1118,7 +1168,7 @@ private async Task copyBodyChunkedAsync(IHttpStreamWriter writer, Action /// /// - private async Task copyBytesToStream(IHttpStreamWriter writer, long count, Action? onCopy, + private async Task copyBytesToStream(IHttpStreamWriter writer, long count, bool isRequest, SessionEventArgs args, CancellationToken cancellationToken) { var buffer = bufferPool.GetBuffer(); @@ -1168,7 +1218,10 @@ private async Task copyBytesToStream(IHttpStreamWriter writer, long count, Actio await writer.WriteAsync(buffer, 0, bytesRead, cancellationToken); - onCopy?.Invoke(buffer, 0, bytesRead); + if (isRequest) + args.OnDataSent(buffer, 0, bytesRead); + else + args.OnDataReceived(buffer, 0, bytesRead); } } finally @@ -1187,7 +1240,6 @@ private async Task copyBytesToStream(IHttpStreamWriter writer, long count, Actio protected async ValueTask WriteAsync(RequestResponseBase requestResponse, HeaderBuilder headerBuilder, CancellationToken cancellationToken = default) { var body = requestResponse.CompressBodyAndUpdateContentLength(); - headerBuilder.WriteHeaders(requestResponse.Headers); await WriteHeadersAsync(headerBuilder, cancellationToken); diff --git a/src/Titanium.Web.Proxy/StreamExtended/Network/ILineStream.cs b/src/Titanium.Web.Proxy/Network/Streams/ILineStream.cs similarity index 100% rename from src/Titanium.Web.Proxy/StreamExtended/Network/ILineStream.cs rename to src/Titanium.Web.Proxy/Network/Streams/ILineStream.cs diff --git a/src/Titanium.Web.Proxy/StreamExtended/Network/IPeekStream.cs b/src/Titanium.Web.Proxy/Network/Streams/IPeekStream.cs similarity index 100% rename from src/Titanium.Web.Proxy/StreamExtended/Network/IPeekStream.cs rename to src/Titanium.Web.Proxy/Network/Streams/IPeekStream.cs diff --git a/src/Titanium.Web.Proxy/EventArguments/LimitedStream.cs b/src/Titanium.Web.Proxy/Network/Streams/LimitedStream.cs similarity index 100% rename from src/Titanium.Web.Proxy/EventArguments/LimitedStream.cs rename to src/Titanium.Web.Proxy/Network/Streams/LimitedStream.cs diff --git a/src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpClientConnection.cs similarity index 88% rename from src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs rename to src/Titanium.Web.Proxy/Network/TcpConnection/TcpClientConnection.cs index 850d63661..a1745fb37 100644 --- a/src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs +++ b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpClientConnection.cs @@ -73,11 +73,15 @@ public int GetProcessId(ProxyEndPoint endPoint) throw new PlatformNotSupportedException(); } - /// - /// Dispose. - /// - public void Dispose() + private bool disposed = false; + + protected virtual void Dispose(bool disposing) { + if (disposed) + { + return; + } + Task.Run(async () => { // delay calling tcp connection close() @@ -95,6 +99,19 @@ public void Dispose() // ignore } }); + + disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~TcpClientConnection() + { + Dispose(false); } } } diff --git a/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs similarity index 94% rename from src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs rename to src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs index 09d8c534d..5d1c433fb 100644 --- a/src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs +++ b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs @@ -242,19 +242,22 @@ internal Task GetServerConnection(ProxyServer proxyServer, { if (cache.TryGetValue(cacheKey, out var existingConnections)) { - // +3 seconds for potential delay after getting connection - var cutOff = DateTime.UtcNow.AddSeconds(-proxyServer.ConnectionTimeOutSeconds + 3); - while (existingConnections.Count > 0) + lock (existingConnections) { - if (existingConnections.TryDequeue(out var recentConnection)) + // +3 seconds for potential delay after getting connection + var cutOff = DateTime.UtcNow.AddSeconds(-proxyServer.ConnectionTimeOutSeconds + 3); + while (existingConnections.Count > 0) { - if (recentConnection.LastAccess > cutOff - && recentConnection.TcpSocket.IsGoodConnection()) + if (existingConnections.TryDequeue(out var recentConnection)) { - return recentConnection; - } + if (recentConnection.LastAccess > cutOff + && recentConnection.TcpSocket.IsGoodConnection()) + { + return recentConnection; + } - disposalBag.Add(recentConnection); + disposalBag.Add(recentConnection); + } } } } @@ -290,7 +293,7 @@ internal Task GetServerConnection(ProxyServer proxyServer, bool prefetch, CancellationToken cancellationToken) { // deny connection to proxy end points to avoid infinite connection loop. - if (Server.ProxyEndPoints.Any(x => x.Port == remotePort) + if (Server.ProxyEndPoints.Any(x => x.Port == remotePort) && NetworkHelper.IsLocalIpAddress(remoteHostName)) { throw new Exception($"A client is making HTTP request to one of the listening ports of this proxy {remoteHostName}:{remotePort}"); @@ -318,7 +321,7 @@ internal Task GetServerConnection(ProxyServer proxyServer, useUpstreamProxy1 = true; // check if we need to ByPass - if (externalProxy.BypassLocalhost && NetworkHelper.IsLocalIpAddress(remoteHostName)) + if (externalProxy.BypassLocalhost && NetworkHelper.IsLocalIpAddress(remoteHostName, externalProxy.ProxyDnsRequests)) { useUpstreamProxy1 = false; } @@ -413,7 +416,7 @@ internal Task GetServerConnection(ProxyServer proxyServer, } Task connectTask; - + if (socks) { if (externalProxy!.ProxyDnsRequests) @@ -513,19 +516,21 @@ internal Task GetServerConnection(ProxyServer proxyServer, await proxyServer.InvokeServerConnectionCreateEvent(tcpServerSocket); - stream = new HttpServerStream(new NetworkStream(tcpServerSocket, true), proxyServer.BufferPool, cancellationToken); + stream = new HttpServerStream(proxyServer, new NetworkStream(tcpServerSocket, true), proxyServer.BufferPool, cancellationToken); if (externalProxy != null && externalProxy.ProxyType == ExternalProxyType.Http && (isConnect || isHttps)) { - var authority = $"{remoteHostName}:{remotePort}".GetByteString(); - var connectRequest = new ConnectRequest(authority) + var authority = $"{remoteHostName}:{remotePort}"; + var authorityBytes = authority.GetByteString(); + var connectRequest = new ConnectRequest(authorityBytes) { IsHttps = isHttps, - RequestUriString8 = authority, + RequestUriString8 = authorityBytes, HttpVersion = httpVersion }; connectRequest.Headers.AddHeader(KnownHeaders.Connection, KnownHeaders.ConnectionKeepAlive); + connectRequest.Headers.AddHeader(KnownHeaders.Host, authority); if (!string.IsNullOrEmpty(externalProxy.UserName) && externalProxy.Password != null) { @@ -555,7 +560,7 @@ internal Task GetServerConnection(ProxyServer proxyServer, (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => proxyServer.SelectClientCertificate(sender, sessionArgs, targetHost, localCertificates, remoteCertificate, acceptableIssuers)); - stream = new HttpServerStream(sslStream, proxyServer.BufferPool, cancellationToken); + stream = new HttpServerStream(proxyServer, sslStream, proxyServer.BufferPool, cancellationToken); var options = new SslClientAuthenticationOptions { @@ -629,6 +634,16 @@ internal Task GetServerConnection(ProxyServer proxyServer, /// Should we just close the connection instead of reusing? internal async Task Release(TcpServerConnection connection, bool close = false) { + if (connection == null) + { + return; + } + + if (disposalBag.Any(x => x == connection)) + { + return; + } + if (close || connection.IsWinAuthenticated || !Server.EnableConnectionPool || connection.IsClosed) { disposalBag.Add(connection); @@ -653,6 +668,11 @@ internal async Task Release(TcpServerConnection connection, bool close = false) } } + if (existingConnections.Any(x => x == connection)) + { + break; + } + existingConnections.Enqueue(connection); break; } @@ -758,8 +778,15 @@ private async Task clearOutdatedConnections() } } - public void Dispose() + private bool disposed = false; + + protected virtual void Dispose(bool disposing) { + if (disposed) + { + return; + } + runCleanUpTask = false; try @@ -791,6 +818,19 @@ public void Dispose() connection?.Dispose(); } } + + disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~TcpConnectionFactory() + { + Dispose(false); } static class SocketConnectionTaskFactory diff --git a/src/Titanium.Web.Proxy/Network/Tcp/TcpServerConnection.cs b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpServerConnection.cs similarity index 90% rename from src/Titanium.Web.Proxy/Network/Tcp/TcpServerConnection.cs rename to src/Titanium.Web.Proxy/Network/TcpConnection/TcpServerConnection.cs index abd71523d..b783b39bc 100644 --- a/src/Titanium.Web.Proxy/Network/Tcp/TcpServerConnection.cs +++ b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpServerConnection.cs @@ -84,11 +84,15 @@ internal TcpServerConnection(ProxyServer proxyServer, Socket tcpSocket, HttpServ /// internal bool IsWinAuthenticated { get; set; } - /// - /// Dispose. - /// - public void Dispose() + private bool disposed = false; + + protected virtual void Dispose(bool disposing) { + if (disposed) + { + return; + } + Task.Run(async () => { // delay calling tcp connection close() @@ -108,6 +112,18 @@ public void Dispose() } }); + disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~TcpServerConnection() + { + Dispose(false); } } } diff --git a/src/Titanium.Web.Proxy/Network/WinAuth/Security/Common.cs b/src/Titanium.Web.Proxy/Network/WinAuth/Security/Common.cs index b9e132869..97b96c0b8 100644 --- a/src/Titanium.Web.Proxy/Network/WinAuth/Security/Common.cs +++ b/src/Titanium.Web.Proxy/Network/WinAuth/Security/Common.cs @@ -129,7 +129,7 @@ internal SecurityInteger(int dummy) } [StructLayout(LayoutKind.Sequential)] - internal struct SecurityBuffer : IDisposable + internal struct SecurityBuffer { internal int cbBuffer; internal int cbBufferType; @@ -158,18 +158,10 @@ internal SecurityBuffer(byte[] secBufferBytes, SecurityBufferType bufferType) Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); } - public void Dispose() - { - if (pvBuffer != IntPtr.Zero) - { - Marshal.FreeHGlobal(pvBuffer); - pvBuffer = IntPtr.Zero; - } - } } [StructLayout(LayoutKind.Sequential)] - internal struct SecurityBufferDesciption : IDisposable + internal struct SecurityBufferDesciption { internal int ulVersion; internal int cBuffers; @@ -193,36 +185,7 @@ internal SecurityBufferDesciption(byte[] secBufferBytes) Marshal.StructureToPtr(thisSecBuffer, pBuffers, false); } - public void Dispose() - { - if (pBuffers != IntPtr.Zero) - { - if (cBuffers == 1) - { - var thisSecBuffer = (SecurityBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecurityBuffer)); - thisSecBuffer.Dispose(); - } - else - { - for (int index = 0; index < cBuffers; index++) - { - // The bits were written out the following order: - // int cbBuffer; - // int BufferType; - // pvBuffer; - // What we need to do here is to grab a hold of the pvBuffer allocate by the individual - // SecBuffer and release it... - int currentOffset = index * Marshal.SizeOf(typeof(Buffer)); - var secBufferpvBuffer = Marshal.ReadIntPtr(pBuffers, - currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); - Marshal.FreeHGlobal(secBufferpvBuffer); - } - } - - Marshal.FreeHGlobal(pBuffers); - pBuffers = IntPtr.Zero; - } - } + internal byte[]? GetBytes() { diff --git a/src/Titanium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs b/src/Titanium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs index 5fa0df535..e740cf278 100644 --- a/src/Titanium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs +++ b/src/Titanium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs @@ -73,8 +73,8 @@ internal class WinAuthEndPoint } finally { - clientToken.Dispose(); - serverToken.Dispose(); + disposeToken(clientToken); + disposeToken(serverToken); } return token; @@ -125,13 +125,54 @@ internal class WinAuthEndPoint } finally { - clientToken.Dispose(); - serverToken.Dispose(); + disposeToken(clientToken); + disposeToken(serverToken); } return token; } + private static void disposeToken(SecurityBufferDesciption clientToken) + { + if (clientToken.pBuffers != IntPtr.Zero) + { + if (clientToken.cBuffers == 1) + { + var thisSecBuffer = (SecurityBuffer)Marshal.PtrToStructure(clientToken.pBuffers, typeof(SecurityBuffer)); + disposeSecBuffer(thisSecBuffer); + } + else + { + for (int index = 0; index < clientToken.cBuffers; index++) + { + // The bits were written out the following order: + // int cbBuffer; + // int BufferType; + // pvBuffer; + // What we need to do here is to grab a hold of the pvBuffer allocate by the individual + // SecBuffer and release it... + int currentOffset = index * Marshal.SizeOf(typeof(Buffer)); + var secBufferpvBuffer = Marshal.ReadIntPtr(clientToken.pBuffers, + currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); + Marshal.FreeHGlobal(secBufferpvBuffer); + } + } + + Marshal.FreeHGlobal(clientToken.pBuffers); + clientToken.pBuffers = IntPtr.Zero; + } + + } + + private static void disposeSecBuffer(SecurityBuffer thisSecBuffer) + { + if (thisSecBuffer.pvBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(thisSecBuffer.pvBuffer); + thisSecBuffer.pvBuffer = IntPtr.Zero; + } + } + /// /// Validates that the current WinAuth state of the connection matches the /// expectation, used to detect failed authentication diff --git a/src/Titanium.Web.Proxy/StreamExtended/Network/IHttpStreamWriter.cs b/src/Titanium.Web.Proxy/Network/Writers/IHttpStreamWriter.cs similarity index 94% rename from src/Titanium.Web.Proxy/StreamExtended/Network/IHttpStreamWriter.cs rename to src/Titanium.Web.Proxy/Network/Writers/IHttpStreamWriter.cs index 6cfd89c40..1457c8dee 100644 --- a/src/Titanium.Web.Proxy/StreamExtended/Network/IHttpStreamWriter.cs +++ b/src/Titanium.Web.Proxy/Network/Writers/IHttpStreamWriter.cs @@ -8,6 +8,8 @@ namespace Titanium.Web.Proxy.StreamExtended.Network /// public interface IHttpStreamWriter { + bool IsNetworkStream { get; } + void Write(byte[] buffer, int offset, int count); Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken); diff --git a/src/Titanium.Web.Proxy/Helpers/NullWriter.cs b/src/Titanium.Web.Proxy/Network/Writers/NullWriter.cs similarity index 95% rename from src/Titanium.Web.Proxy/Helpers/NullWriter.cs rename to src/Titanium.Web.Proxy/Network/Writers/NullWriter.cs index b71892846..c3ef9dc82 100644 --- a/src/Titanium.Web.Proxy/Helpers/NullWriter.cs +++ b/src/Titanium.Web.Proxy/Network/Writers/NullWriter.cs @@ -8,6 +8,8 @@ internal class NullWriter : IHttpStreamWriter { public static NullWriter Instance { get; } = new NullWriter(); + public bool IsNetworkStream => false; + private NullWriter() { } diff --git a/src/Titanium.Web.Proxy/ProxyServer.cs b/src/Titanium.Web.Proxy/ProxyServer.cs index 4d59ce48e..a0bca42c0 100644 --- a/src/Titanium.Web.Proxy/ProxyServer.cs +++ b/src/Titanium.Web.Proxy/ProxyServer.cs @@ -121,9 +121,9 @@ public ProxyServer(string? rootCertificateName, string? rootCertificateIssuerNam private SystemProxyManager? systemProxySettingsManager { get; } /// - /// Number of exception retries when connection pool is enabled. + /// Number of times to retry upon network failures when connection pool is enabled. /// - private int retries => EnableConnectionPool ? MaxCachedConnections : 0; + public int NetworkFailureRetryAttempts { get; set; } = 1; /// /// Is the proxy currently running? @@ -208,9 +208,9 @@ public ProxyServer(string? rootCertificateName, string? rootCertificateIssuerNam /// /// Maximum number of concurrent connections per remote host in cache. /// Only valid when connection pooling is enabled. - /// Default value is 2. + /// Default value is 4. /// - public int MaxCachedConnections { get; set; } = 2; + public int MaxCachedConnections { get; set; } = 4; /// /// Number of seconds to linger when Tcp connection is in TIME_WAIT state. @@ -350,11 +350,23 @@ public ExceptionHandler ExceptionFunc /// public event AsyncEventHandler? BeforeRequest; +#if DEBUG + /// + /// Intercept request body send event to server. + /// + public event AsyncEventHandler? OnRequestBodyWrite; +#endif /// /// Intercept response event from server. /// public event AsyncEventHandler? BeforeResponse; +#if DEBUG + /// + /// Intercept request body send event to client. + /// + public event AsyncEventHandler? OnResponseBodyWrite; +#endif /// /// Intercept after response event from server. /// @@ -765,8 +777,19 @@ private void onAcceptConnection(IAsyncResult asyn) }); } - // Get the listener that handles the client request. - endPoint.Listener!.BeginAcceptSocket(onAcceptConnection, endPoint); + try + { + // based on end point type call appropriate request handlers + // Get the listener that handles the client request. + endPoint.Listener!.BeginAcceptSocket(onAcceptConnection, endPoint); + } + catch (Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException) + { + // The listener was Stop()'d, disposing the underlying socket and + // triggering the completion of the callback. We're already exiting, + // so just return. + return; + } } @@ -905,14 +928,20 @@ internal async Task InvokeServerConnectionCreateEvent(Socket serverSocket) /// private RetryPolicy retryPolicy() where T : Exception { - return new RetryPolicy(retries, tcpConnectionFactory); + return new RetryPolicy(NetworkFailureRetryAttempts, tcpConnectionFactory); } - /// - /// Dispose the Proxy instance. - /// - public void Dispose() + private bool disposed = false; + + protected virtual void Dispose(bool disposing) { + if (disposed) + { + return; + } + + disposed = true; + if (ProxyRunning) { Stop(); @@ -921,5 +950,16 @@ public void Dispose() CertificateManager?.Dispose(); BufferPool?.Dispose(); } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~ProxyServer() + { + Dispose(false); + } } } diff --git a/src/Titanium.Web.Proxy/RequestHandler.cs b/src/Titanium.Web.Proxy/RequestHandler.cs index 1565e0121..005c2daa7 100644 --- a/src/Titanium.Web.Proxy/RequestHandler.cs +++ b/src/Titanium.Web.Proxy/RequestHandler.cs @@ -288,8 +288,13 @@ private async Task handleHttpSessionRequest(SessionEventArgs args, noCache, cancellationToken); - // for connection pool, retry fails until cache is exhausted. - return await retryPolicy().ExecuteAsync(async connection => + /// Retry with new connection if the initial stream.WriteAsync call to server fails. + /// i.e if request line and headers failed to get send. + /// Do not retry after reading data from client stream, + /// because subsequent try will not have data to read from client + /// and will hang at clientStream.ReadAsync call. + /// So, throw RetryableServerConnectionException only when we are sure we can retry safely. + return await retryPolicy().ExecuteAsync(async connection => { // set the connection and send request headers args.HttpClient.SetConnection(connection); @@ -400,5 +405,25 @@ private async Task onBeforeRequest(SessionEventArgs args) await BeforeRequest.InvokeAsync(this, args, ExceptionFunc); } } + +#if DEBUG + internal bool ShouldCallBeforeRequestBodyWrite() + { + if (OnRequestBodyWrite != null) + { + return true; + } + + return false; + } + + internal async Task OnBeforeRequestBodyWrite(BeforeBodyWriteEventArgs args) + { + if (OnRequestBodyWrite != null) + { + await OnRequestBodyWrite.InvokeAsync(this, args, ExceptionFunc); + } + } +#endif } } diff --git a/src/Titanium.Web.Proxy/ResponseHandler.cs b/src/Titanium.Web.Proxy/ResponseHandler.cs index 95a782f71..0d07c9d3b 100644 --- a/src/Titanium.Web.Proxy/ResponseHandler.cs +++ b/src/Titanium.Web.Proxy/ResponseHandler.cs @@ -118,7 +118,7 @@ await handleHttpSessionRequest(args, null, args.ClientConnection.NegotiatedAppli // Copy body if exists var serverStream = args.HttpClient.Connection.Stream; await serverStream.CopyBodyAsync(response, false, clientStream, TransformationMode.None, - args.OnDataReceived, cancellationToken); + false, args, cancellationToken); } } @@ -150,5 +150,24 @@ private async Task onAfterResponse(SessionEventArgs args) await AfterResponse.InvokeAsync(this, args, ExceptionFunc); } } +#if DEBUG + internal bool ShouldCallBeforeResponseBodyWrite() + { + if (OnResponseBodyWrite != null) + { + return true; + } + + return false; + } + + internal async Task OnBeforeResponseBodyWrite(BeforeBodyWriteEventArgs args) + { + if (OnResponseBodyWrite != null) + { + await OnResponseBodyWrite.InvokeAsync(this, args, ExceptionFunc); + } + } +#endif } } diff --git a/src/Titanium.Web.Proxy/SocksClientHandler.cs b/src/Titanium.Web.Proxy/SocksClientHandler.cs index 7716ebbb3..c8f124c18 100644 --- a/src/Titanium.Web.Proxy/SocksClientHandler.cs +++ b/src/Titanium.Web.Proxy/SocksClientHandler.cs @@ -106,7 +106,7 @@ private async Task handleClient(SocksProxyEndPoint endPoint, TcpClientConnection string password = Encoding.ASCII.GetString(buffer, 3 + userNameLength, passwordLength); bool success = true; - if (ProxySchemeAuthenticateFunc != null) + if (ProxyBasicAuthenticateFunc != null) { success = await ProxyBasicAuthenticateFunc.Invoke(null, userName, password); } diff --git a/src/Titanium.Web.Proxy/TransparentClientHandler.cs b/src/Titanium.Web.Proxy/TransparentClientHandler.cs index ce8d3ff8e..4573b31dd 100644 --- a/src/Titanium.Web.Proxy/TransparentClientHandler.cs +++ b/src/Titanium.Web.Proxy/TransparentClientHandler.cs @@ -36,7 +36,7 @@ private async Task handleClient(TransparentBaseProxyEndPoint endPoint, TcpClient int port, CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken) { bool isHttps = false; - var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken); + var clientStream = new HttpClientStream(this, clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken); try { @@ -46,7 +46,7 @@ private async Task handleClient(TransparentBaseProxyEndPoint endPoint, TcpClient { var httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName; - var args = new BeforeSslAuthenticateEventArgs(clientConnection, cancellationTokenSource, httpsHostName); + var args = new BeforeSslAuthenticateEventArgs(this, clientConnection, cancellationTokenSource, httpsHostName); await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc); @@ -74,7 +74,7 @@ private async Task handleClient(TransparentBaseProxyEndPoint endPoint, TcpClient await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls12, false); // HTTPS server created - we can now decrypt the client's traffic - clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool, cancellationToken); + clientStream = new HttpClientStream(this, clientStream.Connection, sslStream, BufferPool, cancellationToken); sslStream = null; // clientStream was created, no need to keep SSL stream reference isHttps = true; } @@ -91,7 +91,7 @@ private async Task handleClient(TransparentBaseProxyEndPoint endPoint, TcpClient else { var sessionArgs = new SessionEventArgs(this, endPoint, clientStream, null, cancellationTokenSource); - var connection = (await tcpConnectionFactory.GetServerConnection(this, httpsHostName, port, + var connection = (await tcpConnectionFactory.GetServerConnection(this, args.ForwardHttpsHostName, args.ForwardHttpsPort, HttpHeader.VersionUnknown, false, null, true, sessionArgs, UpStreamEndPoint, UpStreamHttpsProxy, true, false, cancellationToken))!; diff --git a/tests/Titanium.Web.Proxy.IntegrationTests/HttpsTests.cs b/tests/Titanium.Web.Proxy.IntegrationTests/HttpsTests.cs index 0482f151c..5e03a0e82 100644 --- a/tests/Titanium.Web.Proxy.IntegrationTests/HttpsTests.cs +++ b/tests/Titanium.Web.Proxy.IntegrationTests/HttpsTests.cs @@ -1,6 +1,8 @@ using System; +using System.IO; using System.Net; using System.Net.Http; +using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -62,5 +64,36 @@ public async Task Can_Handle_Https_Fake_Tunnel_Request() Assert.AreEqual("I am server. I received your greetings.", body); } + [TestMethod] + public async Task Can_Handle_Https_Mutual_Tls_Request() + { + var testSuite = new TestSuite(true); + + var server = testSuite.GetServer(); + server.HandleRequest((context) => + { + return context.Response.WriteAsync("I am server. I received your greetings."); + }); + + var proxy = testSuite.GetProxy(); + var clientCert = proxy.CertificateManager.CreateCertificate("client.com", false); + + proxy.ClientCertificateSelectionCallback += async (sender, e) => + { + e.ClientCertificate = clientCert; + await Task.CompletedTask; + }; + + var client = testSuite.GetClient(proxy); + + var response = await client.PostAsync(new Uri(server.ListeningHttpsUrl), + new StringContent("hello server. I am a client.")); + + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + + Assert.AreEqual("I am server. I received your greetings.", body); + } + } } diff --git a/tests/Titanium.Web.Proxy.IntegrationTests/NestedProxyTests.cs b/tests/Titanium.Web.Proxy.IntegrationTests/NestedProxyTests.cs index ef2c088a9..5a95aa8fd 100644 --- a/tests/Titanium.Web.Proxy.IntegrationTests/NestedProxyTests.cs +++ b/tests/Titanium.Web.Proxy.IntegrationTests/NestedProxyTests.cs @@ -1,9 +1,13 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Titanium.Web.Proxy.Models; namespace Titanium.Web.Proxy.IntegrationTests { @@ -73,5 +77,199 @@ public async Task Smoke_Test_Nested_Proxy_UserData() Assert.AreEqual("I am server. I received your greetings.", body); } + [TestMethod] + [Timeout(2 * 60 * 1000)] + public async Task Nested_Proxy_Farm_Without_Connection_Cache_Should_Not_Hang() + { + var rnd = new Random(); + + var testSuite = new TestSuite(); + + var server = testSuite.GetServer(); + server.HandleRequest((context) => + { + return context.Response.WriteAsync("I am server. I received your greetings."); + }); + + var proxies2 = new List(); + + //create a level 2 upstream proxy farm that forwards to server + for (int i = 0; i < 10; i++) + { + var proxy2 = testSuite.GetProxy(); + proxy2.ProxyBasicAuthenticateFunc += (_, _, _) => + { + return Task.FromResult(true); + }; + + proxies2.Add(proxy2); + } + + var proxies1 = new List(); + + //create a level 1 upstream proxy farm that forwards to level 2 farm + for (int i = 0; i < 10; i++) + { + var proxy1 = testSuite.GetProxy(); + proxy1.EnableConnectionPool = false; + var proxy2 = proxies2[rnd.Next() % proxies2.Count]; + + var explicitEndpoint = proxy1.ProxyEndPoints.OfType().First(); + explicitEndpoint.BeforeTunnelConnectRequest += (_, e) => + { + e.CustomUpStreamProxy = new ExternalProxy() + { + HostName = "localhost", + Port = proxy2.ProxyEndPoints[0].Port, + ProxyType = ExternalProxyType.Http, + UserName = "test_user", + Password = "test_password" + }; + + return Task.CompletedTask; + }; + + proxy1.BeforeRequest += (_, e) => + { + e.CustomUpStreamProxy = new ExternalProxy() + { + HostName = "localhost", + Port = proxy2.ProxyEndPoints[0].Port, + ProxyType = ExternalProxyType.Http, + UserName = "test_user", + Password = "test_password" + }; + + return Task.CompletedTask; + }; + + proxies1.Add(proxy1); + } + + var tasks = new List(); + + //send multiple concurrent requests from client => proxy farm 1 => proxy farm 2 => server + for (int j = 0; j < 10_000; j++) + { + var task = Task.Run(async () => + { + try + { + var proxy = proxies1[rnd.Next() % proxies1.Count]; + using var client = testSuite.GetClient(proxy); + + //tests should not keep hanging for 30 mins. + client.Timeout = TimeSpan.FromMinutes(30); + await client.PostAsync(new Uri(server.ListeningHttpsUrl), + new StringContent("hello server. I am a client.")); + + } + //if error is thrown because of server overloading its okay. + //But client.PostAsync should'nt hang in all cases. + catch { } + }); + + tasks.Add(task); + } + + await Task.WhenAll(tasks); + } + + + //Reproduce bug reported so that we can fix it. + //https://github.com/justcoding121/titanium-web-proxy/issues/826 + [TestMethod] + [Timeout(2 * 60 * 1000)] + public async Task Nested_Proxy_Farm_With_Connection_Cache_Should_Not_Hang() + { + var rnd = new Random(); + + var testSuite = new TestSuite(); + + var server = testSuite.GetServer(); + server.HandleRequest((context) => + { + return context.Response.WriteAsync("I am server. I received your greetings."); + }); + + var proxies2 = new List(); + + //create a level 2 upstream proxy farm that forwards to server + for (int i = 0; i < 10; i++) + { + var proxy2 = testSuite.GetProxy(); + proxy2.ProxyBasicAuthenticateFunc += (_, _, _) => + { + return Task.FromResult(true); + }; + proxies2.Add(proxy2); + } + + var proxies1 = new List(); + + //create a level 1 upstream proxy farm that forwards to level 2 farm + for (int i = 0; i < 10; i++) + { + var proxy1 = testSuite.GetProxy(); + var proxy2 = proxies2[rnd.Next() % proxies2.Count]; + var explicitEndpoint = proxy1.ProxyEndPoints.OfType().First(); + explicitEndpoint.BeforeTunnelConnectRequest += (_, e) => + { + e.CustomUpStreamProxy = new ExternalProxy() + { + HostName = "localhost", + Port = proxy2.ProxyEndPoints[0].Port, + ProxyType = ExternalProxyType.Http, + UserName = "test_user", + Password = "test_password" + }; + + return Task.CompletedTask; + }; + + proxy1.BeforeRequest += (_, e) => + { + e.CustomUpStreamProxy = new ExternalProxy() + { + HostName = "localhost", + Port = proxy2.ProxyEndPoints[0].Port, + ProxyType = ExternalProxyType.Http, + UserName = "test_user", + Password = "test_password" + }; + + return Task.CompletedTask; + }; + + proxies1.Add(proxy1); + } + + var tasks = new List(); + + //send multiple concurrent requests from client => proxy farm 1 => proxy farm 2 => server + for (int j = 0; j < 10_000; j++) + { + var task = Task.Run(async () => + { + try + { + var proxy = proxies1[rnd.Next() % proxies1.Count]; + using var client = testSuite.GetClient(proxy); + + //tests should not keep hanging for 30 mins. + client.Timeout = TimeSpan.FromMinutes(30); + await client.PostAsync(new Uri(server.ListeningHttpsUrl), + new StringContent("hello server. I am a client.")); + } + //if error is thrown because of server overloading its okay. + //But client.PostAsync should'nt hang in all cases. + catch { } + }); + + tasks.Add(task); + } + + await Task.WhenAll(tasks); + } } } diff --git a/tests/Titanium.Web.Proxy.IntegrationTests/ReverseProxyTests.cs b/tests/Titanium.Web.Proxy.IntegrationTests/ReverseProxyTests.cs index cf04340ec..3097c9472 100644 --- a/tests/Titanium.Web.Proxy.IntegrationTests/ReverseProxyTests.cs +++ b/tests/Titanium.Web.Proxy.IntegrationTests/ReverseProxyTests.cs @@ -1,9 +1,11 @@ using System; +using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Titanium.Web.Proxy.Models; namespace Titanium.Web.Proxy.IntegrationTests { @@ -126,5 +128,37 @@ public async Task Smoke_Test_Https_To_Https_Reverse_Proxy() Assert.AreEqual("I am server. I received your greetings.", body); } + [TestMethod] + public async Task Smoke_Test_Https_To_Https_Reverse_Proxy_Tunnel_Without_Decryption() + { + var testSuite = new TestSuite(); + + var server = testSuite.GetServer(); + server.HandleRequest((context) => + { + return context.Response.WriteAsync("I am server. I received your greetings."); + }); + + var proxy = testSuite.GetReverseProxy(); + var endpoint = proxy.ProxyEndPoints.Where(x => x is TransparentProxyEndPoint).First() as TransparentProxyEndPoint; + + endpoint.BeforeSslAuthenticate += async (sender, e) => + { + e.DecryptSsl = false; + e.ForwardHttpsPort = server.HttpsListeningPort; + }; + + var client = testSuite.GetReverseProxyClient(); + + var response = await client.PostAsync(new Uri($"https://localhost:{proxy.ProxyEndPoints[0].Port}"), + new StringContent("hello server. I am a client.")); + + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + + Assert.AreEqual("I am server. I received your greetings.", body); + } + } + } diff --git a/tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestServer.cs b/tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestServer.cs index 0812f5882..bff06930f 100644 --- a/tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestServer.cs +++ b/tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestServer.cs @@ -4,12 +4,18 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Authentication.Certificate; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Server.Kestrel.Https; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace Titanium.Web.Proxy.IntegrationTests.Setup { @@ -24,42 +30,57 @@ public class TestServer : IDisposable public int HttpsListeningPort { get; private set; } public int TcpListeningPort { get; private set; } - private IWebHost host; - public TestServer(X509Certificate2 serverCertificate) + private readonly IHost host; + public TestServer(X509Certificate2 serverCertificate, bool requireMutualTls) { - var startUp = new Startup(() => requestHandler); - - host = WebHost.CreateDefaultBuilder() - .ConfigureServices(s => + host = Host.CreateDefaultBuilder() + .ConfigureLogging(logging => + { + logging.ClearProviders(); + logging.AddDebug(); + logging.SetMinimumLevel(LogLevel.Trace); + }) + .ConfigureWebHostDefaults(webBuilder => { - s.AddSingleton(startUp); + webBuilder.UseStartup(x => new Startup(() => requestHandler)); + webBuilder.ConfigureKestrel(options => + { + options.Listen(IPAddress.Loopback, 0); + if (requireMutualTls) + { + options.ConfigureHttpsDefaults(options => + { + options.ClientCertificateValidation = (certificate, chain, errors) => + { + return true; + }; + options.ClientCertificateMode = ClientCertificateMode.RequireCertificate; + }); + } + options.Listen(IPAddress.Loopback, 0, listenOptions => + { + listenOptions.UseHttps(serverCertificate); + }); + options.Listen(IPAddress.Loopback, 0, listenOptions => + { + listenOptions.Run(context => + { + if (tcpRequestHandler == null) + { + throw new Exception("Test server not configured to handle tcp request."); + } + + return tcpRequestHandler(context); + }); + }); + }); }) - .UseKestrel(options => - { - options.Listen(IPAddress.Loopback, 0); - options.Listen(IPAddress.Loopback, 0, listenOptions => - { - listenOptions.UseHttps(serverCertificate); - }); - options.Listen(IPAddress.Loopback, 0, listenOptions => - { - listenOptions.Run(context => - { - if (tcpRequestHandler == null) - { - throw new Exception("Test server not configured to handle tcp request."); - } - - return tcpRequestHandler(context); - }); - }); - }) .Build(); host.Start(); - var addresses = host.ServerFeatures - .Get() + var addresses = host.Services.GetRequiredService() + .Features.Get() .Addresses.ToArray(); HttpListeningPort = new Uri(addresses[0]).Port; @@ -86,7 +107,7 @@ public void Dispose() host.Dispose(); } - private class Startup : IStartup + private class Startup { Func> requestHandler; public Startup(Func> requestHandler) @@ -108,9 +129,9 @@ public void Configure(IApplicationBuilder app) } - public IServiceProvider ConfigureServices(IServiceCollection services) + public void ConfigureServices(IServiceCollection services) { - return services.BuildServiceProvider(); + } } } diff --git a/tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestSuite.cs b/tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestSuite.cs index 26fa258ba..c5f290b1d 100644 --- a/tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestSuite.cs +++ b/tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestSuite.cs @@ -8,11 +8,11 @@ public class TestSuite { private TestServer server; - public TestSuite() + public TestSuite(bool requireMutualTls = false) { var dummyProxy = new ProxyServer(); var serverCertificate = dummyProxy.CertificateManager.CreateServerCertificate("localhost").Result; - server = new TestServer(serverCertificate); + server = new TestServer(serverCertificate, requireMutualTls); } public TestServer GetServer() diff --git a/tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj b/tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj index 8d4037e68..0b45452c3 100644 --- a/tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj +++ b/tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj @@ -14,6 +14,7 @@ + diff --git a/tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs b/tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs index e0034f969..01e58c787 100644 --- a/tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs +++ b/tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs @@ -76,5 +76,31 @@ public async Task Simple_Create_Win_Certificate_Test() mgr.StopClearIdleCertificates(); } + + [TestMethod] + public async Task Create_Server_Certificate_Test() + { + var tasks = new List(); + + var mgr = new CertificateManager(null, null, false, false, false, new Lazy(() => (e => + { + Debug.WriteLine(e.ToString()); + Debug.WriteLine(e.InnerException?.ToString()); + })).Value) + { CertificateEngine = CertificateEngine.BouncyCastleFast }; + + mgr.SaveFakeCertificates = true; + + for (int i = 0; i < 500; i++) + { + tasks.AddRange(hostNames.Select(host => Task.Run(() => + { + var certificate = mgr.CreateServerCertificate(host); + Assert.IsNotNull(certificate); + }))); + } + + await Task.WhenAll(tasks.ToArray()); + } } }