Commit e8e41ca3 authored by Jehonathan Thomas's avatar Jehonathan Thomas Committed by GitHub

Merge pull request #223 from justcoding121/develop

Merge with develop
parents 54cfea8f b6d18a61
Doneness:
- [ ] Build is okay - I made sure that this change is building successfully.
- [ ] No Bugs - I made sure that this change is working properly as expected. It does'nt have any bugs that you are aware of.
- [ ] No Bugs - I made sure that this change is working properly as expected. It doesn't have any bugs that you are aware of.
- [ ] Branching - If this is not a hotfix, I am making this request against develop branch
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BC/@EntryIndexedValue">BC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
......
......@@ -17,10 +17,10 @@ namespace Titanium.Web.Proxy
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
private bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
//if user callback is registered then do it
if (ServerCertificateValidationCallback != null)
......@@ -33,13 +33,12 @@ namespace Titanium.Web.Proxy
};
Delegate[] invocationList = ServerCertificateValidationCallback.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, CertificateValidationEventArgs, Task>)invocationList[i])(null, args);
handlerTasks[i] = ((Func<object, CertificateValidationEventArgs, Task>) invocationList[i])(null, args);
}
Task.WhenAll(handlerTasks).Wait();
......@@ -115,7 +114,7 @@ namespace Titanium.Web.Proxy
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, CertificateSelectionEventArgs, Task>)invocationList[i])(null, args);
handlerTasks[i] = ((Func<object, CertificateSelectionEventArgs, Task>) invocationList[i])(null, args);
}
Task.WhenAll(handlerTasks).Wait();
......
......@@ -15,7 +15,7 @@ namespace Titanium.Web.Proxy.Compression
{
using (var zip = new DeflateStream(ms, CompressionMode.Compress, true))
{
await zip.WriteAsync(responseBody, 0, responseBody.Length);
await zip.WriteAsync(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
......
......@@ -15,7 +15,7 @@ namespace Titanium.Web.Proxy.Compression
{
using (var zip = new GZipStream(ms, CompressionMode.Compress, true))
{
await zip.WriteAsync(responseBody, 0, responseBody.Length);
await zip.WriteAsync(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
......
......@@ -7,7 +7,7 @@
{
internal IDecompression Create(string type)
{
switch(type)
switch (type)
{
case "gzip":
return new GZipDecompression();
......
......@@ -2,7 +2,6 @@
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// When no compression is specified just return the byte array
/// </summary>
......
......@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy.Decompression
int read;
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
output.Write(buffer, 0, read);
}
return output.ToArray();
......
......@@ -7,6 +7,6 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary>
internal interface IDecompression
{
Task<byte[]> Decompress(byte[] compressedArray, int bufferSize);
Task<byte[]> Decompress(byte[] compressedArray, int bufferSize);
}
}
......@@ -12,26 +12,30 @@ namespace Titanium.Web.Proxy.EventArguments
/// Sender object.
/// </summary>
public object Sender { get; internal set; }
/// <summary>
/// Target host.
/// </summary>
public string TargetHost { get; internal set; }
/// <summary>
/// Local certificates.
/// </summary>
public X509CertificateCollection LocalCertificates { get; internal set; }
/// <summary>
/// Remote certificate.
/// </summary>
public X509Certificate RemoteCertificate { get; internal set; }
/// <summary>
/// Acceptable issuers.
/// </summary>
public string[] AcceptableIssuers { get; internal set; }
/// <summary>
/// Client Certificate.
/// </summary>
public X509Certificate ClientCertificate { get; set; }
}
}
......@@ -13,10 +13,12 @@ namespace Titanium.Web.Proxy.EventArguments
/// Certificate
/// </summary>
public X509Certificate Certificate { get; internal set; }
/// <summary>
/// Certificate chain
/// </summary>
public X509Chain Chain { get; internal set; }
/// <summary>
/// SSL policy errors.
/// </summary>
......
......@@ -22,7 +22,6 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public class SessionEventArgs : EventArgs, IDisposable
{
/// <summary>
/// Size of Buffers used by this object
/// </summary>
......@@ -47,11 +46,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Should we send a rerequest
/// </summary>
public bool ReRequest
{
get;
set;
}
public bool ReRequest { get; set; }
/// <summary>
/// Does this session uses SSL
......@@ -61,7 +56,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Client End Point.
/// </summary>
public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
public IPEndPoint ClientEndPoint => (IPEndPoint) ProxyClient.TcpClient.Client.RemoteEndPoint;
/// <summary>
/// A web session corresponding to a single request/response sequence
......@@ -108,14 +103,13 @@ namespace Titanium.Web.Proxy.EventArguments
//Caching check
if (WebSession.Request.RequestBody == null)
{
//If chunked then its easy just read the whole body with the content length mentioned in the request header
using (var requestBodyStream = new MemoryStream())
{
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (WebSession.Request.IsChunked)
{
await ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(bufferSize, requestBodyStream);
await ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(requestBodyStream);
}
else
{
......@@ -125,7 +119,6 @@ namespace Titanium.Web.Proxy.EventArguments
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await ProxyClient.ClientStreamReader.CopyBytesToStream(bufferSize, requestBodyStream,
WebSession.Request.ContentLength);
}
else if (WebSession.Request.HttpVersion.Major == 1 && WebSession.Request.HttpVersion.Minor == 0)
{
......@@ -140,7 +133,6 @@ namespace Titanium.Web.Proxy.EventArguments
//So that next time we can deliver body from cache
WebSession.Request.RequestBodyRead = true;
}
}
/// <summary>
......@@ -156,7 +148,7 @@ namespace Titanium.Web.Proxy.EventArguments
//If chuncked the read chunk by chunk until we hit chunk end symbol
if (WebSession.Response.IsChunked)
{
await WebSession.ServerConnection.StreamReader.CopyBytesToStreamChunked(bufferSize, responseBodyStream);
await WebSession.ServerConnection.StreamReader.CopyBytesToStreamChunked(responseBodyStream);
}
else
{
......@@ -165,7 +157,6 @@ namespace Titanium.Web.Proxy.EventArguments
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream,
WebSession.Response.ContentLength);
}
else if ((WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0) || WebSession.Response.ContentLength == -1)
{
......@@ -175,7 +166,6 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding,
responseBodyStream.ToArray());
}
//set this to true for caching
WebSession.Response.ResponseBodyRead = true;
......@@ -199,6 +189,7 @@ namespace Titanium.Web.Proxy.EventArguments
}
return WebSession.Request.RequestBody;
}
/// <summary>
/// Gets the request body as string
/// </summary>
......@@ -265,7 +256,6 @@ namespace Titanium.Web.Proxy.EventArguments
}
await SetRequestBody(WebSession.Request.Encoding.GetBytes(body));
}
/// <summary>
......@@ -297,7 +287,7 @@ namespace Titanium.Web.Proxy.EventArguments
await GetResponseBody();
return WebSession.Response.ResponseBodyString ??
(WebSession.Response.ResponseBodyString = WebSession.Response.Encoding.GetString(WebSession.Response.ResponseBody));
(WebSession.Response.ResponseBodyString = WebSession.Response.Encoding.GetString(WebSession.Response.ResponseBody));
}
/// <summary>
......@@ -415,8 +405,8 @@ namespace Titanium.Web.Proxy.EventArguments
/// <param name="headers"></param>
public async Task Ok(byte[] result, Dictionary<string, HttpHeader> headers)
{
var response = new OkResponse();           
var response = new OkResponse();
if (headers != null && headers.Count > 0)
{
response.ResponseHeaders = headers;
......@@ -428,7 +418,7 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Request.CancelRequest = true;
}
/// <summary>
/// Before request is made to server 
/// Respond with the specified HTML string to client
......@@ -436,11 +426,11 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
/// <param name="html"></param>
/// <param name="status"></param>
        public async Task GenericResponse(string html, HttpStatusCode status)       
public async Task GenericResponse(string html, HttpStatusCode status)
{
            await GenericResponse(html, null, status);
await GenericResponse(html, null, status);
}
/// <summary>
/// Before request is made to server 
/// Respond with the specified HTML string to client
......@@ -450,23 +440,23 @@ namespace Titanium.Web.Proxy.EventArguments
/// <param name="html"></param>
/// <param name="headers"></param>
/// <param name="status"></param>
        public async Task GenericResponse(string html, Dictionary<string, HttpHeader> headers, HttpStatusCode status)
{           
if (WebSession.Request.RequestLocked)           
{               
throw new Exception("You cannot call this function after request is made to server.");           
public async Task GenericResponse(string html, Dictionary<string, HttpHeader> headers, HttpStatusCode status)
{
if (WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function after request is made to server.");
}
if (html == null)
{               
html = string.Empty;           
{
html = string.Empty;
}
var result = Encoding.Default.GetBytes(html);
await GenericResponse(result, headers, status);       
await GenericResponse(result, headers, status);
}
/// <summary>
/// Before request is made to server
/// Respond with the specified byte[] to client
......@@ -480,21 +470,21 @@ namespace Titanium.Web.Proxy.EventArguments
public async Task GenericResponse(byte[] result, Dictionary<string, HttpHeader> headers, HttpStatusCode status)
{
var response = new GenericResponse(status);
if (headers != null && headers.Count > 0)
{
response.ResponseHeaders = headers;
}
response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseBody = result;
await Respond(response);
WebSession.Request.CancelRequest = true;
}
/// <summary>
/// Redirect to URL.
/// </summary>
......@@ -531,7 +521,6 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public void Dispose()
{
}
}
}

namespace Titanium.Web.Proxy.Exceptions
namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
/// An expception thrown when body is unexpectedly empty
......@@ -15,4 +14,4 @@ namespace Titanium.Web.Proxy.Exceptions
{
}
}
}
\ No newline at end of file
}
......@@ -25,4 +25,4 @@ namespace Titanium.Web.Proxy.Exceptions
/// </summary>
public IEnumerable<HttpHeader> Headers { get; }
}
}
\ No newline at end of file
}
......@@ -24,4 +24,4 @@ namespace Titanium.Web.Proxy.Exceptions
{
}
}
}
\ No newline at end of file
}
......@@ -27,4 +27,4 @@ namespace Titanium.Web.Proxy.Exceptions
/// </remarks>
public SessionEventArgs SessionEventArgs { get; }
}
}
\ No newline at end of file
}
......@@ -21,6 +21,5 @@ namespace Titanium.Web.Proxy.Extensions
Array.Copy(data, index, result, 0, length);
return result;
}
}
}
......@@ -46,4 +46,4 @@ namespace Titanium.Web.Proxy.Extensions
return Encoding.GetEncoding("ISO-8859-1");
}
}
}
\ No newline at end of file
}
......@@ -43,4 +43,4 @@ namespace Titanium.Web.Proxy.Extensions
return Encoding.GetEncoding("ISO-8859-1");
}
}
}
\ No newline at end of file
}
......@@ -30,15 +30,15 @@ namespace Titanium.Web.Proxy.Extensions
await input.CopyToAsync(output);
}
/// <summary>
/// copies the specified bytes to the stream from the input stream
/// </summary>
/// <param name="streamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="stream"></param>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, int bufferSize, Stream stream, long totalBytesToRead)
/// <summary>
/// copies the specified bytes to the stream from the input stream
/// </summary>
/// <param name="streamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="stream"></param>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, int bufferSize, Stream stream, long totalBytesToRead)
{
var totalbytesRead = 0;
......@@ -65,14 +65,13 @@ namespace Titanium.Web.Proxy.Extensions
}
}
/// <summary>
/// Copies the stream chunked
/// </summary>
/// <param name="clientStreamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="stream"></param>
/// <returns></returns>
internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, int bufferSize, Stream stream)
/// <summary>
/// Copies the stream chunked
/// </summary>
/// <param name="clientStreamReader"></param>
/// <param name="stream"></param>
/// <returns></returns>
internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, Stream stream)
{
while (true)
{
......@@ -93,6 +92,7 @@ namespace Titanium.Web.Proxy.Extensions
}
}
}
/// <summary>
/// Writes the byte array body to the given stream; optionally chunked
/// </summary>
......@@ -112,17 +112,17 @@ namespace Titanium.Web.Proxy.Extensions
}
}
/// <summary>
/// Copies the specified content length number of bytes to the output stream from the given inputs stream
/// optionally chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="outStream"></param>
/// <param name="isChunked"></param>
/// <param name="contentLength"></param>
/// <returns></returns>
internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream, bool isChunked, long contentLength)
/// <summary>
/// Copies the specified content length number of bytes to the output stream from the given inputs stream
/// optionally chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="outStream"></param>
/// <param name="isChunked"></param>
/// <param name="contentLength"></param>
/// <returns></returns>
internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream, bool isChunked, long contentLength)
{
if (!isChunked)
{
......@@ -136,7 +136,7 @@ namespace Titanium.Web.Proxy.Extensions
if (contentLength < bufferSize)
{
bytesToRead = (int)contentLength;
bytesToRead = (int) contentLength;
}
var buffer = new byte[bufferSize];
......@@ -154,23 +154,22 @@ namespace Titanium.Web.Proxy.Extensions
bytesRead = 0;
var remainingBytes = (contentLength - totalBytesRead);
bytesToRead = remainingBytes > (long)bufferSize ? bufferSize : (int)remainingBytes;
bytesToRead = remainingBytes > (long) bufferSize ? bufferSize : (int) remainingBytes;
}
}
else
{
await WriteResponseBodyChunked(inStreamReader, bufferSize, outStream);
await WriteResponseBodyChunked(inStreamReader, outStream);
}
}
/// <summary>
/// Copies the streams chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="outStream"></param>
/// <returns></returns>
internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream)
/// <summary>
/// Copies the streams chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="outStream"></param>
/// <returns></returns>
internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inStreamReader, Stream outStream)
{
while (true)
{
......@@ -199,6 +198,7 @@ namespace Titanium.Web.Proxy.Extensions
}
}
}
/// <summary>
/// Copies the given input bytes to output stream chunked
/// </summary>
......@@ -216,6 +216,5 @@ namespace Titanium.Web.Proxy.Extensions
await outStream.WriteAsync(ProxyConstants.ChunkEnd, 0, ProxyConstants.ChunkEnd.Length);
}
}
}
\ No newline at end of file
}
......@@ -33,5 +33,4 @@ namespace Titanium.Web.Proxy.Extensions
}
}
}
}
......@@ -6,7 +6,6 @@ using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Helpers
{
/// <summary>
/// A custom binary reader that would allo us to read string line by line
/// using the specified encoding
......@@ -124,7 +123,7 @@ namespace Titanium.Web.Proxy.Helpers
int bytesToRead = bufferSize;
if (totalBytesToRead < bufferSize)
bytesToRead = (int)totalBytesToRead;
bytesToRead = (int) totalBytesToRead;
var buffer = staticBuffer;
int bytesRead;
......@@ -138,7 +137,7 @@ namespace Titanium.Web.Proxy.Helpers
break;
var remainingBytes = totalBytesToRead - totalBytesRead;
bytesToRead = Math.Min(bufferSize, (int)remainingBytes);
bytesToRead = Math.Min(bufferSize, (int) remainingBytes);
if (totalBytesRead + bytesToRead > buffer.Length)
{
......@@ -168,4 +167,4 @@ namespace Titanium.Web.Proxy.Helpers
buffer = newBuffer;
}
}
}
\ No newline at end of file
}
......@@ -15,11 +15,11 @@ namespace Titanium.Web.Proxy.Helpers
internal class CustomBufferedStream : Stream
{
private readonly Stream baseStream;
private readonly byte[] streamBuffer;
private int bufferLength;
private int bufferPos;
/// <summary>
......@@ -197,7 +197,7 @@ namespace Titanium.Web.Proxy.Helpers
{
if (asyncResult is ReadAsyncResult)
{
return ((ReadAsyncResult)asyncResult).ReadBytes;
return ((ReadAsyncResult) asyncResult).ReadBytes;
}
return baseStream.EndRead(asyncResult);
......
......@@ -68,4 +68,4 @@ namespace Titanium.Web.Proxy.Helpers
}
}
}
}
\ No newline at end of file
}
......@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Helpers
private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion)
{
var tcpRow = TcpHelper.GetExtendedTcpTable(ipVersion).FirstOrDefault(
row => row.LocalEndPoint.Port == port);
row => row.LocalEndPoint.Port == port);
return tcpRow?.ProcessId ?? 0;
}
......@@ -69,7 +69,6 @@ namespace Titanium.Web.Proxy.Helpers
}
catch (SocketException)
{
}
}
}
......
......@@ -36,7 +36,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary>
/// Manage system proxy settings
/// </summary>
internal class SystemProxyManager
internal class SystemProxyManager
{
internal const int InternetOptionSettingsChanged = 39;
internal const int InternetOptionRefresh = 37;
......@@ -186,12 +186,12 @@ namespace Titanium.Web.Proxy.Helpers
HostName = endPoint.Split(':')[0],
Port = int.Parse(endPoint.Split(':')[1]),
IsHttps = tmp.StartsWith("https=")
};
};
}
return null;
}
/// <summary>
/// Prepares the proxy server registry (create empty values if they don't exist)
/// </summary>
......@@ -207,7 +207,6 @@ namespace Titanium.Web.Proxy.Helpers
{
reg.SetValue("ProxyServer", string.Empty);
}
}
/// <summary>
......@@ -219,4 +218,4 @@ namespace Titanium.Web.Proxy.Helpers
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0);
}
}
}
\ No newline at end of file
}
......@@ -93,21 +93,21 @@ namespace Titanium.Web.Proxy.Helpers
var ipVersionValue = ipVersion == IpVersion.Ipv4 ? NativeMethods.AfInet : NativeMethods.AfInet6;
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) != 0)
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, ipVersionValue, (int) NativeMethods.TcpTableType.OwnerPidAll, 0) != 0)
{
try
{
tcpTable = Marshal.AllocHGlobal(tcpTableLength);
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) == 0)
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, (int) NativeMethods.TcpTableType.OwnerPidAll, 0) == 0)
{
NativeMethods.TcpTable table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable));
NativeMethods.TcpTable table = (NativeMethods.TcpTable) Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable));
IntPtr rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length));
IntPtr rowPtr = (IntPtr) ((long) tcpTable + Marshal.SizeOf(table.length));
for (int i = 0; i < table.length; ++i)
{
tcpRows.Add(new TcpRow((NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow))));
rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(NativeMethods.TcpRow)));
tcpRows.Add(new TcpRow((NativeMethods.TcpRow) Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow))));
rowPtr = (IntPtr) ((long) rowPtr + Marshal.SizeOf(typeof(NativeMethods.TcpRow)));
}
}
}
......@@ -144,7 +144,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns></returns>
internal static async Task SendRaw(int bufferSize, int connectionTimeOutSeconds,
string remoteHostName, int remotePort, string httpCmd, Version httpVersion, Dictionary<string, HttpHeader> requestHeaders,
bool isHttps, SslProtocols supportedProtocols,
bool isHttps, SslProtocols supportedProtocols,
RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback,
Stream clientStream, TcpConnectionFactory tcpConnectionFactory, IPEndPoint upStreamEndPoint)
{
......@@ -173,11 +173,11 @@ namespace Titanium.Web.Proxy.Helpers
}
var tcpConnection = await tcpConnectionFactory.CreateClient(bufferSize, connectionTimeOutSeconds,
remoteHostName, remotePort,
httpVersion, isHttps,
supportedProtocols, remoteCertificateValidationCallback, localCertificateSelectionCallback,
null, null, clientStream, upStreamEndPoint);
remoteHostName, remotePort,
httpVersion, isHttps,
supportedProtocols, remoteCertificateValidationCallback, localCertificateSelectionCallback,
null, null, clientStream, upStreamEndPoint);
try
{
Stream tunnelStream = tcpConnection.Stream;
......@@ -195,4 +195,4 @@ namespace Titanium.Web.Proxy.Helpers
}
}
}
}
\ No newline at end of file
}
......@@ -22,14 +22,17 @@ namespace Titanium.Web.Proxy.Http
/// Request ID.
/// </summary>
public Guid RequestId { get; }
/// <summary>
/// Headers passed with Connect.
/// </summary>
public List<HttpHeader> ConnectHeaders { get; set; }
/// <summary>
/// Web Request.
/// </summary>
public Request Request { get; set; }
/// <summary>
/// Web Response.
/// </summary>
......@@ -63,7 +66,7 @@ namespace Titanium.Web.Proxy.Http
connection.LastAccess = DateTime.Now;
ServerConnection = connection;
}
/// <summary>
/// Prepare and send the http(s) request
......@@ -90,7 +93,7 @@ namespace Titanium.Web.Proxy.Http
{
requestLines.AppendLine("Proxy-Connection: keep-alive");
requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(
$"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}")));
$"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}")));
}
//write request headers
foreach (var headerItem in Request.RequestHeaders)
......@@ -133,13 +136,13 @@ namespace Titanium.Web.Proxy.Http
//find if server is willing for expect continue
if (responseStatusCode.Equals("100")
&& responseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase))
&& responseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase))
{
Request.Is100Continue = true;
await ServerConnection.StreamReader.ReadLineAsync();
}
else if (responseStatusCode.Equals("417")
&& responseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
&& responseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
{
Request.ExpectationFailed = true;
await ServerConnection.StreamReader.ReadLineAsync();
......@@ -190,7 +193,7 @@ namespace Titanium.Web.Proxy.Http
return;
}
else if (Response.ResponseStatusCode.Equals("417")
&& Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
&& Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
{
//read next line after expectation failed response
Response.ExpectationFailed = true;
......@@ -233,4 +236,4 @@ namespace Titanium.Web.Proxy.Http
}
}
}
}
\ No newline at end of file
}
......@@ -47,7 +47,6 @@ namespace Titanium.Web.Proxy.Http
{
RequestHeaders.Add("Host", new HttpHeader("Host", value));
}
}
}
......@@ -119,9 +118,7 @@ namespace Titanium.Web.Proxy.Http
{
RequestHeaders.Remove("content-length");
}
}
}
}
......@@ -156,7 +153,6 @@ namespace Titanium.Web.Proxy.Http
RequestHeaders.Add("content-type", new HttpHeader("content-type", value));
}
}
}
/// <summary>
......@@ -226,12 +222,12 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public string Url => RequestUri.OriginalString;
/// <summary>
/// <summary>
/// Encoding for this request
/// </summary>
internal Encoding Encoding => this.GetEncoding();
/// <summary>
/// <summary>
/// Terminates the underlying Tcp Connection to client after current request
/// </summary>
internal bool CancelRequest { get; set; }
......@@ -298,6 +294,5 @@ namespace Titanium.Web.Proxy.Http
RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueRequestHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
}
}
}
......@@ -16,6 +16,7 @@ namespace Titanium.Web.Proxy.Http
/// Response Status Code.
/// </summary>
public string ResponseStatusCode { get; set; }
/// <summary>
/// Response Status description.
/// </summary>
......@@ -23,7 +24,7 @@ namespace Titanium.Web.Proxy.Http
internal Encoding Encoding => this.GetResponseCharacterEncoding();
/// <summary>
/// <summary>
/// Content encoding for this response
/// </summary>
internal string ContentEncoding
......@@ -61,7 +62,6 @@ namespace Titanium.Web.Proxy.Http
}
return true;
}
}
......@@ -82,7 +82,6 @@ namespace Titanium.Web.Proxy.Http
}
return null;
}
}
......@@ -110,7 +109,6 @@ namespace Titanium.Web.Proxy.Http
}
return -1;
}
set
{
......@@ -156,11 +154,10 @@ namespace Titanium.Web.Proxy.Http
if (header.Value.ContainsIgnoreCase("chunked"))
{
return true;
}
}
}
return false;
}
set
{
......@@ -186,9 +183,7 @@ namespace Titanium.Web.Proxy.Http
{
ResponseHeaders.Remove("transfer-encoding");
}
}
}
}
......@@ -240,5 +235,4 @@ namespace Titanium.Web.Proxy.Http
NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
}
}
}
using System.Net;
using System.Net;
namespace Titanium.Web.Proxy.Http.Responses
namespace Titanium.Web.Proxy.Http.Responses
{
    /// <summary>
    /// Anything other than a 200 or 302 response
    /// </summary>
    public class GenericResponse : Response
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="status"></param>
        public GenericResponse(HttpStatusCode status)
        {
            ResponseStatusCode = ((int)status).ToString();
            ResponseStatusDescription = status.ToString(); 
        }
/// <summary>
/// Anything other than a 200 or 302 response
/// </summary>
public class GenericResponse : Response
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="status"></param>
public GenericResponse(HttpStatusCode status)
{
ResponseStatusCode = ((int) status).ToString();
ResponseStatusDescription = status.ToString();
}
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="statusCode"></param>
        /// <param name="statusDescription"></param>
        public GenericResponse(string statusCode, string statusDescription)
        {
            ResponseStatusCode = statusCode;
            ResponseStatusDescription = statusDescription;
        }
    }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="statusCode"></param>
