Commit bff73ef4 authored by justcoding121's avatar justcoding121 Committed by justcoding121

Merge pull request #278 from honfika/develop

Detect HTTPS ClientHello instead of manually listing the HTTPS ports +bugfix
parents 70c107a4 3ab43a56
...@@ -16,8 +16,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -16,8 +16,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
//share requestBody outside handlers //share requestBody outside handlers
//Using a dictionary is not a good idea since it can cause memory overflow //Using a dictionary is not a good idea since it can cause memory overflow
//ideally the data should be moved out of memory //ideally the data should be moved out of memory
//private readonly IDictionary<Guid, string> requestBodyHistory //private readonly IDictionary<Guid, string> requestBodyHistory = new ConcurrentDictionary<Guid, string>();
// = new ConcurrentDictionary<Guid, string>();
public ProxyTestController() public ProxyTestController()
{ {
...@@ -44,6 +43,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -44,6 +43,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
proxyServer.BeforeRequest += OnRequest; proxyServer.BeforeRequest += OnRequest;
proxyServer.BeforeResponse += OnResponse; proxyServer.BeforeResponse += OnResponse;
proxyServer.TunnelConnectRequest += OnTunnelConnectRequest;
proxyServer.TunnelConnectResponse += OnTunnelConnectResponse;
proxyServer.ServerCertificateValidationCallback += OnCertificateValidation; proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection; proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
...@@ -57,11 +58,14 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -57,11 +58,14 @@ namespace Titanium.Web.Proxy.Examples.Basic
ExcludedHttpsHostNameRegex = new List<string> ExcludedHttpsHostNameRegex = new List<string>
{ {
"dropbox.com" "dropbox.com"
} },
//Include Https addresses you want to proxy (others will be excluded) //Include Https addresses you want to proxy (others will be excluded)
//for example github.com //for example github.com
//IncludedHttpsHostNameRegex = new List<string> { "github.com" } //IncludedHttpsHostNameRegex = new List<string>
//{
// "github.com"
//},
//You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown //You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown
...@@ -103,6 +107,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -103,6 +107,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
public void Stop() public void Stop()
{ {
proxyServer.TunnelConnectRequest -= OnTunnelConnectRequest;
proxyServer.TunnelConnectResponse -= OnTunnelConnectResponse;
proxyServer.BeforeRequest -= OnRequest; proxyServer.BeforeRequest -= OnRequest;
proxyServer.BeforeResponse -= OnResponse; proxyServer.BeforeResponse -= OnResponse;
proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation; proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
...@@ -114,6 +120,15 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -114,6 +120,15 @@ namespace Titanium.Web.Proxy.Examples.Basic
//proxyServer.CertificateManager.RemoveTrustedRootCertificates(); //proxyServer.CertificateManager.RemoveTrustedRootCertificates();
} }
private async Task OnTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
Console.WriteLine("Tunnel to: " + e.WebSession.Request.Host);
}
private async Task OnTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
{
}
//intecept & cancel redirect or update requests //intecept & cancel redirect or update requests
public async Task OnRequest(object sender, SessionEventArgs e) public async Task OnRequest(object sender, SessionEventArgs e)
{ {
......
...@@ -434,7 +434,10 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -434,7 +434,10 @@ namespace Titanium.Web.Proxy.EventArguments
if (headers != null && headers.Count > 0) if (headers != null && headers.Count > 0)
{ {
response.ResponseHeaders = headers; foreach (var header in headers)
{
response.ResponseHeaders.AddHeader(header.Key, header.Value.Value);
}
} }
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
...@@ -501,7 +504,10 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -501,7 +504,10 @@ namespace Titanium.Web.Proxy.EventArguments
if (headers != null && headers.Count > 0) if (headers != null && headers.Count > 0)
{ {
response.ResponseHeaders = headers; foreach (var header in headers)
{
response.ResponseHeaders.AddHeader(header.Key, header.Value.Value);
}
} }
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
...@@ -523,7 +529,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -523,7 +529,7 @@ namespace Titanium.Web.Proxy.EventArguments
var response = new RedirectResponse(); var response = new RedirectResponse();
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseHeaders.Add("Location", new HttpHeader("Location", url)); response.ResponseHeaders.AddHeader("Location", url);
response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty); response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty);
await Respond(response); await Respond(response);
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.EventArguments
{
public class TunnelConnectSessionEventArgs : SessionEventArgs
{
public bool IsHttps { get; set; }
public TunnelConnectSessionEventArgs() : base(0, null)
{
}
}
}
...@@ -306,6 +306,21 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -306,6 +306,21 @@ namespace Titanium.Web.Proxy.Helpers
return streamBuffer[bufferPos++]; return streamBuffer[bufferPos++];
} }
public async Task<int> PeekByteAsync(int index)
{
if (Available <= index)
{
await FillBufferAsync();
}
if (Available <= index)
{
return -1;
}
return streamBuffer[bufferPos + index];
}
public byte ReadByteFromBuffer() public byte ReadByteFromBuffer()
{ {
if (bufferLength == 0) if (bufferLength == 0)
...@@ -396,6 +411,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -396,6 +411,8 @@ namespace Titanium.Web.Proxy.Helpers
public bool DataAvailable => bufferLength > 0; public bool DataAvailable => bufferLength > 0;
public int Available => bufferLength;
/// <summary> /// <summary>
/// When overridden in a derived class, gets or sets the position within the current stream. /// When overridden in a derived class, gets or sets the position within the current stream.
/// </summary> /// </summary>
...@@ -428,14 +445,22 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -428,14 +445,22 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
public bool FillBuffer() public bool FillBuffer()
{ {
bufferLength = baseStream.Read(streamBuffer, 0, streamBuffer.Length);
bufferPos = 0;
if (bufferLength > 0) if (bufferLength > 0)
{ {
OnDataReceived(streamBuffer, 0, bufferLength); //normally we fill the buffer only when it is empty, but sometimes we need more data
//move the remanining data to the beginning of the buffer
Buffer.BlockCopy(streamBuffer, bufferPos, streamBuffer, 0, bufferLength);
}
bufferPos = 0;
int readBytes = baseStream.Read(streamBuffer, bufferLength, streamBuffer.Length - bufferLength);
if (readBytes > 0)
{
OnDataReceived(streamBuffer, bufferLength, readBytes);
bufferLength += readBytes;
} }
return bufferLength > 0; return readBytes > 0;
} }
/// <summary> /// <summary>
...@@ -454,14 +479,22 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -454,14 +479,22 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns></returns> /// <returns></returns>
public async Task<bool> FillBufferAsync(CancellationToken cancellationToken) public async Task<bool> FillBufferAsync(CancellationToken cancellationToken)
{ {
bufferLength = await baseStream.ReadAsync(streamBuffer, 0, streamBuffer.Length, cancellationToken);
bufferPos = 0;
if (bufferLength > 0) if (bufferLength > 0)
{ {
OnDataReceived(streamBuffer, 0, bufferLength); //normally we fill the buffer only when it is empty, but sometimes we need more data
//move the remanining data to the beginning of the buffer
Buffer.BlockCopy(streamBuffer, bufferPos, streamBuffer, 0, bufferLength);
}
bufferPos = 0;
int readBytes = await baseStream.ReadAsync(streamBuffer, bufferLength, streamBuffer.Length - bufferLength, cancellationToken);
if (readBytes > 0)
{
OnDataReceived(streamBuffer, bufferLength, readBytes);
bufferLength += readBytes;
} }
return bufferLength > 0; return readBytes > 0;
} }
private class ReadAsyncResult : IAsyncResult private class ReadAsyncResult : IAsyncResult
......
...@@ -170,20 +170,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -170,20 +170,12 @@ namespace Titanium.Web.Proxy.Helpers
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix /// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix
/// Usefull for websocket requests /// Usefull for websocket requests
/// </summary> /// </summary>
/// <param name="server"></param>
/// <param name="remoteHostName"></param>
/// <param name="remotePort"></param>
/// <param name="httpCmd"></param> /// <param name="httpCmd"></param>
/// <param name="httpVersion"></param>
/// <param name="requestHeaders"></param> /// <param name="requestHeaders"></param>
/// <param name="isHttps"></param>
/// <param name="clientStream"></param> /// <param name="clientStream"></param>
/// <param name="tcpConnectionFactory"></param>
/// <param name="connection"></param> /// <param name="connection"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task SendRaw(ProxyServer server, string remoteHostName, int remotePort, string httpCmd, Version httpVersion, internal static async Task SendRaw(string httpCmd, IEnumerable<HttpHeader> requestHeaders, Stream clientStream, TcpConnection connection)
Dictionary<string, HttpHeader> requestHeaders, bool isHttps, Stream clientStream, TcpConnectionFactory tcpConnectionFactory,
TcpConnection connection = null)
{ {
//prepare the prefix content //prepare the prefix content
StringBuilder sb = null; StringBuilder sb = null;
...@@ -199,7 +191,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -199,7 +191,7 @@ namespace Titanium.Web.Proxy.Helpers
if (requestHeaders != null) if (requestHeaders != null)
{ {
foreach (string header in requestHeaders.Select(t => t.Value.ToString())) foreach (string header in requestHeaders.Select(t => t.ToString()))
{ {
sb.Append(header); sb.Append(header);
sb.Append(ProxyConstants.NewLine); sb.Append(ProxyConstants.NewLine);
...@@ -209,43 +201,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -209,43 +201,14 @@ namespace Titanium.Web.Proxy.Helpers
sb.Append(ProxyConstants.NewLine); sb.Append(ProxyConstants.NewLine);
} }
bool connectionCreated = false; var tunnelStream = connection.Stream;
TcpConnection tcpConnection;
//create new connection if connection is null //Now async relay all server=>client & client=>server data
if (connection == null) var sendRelay = clientStream.CopyToAsync(sb?.ToString() ?? string.Empty, tunnelStream);
{
tcpConnection = await tcpConnectionFactory.CreateClient(server, remoteHostName, remotePort, httpVersion, isHttps, null, null);
connectionCreated = true;
}
else
{
tcpConnection = connection;
}
try
{
var tunnelStream = tcpConnection.Stream;
//Now async relay all server=>client & client=>server data var receiveRelay = tunnelStream.CopyToAsync(string.Empty, clientStream);
var sendRelay = clientStream.CopyToAsync(sb?.ToString() ?? string.Empty, tunnelStream);
var receiveRelay = tunnelStream.CopyToAsync(string.Empty, clientStream); await Task.WhenAll(sendRelay, receiveRelay);
await Task.WhenAll(sendRelay, receiveRelay);
}
finally
{
//if connection was null
//then a new connection was created
//so dispose the new connection
if (connectionCreated)
{
tcpConnection.Dispose();
Interlocked.Decrement(ref server.serverConnectionCount);
}
}
} }
} }
} }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Http
{
public class ConnectRequest : Request
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Http
{
public class ConnectResponse : Response
{
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http
{
public class HeaderCollection : IEnumerable<HttpHeader>
{
/// <summary>
/// Unique Request header collection
/// </summary>
public Dictionary<string, HttpHeader> Headers { get; set; }
/// <summary>
/// Non Unique headers
/// </summary>
public Dictionary<string, List<HttpHeader>> NonUniqueHeaders { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="HeaderCollection"/> class.
/// </summary>
public HeaderCollection()
{
Headers = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// True if header exists
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public bool HeaderExists(string name)
{
return Headers.ContainsKey(name) || NonUniqueHeaders.ContainsKey(name);
}
/// <summary>
/// Returns all headers with given name if exists
/// Returns null if does'nt exist
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public List<HttpHeader> GetHeaders(string name)
{
if (Headers.ContainsKey(name))
{
return new List<HttpHeader>
{
Headers[name]
};
}
if (NonUniqueHeaders.ContainsKey(name))
{
return new List<HttpHeader>(NonUniqueHeaders[name]);
}
return null;
}
/// <summary>
/// Returns all headers
/// </summary>
/// <returns></returns>
public List<HttpHeader> GetAllHeaders()
{
var result = new List<HttpHeader>();
result.AddRange(Headers.Select(x => x.Value));
result.AddRange(NonUniqueHeaders.SelectMany(x => x.Value));
return result;
}
/// <summary>
/// Add a new header with given name and value
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
public void AddHeader(string name, string value)
{
AddHeader(new HttpHeader(name, value));
}
/// <summary>
/// Adds the given header object to Request
/// </summary>
/// <param name="newHeader"></param>
public void AddHeader(HttpHeader newHeader)
{
if (NonUniqueHeaders.ContainsKey(newHeader.Name))
{
NonUniqueHeaders[newHeader.Name].Add(newHeader);
return;
}
if (Headers.ContainsKey(newHeader.Name))
{
var existing = Headers[newHeader.Name];
Headers.Remove(newHeader.Name);
NonUniqueHeaders.Add(newHeader.Name, new List<HttpHeader>
{
existing,
newHeader
});
}
else
{
Headers.Add(newHeader.Name, newHeader);
}
}
/// <summary>
/// removes all headers with given name
/// </summary>
/// <param name="headerName"></param>
/// <returns>True if header was removed
/// False if no header exists with given name</returns>
public bool RemoveHeader(string headerName)
{
bool result = Headers.Remove(headerName);
// do not convert to '||' expression to avoid lazy evaluation
if (NonUniqueHeaders.Remove(headerName))
{
result = true;
}
return result;
}
/// <summary>
/// Removes given header object if it exist
/// </summary>
/// <param name="header">Returns true if header exists and was removed </param>
public bool RemoveHeader(HttpHeader header)
{
if (Headers.ContainsKey(header.Name))
{
if (Headers[header.Name].Equals(header))
{
Headers.Remove(header.Name);
return true;
}
}
else if (NonUniqueHeaders.ContainsKey(header.Name))
{
if (NonUniqueHeaders[header.Name].RemoveAll(x => x.Equals(header)) > 0)
{
return true;
}
}
return false;
}
internal string GetHeaderValueOrNull(string headerName)
{
HttpHeader header;
if (Headers.TryGetValue(headerName, out header))
{
return header.Value;
}
return null;
}
internal string SetOrAddHeaderValue(string headerName, string value)
{
HttpHeader header;
if (Headers.TryGetValue(headerName, out header))
{
header.Value = value;
}
else
{
Headers.Add(headerName, new HttpHeader(headerName, value));
}
return null;
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// An enumerator that can be used to iterate through the collection.
/// </returns>
public IEnumerator<HttpHeader> GetEnumerator()
{
return Headers.Values.Concat(NonUniqueHeaders.Values.SelectMany(x => x)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
...@@ -8,9 +8,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -8,9 +8,11 @@ namespace Titanium.Web.Proxy.Http
{ {
internal static class HeaderParser internal static class HeaderParser
{ {
internal static async Task ReadHeaders(CustomBinaryReader reader, Dictionary<string, List<HttpHeader>> nonUniqueResponseHeaders, internal static async Task ReadHeaders(CustomBinaryReader reader, HeaderCollection headerCollection)
Dictionary<string, HttpHeader> headers)
{ {
var nonUniqueResponseHeaders = headerCollection.NonUniqueHeaders;
var headers = headerCollection.Headers;
string tmpLine; string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync())) while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync()))
{ {
......
...@@ -27,7 +27,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -27,7 +27,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Headers passed with Connect. /// Headers passed with Connect.
/// </summary> /// </summary>
public List<HttpHeader> ConnectHeaders { get; set; } public ConnectRequest ConnectRequest { get; set; }
/// <summary> /// <summary>
/// Web Request. /// Web Request.
...@@ -94,28 +94,14 @@ namespace Titanium.Web.Proxy.Http ...@@ -94,28 +94,14 @@ namespace Titanium.Web.Proxy.Http
$"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}"))); $"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}")));
} }
//write request headers //write request headers
foreach (var headerItem in Request.RequestHeaders) foreach (var header in Request.RequestHeaders)
{ {
var header = headerItem.Value; if (header.Name != "Proxy-Authorization")
if (headerItem.Key != "Proxy-Authorization")
{ {
requestLines.AppendLine($"{header.Name}: {header.Value}"); requestLines.AppendLine($"{header.Name}: {header.Value}");
} }
} }
//write non unique request headers
foreach (var headerItem in Request.NonUniqueRequestHeaders)
{
var headers = headerItem.Value;
foreach (var header in headers)
{
if (headerItem.Key != "Proxy-Authorization")
{
requestLines.AppendLine($"{header.Name}: {header.Value}");
}
}
}
requestLines.AppendLine(); requestLines.AppendLine();
string request = requestLines.ToString(); string request = requestLines.ToString();
...@@ -211,7 +197,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -211,7 +197,7 @@ namespace Titanium.Web.Proxy.Http
} }
//Read the response headers in to unique and non-unique header collections //Read the response headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(ServerConnection.StreamReader, Response.NonUniqueResponseHeaders, Response.ResponseHeaders); await HeaderParser.ReadHeaders(ServerConnection.StreamReader, Response.ResponseHeaders);
} }
/// <summary> /// <summary>
...@@ -219,7 +205,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -219,7 +205,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
ConnectHeaders = null; ConnectRequest = null;
Request.Dispose(); Request.Dispose();
Response.Dispose(); Response.Dispose();
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
namespace Titanium.Web.Proxy.Http
{
class HttpsTools
{
public static async Task<bool> IsClientHello(CustomBufferedStream clientStream)
{
//detects the HTTPS ClientHello message as it is described in the following url:
//https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format
int recordType = await clientStream.PeekByteAsync(0);
if (recordType == 0x80)
{
//SSL 2
int length = await clientStream.PeekByteAsync(1);
if (length < 9)
{
// Message body too short.
return false;
}
if (await clientStream.PeekByteAsync(2) != 0x01)
{
// should be ClientHello
return false;
}
int majorVersion = await clientStream.PeekByteAsync(3);
int minorVersion = await clientStream.PeekByteAsync(4);
return true;
}
else if (recordType == 0x16)
{
//SSL 3.0 or TLS 1.0, 1.1 and 1.2
int majorVersion = await clientStream.PeekByteAsync(1);
int minorVersion = await clientStream.PeekByteAsync(2);
int length1 = await clientStream.PeekByteAsync(3);
int length2 = await clientStream.PeekByteAsync(4);
int length = (length1 << 8) + length2;
if (await clientStream.PeekByteAsync(5) != 0x01)
{
// should be ClientHello
return false;
}
return true;
}
return false;
}
}
}
This diff is collapsed.
This diff is collapsed.
...@@ -53,6 +53,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -53,6 +53,7 @@ namespace Titanium.Web.Proxy.Models
public bool IpV6Enabled => Equals(IpAddress, IPAddress.IPv6Any) public bool IpV6Enabled => Equals(IpAddress, IPAddress.IPv6Any)
|| Equals(IpAddress, IPAddress.IPv6Loopback) || Equals(IpAddress, IPAddress.IPv6Loopback)
|| Equals(IpAddress, IPAddress.IPv6None); || Equals(IpAddress, IPAddress.IPv6None);
} }
/// <summary> /// <summary>
...@@ -68,12 +69,6 @@ namespace Titanium.Web.Proxy.Models ...@@ -68,12 +69,6 @@ namespace Titanium.Web.Proxy.Models
internal bool IsSystemHttpsProxy { get; set; } internal bool IsSystemHttpsProxy { get; set; }
/// <summary>
/// Remote HTTPS ports we are allowed to communicate with
/// CONNECT request to ports other than these will not be decrypted
/// </summary>
public List<int> RemoteHttpsPorts { get; set; }
/// <summary> /// <summary>
/// List of host names to exclude using Regular Expressions. /// List of host names to exclude using Regular Expressions.
/// </summary> /// </summary>
...@@ -121,12 +116,6 @@ namespace Titanium.Web.Proxy.Models ...@@ -121,12 +116,6 @@ namespace Titanium.Web.Proxy.Models
/// <param name="enableSsl"></param> /// <param name="enableSsl"></param>
public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl) : base(ipAddress, port, enableSsl) public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl) : base(ipAddress, port, enableSsl)
{ {
//init to well known HTTPS ports
RemoteHttpsPorts = new List<int>
{
443,
8443
};
} }
} }
......
...@@ -15,12 +15,16 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -15,12 +15,16 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal ExternalProxy UpStreamHttpsProxy { get; set; } internal ExternalProxy UpStreamHttpsProxy { get; set; }
internal ExternalProxy UpStreamProxy => UseProxy ? IsHttps ? UpStreamHttpsProxy : UpStreamHttpProxy : null;
internal string HostName { get; set; } internal string HostName { get; set; }
internal int Port { get; set; } internal int Port { get; set; }
internal bool IsHttps { get; set; } internal bool IsHttps { get; set; }
internal bool UseProxy { get; set; }
/// <summary> /// <summary>
/// Http version /// Http version
/// </summary> /// </summary>
......
...@@ -32,29 +32,18 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -32,29 +32,18 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal async Task<TcpConnection> CreateClient(ProxyServer server, string remoteHostName, int remotePort, Version httpVersion, bool isHttps, internal async Task<TcpConnection> CreateClient(ProxyServer server, string remoteHostName, int remotePort, Version httpVersion, bool isHttps,
ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy) ExternalProxy externalHttpProxy, ExternalProxy externalHttpsProxy)
{ {
bool useHttpProxy = false; bool useProxy = false;
//check if external proxy is set for HTTP var externalProxy = isHttps ? externalHttpsProxy : externalHttpProxy;
if (!isHttps && externalHttpProxy != null && !(externalHttpProxy.HostName == remoteHostName && externalHttpProxy.Port == remotePort))
{
useHttpProxy = true;
//check if we need to ByPass
if (externalHttpProxy.BypassLocalhost && NetworkHelper.IsLocalIpAddress(remoteHostName))
{
useHttpProxy = false;
}
}
bool useHttpsProxy = false; //check if external proxy is set for HTTP/HTTPS
//check if external proxy is set for HTTPS if (externalProxy != null && !(externalProxy.HostName == remoteHostName && externalProxy.Port == remotePort))
if (isHttps && externalHttpsProxy != null && !(externalHttpsProxy.HostName == remoteHostName && externalHttpsProxy.Port == remotePort))
{ {
useHttpsProxy = true; useProxy = true;
//check if we need to ByPass //check if we need to ByPass
if (externalHttpsProxy.BypassLocalhost && NetworkHelper.IsLocalIpAddress(remoteHostName)) if (externalProxy.BypassLocalhost && NetworkHelper.IsLocalIpAddress(remoteHostName))
{ {
useHttpsProxy = false; useProxy = false;
} }
} }
...@@ -63,75 +52,60 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -63,75 +52,60 @@ namespace Titanium.Web.Proxy.Network.Tcp
try try
{ {
if (isHttps) //If this proxy uses another external proxy then create a tunnel request for HTTP/HTTPS connections
if (useProxy)
{ {
//If this proxy uses another external proxy then create a tunnel request for HTTPS connections client = new TcpClient(server.UpStreamEndPoint);
if (useHttpsProxy) await client.ConnectAsync(externalProxy.HostName, externalProxy.Port);
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
using (var writer = new StreamWriter(stream, Encoding.ASCII, server.BufferSize, true)
{ {
client = new TcpClient(server.UpStreamEndPoint); NewLine = ProxyConstants.NewLine
await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port); })
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); {
await writer.WriteLineAsync($"CONNECT {remoteHostName}:{remotePort} HTTP/{httpVersion}");
await writer.WriteLineAsync($"Host: {remoteHostName}:{remotePort}");
await writer.WriteLineAsync("Connection: Keep-Alive");
using (var writer = new StreamWriter(stream, Encoding.ASCII, server.BufferSize, true) if (!string.IsNullOrEmpty(externalProxy.UserName) && externalProxy.Password != null)
{
NewLine = ProxyConstants.NewLine
})
{ {
await writer.WriteLineAsync($"CONNECT {remoteHostName}:{remotePort} HTTP/{httpVersion}"); await writer.WriteLineAsync("Proxy-Connection: keep-alive");
await writer.WriteLineAsync($"Host: {remoteHostName}:{remotePort}"); await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " +
await writer.WriteLineAsync("Connection: Keep-Alive"); Convert.ToBase64String(Encoding.UTF8.GetBytes(
externalProxy.UserName + ":" + externalProxy.Password)));
if (!string.IsNullOrEmpty(externalHttpsProxy.UserName) && externalHttpsProxy.Password != null)
{
await writer.WriteLineAsync("Proxy-Connection: keep-alive");
await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " +
Convert.ToBase64String(Encoding.UTF8.GetBytes(
externalHttpsProxy.UserName + ":" + externalHttpsProxy.Password)));
}
await writer.WriteLineAsync();
await writer.FlushAsync();
writer.Close();
} }
await writer.WriteLineAsync();
await writer.FlushAsync();
writer.Close();
}
using (var reader = new CustomBinaryReader(stream, server.BufferSize)) using (var reader = new CustomBinaryReader(stream, server.BufferSize))
{ {
string result = await reader.ReadLineAsync(); string result = await reader.ReadLineAsync();
if (!new[] { "200 OK", "connection established" }.Any(s => result.ContainsIgnoreCase(s)))
{
throw new Exception("Upstream proxy failed to create a secure tunnel");
}
await reader.ReadAndIgnoreAllLinesAsync(); if (!new[] { "200 OK", "connection established" }.Any(s => result.ContainsIgnoreCase(s)))
{
throw new Exception("Upstream proxy failed to create a secure tunnel");
} }
await reader.ReadAndIgnoreAllLinesAsync();
} }
else }
{ else
client = new TcpClient(server.UpStreamEndPoint); {
await client.ConnectAsync(remoteHostName, remotePort); client = new TcpClient(server.UpStreamEndPoint);
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize); await client.ConnectAsync(remoteHostName, remotePort);
} stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
}
if (isHttps)
{
var sslStream = new SslStream(stream, false, server.ValidateServerCertificate, server.SelectClientCertificate); var sslStream = new SslStream(stream, false, server.ValidateServerCertificate, server.SelectClientCertificate);
stream = new CustomBufferedStream(sslStream, server.BufferSize); stream = new CustomBufferedStream(sslStream, server.BufferSize);
await sslStream.AuthenticateAsClientAsync(remoteHostName, null, server.SupportedSslProtocols, server.CheckCertificateRevocation); await sslStream.AuthenticateAsClientAsync(remoteHostName, null, server.SupportedSslProtocols, server.CheckCertificateRevocation);
} }
else
{
if (useHttpProxy)
{
client = new TcpClient(server.UpStreamEndPoint);
await client.ConnectAsync(externalHttpProxy.HostName, externalHttpProxy.Port);
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
}
else
{
client = new TcpClient(server.UpStreamEndPoint);
await client.ConnectAsync(remoteHostName, remotePort);
stream = new CustomBufferedStream(client.GetStream(), server.BufferSize);
}
}
client.ReceiveTimeout = server.ConnectionTimeOutSeconds * 1000; client.ReceiveTimeout = server.ConnectionTimeOutSeconds * 1000;
client.SendTimeout = server.ConnectionTimeOutSeconds * 1000; client.SendTimeout = server.ConnectionTimeOutSeconds * 1000;
...@@ -152,6 +126,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -152,6 +126,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
HostName = remoteHostName, HostName = remoteHostName,
Port = remotePort, Port = remotePort,
IsHttps = isHttps, IsHttps = isHttps,
UseProxy = useProxy,
TcpClient = client, TcpClient = client,
StreamReader = new CustomBinaryReader(stream, server.BufferSize), StreamReader = new CustomBinaryReader(stream, server.BufferSize),
Stream = stream, Stream = stream,
......
...@@ -4,6 +4,7 @@ using System.IO; ...@@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -12,31 +13,29 @@ namespace Titanium.Web.Proxy ...@@ -12,31 +13,29 @@ namespace Titanium.Web.Proxy
{ {
public partial class ProxyServer public partial class ProxyServer
{ {
private async Task<bool> CheckAuthorization(StreamWriter clientStreamWriter, IEnumerable<HttpHeader> headers) private async Task<bool> CheckAuthorization(StreamWriter clientStreamWriter, SessionEventArgs session)
{ {
if (AuthenticateUserFunc == null) if (AuthenticateUserFunc == null)
{ {
return true; return true;
} }
var httpHeaders = headers as ICollection<HttpHeader> ?? headers.ToArray(); var httpHeaders = session.WebSession.Request.RequestHeaders.ToArray();
try try
{ {
if (httpHeaders.All(t => t.Name != "Proxy-Authorization")) var header = httpHeaders.FirstOrDefault(t => t.Name == "Proxy-Authorization");
if (header == null)
{ {
await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Required"); session.WebSession.Response = await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Required");
return false; return false;
} }
var header = httpHeaders.FirstOrDefault(t => t.Name == "Proxy-Authorization");
if (header == null)
throw new NullReferenceException();
string headerValue = header.Value.Trim(); string headerValue = header.Value.Trim();
if (!headerValue.StartsWith("basic", StringComparison.CurrentCultureIgnoreCase)) if (!headerValue.StartsWith("basic", StringComparison.CurrentCultureIgnoreCase))
{ {
//Return not authorized //Return not authorized
await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid"); session.WebSession.Response = await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid");
return false; return false;
} }
...@@ -46,7 +45,7 @@ namespace Titanium.Web.Proxy ...@@ -46,7 +45,7 @@ namespace Titanium.Web.Proxy
if (decoded.Contains(":") == false) if (decoded.Contains(":") == false)
{ {
//Return not authorized //Return not authorized
await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid"); session.WebSession.Response = await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid");
return false; return false;
} }
...@@ -59,25 +58,25 @@ namespace Titanium.Web.Proxy ...@@ -59,25 +58,25 @@ namespace Titanium.Web.Proxy
ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", e, httpHeaders)); ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", e, httpHeaders));
//Return not authorized //Return not authorized
await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid"); session.WebSession.Response = await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid");
return false; return false;
} }
} }
private async Task SendAuthentication407Response(StreamWriter clientStreamWriter, string description) private async Task<Response> SendAuthentication407Response(StreamWriter clientStreamWriter, string description)
{ {
await WriteResponseStatus(HttpHeader.Version11, "407", description, clientStreamWriter);
var response = new Response var response = new Response
{ {
ResponseHeaders = new Dictionary<string, HttpHeader> HttpVersion = HttpHeader.Version11,
{ ResponseStatusCode = "407",
{ "Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"") }, ResponseStatusDescription = description
{ "Proxy-Connection", new HttpHeader("Proxy-Connection", "close") }
}
}; };
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync(); response.ResponseHeaders.AddHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"");
response.ResponseHeaders.AddHeader("Proxy-Connection", "close");
await WriteResponse(response, clientStreamWriter);
return response;
} }
} }
} }
...@@ -172,6 +172,16 @@ namespace Titanium.Web.Proxy ...@@ -172,6 +172,16 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public event Func<object, SessionEventArgs, Task> BeforeResponse; public event Func<object, SessionEventArgs, Task> BeforeResponse;
/// <summary>
/// Intercept tunnel connect reques
/// </summary>
public event Func<object, TunnelConnectSessionEventArgs, Task> TunnelConnectRequest;
/// <summary>
/// Intercept tunnel connect response
/// </summary>
public event Func<object, TunnelConnectSessionEventArgs, Task> TunnelConnectResponse;
/// <summary> /// <summary>
/// External proxy for Http /// External proxy for Http
/// </summary> /// </summary>
......
This diff is collapsed.
...@@ -149,6 +149,19 @@ namespace Titanium.Web.Proxy ...@@ -149,6 +149,19 @@ namespace Titanium.Web.Proxy
return await compressor.Compress(responseBodyStream); return await compressor.Compress(responseBodyStream);
} }
/// <summary>
/// Writes the response.
/// </summary>
/// <param name="response"></param>
/// <param name="responseWriter"></param>
/// <param name="flush"></param>
/// <returns></returns>
private async Task WriteResponse(Response response, StreamWriter responseWriter, bool flush = true)
{
await WriteResponseStatus(response.HttpVersion, response.ResponseStatusCode, response.ResponseStatusDescription, responseWriter);
await WriteResponseHeaders(responseWriter, response, flush);
}
/// <summary> /// <summary>
/// Write response status /// Write response status
/// </summary> /// </summary>
...@@ -167,54 +180,37 @@ namespace Titanium.Web.Proxy ...@@ -167,54 +180,37 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="responseWriter"></param> /// <param name="responseWriter"></param>
/// <param name="response"></param> /// <param name="response"></param>
/// <param name="flush"></param>
/// <returns></returns> /// <returns></returns>
private async Task WriteResponseHeaders(StreamWriter responseWriter, Response response) private async Task WriteResponseHeaders(StreamWriter responseWriter, Response response, bool flush = true)
{ {
FixProxyHeaders(response.ResponseHeaders); FixProxyHeaders(response.ResponseHeaders);
foreach (var header in response.ResponseHeaders) foreach (var header in response.ResponseHeaders)
{ {
await header.Value.WriteToStream(responseWriter); await header.WriteToStream(responseWriter);
} }
//write non unique request headers await responseWriter.WriteLineAsync();
foreach (var headerItem in response.NonUniqueResponseHeaders) if (flush)
{ {
var headers = headerItem.Value; await responseWriter.FlushAsync();
foreach (var header in headers)
{
await header.WriteToStream(responseWriter);
}
} }
await responseWriter.WriteLineAsync();
await responseWriter.FlushAsync();
} }
/// <summary> /// <summary>
/// Fix proxy specific headers /// Fix proxy specific headers
/// </summary> /// </summary>
/// <param name="headers"></param> /// <param name="headers"></param>
private void FixProxyHeaders(Dictionary<string, HttpHeader> headers) private void FixProxyHeaders(HeaderCollection headers)
{ {
//If proxy-connection close was returned inform to close the connection //If proxy-connection close was returned inform to close the connection
bool hasProxyHeader = headers.ContainsKey("proxy-connection"); string proxyHeader = headers.GetHeaderValueOrNull("proxy-connection");
bool hasConnectionheader = headers.ContainsKey("connection"); headers.RemoveHeader("proxy-connection");
if (hasProxyHeader) if (proxyHeader != null)
{ {
var proxyHeader = headers["proxy-connection"]; headers.SetOrAddHeaderValue("connection", proxyHeader);
if (hasConnectionheader == false)
{
headers.Add("connection", new HttpHeader("connection", proxyHeader.Value));
}
else
{
var connectionHeader = headers["connection"];
connectionHeader.Value = proxyHeader.Value;
}
headers.Remove("proxy-connection");
} }
} }
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
<Compile Include="Decompression\IDecompression.cs" /> <Compile Include="Decompression\IDecompression.cs" />
<Compile Include="EventArguments\CertificateSelectionEventArgs.cs" /> <Compile Include="EventArguments\CertificateSelectionEventArgs.cs" />
<Compile Include="EventArguments\CertificateValidationEventArgs.cs" /> <Compile Include="EventArguments\CertificateValidationEventArgs.cs" />
<Compile Include="EventArguments\TunnelConnectEventArgs.cs" />
<Compile Include="Extensions\ByteArrayExtensions.cs" /> <Compile Include="Extensions\ByteArrayExtensions.cs" />
<Compile Include="Extensions\FuncExtensions.cs" /> <Compile Include="Extensions\FuncExtensions.cs" />
<Compile Include="Helpers\HttpHelper.cs" /> <Compile Include="Helpers\HttpHelper.cs" />
...@@ -85,7 +86,11 @@ ...@@ -85,7 +86,11 @@
<Compile Include="Helpers\RunTime.cs" /> <Compile Include="Helpers\RunTime.cs" />
<Compile Include="Helpers\WinHttp\WinHttpHandle.cs" /> <Compile Include="Helpers\WinHttp\WinHttpHandle.cs" />
<Compile Include="Helpers\WinHttp\WinHttpWebProxyFinder.cs" /> <Compile Include="Helpers\WinHttp\WinHttpWebProxyFinder.cs" />
<Compile Include="Http\ConnectRequest.cs" />
<Compile Include="Http\ConnectResponse.cs" />
<Compile Include="Http\HeaderCollection.cs" />
<Compile Include="Http\HeaderParser.cs" /> <Compile Include="Http\HeaderParser.cs" />
<Compile Include="Http\HttpsTools.cs" />
<Compile Include="Http\Responses\GenericResponse.cs" /> <Compile Include="Http\Responses\GenericResponse.cs" />
<Compile Include="Network\CachedCertificate.cs" /> <Compile Include="Network\CachedCertificate.cs" />
<Compile Include="Network\Certificate\WinCertificateMaker.cs" /> <Compile Include="Network\Certificate\WinCertificateMaker.cs" />
...@@ -163,4 +168,4 @@ ...@@ -163,4 +168,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>
\ No newline at end of file
...@@ -44,7 +44,7 @@ namespace Titanium.Web.Proxy ...@@ -44,7 +44,7 @@ namespace Titanium.Web.Proxy
//check in non-unique headers first //check in non-unique headers first
var header = var header =
args.WebSession.Response.NonUniqueResponseHeaders.FirstOrDefault( args.WebSession.Response.ResponseHeaders.NonUniqueHeaders.FirstOrDefault(
x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase))); x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase)));
if (!header.Equals(new KeyValuePair<string, List<HttpHeader>>())) if (!header.Equals(new KeyValuePair<string, List<HttpHeader>>()))
...@@ -54,7 +54,7 @@ namespace Titanium.Web.Proxy ...@@ -54,7 +54,7 @@ namespace Titanium.Web.Proxy
if (headerName != null) if (headerName != null)
{ {
authHeader = args.WebSession.Response.NonUniqueResponseHeaders[headerName] authHeader = args.WebSession.Response.ResponseHeaders.NonUniqueHeaders[headerName]
.FirstOrDefault(x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase))); .FirstOrDefault(x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase)));
} }
...@@ -63,7 +63,7 @@ namespace Titanium.Web.Proxy ...@@ -63,7 +63,7 @@ namespace Titanium.Web.Proxy
{ {
//check in non-unique headers first //check in non-unique headers first
var uHeader = var uHeader =
args.WebSession.Response.ResponseHeaders.FirstOrDefault(x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase))); args.WebSession.Response.ResponseHeaders.Headers.FirstOrDefault(x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase)));
if (!uHeader.Equals(new KeyValuePair<string, HttpHeader>())) if (!uHeader.Equals(new KeyValuePair<string, HttpHeader>()))
{ {
...@@ -72,9 +72,9 @@ namespace Titanium.Web.Proxy ...@@ -72,9 +72,9 @@ namespace Titanium.Web.Proxy
if (headerName != null) if (headerName != null)
{ {
authHeader = authSchemes.Any(x => args.WebSession.Response.ResponseHeaders[headerName].Value authHeader = authSchemes.Any(x => args.WebSession.Response.ResponseHeaders.Headers[headerName].Value
.StartsWith(x, StringComparison.OrdinalIgnoreCase)) .StartsWith(x, StringComparison.OrdinalIgnoreCase))
? args.WebSession.Response.ResponseHeaders[headerName] ? args.WebSession.Response.ResponseHeaders.Headers[headerName]
: null; : null;
} }
} }
...@@ -84,9 +84,9 @@ namespace Titanium.Web.Proxy ...@@ -84,9 +84,9 @@ namespace Titanium.Web.Proxy
string scheme = authSchemes.FirstOrDefault(x => authHeader.Value.Equals(x, StringComparison.OrdinalIgnoreCase)); string scheme = authSchemes.FirstOrDefault(x => authHeader.Value.Equals(x, StringComparison.OrdinalIgnoreCase));
//clear any existing headers to avoid confusing bad servers //clear any existing headers to avoid confusing bad servers
if (args.WebSession.Request.NonUniqueRequestHeaders.ContainsKey("Authorization")) if (args.WebSession.Request.RequestHeaders.NonUniqueHeaders.ContainsKey("Authorization"))
{ {
args.WebSession.Request.NonUniqueRequestHeaders.Remove("Authorization"); args.WebSession.Request.RequestHeaders.NonUniqueHeaders.Remove("Authorization");
} }
//initial value will match exactly any of the schemes //initial value will match exactly any of the schemes
...@@ -97,13 +97,13 @@ namespace Titanium.Web.Proxy ...@@ -97,13 +97,13 @@ namespace Titanium.Web.Proxy
var auth = new HttpHeader("Authorization", string.Concat(scheme, clientToken)); var auth = new HttpHeader("Authorization", string.Concat(scheme, clientToken));
//replace existing authorization header if any //replace existing authorization header if any
if (args.WebSession.Request.RequestHeaders.ContainsKey("Authorization")) if (args.WebSession.Request.RequestHeaders.Headers.ContainsKey("Authorization"))
{ {
args.WebSession.Request.RequestHeaders["Authorization"] = auth; args.WebSession.Request.RequestHeaders.Headers["Authorization"] = auth;
} }
else else
{ {
args.WebSession.Request.RequestHeaders.Add("Authorization", auth); args.WebSession.Request.RequestHeaders.Headers.Add("Authorization", auth);
} }
//don't need to send body for Authorization request //don't need to send body for Authorization request
...@@ -122,7 +122,7 @@ namespace Titanium.Web.Proxy ...@@ -122,7 +122,7 @@ namespace Titanium.Web.Proxy
string clientToken = WinAuthHandler.GetFinalAuthToken(args.WebSession.Request.Host, serverToken, args.Id); string clientToken = WinAuthHandler.GetFinalAuthToken(args.WebSession.Request.Host, serverToken, args.Id);
//there will be an existing header from initial client request //there will be an existing header from initial client request
args.WebSession.Request.RequestHeaders["Authorization"] = new HttpHeader("Authorization", string.Concat(scheme, clientToken)); args.WebSession.Request.RequestHeaders.Headers["Authorization"] = new HttpHeader("Authorization", string.Concat(scheme, clientToken));
//send body for final auth request //send body for final auth request
if (args.WebSession.Request.HasBody) if (args.WebSession.Request.HasBody)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment