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

Merge pull request #295 from justcoding121/develop

Beta release
parents ac2877be 103754dc
No preview for this file type
...@@ -25,8 +25,7 @@ namespace Titanium.Web.Proxy.Examples.Basic.Helpers ...@@ -25,8 +25,7 @@ namespace Titanium.Web.Proxy.Examples.Basic.Helpers
internal static bool DisableQuickEditMode() internal static bool DisableQuickEditMode()
{ {
var consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
// get current console mode // get current console mode
uint consoleMode; uint consoleMode;
......
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Titanium.Web.Proxy.Examples.Basic.Helpers; using Titanium.Web.Proxy.Examples.Basic.Helpers;
namespace Titanium.Web.Proxy.Examples.Basic namespace Titanium.Web.Proxy.Examples.Basic
......
...@@ -10,7 +10,7 @@ using System.Runtime.InteropServices; ...@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Demo")] [assembly: AssemblyProduct("Demo")]
[assembly: AssemblyCopyright("Copyright ©2013 Telerik")] [assembly: AssemblyCopyright("Copyright © Titanium 2015-2017")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
......
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
...@@ -17,8 +16,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -17,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()
{ {
...@@ -45,6 +43,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -45,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;
...@@ -55,11 +55,17 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -55,11 +55,17 @@ namespace Titanium.Web.Proxy.Examples.Basic
//Exclude Https addresses you don't want to proxy //Exclude Https addresses you don't want to proxy
//Useful for clients that use certificate pinning //Useful for clients that use certificate pinning
//for example google.com and dropbox.com //for example google.com and dropbox.com
ExcludedHttpsHostNameRegex = new List<string> { "dropbox.com" } ExcludedHttpsHostNameRegex = new List<string>
{
"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
...@@ -75,24 +81,23 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -75,24 +81,23 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer.Start(); proxyServer.Start();
//Transparent endpoint is useful for reverse proxying (client is not aware of the existence of proxy) //Transparent endpoint is useful for reverse proxy (client is not aware of the existence of proxy)
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint //A transparent endpoint usually requires a network router port forwarding HTTP(S) packets or DNS
//Currently do not support Server Name Indication (It is not currently supported by SslStream class) //to send data to this endPoint
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests //var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 443, true)
//In this example only google.com will work for HTTPS requests //{
//Other sites will receive a certificate mismatch warning on browser // //Generic Certificate hostname to use
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true) // //When SNI is disabled by client
{ // GenericCertificateName = "google.com"
GenericCertificateName = "google.com" //};
};
proxyServer.AddEndPoint(transparentEndPoint); //proxyServer.AddEndPoint(transparentEndPoint);
//proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; //proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
//proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; //proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
foreach (var endPoint in proxyServer.ProxyEndPoints) foreach (var endPoint in proxyServer.ProxyEndPoints)
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ", Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
//Only explicit proxies can be set as system proxy! //Only explicit proxies can be set as system proxy!
//proxyServer.SetAsSystemHttpProxy(explicitEndPoint); //proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
...@@ -102,6 +107,8 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -102,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;
...@@ -113,6 +120,15 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -113,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)
{ {
...@@ -125,7 +141,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -125,7 +141,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
if (e.WebSession.Request.HasBody) if (e.WebSession.Request.HasBody)
{ {
//Get/Set request body bytes //Get/Set request body bytes
byte[] bodyBytes = await e.GetRequestBody(); var bodyBytes = await e.GetRequestBody();
await e.SetRequestBody(bodyBytes); await e.SetRequestBody(bodyBytes);
//Get/Set request body as string //Get/Set request body as string
...@@ -175,11 +191,11 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -175,11 +191,11 @@ namespace Titanium.Web.Proxy.Examples.Basic
//if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return; //if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST") if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST")
{ {
if (e.WebSession.Response.ResponseStatusCode == "200") if (e.WebSession.Response.ResponseStatusCode == (int)HttpStatusCode.OK)
{ {
if (e.WebSession.Response.ContentType != null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html")) if (e.WebSession.Response.ContentType != null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
{ {
byte[] bodyBytes = await e.GetResponseBody(); var bodyBytes = await e.GetResponseBody();
await e.SetResponseBody(bodyBytes); await e.SetResponseBody(bodyBytes);
string body = await e.GetResponseBodyAsString(); string body = await e.GetResponseBodyAsString();
......
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
</configuration>
<Application x:Class="Titanium.Web.Proxy.Examples.Wpf.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Titanium.Web.Proxy.Examples.Wpf"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace Titanium.Web.Proxy.Examples.Wpf
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}
<Window x:Class="Titanium.Web.Proxy.Examples.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Titanium.Web.Proxy.Examples.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="1000" WindowState="Maximized"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500" />
<ColumnDefinition Width="3" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GridSplitter Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" />
<ListView Grid.Column="0" Grid.Row="0" HorizontalAlignment="Stretch" ItemsSource="{Binding Sessions}" SelectedItem="{Binding SelectedSession}"
KeyDown="ListViewSessions_OnKeyDown">
<ListView.View>
<GridView>
<GridViewColumn Header="Result" DisplayMemberBinding="{Binding StatusCode}" />
<GridViewColumn Header="Protocol" DisplayMemberBinding="{Binding Protocol}" />
<GridViewColumn Header="Host" DisplayMemberBinding="{Binding Host}" />
<GridViewColumn Header="Url" DisplayMemberBinding="{Binding Url}" />
<GridViewColumn Header="BodySize" DisplayMemberBinding="{Binding BodySize}" />
<GridViewColumn Header="Process" DisplayMemberBinding="{Binding Process}" />
<GridViewColumn Header="SentBytes" DisplayMemberBinding="{Binding SentDataCount}" />
<GridViewColumn Header="ReceivedBytes" DisplayMemberBinding="{Binding ReceivedDataCount}" />
</GridView>
</ListView.View>
</ListView>
<TabControl Grid.Column="2" Grid.Row="0">
<TabItem Header="Session">
<Grid Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox x:Name="TextBoxRequest" Grid.Row="0" />
<TextBox x:Name="TextBoxResponse" Grid.Row="1" />
</Grid>
</TabItem>
</TabControl>
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" Orientation="Horizontal">
<TextBlock Text="ClientConnectionCount:" />
<TextBlock Text="{Binding ClientConnectionCount}" Margin="10,0,20,0" />
<TextBlock Text="ServerConnectionCount:" />
<TextBlock Text="{Binding ServerConnectionCount}" Margin="10,0,20,0" />
</StackPanel>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Examples.Wpf
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly ProxyServer proxyServer;
private int lastSessionNumber;
public ObservableCollection<SessionListItem> Sessions { get; } = new ObservableCollection<SessionListItem>();
public SessionListItem SelectedSession
{
get { return selectedSession; }
set
{
if (value != selectedSession)
{
selectedSession = value;
SelectedSessionChanged();
}
}
}
public static readonly DependencyProperty ClientConnectionCountProperty = DependencyProperty.Register(
nameof(ClientConnectionCount), typeof(int), typeof(MainWindow), new PropertyMetadata(default(int)));
public int ClientConnectionCount
{
get { return (int)GetValue(ClientConnectionCountProperty); }
set { SetValue(ClientConnectionCountProperty, value); }
}
public static readonly DependencyProperty ServerConnectionCountProperty = DependencyProperty.Register(
nameof(ServerConnectionCount), typeof(int), typeof(MainWindow), new PropertyMetadata(default(int)));
public int ServerConnectionCount
{
get { return (int)GetValue(ServerConnectionCountProperty); }
set { SetValue(ServerConnectionCountProperty, value); }
}
private readonly Dictionary<SessionEventArgs, SessionListItem> sessionDictionary = new Dictionary<SessionEventArgs, SessionListItem>();
private SessionListItem selectedSession;
public MainWindow()
{
proxyServer = new ProxyServer();
proxyServer.TrustRootCertificate = true;
proxyServer.ForwardToUpstreamGateway = true;
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
{
//IncludedHttpsHostNameRegex = new string[0],
};
proxyServer.AddEndPoint(explicitEndPoint);
//proxyServer.UpStreamHttpProxy = new ExternalProxy
//{
// HostName = "158.69.115.45",
// Port = 3128,
// UserName = "Titanium",
// Password = "Titanium",
//};
proxyServer.BeforeRequest += ProxyServer_BeforeRequest;
proxyServer.BeforeResponse += ProxyServer_BeforeResponse;
proxyServer.TunnelConnectRequest += ProxyServer_TunnelConnectRequest;
proxyServer.TunnelConnectResponse += ProxyServer_TunnelConnectResponse;
proxyServer.ClientConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ClientConnectionCount = proxyServer.ClientConnectionCount; }); };
proxyServer.ServerConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ServerConnectionCount = proxyServer.ServerConnectionCount; }); };
proxyServer.Start();
proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp);
InitializeComponent();
}
private async Task ProxyServer_TunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
await Dispatcher.InvokeAsync(() =>
{
AddSession(e);
});
}
private async Task ProxyServer_TunnelConnectResponse(object sender, SessionEventArgs e)
{
await Dispatcher.InvokeAsync(() =>
{
SessionListItem item;
if (sessionDictionary.TryGetValue(e, out item))
{
item.Response = e.WebSession.Response;
item.Update();
}
});
}
private async Task ProxyServer_BeforeRequest(object sender, SessionEventArgs e)
{
SessionListItem item = null;
await Dispatcher.InvokeAsync(() =>
{
item = AddSession(e);
});
if (e.WebSession.Request.HasBody)
{
item.RequestBody = await e.GetRequestBody();
}
}
private async Task ProxyServer_BeforeResponse(object sender, SessionEventArgs e)
{
SessionListItem item = null;
await Dispatcher.InvokeAsync(() =>
{
SessionListItem item2;
if (sessionDictionary.TryGetValue(e, out item2))
{
item2.Response = e.WebSession.Response;
item2.Update();
item = item2;
}
});
if (item != null)
{
if (e.WebSession.Response.HasBody)
{
item.ResponseBody = await e.GetResponseBody();
}
}
}
private SessionListItem AddSession(SessionEventArgs e)
{
var item = CreateSessionListItem(e);
Sessions.Add(item);
sessionDictionary.Add(e, item);
return item;
}
private SessionListItem CreateSessionListItem(SessionEventArgs e)
{
lastSessionNumber++;
var item = new SessionListItem
{
Number = lastSessionNumber,
SessionArgs = e,
// save the headers because TWP will set it to null in Dispose
RequestHeaders = e.WebSession.Request.RequestHeaders,
ResponseHeaders = e.WebSession.Response.ResponseHeaders,
Request = e.WebSession.Request,
Response = e.WebSession.Response,
};
if (e is TunnelConnectSessionEventArgs || e.WebSession.Request.UpgradeToWebSocket)
{
e.DataReceived += (sender, args) =>
{
var session = (SessionEventArgs)sender;
SessionListItem li;
if (sessionDictionary.TryGetValue(session, out li))
{
li.ReceivedDataCount += args.Count;
}
};
e.DataSent += (sender, args) =>
{
var session = (SessionEventArgs)sender;
SessionListItem li;
if (sessionDictionary.TryGetValue(session, out li))
{
li.SentDataCount += args.Count;
}
};
}
item.Update();
return item;
}
private void ListViewSessions_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
var selectedItems = ((ListView)sender).SelectedItems;
foreach (var item in selectedItems.Cast<SessionListItem>().ToArray())
{
Sessions.Remove(item);
sessionDictionary.Remove(item.SessionArgs);
}
}
}
private void SelectedSessionChanged()
{
if (SelectedSession == null)
{
return;
}
const int truncateLimit = 1024;
var session = SelectedSession;
var data = session.RequestBody ?? new byte[0];
bool truncated = data.Length > truncateLimit;
if (truncated)
{
data = data.Take(truncateLimit).ToArray();
}
//restore the headers
typeof(Request).GetProperty(nameof(Request.RequestHeaders)).SetValue(session.Request, session.RequestHeaders);
typeof(Response).GetProperty(nameof(Response.ResponseHeaders)).SetValue(session.Response, session.ResponseHeaders);
//string hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxRequest.Text = session.Request.HeaderText + session.Request.Encoding.GetString(data) +
(truncated ? Environment.NewLine + $"Data is truncated after {truncateLimit} bytes" : null) +
(session.Request as ConnectRequest)?.ClientHelloInfo;
data = session.ResponseBody ?? new byte[0];
truncated = data.Length > truncateLimit;
if (truncated)
{
data = data.Take(truncateLimit).ToArray();
}
//hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxResponse.Text = session.Response.HeaderText + session.Response.Encoding.GetString(data) +
(truncated ? Environment.NewLine + $"Data is truncated after {truncateLimit} bytes" : null) +
(session.Response as ConnectResponse)?.ServerHelloInfo;
}
}
}
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Demo WPF")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Demo WPF")]
[assembly: AssemblyCopyright("Copyright ©2017 Titanium")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 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
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Titanium.Web.Proxy.Examples.Wpf.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Titanium.Web.Proxy.Examples.Wpf.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
\ No newline at end of file
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Titanium.Web.Proxy.Examples.Wpf.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Examples.Wpf.Annotations;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Examples.Wpf
{
public class SessionListItem : INotifyPropertyChanged
{
private string statusCode;
private string protocol;
private string host;
private string url;
private long bodySize;
private string process;
private long receivedDataCount;
private long sentDataCount;
public int Number { get; set; }
public SessionEventArgs SessionArgs { get; set; }
public string StatusCode
{
get { return statusCode; }
set { SetField(ref statusCode, value);}
}
public string Protocol
{
get { return protocol; }
set { SetField(ref protocol, value); }
}
public string Host
{
get { return host; }
set { SetField(ref host, value); }
}
public string Url
{
get { return url; }
set { SetField(ref url, value); }
}
public long BodySize
{
get { return bodySize; }
set { SetField(ref bodySize, value); }
}
public string Process
{
get { return process; }
set { SetField(ref process, value); }
}
public long ReceivedDataCount
{
get { return receivedDataCount; }
set { SetField(ref receivedDataCount, value); }
}
public long SentDataCount
{
get { return sentDataCount; }
set { SetField(ref sentDataCount, value); }
}
public byte[] RequestBody { get; set; }
public byte[] ResponseBody { get; set; }
public Request Request { get; set; }
public Response Response { get; set; }
public HeaderCollection RequestHeaders { get; set; }
public HeaderCollection ResponseHeaders { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void SetField<T>(ref T field, T value,[CallerMemberName] string propertyName = null)
{
field = value;
OnPropertyChanged(propertyName);
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void Update()
{
var request = SessionArgs.WebSession.Request;
var response = SessionArgs.WebSession.Response;
int statusCode = response?.ResponseStatusCode ?? 0;
StatusCode = statusCode == 0 ? "-" : statusCode.ToString();
Protocol = request.RequestUri.Scheme;
if (SessionArgs is TunnelConnectSessionEventArgs)
{
Host = "Tunnel to";
Url = request.RequestUri.Host + ":" + request.RequestUri.Port;
}
else
{
Host = request.RequestUri.Host;
Url = request.RequestUri.AbsolutePath;
}
BodySize = response?.ContentLength ?? -1;
Process = GetProcessDescription(SessionArgs.WebSession.ProcessId.Value);
}
private string GetProcessDescription(int processId)
{
try
{
var process = System.Diagnostics.Process.GetProcessById(processId);
return process.ProcessName + ":" + processId;
}
catch (Exception)
{
return string.Empty;
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4406CE17-9A39-4F28-8363-6169A4F799C1}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>Titanium.Web.Proxy.Examples.Wpf</RootNamespace>
<AssemblyName>Titanium.Web.Proxy.Examples.Wpf</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Properties\Annotations.cs" />
<Compile Include="SessionListItem.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj">
<Project>{8d73a1be-868c-42d2-9ece-f32cc1a02906}</Project>
<Name>Titanium.Web.Proxy</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
\ No newline at end of file
...@@ -2,10 +2,12 @@ Titanium ...@@ -2,10 +2,12 @@ Titanium
======== ========
A light weight HTTP(S) proxy server written in C# A light weight HTTP(S) proxy server written in C#
![Build Status](https://ci.appveyor.com/api/projects/status/rvlxv8xgj0m7lkr4?svg=true) <a href="https://ci.appveyor.com/project/justcoding121/titanium-web-proxy">![Build Status](https://ci.appveyor.com/api/projects/status/rvlxv8xgj0m7lkr4?svg=true)</a>
Kindly report only issues/bugs here . For programming help or questions use [StackOverflow](http://stackoverflow.com/questions/tagged/titanium-web-proxy) with the tag Titanium-Web-Proxy. Kindly report only issues/bugs here . For programming help or questions use [StackOverflow](http://stackoverflow.com/questions/tagged/titanium-web-proxy) with the tag Titanium-Web-Proxy.
([Wiki & Contribution guidelines](https://github.com/justcoding121/Titanium-Web-Proxy/wiki))
![alt tag](https://raw.githubusercontent.com/justcoding121/Titanium-Web-Proxy/develop/Examples/Titanium.Web.Proxy.Examples.Basic/Capture.PNG) ![alt tag](https://raw.githubusercontent.com/justcoding121/Titanium-Web-Proxy/develop/Examples/Titanium.Web.Proxy.Examples.Basic/Capture.PNG)
Features Features
...@@ -23,7 +25,7 @@ Features ...@@ -23,7 +25,7 @@ Features
Usage Usage
===== =====
Refer the HTTP Proxy Server library in your project, look up Test project to learn usage. ([Wiki & Contribution guidelines](https://github.com/justcoding121/Titanium-Web-Proxy/wiki)) Refer the HTTP Proxy Server library in your project, look up Test project to learn usage.
Install by nuget: Install by nuget:
...@@ -71,17 +73,16 @@ var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true) ...@@ -71,17 +73,16 @@ var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
proxyServer.AddEndPoint(explicitEndPoint); proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start(); proxyServer.Start();
//Warning! Transparent endpoint is not tested end to end
//Transparent endpoint is useful for reverse proxy (client is not aware of the existence of proxy) //Transparent endpoint is useful for reverse proxy (client is not aware of the existence of proxy)
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint //A transparent endpoint usually requires a network router port forwarding HTTP(S) packets or DNS
//Currently do not support Server Name Indication (It is not currently supported by SslStream class) //to send data to this endPoint
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests
//In this example only google.com will work for HTTPS requests
//Other sites will receive a certificate mismatch warning on browser
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true) var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true)
{ {
GenericCertificateName = "google.com" //Generic Certificate hostname to use
//when SNI is disabled by client
GenericCertificateName = "google.com"
}; };
proxyServer.AddEndPoint(transparentEndPoint); proxyServer.AddEndPoint(transparentEndPoint);
//proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; //proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
......
...@@ -9,7 +9,7 @@ using System.Runtime.InteropServices; ...@@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Titanium.Web.Proxy.IntegrationTests")] [assembly: AssemblyProduct("Titanium.Web.Proxy.IntegrationTests")]
[assembly: AssemblyCopyright("Copyright © 2017")] [assembly: AssemblyCopyright("Copyright © Titanium 2015-2017")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
......
...@@ -13,12 +13,13 @@ namespace Titanium.Web.Proxy.IntegrationTests ...@@ -13,12 +13,13 @@ namespace Titanium.Web.Proxy.IntegrationTests
[TestClass] [TestClass]
public class SslTests public class SslTests
{ {
[TestMethod] //[TestMethod]
//disable this test until CI is prepared to handle
public void TestSsl() public void TestSsl()
{ {
//expand this to stress test to find //expand this to stress test to find
//why in long run proxy becomes unresponsive as per issue #184 //why in long run proxy becomes unresponsive as per issue #184
var testUrl = "https://google.com"; string testUrl = "https://google.com";
int proxyPort = 8086; int proxyPort = 8086;
var proxy = new ProxyTestController(); var proxy = new ProxyTestController();
proxy.StartProxy(proxyPort); proxy.StartProxy(proxyPort);
......
...@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy.UnitTests ...@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy.UnitTests
for (int i = 0; i < 1000; i++) for (int i = 0; i < 1000; i++)
{ {
foreach (var host in hostNames) foreach (string host in hostNames)
{ {
tasks.Add(Task.Run(async () => tasks.Add(Task.Run(async () =>
{ {
......
...@@ -9,7 +9,7 @@ using System.Runtime.InteropServices; ...@@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Titanium.Web.Proxy.UnitTests")] [assembly: AssemblyProduct("Titanium.Web.Proxy.UnitTests")]
[assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyCopyright("Copyright © Titanium 2015-2017")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
......
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Helpers.WinHttp; using Titanium.Web.Proxy.Helpers.WinHttp;
......
using Microsoft.VisualStudio.TestTools.UnitTesting; using System;
using System; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.Network.WinAuth; using Titanium.Web.Proxy.Network.WinAuth;
namespace Titanium.Web.Proxy.UnitTests namespace Titanium.Web.Proxy.UnitTests
...@@ -10,7 +10,7 @@ namespace Titanium.Web.Proxy.UnitTests ...@@ -10,7 +10,7 @@ namespace Titanium.Web.Proxy.UnitTests
[TestMethod] [TestMethod]
public void Test_Acquire_Client_Token() public void Test_Acquire_Client_Token()
{ {
var token = WinAuthHandler.GetInitialAuthToken("mylocalserver.com", "NTLM", Guid.NewGuid()); string token = WinAuthHandler.GetInitialAuthToken("mylocalserver.com", "NTLM", Guid.NewGuid());
Assert.IsTrue(token.Length > 1); Assert.IsTrue(token.Length > 1);
} }
} }
......
...@@ -35,6 +35,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.UnitTest ...@@ -35,6 +35,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.UnitTest
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.IntegrationTests", "Tests\Titanium.Web.Proxy.IntegrationTests\Titanium.Web.Proxy.IntegrationTests.csproj", "{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.IntegrationTests", "Tests\Titanium.Web.Proxy.IntegrationTests\Titanium.Web.Proxy.IntegrationTests.csproj", "{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.Examples.Wpf", "Examples\Titanium.Web.Proxy.Examples.Wpf\Titanium.Web.Proxy.Examples.Wpf.csproj", "{4406CE17-9A39-4F28-8363-6169A4F799C1}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
...@@ -57,6 +59,10 @@ Global ...@@ -57,6 +59,10 @@ Global
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Debug|Any CPU.Build.0 = Debug|Any CPU {32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.ActiveCfg = Release|Any CPU {32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.Build.0 = Release|Any CPU {32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.Build.0 = Release|Any CPU
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
...@@ -65,6 +71,7 @@ Global ...@@ -65,6 +71,7 @@ Global
{F3B7E553-1904-4E80-BDC7-212342B5C952} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B} {F3B7E553-1904-4E80-BDC7-212342B5C952} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B}
{B517E3D0-D03B-436F-AB03-34BA0D5321AF} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64} {B517E3D0-D03B-436F-AB03-34BA0D5321AF} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64} {32231301-B0FB-4F9E-98DF-B3E8A88F4C16} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
{4406CE17-9A39-4F28-8363-6169A4F799C1} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35 EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35
......
<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"> <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/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Titanium_002EWeb_002EProxy_002EExamples_002EWpf_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_CASE_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_EMBEDDED_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">240</s:Int64> <s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">160</s:Int64>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseVar</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BC/@EntryIndexedValue">BC</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BC/@EntryIndexedValue">BC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CN/@EntryIndexedValue">CN</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CN/@EntryIndexedValue">CN</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DN/@EntryIndexedValue">DN</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DN/@EntryIndexedValue">DN</s:String>
......
...@@ -16,11 +16,7 @@ namespace Titanium.Web.Proxy ...@@ -16,11 +16,7 @@ namespace Titanium.Web.Proxy
/// <param name="chain"></param> /// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param> /// <param name="sslPolicyErrors"></param>
/// <returns></returns> /// <returns></returns>
internal bool ValidateServerCertificate( internal 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 user callback is registered then do it
if (ServerCertificateValidationCallback != null) if (ServerCertificateValidationCallback != null)
...@@ -56,22 +52,15 @@ namespace Titanium.Web.Proxy ...@@ -56,22 +52,15 @@ namespace Titanium.Web.Proxy
/// <param name="remoteCertificate"></param> /// <param name="remoteCertificate"></param>
/// <param name="acceptableIssuers"></param> /// <param name="acceptableIssuers"></param>
/// <returns></returns> /// <returns></returns>
internal X509Certificate SelectClientCertificate( internal X509Certificate SelectClientCertificate(object sender, string targetHost, X509CertificateCollection localCertificates,
object sender, X509Certificate remoteCertificate, string[] acceptableIssuers)
string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate,
string[] acceptableIssuers)
{ {
X509Certificate clientCertificate = null; X509Certificate clientCertificate = null;
if (acceptableIssuers != null && if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null && localCertificates.Count > 0)
acceptableIssuers.Length > 0 &&
localCertificates != null &&
localCertificates.Count > 0)
{ {
// Use the first certificate that is from an acceptable issuer. // Use the first certificate that is from an acceptable issuer.
foreach (X509Certificate certificate in localCertificates) foreach (var certificate in localCertificates)
{ {
string issuer = certificate.Issuer; string issuer = certificate.Issuer;
if (Array.IndexOf(acceptableIssuers, issuer) != -1) if (Array.IndexOf(acceptableIssuers, issuer) != -1)
...@@ -81,8 +70,7 @@ namespace Titanium.Web.Proxy ...@@ -81,8 +70,7 @@ namespace Titanium.Web.Proxy
} }
} }
if (localCertificates != null && if (localCertificates != null && localCertificates.Count > 0)
localCertificates.Count > 0)
{ {
clientCertificate = localCertificates[0]; clientCertificate = localCertificates[0];
} }
......
using System;
namespace Titanium.Web.Proxy.EventArguments
{
public class DataEventArgs : EventArgs
{
public byte[] Buffer { get; }
public int Offset { get; }
public int Count { get; }
public DataEventArgs(byte[] buffer, int offset, int count)
{
Buffer = buffer;
Offset = offset;
Count = count;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.EventArguments
{
public class TunnelConnectSessionEventArgs : SessionEventArgs
{
public bool IsHttpsConnect { get; set; }
public TunnelConnectSessionEventArgs(ProxyEndPoint endPoint) : base(0, endPoint, null)
{
}
}
}
...@@ -9,8 +9,7 @@ ...@@ -9,8 +9,7 @@
/// Constructor. /// Constructor.
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
public BodyNotFoundException(string message) public BodyNotFoundException(string message) : base(message)
: base(message)
{ {
} }
} }
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Extensions
{
internal static class DotNetStandardExtensions
{
#if NET45
/// <summary>
/// Disposes the specified client.
/// Int .NET framework 4.5 the TcpClient class has no Dispose method,
/// it is available from .NET 4.6, see
/// https://msdn.microsoft.com/en-us/library/dn823304(v=vs.110).aspx
/// </summary>
/// <param name="client">The client.</param>
internal static void Dispose(this TcpClient client)
{
client.Close();
}
/// <summary>
/// Disposes the specified store.
/// Int .NET framework 4.5 the X509Store class has no Dispose method,
/// it is available from .NET 4.6, see
/// https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509store.dispose(v=vs.110).aspx
/// </summary>
/// <param name="store">The store.</param>
internal static void Dispose(this X509Store store)
{
store.Close();
}
#endif
}
}
...@@ -7,8 +7,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -7,8 +7,8 @@ namespace Titanium.Web.Proxy.Extensions
{ {
public static void InvokeParallel<T>(this Func<object, T, Task> callback, object sender, T args) public static void InvokeParallel<T>(this Func<object, T, Task> callback, object sender, T args)
{ {
Delegate[] invocationList = callback.GetInvocationList(); var invocationList = callback.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length]; var handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++) for (int i = 0; i < invocationList.Length; i++)
{ {
...@@ -18,17 +18,30 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -18,17 +18,30 @@ namespace Titanium.Web.Proxy.Extensions
Task.WhenAll(handlerTasks).Wait(); Task.WhenAll(handlerTasks).Wait();
} }
public static async Task InvokeParallelAsync<T>(this Func<object, T, Task> callback, object sender, T args) public static async Task InvokeParallelAsync<T>(this Func<object, T, Task> callback, object sender, T args, Action<Exception> exceptionFunc)
{ {
Delegate[] invocationList = callback.GetInvocationList(); var invocationList = callback.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length]; var handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++) for (int i = 0; i < invocationList.Length; i++)
{ {
handlerTasks[i] = ((Func<object, T, Task>)invocationList[i])(sender, args); handlerTasks[i] = InvokeAsync((Func<object, T, Task>)invocationList[i], sender, args, exceptionFunc);
} }
await Task.WhenAll(handlerTasks); await Task.WhenAll(handlerTasks);
} }
private static async Task InvokeAsync<T>(Func<object, T, Task> callback, object sender, T args, Action<Exception> exceptionFunc)
{
try
{
await callback(sender, args);
}
catch (Exception ex)
{
var ex2 = new Exception("Exception thrown in user event", ex);
exceptionFunc(ex2);
}
}
} }
} }
using System; using System.Text;
using System.Text;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
......
using System; using System.Text;
using System.Text;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
......
using System.Globalization; using System;
using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -30,6 +31,25 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -30,6 +31,25 @@ namespace Titanium.Web.Proxy.Extensions
await input.CopyToAsync(output); await input.CopyToAsync(output);
} }
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy)
{
byte[] buffer = new byte[81920];
while (true)
{
int num = await input.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
int bytesRead;
if ((bytesRead = num) != 0)
{
await output.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false);
onCopy?.Invoke(buffer, 0, bytesRead);
}
else
{
break;
}
}
}
/// <summary> /// <summary>
/// copies the specified bytes to the stream from the input stream /// copies the specified bytes to the stream from the input stream
/// </summary> /// </summary>
...@@ -39,7 +59,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -39,7 +59,7 @@ namespace Titanium.Web.Proxy.Extensions
/// <returns></returns> /// <returns></returns>
internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, Stream stream, long totalBytesToRead) internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, Stream stream, long totalBytesToRead)
{ {
byte[] buffer = streamReader.Buffer; var buffer = streamReader.Buffer;
long remainingBytes = totalBytesToRead; long remainingBytes = totalBytesToRead;
while (remainingBytes > 0) while (remainingBytes > 0)
...@@ -72,8 +92,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -72,8 +92,8 @@ namespace Titanium.Web.Proxy.Extensions
{ {
while (true) while (true)
{ {
var chuchkHead = await clientStreamReader.ReadLineAsync(); string chuchkHead = await clientStreamReader.ReadLineAsync();
var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber); int chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber);
if (chunkSize != 0) if (chunkSize != 0)
{ {
...@@ -119,7 +139,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -119,7 +139,8 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="isChunked"></param> /// <param name="isChunked"></param>
/// <param name="contentLength"></param> /// <param name="contentLength"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream, bool isChunked, long contentLength) internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream, bool isChunked,
long contentLength)
{ {
if (!isChunked) if (!isChunked)
{ {
...@@ -147,8 +168,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -147,8 +168,8 @@ namespace Titanium.Web.Proxy.Extensions
{ {
while (true) while (true)
{ {
var chunkHead = await inStreamReader.ReadLineAsync(); string chunkHead = await inStreamReader.ReadLineAsync();
var chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber); int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber);
if (chunkSize != 0) if (chunkSize != 0)
{ {
......
...@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Extensions
internal static bool IsConnected(this Socket client) internal static bool IsConnected(this Socket client)
{ {
// This is how you can determine whether a socket is still connected. // This is how you can determine whether a socket is still connected.
var blockingState = client.Blocking; bool blockingState = client.Blocking;
try try
{ {
...@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.Extensions
catch (SocketException e) catch (SocketException e)
{ {
// 10035 == WSAEWOULDBLOCK // 10035 == WSAEWOULDBLOCK
return e.NativeErrorCode.Equals(10035); return e.SocketErrorCode == SocketError.WouldBlock;
} }
finally finally
{ {
...@@ -34,6 +34,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -34,6 +34,7 @@ namespace Titanium.Web.Proxy.Extensions
} }
} }
#if NET45
/// <summary> /// <summary>
/// Gets the local port from a native TCP row object. /// Gets the local port from a native TCP row object.
/// </summary> /// </summary>
...@@ -53,5 +54,6 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -53,5 +54,6 @@ namespace Titanium.Web.Proxy.Extensions
{ {
return (tcpRow.remotePort1 << 8) + tcpRow.remotePort2 + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16); return (tcpRow.remotePort1 << 8) + tcpRow.remotePort2 + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16);
} }
#endif
} }
} }
using System; using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
......
...@@ -34,7 +34,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -34,7 +34,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns></returns> /// <returns></returns>
internal async Task<string> ReadLineAsync() internal async Task<string> ReadLineAsync()
{ {
var lastChar = default(byte); byte lastChar = default(byte);
int bufferDataLength = 0; int bufferDataLength = 0;
...@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy.Helpers
while (stream.DataAvailable || await stream.FillBufferAsync()) while (stream.DataAvailable || await stream.FillBufferAsync())
{ {
var newChar = stream.ReadByteFromBuffer(); byte newChar = stream.ReadByteFromBuffer();
buffer[bufferDataLength] = newChar; buffer[bufferDataLength] = newChar;
//if new line //if new line
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Helpers
{
class CustomBufferedPeekStream
{
private readonly CustomBufferedStream baseStream;
private int position;
public CustomBufferedPeekStream(CustomBufferedStream baseStream, int startPosition = 0)
{
this.baseStream = baseStream;
position = startPosition;
}
public int Available => baseStream.Available - position;
public async Task<bool> EnsureBufferLength(int length)
{
var val = await baseStream.PeekByteAsync(position + length - 1);
return val != -1;
}
public byte ReadByte()
{
return baseStream.PeekByteFromBuffer(position++);
}
public int ReadInt16()
{
int i1 = ReadByte();
int i2 = ReadByte();
return (i1 << 8) + i2;
}
public int ReadInt24()
{
int i1 = ReadByte();
int i2 = ReadByte();
int i3 = ReadByte();
return (i1 << 16) + (i2 << 8) + i3;
}
public byte[] ReadBytes(int length)
{
var buffer = new byte[length];
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = ReadByte();
}
return buffer;
}
}
}
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.Remoting;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -14,7 +13,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -14,7 +13,9 @@ namespace Titanium.Web.Proxy.Helpers
/// <seealso cref="System.IO.Stream" /> /// <seealso cref="System.IO.Stream" />
internal class CustomBufferedStream : Stream internal class CustomBufferedStream : Stream
{ {
#if NET45
private AsyncCallback readCallback; private AsyncCallback readCallback;
#endif
private readonly Stream baseStream; private readonly Stream baseStream;
...@@ -35,7 +36,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -35,7 +36,9 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="bufferSize">Size of the buffer.</param> /// <param name="bufferSize">Size of the buffer.</param>
public CustomBufferedStream(Stream baseStream, int bufferSize) public CustomBufferedStream(Stream baseStream, int bufferSize)
{ {
readCallback = new AsyncCallback(ReadCallback); #if NET45
readCallback = ReadCallback;
#endif
this.baseStream = baseStream; this.baseStream = baseStream;
streamBuffer = BufferPool.GetBuffer(bufferSize); streamBuffer = BufferPool.GetBuffer(bufferSize);
} }
...@@ -111,6 +114,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -111,6 +114,7 @@ namespace Titanium.Web.Proxy.Helpers
baseStream.Write(buffer, offset, count); baseStream.Write(buffer, offset, count);
} }
#if NET45
/// <summary> /// <summary>
/// Begins an asynchronous read operation. (Consider using <see cref="M:System.IO.Stream.ReadAsync(System.Byte[],System.Int32,System.Int32)" /> instead; see the Remarks section.) /// Begins an asynchronous read operation. (Consider using <see cref="M:System.IO.Stream.ReadAsync(System.Byte[],System.Int32,System.Int32)" /> instead; see the Remarks section.)
/// </summary> /// </summary>
...@@ -163,6 +167,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -163,6 +167,7 @@ namespace Titanium.Web.Proxy.Helpers
OnDataSent(buffer, offset, count); OnDataSent(buffer, offset, count);
return baseStream.BeginWrite(buffer, offset, count, callback, state); return baseStream.BeginWrite(buffer, offset, count, callback, state);
} }
#endif
/// <summary> /// <summary>
/// Asynchronously reads the bytes from the current stream and writes them to another stream, using a specified buffer size and cancellation token. /// Asynchronously reads the bytes from the current stream and writes them to another stream, using a specified buffer size and cancellation token.
...@@ -184,18 +189,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -184,18 +189,7 @@ namespace Titanium.Web.Proxy.Helpers
await base.CopyToAsync(destination, bufferSize, cancellationToken); await base.CopyToAsync(destination, bufferSize, cancellationToken);
} }
/// <summary> #if NET45
/// Creates an object that contains all the relevant information required to generate a proxy used to communicate with a remote object.
/// </summary>
/// <param name="requestedType">The <see cref="T:System.Type" /> of the object that the new <see cref="T:System.Runtime.Remoting.ObjRef" /> will reference.</param>
/// <returns>
/// Information required to generate a proxy.
/// </returns>
public override ObjRef CreateObjRef(Type requestedType)
{
return baseStream.CreateObjRef(requestedType);
}
/// <summary> /// <summary>
/// Waits for the pending asynchronous read to complete. (Consider using <see cref="M:System.IO.Stream.ReadAsync(System.Byte[],System.Int32,System.Int32)" /> instead; see the Remarks section.) /// Waits for the pending asynchronous read to complete. (Consider using <see cref="M:System.IO.Stream.ReadAsync(System.Byte[],System.Int32,System.Int32)" /> instead; see the Remarks section.)
/// </summary> /// </summary>
...@@ -222,6 +216,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -222,6 +216,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
baseStream.EndWrite(asyncResult); baseStream.EndWrite(asyncResult);
} }
#endif
/// <summary> /// <summary>
/// Asynchronously clears all buffers for this stream, causes any buffered data to be written to the underlying device, and monitors cancellation requests. /// Asynchronously clears all buffers for this stream, causes any buffered data to be written to the underlying device, and monitors cancellation requests.
...@@ -235,17 +230,6 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -235,17 +230,6 @@ namespace Titanium.Web.Proxy.Helpers
return baseStream.FlushAsync(cancellationToken); return baseStream.FlushAsync(cancellationToken);
} }
/// <summary>
/// Obtains a lifetime service object to control the lifetime policy for this instance.
/// </summary>
/// <returns>
/// An object of type <see cref="T:System.Runtime.Remoting.Lifetime.ILease" /> used to control the lifetime policy for this instance. This is the current lifetime service object for this instance if one exists; otherwise, a new lifetime service object initialized to the value of the <see cref="P:System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseManagerPollTime" /> property.
/// </returns>
public override object InitializeLifetimeService()
{
return baseStream.InitializeLifetimeService();
}
/// <summary> /// <summary>
/// Asynchronously reads a sequence of bytes from the current stream, /// Asynchronously reads a sequence of bytes from the current stream,
/// advances the position within the stream by the number of bytes read, /// advances the position within the stream by the number of bytes read,
...@@ -306,6 +290,31 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -306,6 +290,31 @@ 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 PeekByteFromBuffer(int index)
{
if (bufferLength <= index)
{
throw new Exception("Index is out of buffer size");
}
return streamBuffer[bufferPos + index];
}
public byte ReadByteFromBuffer() public byte ReadByteFromBuffer()
{ {
if (bufferLength == 0) if (bufferLength == 0)
...@@ -359,15 +368,16 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -359,15 +368,16 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if(!disposed) if (!disposed)
{ {
disposed = true; disposed = true;
baseStream.Dispose(); baseStream.Dispose();
BufferPool.ReturnBuffer(streamBuffer); BufferPool.ReturnBuffer(streamBuffer);
streamBuffer = null; streamBuffer = null;
#if NET45
readCallback = null; readCallback = null;
#endif
} }
} }
/// <summary> /// <summary>
...@@ -397,6 +407,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -397,6 +407,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>
...@@ -429,14 +441,22 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -429,14 +441,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>
...@@ -455,14 +475,22 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -455,14 +475,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
......
...@@ -16,9 +16,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -16,9 +16,9 @@ namespace Titanium.Web.Proxy.Helpers
try try
{ {
var myProfileDirectory = var myProfileDirectory =
new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Mozilla\\Firefox\\Profiles\\")
"\\Mozilla\\Firefox\\Profiles\\").GetDirectories("*.default"); .GetDirectories("*.default");
var myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js"; string myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js";
if (!File.Exists(myFfPrefFile)) if (!File.Exists(myFfPrefFile))
{ {
return; return;
...@@ -26,18 +26,17 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -26,18 +26,17 @@ namespace Titanium.Web.Proxy.Helpers
// We have a pref file so let''s make sure it has the proxy setting // We have a pref file so let''s make sure it has the proxy setting
var myReader = new StreamReader(myFfPrefFile); var myReader = new StreamReader(myFfPrefFile);
var myPrefContents = myReader.ReadToEnd(); string myPrefContents = myReader.ReadToEnd();
myReader.Close(); myReader.Close();
for (int i = 0; i <= 4; i++) for (int i = 0; i <= 4; i++)
{ {
var searchStr = $"user_pref(\"network.proxy.type\", {i});"; string searchStr = $"user_pref(\"network.proxy.type\", {i});";
if (myPrefContents.Contains(searchStr)) if (myPrefContents.Contains(searchStr))
{ {
// Add the proxy enable line and write it back to the file // Add the proxy enable line and write it back to the file
myPrefContents = myPrefContents.Replace(searchStr, myPrefContents = myPrefContents.Replace(searchStr, "user_pref(\"network.proxy.type\", 5);");
"user_pref(\"network.proxy.type\", 5);");
} }
} }
......
...@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy.Helpers
//extract the encoding by finding the charset //extract the encoding by finding the charset
var parameters = contentType.Split(ProxyConstants.SemiColonSplit); var parameters = contentType.Split(ProxyConstants.SemiColonSplit);
foreach (var parameter in parameters) foreach (string parameter in parameters)
{ {
var encodingSplit = parameter.Split(ProxyConstants.EqualSplit, 2); var encodingSplit = parameter.Split(ProxyConstants.EqualSplit, 2);
if (encodingSplit.Length == 2 && encodingSplit[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase)) if (encodingSplit.Length == 2 && encodingSplit[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase))
...@@ -66,9 +66,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -66,9 +66,8 @@ namespace Titanium.Web.Proxy.Helpers
if (hostname.Split(ProxyConstants.DotSplit).Length > 2) if (hostname.Split(ProxyConstants.DotSplit).Length > 2)
{ {
int idx = hostname.IndexOf(ProxyConstants.DotSplit); int idx = hostname.IndexOf(ProxyConstants.DotSplit);
var rootDomain = hostname.Substring(idx + 1); string rootDomain = hostname.Substring(idx + 1);
return "*." + rootDomain; return "*." + rootDomain;
} }
//return as it is //return as it is
......
using System;
using System.Runtime.InteropServices;
namespace Titanium.Web.Proxy.Helpers
{
internal partial class NativeMethods
{
[DllImport("wininet.dll")]
internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetConsoleWindow();
// Keeps it from getting garbage collected
internal static ConsoleEventDelegate Handler;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
// Pinvoke
internal delegate bool ConsoleEventDelegate(int eventType);
}
}
\ No newline at end of file
using System;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
namespace Titanium.Web.Proxy.Helpers
{
internal partial class NativeMethods
{
internal const int AfInet = 2;
internal const int AfInet6 = 23;
internal enum TcpTableType
{
BasicListener,
BasicConnections,
BasicAll,
OwnerPidListener,
OwnerPidConnections,
OwnerPidAll,
OwnerModuleListener,
OwnerModuleConnections,
OwnerModuleAll,
}
/// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa366921.aspx"/>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct TcpTable
{
public uint length;
public TcpRow row;
}
/// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa366913.aspx"/>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct TcpRow
{
public TcpState state;
public uint localAddr;
public byte localPort1;
public byte localPort2;
public byte localPort3;
public byte localPort4;
public uint remoteAddr;
public byte remotePort1;
public byte remotePort2;
public byte remotePort3;
public byte remotePort4;
public int owningPid;
}
/// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa365928.aspx"/>
/// </summary>
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static extern uint GetExtendedTcpTable(IntPtr tcpTable, ref int size, bool sort, int ipVersion, int tableClass, int reserved);
}
}
\ No newline at end of file
...@@ -6,6 +6,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -6,6 +6,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
internal class NetworkHelper internal class NetworkHelper
{ {
#if NET45
private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion) private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion)
{ {
var tcpRow = TcpHelper.GetTcpRowByLocalPort(ipVersion, port); var tcpRow = TcpHelper.GetTcpRowByLocalPort(ipVersion, port);
...@@ -15,7 +16,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -15,7 +16,7 @@ namespace Titanium.Web.Proxy.Helpers
internal static int GetProcessIdFromPort(int port, bool ipV6Enabled) internal static int GetProcessIdFromPort(int port, bool ipV6Enabled)
{ {
var processId = FindProcessIdFromLocalPort(port, IpVersion.Ipv4); int processId = FindProcessIdFromLocalPort(port, IpVersion.Ipv4);
if (processId > 0 && !ipV6Enabled) if (processId > 0 && !ipV6Enabled)
{ {
...@@ -43,10 +44,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -43,10 +44,10 @@ namespace Titanium.Web.Proxy.Helpers
{ {
bool isLocalhost = false; bool isLocalhost = false;
IPHostEntry localhost = Dns.GetHostEntry("127.0.0.1"); var localhost = Dns.GetHostEntry("127.0.0.1");
if (hostName == localhost.HostName) if (hostName == localhost.HostName)
{ {
IPHostEntry hostEntry = Dns.GetHostEntry(hostName); var hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = hostEntry.AddressList.Any(IPAddress.IsLoopback); isLocalhost = hostEntry.AddressList.Any(IPAddress.IsLoopback);
} }
...@@ -74,5 +75,56 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -74,5 +75,56 @@ namespace Titanium.Web.Proxy.Helpers
return isLocalhost; return isLocalhost;
} }
#else
/// <summary>
/// Adapated from below link
/// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
internal static bool IsLocalIpAddress(IPAddress address)
{
// get local IP addresses
var localIPs = Dns.GetHostAddressesAsync(Dns.GetHostName()).Result;
// test if any host IP equals to any local IP or to localhost
return IPAddress.IsLoopback(address) || localIPs.Contains(address);
}
internal static bool IsLocalIpAddress(string hostName)
{
bool isLocalhost = false;
var localhost = Dns.GetHostEntryAsync("127.0.0.1").Result;
if (hostName == localhost.HostName)
{
var hostEntry = Dns.GetHostEntryAsync(hostName).Result;
isLocalhost = hostEntry.AddressList.Any(IPAddress.IsLoopback);
}
if (!isLocalhost)
{
localhost = Dns.GetHostEntryAsync(Dns.GetHostName()).Result;
IPAddress ipAddress;
if (IPAddress.TryParse(hostName, out ipAddress))
isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress));
if (!isLocalhost)
{
try
{
var hostEntry = Dns.GetHostEntryAsync(hostName).Result;
isLocalhost = localhost.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals));
}
catch (SocketException)
{
}
}
}
return isLocalhost;
}
#endif
} }
} }
...@@ -3,7 +3,6 @@ using System.Collections.Generic; ...@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
...@@ -37,14 +36,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -37,14 +36,14 @@ namespace Titanium.Web.Proxy.Helpers
if (proxyServer != null) if (proxyServer != null)
{ {
Proxies = GetSystemProxyValues(proxyServer).ToDictionary(x=>x.ProtocolType); Proxies = GetSystemProxyValues(proxyServer).ToDictionary(x => x.ProtocolType);
} }
if (proxyOverride != null) if (proxyOverride != null)
{ {
var overrides = proxyOverride.Split(';'); var overrides = proxyOverride.Split(';');
var overrides2 = new List<string>(); var overrides2 = new List<string>();
foreach (var overrideHost in overrides) foreach (string overrideHost in overrides)
{ {
if (overrideHost == "<-loopback>") if (overrideHost == "<-loopback>")
{ {
...@@ -71,7 +70,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -71,7 +70,8 @@ namespace Titanium.Web.Proxy.Helpers
private static string BypassStringEscape(string rawString) private static string BypassStringEscape(string rawString)
{ {
Match match = new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Match(rawString); var match =
new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Match(rawString);
string empty1; string empty1;
string rawString1; string rawString1;
string empty2; string empty2;
...@@ -127,11 +127,11 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -127,11 +127,11 @@ namespace Titanium.Web.Proxy.Helpers
} }
ProxyProtocolType? protocolType = null; ProxyProtocolType? protocolType = null;
if (protocolTypeStr.Equals("http", StringComparison.InvariantCultureIgnoreCase)) if (protocolTypeStr.Equals(Proxy.ProxyServer.UriSchemeHttp, StringComparison.InvariantCultureIgnoreCase))
{ {
protocolType = ProxyProtocolType.Http; protocolType = ProxyProtocolType.Http;
} }
else if (protocolTypeStr.Equals("https", StringComparison.InvariantCultureIgnoreCase)) else if (protocolTypeStr.Equals(Proxy.ProxyServer.UriSchemeHttps, StringComparison.InvariantCultureIgnoreCase))
{ {
protocolType = ProxyProtocolType.Https; protocolType = ProxyProtocolType.Https;
} }
...@@ -174,7 +174,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -174,7 +174,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns></returns> /// <returns></returns>
private static HttpSystemProxyValue ParseProxyValue(string value) private static HttpSystemProxyValue ParseProxyValue(string value)
{ {
var tmp = Regex.Replace(value, @"\s+", " ").Trim(); string tmp = Regex.Replace(value, @"\s+", " ").Trim();
int equalsIndex = tmp.IndexOf("=", StringComparison.InvariantCulture); int equalsIndex = tmp.IndexOf("=", StringComparison.InvariantCulture);
if (equalsIndex >= 0) if (equalsIndex >= 0)
......
...@@ -11,9 +11,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -11,9 +11,8 @@ namespace Titanium.Web.Proxy.Helpers
/// cache for mono runtime check /// cache for mono runtime check
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private static Lazy<bool> isRunningOnMono private static readonly Lazy<bool> isRunningOnMono = new Lazy<bool>(() => Type.GetType("Mono.Runtime") != null);
= new Lazy<bool>(()=> Type.GetType("Mono.Runtime") != null);
/// <summary> /// <summary>
/// Is running on Mono? /// Is running on Mono?
/// </summary> /// </summary>
......
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Win32; using Microsoft.Win32;
// Helper classes for setting system proxy settings // Helper classes for setting system proxy settings
...@@ -31,25 +29,6 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -31,25 +29,6 @@ namespace Titanium.Web.Proxy.Helpers
AllHttp = Http | Https, AllHttp = Http | Https,
} }
internal partial class NativeMethods
{
[DllImport("wininet.dll")]
internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer,
int dwBufferLength);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetConsoleWindow();
// Keeps it from getting garbage collected
internal static ConsoleEventDelegate Handler;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
// Pinvoke
internal delegate bool ConsoleEventDelegate(int eventType);
}
internal class HttpSystemProxyValue internal class HttpSystemProxyValue
{ {
internal string HostName { get; set; } internal string HostName { get; set; }
...@@ -64,10 +43,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -64,10 +43,10 @@ namespace Titanium.Web.Proxy.Helpers
switch (ProtocolType) switch (ProtocolType)
{ {
case ProxyProtocolType.Http: case ProxyProtocolType.Http:
protocol = "http"; protocol = ProxyServer.UriSchemeHttp;
break; break;
case ProxyProtocolType.Https: case ProxyProtocolType.Https:
protocol = "https"; protocol = Proxy.ProxyServer.UriSchemeHttps;
break; break;
default: default:
throw new Exception("Unsupported protocol type"); throw new Exception("Unsupported protocol type");
...@@ -129,7 +108,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -129,7 +108,7 @@ namespace Titanium.Web.Proxy.Helpers
SaveOriginalProxyConfiguration(reg); SaveOriginalProxyConfiguration(reg);
PrepareRegistry(reg); PrepareRegistry(reg);
var exisitingContent = reg.GetValue(regProxyServer) as string; string exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent); var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0); existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if ((protocolType & ProxyProtocolType.Http) != 0) if ((protocolType & ProxyProtocolType.Http) != 0)
...@@ -175,7 +154,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -175,7 +154,7 @@ namespace Titanium.Web.Proxy.Helpers
if (reg.GetValue(regProxyServer) != null) if (reg.GetValue(regProxyServer) != null)
{ {
var exisitingContent = reg.GetValue(regProxyServer) as string; string exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent); var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0); existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
...@@ -305,11 +284,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -305,11 +284,7 @@ namespace Titanium.Web.Proxy.Helpers
private ProxyInfo GetProxyInfoFromRegistry(RegistryKey reg) private ProxyInfo GetProxyInfoFromRegistry(RegistryKey reg)
{ {
var pi = new ProxyInfo( var pi = new ProxyInfo(null, reg.GetValue(regAutoConfigUrl) as string, reg.GetValue(regProxyEnable) as int?, reg.GetValue(regProxyServer) as string,
null,
reg.GetValue(regAutoConfigUrl) as string,
reg.GetValue(regProxyEnable) as int?,
reg.GetValue(regProxyServer) as string,
reg.GetValue(regProxyOverride) as string); reg.GetValue(regProxyOverride) as string);
return pi; return pi;
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
...@@ -20,75 +16,21 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -20,75 +16,21 @@ namespace Titanium.Web.Proxy.Helpers
Ipv6 = 2, Ipv6 = 2,
} }
internal partial class NativeMethods
{
internal const int AfInet = 2;
internal const int AfInet6 = 23;
internal enum TcpTableType
{
BasicListener,
BasicConnections,
BasicAll,
OwnerPidListener,
OwnerPidConnections,
OwnerPidAll,
OwnerModuleListener,
OwnerModuleConnections,
OwnerModuleAll,
}
/// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa366921.aspx"/>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct TcpTable
{
public uint length;
public TcpRow row;
}
/// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa366913.aspx"/>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct TcpRow
{
public TcpState state;
public uint localAddr;
public byte localPort1;
public byte localPort2;
public byte localPort3;
public byte localPort4;
public uint remoteAddr;
public byte remotePort1;
public byte remotePort2;
public byte remotePort3;
public byte remotePort4;
public int owningPid;
}
/// <summary>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa365928.aspx"/>
/// </summary>
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static extern uint GetExtendedTcpTable(IntPtr tcpTable, ref int size, bool sort, int ipVersion, int tableClass, int reserved);
}
internal class TcpHelper internal class TcpHelper
{ {
#if NET45
/// <summary> /// <summary>
/// Gets the extended TCP table. /// Gets the extended TCP table.
/// </summary> /// </summary>
/// <returns>Collection of <see cref="TcpRow"/>.</returns> /// <returns>Collection of <see cref="TcpRow"/>.</returns>
internal static TcpTable GetExtendedTcpTable(IpVersion ipVersion) internal static TcpTable GetExtendedTcpTable(IpVersion ipVersion)
{ {
List<TcpRow> tcpRows = new List<TcpRow>(); var tcpRows = new List<TcpRow>();
IntPtr tcpTable = IntPtr.Zero; var tcpTable = IntPtr.Zero;
int tcpTableLength = 0; int tcpTableLength = 0;
var ipVersionValue = ipVersion == IpVersion.Ipv4 ? NativeMethods.AfInet : NativeMethods.AfInet6; int 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)
{ {
...@@ -97,9 +39,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -97,9 +39,9 @@ namespace Titanium.Web.Proxy.Helpers
tcpTable = Marshal.AllocHGlobal(tcpTableLength); 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)); var table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable));
IntPtr rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length)); var rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length));
for (int i = 0; i < table.length; ++i) for (int i = 0; i < table.length; ++i)
{ {
...@@ -126,10 +68,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -126,10 +68,10 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns><see cref="TcpRow"/>.</returns> /// <returns><see cref="TcpRow"/>.</returns>
internal static TcpRow GetTcpRowByLocalPort(IpVersion ipVersion, int localPort) internal static TcpRow GetTcpRowByLocalPort(IpVersion ipVersion, int localPort)
{ {
IntPtr tcpTable = IntPtr.Zero; var tcpTable = IntPtr.Zero;
int tcpTableLength = 0; int tcpTableLength = 0;
var ipVersionValue = ipVersion == IpVersion.Ipv4 ? NativeMethods.AfInet : NativeMethods.AfInet6; int 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)
{ {
...@@ -138,9 +80,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -138,9 +80,9 @@ namespace Titanium.Web.Proxy.Helpers
tcpTable = Marshal.AllocHGlobal(tcpTableLength); 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)); var table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable));
IntPtr rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length)); var rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length));
for (int i = 0; i < table.length; ++i) for (int i = 0; i < table.length; ++i)
{ {
...@@ -165,93 +107,27 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -165,93 +107,27 @@ namespace Titanium.Web.Proxy.Helpers
return null; return null;
} }
#endif
/// <summary> /// <summary>
/// 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="httpVersion"></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>
/// <param name="onDataSend"></param>
/// <param name="onDataReceive"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task SendRaw(ProxyServer server, internal static async Task SendRaw(Stream clientStream, TcpConnection connection, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive)
string remoteHostName, int remotePort,
string httpCmd, Version httpVersion, Dictionary<string, HttpHeader> requestHeaders,
bool isHttps,
Stream clientStream, TcpConnectionFactory tcpConnectionFactory,
TcpConnection connection = null)
{ {
//prepare the prefix content var tunnelStream = connection.Stream;
StringBuilder sb = null;
if (httpCmd != null || requestHeaders != null)
{
sb = new StringBuilder();
if (httpCmd != null)
{
sb.Append(httpCmd);
sb.Append(ProxyConstants.NewLine);
}
if (requestHeaders != null)
{
foreach (var header in requestHeaders.Select(t => t.Value.ToString()))
{
sb.Append(header);
sb.Append(ProxyConstants.NewLine);
}
}
sb.Append(ProxyConstants.NewLine);
}
bool connectionCreated = false;
TcpConnection tcpConnection;
//create new connection if connection is null
if (connection == null)
{
tcpConnection = await tcpConnectionFactory.CreateClient(server,
remoteHostName, remotePort,
httpVersion, isHttps,
null, null);
connectionCreated = true; //Now async relay all server=>client & client=>server data
} var sendRelay = clientStream.CopyToAsync(tunnelStream, onDataSend);
else
{
tcpConnection = connection;
}
try
{
Stream tunnelStream = tcpConnection.Stream;
//Now async relay all server=>client & client=>server data
var sendRelay = clientStream.CopyToAsync(sb?.ToString() ?? string.Empty, tunnelStream);
var receiveRelay = tunnelStream.CopyToAsync(string.Empty, clientStream); var receiveRelay = tunnelStream.CopyToAsync(clientStream, onDataReceive);
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);
}
}
} }
} }
} }
\ No newline at end of file
...@@ -18,7 +18,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -18,7 +18,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
internal static extern bool WinHttpSetTimeouts(WinHttpHandle session, int resolveTimeout, int connectTimeout, int sendTimeout, int receiveTimeout); internal static extern bool WinHttpSetTimeouts(WinHttpHandle session, int resolveTimeout, int connectTimeout, int sendTimeout, int receiveTimeout);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool WinHttpGetProxyForUrl(WinHttpHandle session, string url, [In] ref WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out WINHTTP_PROXY_INFO proxyInfo); internal static extern bool WinHttpGetProxyForUrl(WinHttpHandle session, string url, [In] ref WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions,
out WINHTTP_PROXY_INFO proxyInfo);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool WinHttpCloseHandle(IntPtr httpSession); internal static extern bool WinHttpCloseHandle(IntPtr httpSession);
...@@ -62,8 +63,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -62,8 +63,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public AutoProxyFlags Flags; public AutoProxyFlags Flags;
public AutoDetectType AutoDetectFlags; public AutoDetectType AutoDetectFlags;
[MarshalAs(UnmanagedType.LPWStr)] public string AutoConfigUrl; [MarshalAs(UnmanagedType.LPWStr)] public string AutoConfigUrl;
private IntPtr lpvReserved; private readonly IntPtr lpvReserved;
private int dwReserved; private readonly int dwReserved;
public bool AutoLogonIfChallenged; public bool AutoLogonIfChallenged;
} }
......
...@@ -16,4 +16,4 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -16,4 +16,4 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public override bool IsInvalid => handle == IntPtr.Zero; public override bool IsInvalid => handle == IntPtr.Zero;
} }
} }
\ No newline at end of file
...@@ -21,12 +21,12 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -21,12 +21,12 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public bool BypassLoopback { get; internal set; } public bool BypassLoopback { get; internal set; }
public bool BypassOnLocal { get; internal set; } public bool BypassOnLocal { get; internal set; }
public Uri AutomaticConfigurationScript { get; internal set; } public Uri AutomaticConfigurationScript { get; internal set; }
public bool AutomaticallyDetectSettings { get; internal set; } public bool AutomaticallyDetectSettings { get; internal set; }
private WebProxy Proxy { get; set; } private WebProxy proxy { get; set; }
public WinHttpWebProxyFinder() public WinHttpWebProxyFinder()
{ {
...@@ -89,26 +89,26 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -89,26 +89,26 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return null; return null;
} }
string proxy = proxies[0]; string proxyStr = proxies[0];
int port = 80; int port = 80;
if (proxy.Contains(":")) if (proxyStr.Contains(":"))
{ {
var parts = proxy.Split(new[] { ':' }, 2); var parts = proxyStr.Split(new[] { ':' }, 2);
proxy = parts[0]; proxyStr = parts[0];
port = int.Parse(parts[1]); port = int.Parse(parts[1]);
} }
// TODO: Apply authorization // TODO: Apply authorization
var systemProxy = new ExternalProxy var systemProxy = new ExternalProxy
{ {
HostName = proxy, HostName = proxyStr,
Port = port, Port = port,
}; };
return systemProxy; return systemProxy;
} }
if (Proxy?.IsBypassed(destination) == true) if (proxy?.IsBypassed(destination) == true)
return null; return null;
var protocolType = ProxyInfo.ParseProtocolType(destination.Scheme); var protocolType = ProxyInfo.ParseProtocolType(destination.Scheme);
...@@ -138,7 +138,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -138,7 +138,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
AutomaticConfigurationScript = pi.AutoConfigUrl == null ? null : new Uri(pi.AutoConfigUrl); AutomaticConfigurationScript = pi.AutoConfigUrl == null ? null : new Uri(pi.AutoConfigUrl);
BypassLoopback = pi.BypassLoopback; BypassLoopback = pi.BypassLoopback;
BypassOnLocal = pi.BypassOnLocal; BypassOnLocal = pi.BypassOnLocal;
Proxy = new WebProxy(new Uri("http://localhost"), BypassOnLocal, pi.BypassList); proxy = new WebProxy(new Uri("http://localhost"), BypassOnLocal, pi.BypassList);
} }
private ProxyInfo GetProxyInfo() private ProxyInfo GetProxyInfo()
...@@ -214,7 +214,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -214,7 +214,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString)) if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString))
{ {
num = GetLastWin32Error(); num = GetLastWin32Error();
if (num == (int)NativeMethods.WinHttp.ErrorCodes.LoginFailure && Credentials != null) if (num == (int)NativeMethods.WinHttp.ErrorCodes.LoginFailure && Credentials != null)
{ {
autoProxyOptions.AutoLogonIfChallenged = true; autoProxyOptions.AutoLogonIfChallenged = true;
...@@ -329,4 +329,4 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -329,4 +329,4 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
Completed, Completed,
} }
} }
} }
\ No newline at end of file
using System.Threading.Tasks;
using Titanium.Web.Proxy.Ssl;
namespace Titanium.Web.Proxy.Http
{
public class ConnectRequest : Request
{
public ClientHelloInfo ClientHelloInfo { get; set; }
}
}
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
{
public string ServerHelloInfo { get; set; }
}
}
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>
/// Adds the given header objects to Request
/// </summary>
/// <param name="newHeaders"></param>
public void AddHeaders(IEnumerable<HttpHeader> newHeaders)
{
if (newHeaders == null)
{
return;
}
foreach (var header in newHeaders)
{
AddHeader(header);
}
}
/// <summary>
/// Adds the given header objects to Request
/// </summary>
/// <param name="newHeaders"></param>
public void AddHeaders(IEnumerable<KeyValuePair<string, string>> newHeaders)
{
if (newHeaders == null)
{
return;
}
foreach (var header in newHeaders)
{
AddHeader(header.Key, header.Value);
}
}
/// <summary>
/// Adds the given header objects to Request
/// </summary>
/// <param name="newHeaders"></param>
public void AddHeaders(IEnumerable<KeyValuePair<string, HttpHeader>> newHeaders)
{
if (newHeaders == null)
{
return;
}
foreach (var header in newHeaders)
{
if (header.Key != header.Value.Name)
{
throw new Exception("Header name mismatch. Key and the name of the HttpHeader object should be the same.");
}
AddHeader(header.Value);
}
}
/// <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;
}
/// <summary>
/// Removes all the headers.
/// </summary>
public void Clear()
{
Headers.Clear();
NonUniqueHeaders.Clear();
}
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,10 +8,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -8,10 +8,11 @@ namespace Titanium.Web.Proxy.Http
{ {
internal static class HeaderParser internal static class HeaderParser
{ {
internal static async Task ReadHeaders(CustomBinaryReader reader, internal static async Task ReadHeaders(CustomBinaryReader reader, HeaderCollection headerCollection)
Dictionary<string, List<HttpHeader>> nonUniqueResponseHeaders,
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()))
{ {
...@@ -29,7 +30,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -29,7 +30,11 @@ namespace Titanium.Web.Proxy.Http
{ {
var existing = headers[newHeader.Name]; var existing = headers[newHeader.Name];
var nonUniqueHeaders = new List<HttpHeader> { existing, newHeader }; var nonUniqueHeaders = new List<HttpHeader>
{
existing,
newHeader
};
nonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders); nonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders);
headers.Remove(newHeader.Name); headers.Remove(newHeader.Name);
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -27,7 +28,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -27,7 +28,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.
...@@ -48,8 +49,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -48,8 +49,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Is Https? /// Is Https?
/// </summary> /// </summary>
public bool IsHttps => Request.RequestUri.Scheme == Uri.UriSchemeHttps; public bool IsHttps => Request.IsHttps;
internal HttpWebClient() internal HttpWebClient()
{ {
...@@ -84,8 +84,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -84,8 +84,8 @@ namespace Titanium.Web.Proxy.Http
//Send Authentication to Upstream proxy if needed //Send Authentication to Upstream proxy if needed
if (ServerConnection.UpStreamHttpProxy != null if (ServerConnection.UpStreamHttpProxy != null
&& ServerConnection.IsHttps == false && ServerConnection.IsHttps == false
&& !string.IsNullOrEmpty(ServerConnection.UpStreamHttpProxy.UserName) && !string.IsNullOrEmpty(ServerConnection.UpStreamHttpProxy.UserName)
&& ServerConnection.UpStreamHttpProxy.Password != null) && ServerConnection.UpStreamHttpProxy.Password != null)
{ {
...@@ -94,31 +94,17 @@ namespace Titanium.Web.Proxy.Http ...@@ -94,31 +94,17 @@ 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();
var request = requestLines.ToString(); string request = requestLines.ToString();
var requestBytes = Encoding.ASCII.GetBytes(request); var requestBytes = Encoding.ASCII.GetBytes(request);
await stream.WriteAsync(requestBytes, 0, requestBytes.Length); await stream.WriteAsync(requestBytes, 0, requestBytes.Length);
...@@ -128,18 +114,21 @@ namespace Titanium.Web.Proxy.Http ...@@ -128,18 +114,21 @@ namespace Titanium.Web.Proxy.Http
{ {
if (Request.ExpectContinue) if (Request.ExpectContinue)
{ {
var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3); string httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
var responseStatusCode = httpResult[1].Trim();
var responseStatusDescription = httpResult[2].Trim(); Version version;
int responseStatusCode;
string responseStatusDescription;
Response.ParseResponseLine(httpStatus, out version, out responseStatusCode, out responseStatusDescription);
//find if server is willing for expect continue //find if server is willing for expect continue
if (responseStatusCode.Equals("100") if (responseStatusCode == (int)HttpStatusCode.Continue
&& responseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase)) && responseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase))
{ {
Request.Is100Continue = true; Request.Is100Continue = true;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
} }
else if (responseStatusCode.Equals("417") else if (responseStatusCode == (int)HttpStatusCode.ExpectationFailed
&& responseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase)) && responseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
{ {
Request.ExpectationFailed = true; Request.ExpectationFailed = true;
...@@ -156,53 +145,49 @@ namespace Titanium.Web.Proxy.Http ...@@ -156,53 +145,49 @@ namespace Titanium.Web.Proxy.Http
internal async Task ReceiveResponse() internal async Task ReceiveResponse()
{ {
//return if this is already read //return if this is already read
if (Response.ResponseStatusCode != null) return; if (Response.ResponseStatusCode != 0)
return;
string line = await ServerConnection.StreamReader.ReadLineAsync(); string httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
if (line == null) if (httpStatus == null)
{ {
throw new IOException(); throw new IOException();
} }
var httpResult = line.Split(ProxyConstants.SpaceSplit, 3); if (httpStatus == string.Empty)
if (string.IsNullOrEmpty(httpResult[0]))
{ {
//Empty content in first-line, try again //Empty content in first-line, try again
httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3); httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
} }
var httpVersion = httpResult[0]; Version version;
int statusCode;
var version = HttpHeader.Version11; string statusDescription;
if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase)) Response.ParseResponseLine(httpStatus, out version, out statusCode, out statusDescription);
{
version = HttpHeader.Version10;
}
Response.HttpVersion = version; Response.HttpVersion = version;
Response.ResponseStatusCode = httpResult[1].Trim(); Response.ResponseStatusCode = statusCode;
Response.ResponseStatusDescription = httpResult[2].Trim(); Response.ResponseStatusDescription = statusDescription;
//For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request //For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
if (Response.ResponseStatusCode.Equals("100") if (Response.ResponseStatusCode == (int)HttpStatusCode.Continue
&& Response.ResponseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase)) && Response.ResponseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase))
{ {
//Read the next line after 100-continue //Read the next line after 100-continue
Response.Is100Continue = true; Response.Is100Continue = true;
Response.ResponseStatusCode = null; Response.ResponseStatusCode = 0;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
//now receive response //now receive response
await ReceiveResponse(); await ReceiveResponse();
return; return;
} }
if (Response.ResponseStatusCode.Equals("417") if (Response.ResponseStatusCode == (int)HttpStatusCode.ExpectationFailed
&& Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase)) && Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
{ {
//read next line after expectation failed response //read next line after expectation failed response
Response.ExpectationFailed = true; Response.ExpectationFailed = true;
Response.ResponseStatusCode = null; Response.ResponseStatusCode = 0;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
//now receive response //now receive response
await ReceiveResponse(); await ReceiveResponse();
...@@ -210,7 +195,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -210,7 +195,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>
...@@ -218,7 +203,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -218,7 +203,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();
......
This diff is collapsed.
This diff is collapsed.
...@@ -13,7 +13,10 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -13,7 +13,10 @@ namespace Titanium.Web.Proxy.Http.Responses
/// <param name="status"></param> /// <param name="status"></param>
public GenericResponse(HttpStatusCode status) public GenericResponse(HttpStatusCode status)
{ {
ResponseStatusCode = ((int)status).ToString(); ResponseStatusCode = (int)status;
//todo: this is not really correct, status description should contain spaces, too
//see: https://tools.ietf.org/html/rfc7231#section-6.1
ResponseStatusDescription = status.ToString(); ResponseStatusDescription = status.ToString();
} }
...@@ -22,7 +25,7 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -22,7 +25,7 @@ namespace Titanium.Web.Proxy.Http.Responses
/// </summary> /// </summary>
/// <param name="statusCode"></param> /// <param name="statusCode"></param>
/// <param name="statusDescription"></param> /// <param name="statusDescription"></param>
public GenericResponse(string statusCode, string statusDescription) public GenericResponse(int statusCode, string statusDescription)
{ {
ResponseStatusCode = statusCode; ResponseStatusCode = statusCode;
ResponseStatusDescription = statusDescription; ResponseStatusDescription = statusDescription;
......
namespace Titanium.Web.Proxy.Http.Responses using System.Net;
namespace Titanium.Web.Proxy.Http.Responses
{ {
/// <summary> /// <summary>
/// 200 Ok response /// 200 Ok response
...@@ -10,8 +12,8 @@ ...@@ -10,8 +12,8 @@
/// </summary> /// </summary>
public OkResponse() public OkResponse()
{ {
ResponseStatusCode = "200"; ResponseStatusCode = (int)HttpStatusCode.OK;
ResponseStatusDescription = "Ok"; ResponseStatusDescription = "OK";
} }
} }
} }
namespace Titanium.Web.Proxy.Http.Responses using System.Net;
namespace Titanium.Web.Proxy.Http.Responses
{ {
/// <summary> /// <summary>
/// Redirect response /// Redirect response
...@@ -10,7 +12,7 @@ ...@@ -10,7 +12,7 @@
/// </summary> /// </summary>
public RedirectResponse() public RedirectResponse()
{ {
ResponseStatusCode = "302"; ResponseStatusCode = (int)HttpStatusCode.Found;
ResponseStatusDescription = "Found"; ResponseStatusDescription = "Found";
} }
} }
......
...@@ -69,12 +69,6 @@ namespace Titanium.Web.Proxy.Models ...@@ -69,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>
...@@ -120,11 +114,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -120,11 +114,8 @@ namespace Titanium.Web.Proxy.Models
/// <param name="ipAddress"></param> /// <param name="ipAddress"></param>
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="enableSsl"></param> /// <param name="enableSsl"></param>
public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl) public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl) : base(ipAddress, port, enableSsl)
: base(ipAddress, port, enableSsl)
{ {
//init to well known HTTPS ports
RemoteHttpsPorts = new List<int> { 443, 8443 };
} }
} }
...@@ -146,8 +137,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -146,8 +137,7 @@ namespace Titanium.Web.Proxy.Models
/// <param name="ipAddress"></param> /// <param name="ipAddress"></param>
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="enableSsl"></param> /// <param name="enableSsl"></param>
public TransparentProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl) public TransparentProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl) : base(ipAddress, port, enableSsl)
: base(ipAddress, port, enableSsl)
{ {
GenericCertificateName = "localhost"; GenericCertificateName = "localhost";
} }
......
...@@ -9,9 +9,9 @@ namespace Titanium.Web.Proxy.Models ...@@ -9,9 +9,9 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class HttpHeader public class HttpHeader
{ {
internal static Version Version10 = new Version(1, 0); internal static readonly Version Version10 = new Version(1, 0);
internal static Version Version11 = new Version(1, 1); internal static readonly Version Version11 = new Version(1, 1);
/// <summary> /// <summary>
/// Constructor. /// Constructor.
......
using System; #if NET45
using System;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1;
...@@ -81,14 +82,10 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -81,14 +82,10 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (hostName != null) if (hostName != null)
{ {
//add subject alternative names //add subject alternative names
var subjectAlternativeNames = new Asn1Encodable[] var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(GeneralName.DnsName, hostName), };
{
new GeneralName(GeneralName.DnsName, hostName),
};
var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames); var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames);
certificateGenerator.AddExtension( certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension);
X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension);
} }
// Subject Public Key // Subject Public Key
var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyStrength); var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyStrength);
...@@ -118,7 +115,8 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -118,7 +115,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
} }
var rsa = RsaPrivateKeyStructure.GetInstance(seq); var rsa = RsaPrivateKeyStructure.GetInstance(seq);
var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, rsa.Coefficient); var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1,
rsa.Exponent2, rsa.Coefficient);
// Set private key onto certificate instance // Set private key onto certificate instance
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams); x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
...@@ -138,9 +136,8 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -138,9 +136,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="signingCertificate">The signing certificate.</param> /// <param name="signingCertificate">The signing certificate.</param>
/// <returns>X509Certificate2 instance.</returns> /// <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> /// <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,
string hostName, string subjectName, X509Certificate2 signingCertificate)
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{ {
if (isRoot != (null == signingCertificate)) if (isRoot != (null == signingCertificate))
{ {
...@@ -149,7 +146,8 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -149,7 +146,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
return isRoot return isRoot
? GenerateCertificate(null, subjectName, subjectName, validFrom, validTo) ? GenerateCertificate(null, subjectName, subjectName, validFrom, validTo)
: GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, issuerPrivateKey: DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey).Private); : GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo,
issuerPrivateKey: DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey).Private);
} }
/// <summary> /// <summary>
...@@ -187,7 +185,9 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -187,7 +185,9 @@ namespace Titanium.Web.Proxy.Network.Certificate
return certificate; return certificate;
} }
return MakeCertificateInternal(isRoot, subject, $"CN={subject}", DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), isRoot ? null : signingCert); return MakeCertificateInternal(isRoot, subject, $"CN={subject}", DateTime.UtcNow.AddDays(-certificateGraceDays),
DateTime.UtcNow.AddDays(certificateValidDays), isRoot ? null : signingCert);
} }
} }
} }
#endif
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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