/// <param name="statusDescription"></param>
public GenericResponse(string statusCode, string statusDescription)
{
ResponseStatusCode = statusCode;
ResponseStatusDescription = statusDescription;
}
}
}
......@@ -30,10 +30,12 @@ namespace Titanium.Web.Proxy.Models
/// Ip Address.
/// </summary>
public IPAddress IpAddress { get; internal set; }
/// <summary>
/// Port.
/// </summary>
public int Port { get; internal set; }
/// <summary>
/// Enable SSL?
/// </summary>
......@@ -75,7 +77,7 @@ namespace Titanium.Web.Proxy.Models
throw new ArgumentException("Cannot set excluded when included is set");
}
ExcludedHttpsHostNameRegexList = value?.Select(x=>new Regex(x, RegexOptions.Compiled)).ToList();
ExcludedHttpsHostNameRegexList = value?.Select(x => new Regex(x, RegexOptions.Compiled)).ToList();
}
}
......@@ -110,7 +112,6 @@ namespace Titanium.Web.Proxy.Models
public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl)
: base(ipAddress, port, enableSsl)
{
}
}
......@@ -120,7 +121,6 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public class TransparentProxyEndPoint : ProxyEndPoint
{
/// <summary>
/// Name of the Certificate need to be sent (same as the hostname we want to proxy)
/// This is valid only when UseServerNameIndication is set to false
......@@ -139,5 +139,4 @@ namespace Titanium.Web.Proxy.Models
GenericCertificateName = "localhost";
}
}
}
......@@ -12,7 +12,7 @@ namespace Titanium.Web.Proxy.Models
private string userName;
private string password;
/// <summary>
/// Use default windows credentials?
/// </summary>
......@@ -26,8 +26,9 @@ namespace Titanium.Web.Proxy.Models
/// <summary>
/// Username.
/// </summary>
public string UserName {
get { return UseDefaultCredentials ? defaultCredentials.Value.UserName : userName; }
public string UserName
{
get { return UseDefaultCredentials ? defaultCredentials.Value.UserName : userName; }
set
{
userName = value;
......@@ -60,6 +61,7 @@ namespace Titanium.Web.Proxy.Models
/// Host name.
/// </summary>
public string HostName { get; set; }
/// <summary>
/// Port.
/// </summary>
......
......@@ -52,4 +52,4 @@ namespace Titanium.Web.Proxy.Models
await writer.WriteLineAsync(Value);
}
}
}
\ No newline at end of file
}
......@@ -20,6 +20,5 @@ namespace Titanium.Web.Proxy.Network
{
LastAccess = DateTime.Now;
}
}
}
......@@ -48,7 +48,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="keyStrength">The key strength.</param>
/// <param name="signatureAlgorithm">The signature algorithm.</param>
/// <param name="issuerPrivateKey">The issuer private key.</param>
/// <param name="hostName">The host name</param>
/// <param name="hostName">The host name</param>
/// <returns>X509Certificate2 instance.</returns>
/// <exception cref="PemException">Malformed sequence in RSA private key</exception>
private static X509Certificate2 GenerateCertificate(string hostName,
......@@ -83,7 +83,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
//add subject alternative names
var subjectAlternativeNames = new Asn1Encodable[]
{
new GeneralName(GeneralName.DnsName, hostName),
new GeneralName(GeneralName.DnsName, hostName),
};
var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames);
......@@ -110,7 +110,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
// Corresponding private key
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
var seq = (Asn1Sequence)Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded());
var seq = (Asn1Sequence) Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded());
if (seq.Count != 9)
{
......@@ -138,7 +138,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="signingCertificate">The signing certificate.</param>
/// <returns>X509Certificate2 instance.</returns>
/// <exception cref="System.ArgumentException">You must specify a Signing Certificate if and only if you are not creating a root.</exception>
private X509Certificate2 MakeCertificateInternal(bool isRoot,
private X509Certificate2 MakeCertificateInternal(bool isRoot,
string hostName, string subjectName,
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{
......@@ -190,5 +190,4 @@ namespace Titanium.Web.Proxy.Network.Certificate
return MakeCertificateInternal(isRoot, subject, $"CN={subject}", DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), isRoot ? null : signingCert);
}
}
}
\ No newline at end of file
}
......@@ -7,6 +7,6 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// </summary>
internal interface ICertificateMaker
{
X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert);
X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert);
}
}
......@@ -5,7 +5,6 @@ using System.Threading;
namespace Titanium.Web.Proxy.Network.Certificate
{
/// <summary>
/// Certificate Maker - uses MakeCert
/// Calls COM objects using reflection
......@@ -81,7 +80,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
}
private X509Certificate2 MakeCertificate(bool isRoot, string subject, string fullSubject,
int privateKeyLength, string hashAlg, DateTime validFrom, DateTime validTo,
int privateKeyLength, string hashAlg, DateTime validFrom, DateTime validTo,
X509Certificate2 signingCertificate)
{
if (isRoot != (null == signingCertificate))
......@@ -90,7 +89,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
}
var x500CertDN = Activator.CreateInstance(typeX500DN);
var typeValue = new object[] { fullSubject, 0 };
var typeValue = new object[] {fullSubject, 0};
typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500CertDN, typeValue);
var x500RootCertDN = Activator.CreateInstance(typeX500DN);
......@@ -111,16 +110,16 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (sharedPrivateKey == null)
{
sharedPrivateKey = Activator.CreateInstance(typeX509PrivateKey);
typeValue = new object[] { sProviderName };
typeValue = new object[] {sProviderName};
typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue);
typeValue[0] = 2;
typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue);
typeValue = new object[] { (isRoot ? 2 : 1) };
typeValue = new object[] {(isRoot ? 2 : 1)};
typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue);
if (!isRoot)
{
typeValue = new object[] { 176 };
typeValue = new object[] {176};
typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue);
}
......@@ -150,9 +149,9 @@ namespace Titanium.Web.Proxy.Network.Certificate
var requestCert = Activator.CreateInstance(typeRequestCert);
typeValue = new[] { 1, sharedPrivateKey, string.Empty };
typeValue = new[] {1, sharedPrivateKey, string.Empty};
typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, requestCert, typeValue);
typeValue = new[] { x500CertDN };
typeValue = new[] {x500CertDN};
typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, requestCert, typeValue);
typeValue[0] = x500RootCertDN;
typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, requestCert, typeValue);
......@@ -187,14 +186,14 @@ namespace Titanium.Web.Proxy.Network.Certificate
var extNames = Activator.CreateInstance(typeExtNames);
var altDnsNames = Activator.CreateInstance(typeCAlternativeName);
typeValue = new object[] { 3, subject };
typeValue = new object[] {3, subject};
typeCAlternativeName.InvokeMember("InitializeFromString", BindingFlags.InvokeMethod, null, altDnsNames, typeValue);
typeValue = new[] { altDnsNames };
typeValue = new[] {altDnsNames};
typeAltNamesCollection.InvokeMember("Add", BindingFlags.InvokeMethod, null, altNameCollection, typeValue);
typeValue = new[] { altNameCollection };
typeValue = new[] {altNameCollection};
typeExtNames.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, extNames, typeValue);
typeValue[0] = extNames;
......@@ -205,27 +204,27 @@ namespace Titanium.Web.Proxy.Network.Certificate
{
var signerCertificate = Activator.CreateInstance(typeSignerCertificate);
typeValue = new object[] { 0, 0, 12, signingCertificate.Thumbprint };
typeValue = new object[] {0, 0, 12, signingCertificate.Thumbprint};
typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, signerCertificate, typeValue);
typeValue = new[] { signerCertificate };
typeValue = new[] {signerCertificate};
typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, requestCert, typeValue);
}
else
{
var basicConstraints = Activator.CreateInstance(typeBasicConstraints);
typeValue = new object[] { "true", "0" };
typeValue = new object[] {"true", "0"};
typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, basicConstraints, typeValue);
typeValue = new[] { basicConstraints };
typeValue = new[] {basicConstraints};
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue);
}
oid = Activator.CreateInstance(typeOID);
typeValue = new object[] { 1, 0, 0, hashAlg };
typeValue = new object[] {1, 0, 0, hashAlg};
typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, oid, typeValue);
typeValue = new[] { oid };
typeValue = new[] {oid};
typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, requestCert, typeValue);
typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, requestCert, null);
......@@ -238,22 +237,21 @@ namespace Titanium.Web.Proxy.Network.Certificate
{
typeValue[0] = fullSubject;
typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null, x509Enrollment, typeValue);
}
var members = typeX509Enrollment.GetMembers();
typeValue[0] = 0;
var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
typeValue = new[] { 2, createCertRequest, 0, string.Empty };
var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
typeValue = new[] {2, createCertRequest, 0, string.Empty};
typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
typeValue = new object[] { null, 0, 1 };
typeValue = new object[] {null, 0, 1};
try
{
var empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
var empty = (string) typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
}
catch (Exception)
......@@ -264,8 +262,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
return null;
}
private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot,
bool switchToMTAIfNeeded,
private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot,
bool switchToMTAIfNeeded,
X509Certificate2 signingCert = null)
{
X509Certificate2 rCert = null;
......@@ -300,5 +298,4 @@ namespace Titanium.Web.Proxy.Network.Certificate
return rCert;
}
}
}
......@@ -50,8 +50,8 @@ namespace Titanium.Web.Proxy.Network
if (certEngine == null)
{
certEngine = engine == CertificateEngine.BouncyCastle ?
(ICertificateMaker) new BCCertificateMaker()
certEngine = engine == CertificateEngine.BouncyCastle
? (ICertificateMaker) new BCCertificateMaker()
: new WinCertificateMaker();
}
}
......@@ -95,7 +95,7 @@ namespace Titanium.Web.Proxy.Network
get { return rootCertificateName ?? defaultRootRootCertificateName; }
set
{
rootCertificateName = value;
rootCertificateName = value;
ClearRootCertificate();
}
}
......@@ -133,7 +133,7 @@ namespace Titanium.Web.Proxy.Network
private string GetRootCertificatePath()
{
var assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
// dynamically loaded assemblies returns string.Empty location
if (assemblyLocation == string.Empty)
{
......@@ -160,6 +160,7 @@ namespace Titanium.Web.Proxy.Network
return null;
}
}
/// <summary>
/// Attempts to create a RootCertificate
/// </summary>
......@@ -208,7 +209,7 @@ namespace Titanium.Web.Proxy.Network
{
//current user
TrustRootCertificate(StoreLocation.CurrentUser);
//current system
TrustRootCertificate(StoreLocation.LocalMachine);
}
......@@ -227,7 +228,7 @@ namespace Titanium.Web.Proxy.Network
cached.LastAccess = DateTime.Now;
return cached.Certificate;
}
X509Certificate2 certificate = null;
lock (string.Intern(certificateName))
{
......@@ -248,7 +249,7 @@ namespace Titanium.Web.Proxy.Network
}
if (certificate != null && !certificateCache.ContainsKey(certificateName))
{
certificateCache.Add(certificateName, new CachedCertificate { Certificate = certificate });
certificateCache.Add(certificateName, new CachedCertificate {Certificate = certificate});
}
}
else
......@@ -306,7 +307,7 @@ namespace Titanium.Web.Proxy.Network
{
exceptionFunc(
new Exception("Could not set root certificate"
+ " as system proxy since it is null or empty."));
+ " as system proxy since it is null or empty."));
return;
}
......@@ -326,7 +327,7 @@ namespace Titanium.Web.Proxy.Network
{
exceptionFunc(
new Exception("Failed to make system trust root certificate "
+ $" for {storeLocation} store location. You may need admin rights.", e));
+ $" for {storeLocation} store location. You may need admin rights.", e));
}
finally
{
......@@ -339,4 +340,4 @@ namespace Titanium.Web.Proxy.Network
{
}
}
}
\ No newline at end of file
}
......@@ -28,6 +28,5 @@ namespace Titanium.Web.Proxy.Network
/// used to write line by line to client
/// </summary>
internal StreamWriter ClientStreamWriter { get; set; }
}
}
......@@ -45,10 +45,10 @@ namespace Titanium.Web.Proxy.Network.Tcp
Stream clientStream, IPEndPoint upStreamEndPoint)
{
TcpClient client;
CustomBufferedStream stream;
CustomBufferedStream stream;
bool isLocalhost = (externalHttpsProxy == null && externalHttpProxy == null) ? false : NetworkHelper.IsLocalIpAddress(remoteHostName);
bool useHttpsProxy = externalHttpsProxy != null && externalHttpsProxy.HostName != remoteHostName && (externalHttpsProxy.BypassForLocalhost && !isLocalhost);
bool useHttpProxy = externalHttpProxy != null && externalHttpProxy.HostName != remoteHostName && (externalHttpProxy.BypassForLocalhost && !isLocalhost);
......@@ -63,7 +63,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port);
stream = new CustomBufferedStream(client.GetStream(), bufferSize);
using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true) { NewLine = ProxyConstants.NewLine })
using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true) {NewLine = ProxyConstants.NewLine})
{
await writer.WriteLineAsync($"CONNECT {remoteHostName}:{remotePort} HTTP/{httpVersion}");
await writer.WriteLineAsync($"Host: {remoteHostName}:{remotePort}");
......@@ -83,7 +83,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
var result = await reader.ReadLineAsync();
if (!new[] { "200 OK", "connection established" }.Any(s => result.ContainsIgnoreCase(s)))
if (!new[] {"200 OK", "connection established"}.Any(s => result.ContainsIgnoreCase(s)))
{
throw new Exception("Upstream proxy failed to create a secure tunnel");
}
......@@ -151,4 +151,4 @@ namespace Titanium.Web.Proxy.Network.Tcp
};
}
}
}
\ No newline at end of file
}
......@@ -21,10 +21,10 @@ namespace Titanium.Web.Proxy.Network.Tcp
long localAddress = tcpRow.localAddr;
LocalEndPoint = new IPEndPoint(localAddress, localPort);
int remotePort = (tcpRow.remotePort1 << 8) + (tcpRow.remotePort2) + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16);
long remoteAddress = tcpRow.remoteAddr;
RemoteEndPoint = new IPEndPoint(remoteAddress, remotePort);
}
int remotePort = (tcpRow.remotePort1 << 8) + (tcpRow.remotePort2) + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16);
long remoteAddress = tcpRow.remoteAddr;
RemoteEndPoint = new IPEndPoint(remoteAddress, remotePort);
}
/// <summary>
/// Gets the local end point.
......@@ -41,4 +41,4 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal int ProcessId { get; }
}
}
\ No newline at end of file
}
......@@ -45,4 +45,4 @@ namespace Titanium.Web.Proxy.Network.Tcp
return GetEnumerator();
}
}
}
\ No newline at end of file
}
......@@ -15,11 +15,11 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: InternalsVisibleTo("Titanium.Web.Proxy.UnitTests, PublicKey=" +
"0024000004800000940000000602000000240000525341310004000001000100e7368e0ccc717e" +
"eb4d57d35ad6a8305cbbed14faa222e13869405e92c83856266d400887d857005f1393ffca2b92" +
"de7f3ba0bdad35ec2d6057ee1846091b34be2abc3f97dc7e72c16fd4958c15126b12923df76964" +
"7d84922c3f4f3b80ee0ae8e4cb40bc1973b782afb90bb00519fd16adf960f217e23696e7c31654" +
"01d0acd6")]
"0024000004800000940000000602000000240000525341310004000001000100e7368e0ccc717e" +
"eb4d57d35ad6a8305cbbed14faa222e13869405e92c83856266d400887d857005f1393ffca2b92" +
"de7f3ba0bdad35ec2d6057ee1846091b34be2abc3f97dc7e72c16fd4958c15126b12923df76964" +
"7d84922c3f4f3b80ee0ae8e4cb40bc1973b782afb90bb00519fd16adf960f217e23696e7c31654" +
"01d0acd6")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
......
......@@ -12,7 +12,6 @@ namespace Titanium.Web.Proxy
{
public partial class ProxyServer
{
private async Task<bool> CheckAuthorization(StreamWriter clientStreamWriter, IEnumerable<HttpHeader> headers)
{
if (AuthenticateUserFunc == null)
......@@ -26,9 +25,8 @@ namespace Titanium.Web.Proxy
{
if (httpHeaders.All(t => t.Name != "Proxy-Authorization"))
{
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Required", clientStreamWriter);
"Proxy Authentication Required", clientStreamWriter);
var response = new Response
{
ResponseHeaders = new Dictionary<string, HttpHeader>
......@@ -102,7 +100,7 @@ namespace Titanium.Web.Proxy
ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", e, httpHeaders));
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response
{
ResponseHeaders = new Dictionary<string, HttpHeader>
......@@ -116,7 +114,6 @@ namespace Titanium.Web.Proxy
await clientStreamWriter.WriteLineAsync();
return false;
}
}
}
}
......@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy
/// </summary>
public partial class ProxyServer : IDisposable
{
/// <summary>
/// <summary>
/// Is the proxy currently running
/// </summary>
private bool proxyRunning { get; set; }
......@@ -52,9 +52,9 @@ namespace Titanium.Web.Proxy
private SystemProxyManager systemProxySettingsManager { get; }
#if !DEBUG
/// <summary>
/// Set firefox to use default system proxy
/// </summary>
/// <summary>
/// Set firefox to use default system proxy
/// </summary>
private FireFoxProxySettingsManager firefoxProxySettingsManager
= new FireFoxProxySettingsManager();
#endif
......@@ -201,31 +201,19 @@ namespace Titanium.Web.Proxy
/// Parameters are username, password provided by client
/// return true for successful authentication
/// </summary>
public Func<string, string, Task<bool>> AuthenticateUserFunc
{
get;
set;
}
public Func<string, string, Task<bool>> AuthenticateUserFunc { get; set; }
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpProxyFunc
{
get;
set;
}
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpProxyFunc { get; set; }
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTPS requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpsProxyFunc
{
get;
set;
}
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpsProxyFunc { get; set; }
/// <summary>
/// A list of IpAddress & port this proxy is listening to
......@@ -235,7 +223,7 @@ namespace Titanium.Web.Proxy
/// <summary>
/// List of supported Ssl versions
/// </summary>
public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Tls
public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Tls
| SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3;
/// <summary>
......@@ -281,8 +269,8 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint"></param>
public void AddEndPoint(ProxyEndPoint endPoint)
{
if (ProxyEndPoints.Any(x => x.IpAddress.Equals(endPoint.IpAddress)
&& endPoint.Port != 0 && x.Port == endPoint.Port))
if (ProxyEndPoints.Any(x => x.IpAddress.Equals(endPoint.IpAddress)
&& endPoint.Port != 0 && x.Port == endPoint.Port))
{
throw new Exception("Cannot add another endpoint to same port & ip address");
}
......@@ -321,7 +309,7 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint"></param>
public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint)
{
if(RunTime.IsRunningOnMono())
if (RunTime.IsRunningOnMono())
{
throw new Exception("Mono Runtime do not support system proxy settings.");
}
......@@ -339,7 +327,6 @@ namespace Titanium.Web.Proxy
firefoxProxySettingsManager.AddFirefox();
#endif
Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTP Proxy", endPoint.IpAddress, endPoint.Port);
}
......@@ -372,9 +359,8 @@ namespace Titanium.Web.Proxy
if (certificateManager.CertValidated)
{
systemProxySettingsManager.SetHttpsProxy(
Equals(endPoint.IpAddress, IPAddress.Any) |
Equals(endPoint.IpAddress, IPAddress.Loopback) ?
"127.0.0.1" : endPoint.IpAddress.ToString(),
Equals(endPoint.IpAddress, IPAddress.Any) |
Equals(endPoint.IpAddress, IPAddress.Loopback) ? "127.0.0.1" : endPoint.IpAddress.ToString(),
endPoint.Port);
}
......@@ -437,7 +423,7 @@ namespace Titanium.Web.Proxy
throw new Exception("Proxy is already running.");
}
if (ForwardToUpstreamGateway && GetCustomUpStreamHttpProxyFunc == null
if (ForwardToUpstreamGateway && GetCustomUpStreamHttpProxyFunc == null
&& GetCustomUpStreamHttpsProxyFunc == null)
{
GetCustomUpStreamHttpProxyFunc = GetSystemUpStreamProxy;
......@@ -509,13 +495,11 @@ namespace Titanium.Web.Proxy
endPoint.Listener = new TcpListener(endPoint.IpAddress, endPoint.Port);
endPoint.Listener.Start();
endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port;
endPoint.Port = ((IPEndPoint) endPoint.Listener.LocalEndpoint).Port;
// accept clients asynchronously
endPoint.Listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
}
/// <summary>
/// Verifiy if its safe to set this end point as System proxy
/// </summary>
......@@ -576,7 +560,7 @@ namespace Titanium.Web.Proxy
/// <param name="asyn"></param>
private void OnAcceptConnection(IAsyncResult asyn)
{
var endPoint = (ProxyEndPoint)asyn.AsyncState;
var endPoint = (ProxyEndPoint) asyn.AsyncState;
TcpClient tcpClient = null;
......@@ -612,7 +596,6 @@ namespace Titanium.Web.Proxy
{
await HandleClient(endPoint as ExplicitProxyEndPoint, tcpClient);
}
}
finally
{
......@@ -664,6 +647,7 @@ namespace Titanium.Web.Proxy
{
BeforeResponse?.Invoke(sender, e);
}
/// <summary>
/// Invocator for ServerCertificateValidationCallback event.
/// </summary>
......@@ -684,4 +668,4 @@ namespace Titanium.Web.Proxy
ClientCertificateSelectionCallback?.Invoke(sender, e);
}
}
}
\ No newline at end of file
}
......@@ -23,19 +23,17 @@ namespace Titanium.Web.Proxy
/// </summary>
partial class ProxyServer
{
//This is called when client is aware of proxy
//So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
{
CustomBufferedStream clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);
clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000;
clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000;
var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
var clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine };
var clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine};
Uri httpRemoteUri;
try
......@@ -55,8 +53,7 @@ namespace Titanium.Web.Proxy
//Find the request Verb
var httpVerb = httpCmdSplit[0].ToUpper();
httpRemoteUri = httpVerb == "CONNECT" ?
new Uri("http://" + httpCmdSplit[1]) : new Uri(httpCmdSplit[1]);
httpRemoteUri = httpVerb == "CONNECT" ? new Uri("http://" + httpCmdSplit[1]) : new Uri(httpCmdSplit[1]);
//parse the HTTP version
var version = new Version(1, 1);
......@@ -122,7 +119,7 @@ namespace Titanium.Web.Proxy
clientStream = new CustomBufferedStream(sslStream, BufferSize);
clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine };
clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine};
}
catch
{
......@@ -134,7 +131,6 @@ namespace Titanium.Web.Proxy
//Now read the actual HTTPS request line
httpCmd = await clientStreamReader.ReadLineAsync();
}
//Sorry cannot do a HTTPS request decrypt to port 80 at this time
else if (httpVerb == "CONNECT")
......@@ -145,18 +141,18 @@ namespace Titanium.Web.Proxy
await WriteConnectResponse(clientStreamWriter, version);
await TcpHelper.SendRaw(BufferSize, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
null, version, null,
false, SupportedSslProtocols,
ValidateServerCertificate,
SelectClientCertificate,
clientStream, tcpConnectionFactory, UpStreamEndPoint);
null, version, null,
false, SupportedSslProtocols,
ValidateServerCertificate,
SelectClientCertificate,
clientStream, tcpConnectionFactory, UpStreamEndPoint);
Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
return;
}
//Now create the request
await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, endPoint, connectRequestHeaders);
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, endPoint, connectRequestHeaders);
}
catch (Exception)
{
......@@ -191,9 +187,8 @@ namespace Titanium.Web.Proxy
clientStream = new CustomBufferedStream(sslStream, BufferSize);
clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine };
clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine};
//HTTPS server created - we can now decrypt the client's traffic
}
catch (Exception)
{
......@@ -206,7 +201,7 @@ namespace Titanium.Web.Proxy
else
{
clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine };
clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine};
}
//now read the request line
......@@ -214,7 +209,7 @@ namespace Titanium.Web.Proxy
//Now create the request
await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null);
endPoint.EnableSsl ? endPoint.GenericCertificateName : null, endPoint, null);
}
private async Task HandleHttpSessionRequestInternal(TcpConnection connection, SessionEventArgs args, ExternalProxy customUpStreamHttpProxy, ExternalProxy customUpStreamHttpsProxy, bool closeConnection)
......@@ -272,13 +267,13 @@ namespace Titanium.Web.Proxy
if (args.WebSession.Request.Is100Continue)
{
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100",
"Continue", args.ProxyClient.ClientStreamWriter);
"Continue", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
}
else if (args.WebSession.Request.ExpectationFailed)
{
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417",
"Expectation Failed", args.ProxyClient.ClientStreamWriter);
"Expectation Failed", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
}
}
......@@ -358,7 +353,7 @@ namespace Titanium.Web.Proxy
/// <param name="customUpStreamHttpsProxy"></param>
/// <returns></returns>
private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName,
ProxyEndPoint endPoint, List<HttpHeader> connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null)
{
TcpConnection connection = null;
......@@ -382,7 +377,7 @@ namespace Titanium.Web.Proxy
args.WebSession.ProcessId = new Lazy<int>(() =>
{
var remoteEndPoint = (IPEndPoint)args.ProxyClient.TcpClient.Client.RemoteEndPoint;
var remoteEndPoint = (IPEndPoint) args.ProxyClient.TcpClient.Client.RemoteEndPoint;
//If client is localhost get the process id
if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
......@@ -392,7 +387,6 @@ namespace Titanium.Web.Proxy
//can't access process Id of remote request from remote machine
return -1;
});
try
{
......@@ -458,7 +452,6 @@ namespace Titanium.Web.Proxy
if (httpsHostName == null && (await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false))
{
Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
break;
}
......@@ -474,7 +467,7 @@ namespace Titanium.Web.Proxy
for (var i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(null, args);
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>) invocationList[i])(null, args);
}
await Task.WhenAll(handlerTasks);
......@@ -484,10 +477,10 @@ namespace Titanium.Web.Proxy
if (args.WebSession.Request.UpgradeToWebSocket)
{
await TcpHelper.SendRaw(BufferSize, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps,
SupportedSslProtocols, ValidateServerCertificate,
SelectClientCertificate,
clientStream, tcpConnectionFactory, UpStreamEndPoint);
httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps,
SupportedSslProtocols, ValidateServerCertificate,
SelectClientCertificate,
clientStream, tcpConnectionFactory, UpStreamEndPoint);
Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
break;
......@@ -510,7 +503,6 @@ namespace Titanium.Web.Proxy
// read the next request
httpCmd = await clientStreamReader.ReadLineAsync();
}
catch (Exception e)
{
......@@ -518,7 +510,6 @@ namespace Titanium.Web.Proxy
Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
break;
}
}
//dispose
......@@ -578,14 +569,13 @@ namespace Titanium.Web.Proxy
if (args.WebSession.Request.ContentLength > 0)
{
await args.ProxyClient.ClientStreamReader.CopyBytesToStream(BufferSize, postStream, args.WebSession.Request.ContentLength);
}
//Need to revist, find any potential bugs
//send the request body bytes to server in chunks
else if (args.WebSession.Request.IsChunked)
{
await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(BufferSize, postStream);
await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(postStream);
}
}
}
}
\ No newline at end of file
}
......@@ -44,7 +44,7 @@ namespace Titanium.Web.Proxy
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(this, args);
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>) invocationList[i])(this, args);
}
await Task.WhenAll(handlerTasks);
......@@ -62,19 +62,19 @@ namespace Titanium.Web.Proxy
if (args.WebSession.Response.Is100Continue)
{
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100",
"Continue", args.ProxyClient.ClientStreamWriter);
"Continue", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
}
else if (args.WebSession.Response.ExpectationFailed)
{
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417",
"Expectation Failed", args.ProxyClient.ClientStreamWriter);
"Expectation Failed", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
}
//Write back response status to client
await WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession.Response.ResponseStatusCode,
args.WebSession.Response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter);
args.WebSession.Response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter);
if (args.WebSession.Response.ResponseBodyRead)
{
......@@ -109,7 +109,7 @@ namespace Titanium.Web.Proxy
{
await args.WebSession.ServerConnection.StreamReader
.WriteResponseBody(BufferSize, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked,
args.WebSession.Response.ContentLength);
args.WebSession.Response.ContentLength);
}
//write response if connection:keep-alive header exist and when version is http/1.0
//Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream)
......@@ -117,12 +117,11 @@ namespace Titanium.Web.Proxy
{
await args.WebSession.ServerConnection.StreamReader
.WriteResponseBody(BufferSize, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked,
args.WebSession.Response.ContentLength);
args.WebSession.Response.ContentLength);
}
}
await args.ProxyClient.ClientStream.FlushAsync();
}
catch (Exception e)
{
......@@ -217,7 +216,6 @@ namespace Titanium.Web.Proxy
headers.Remove("proxy-connection");
}
}
/// <summary>
......@@ -241,4 +239,4 @@ namespace Titanium.Web.Proxy
args?.Dispose();
}
}
}
\ No newline at end of file
}
......@@ -6,10 +6,10 @@ namespace Titanium.Web.Proxy.Shared
/// Literals shared by Proxy Server
/// </summary>
internal class ProxyConstants
{
internal static readonly char[] SpaceSplit = { ' ' };
internal static readonly char[] ColonSplit = { ':' };
internal static readonly char[] SemiColonSplit = { ';' };
{
internal static readonly char[] SpaceSplit = {' '};
internal static readonly char[] ColonSplit = {':'};
internal static readonly char[] SemiColonSplit = {';'};
internal static readonly byte[] NewLineBytes = Encoding.ASCII.GetBytes(NewLine);
......
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
......@@ -12,4 +12,7 @@
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
</configuration>
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