Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Commit 603223a

Browse files
authored
Merge pull request #361 from justcoding121/develop
beta|develop
2 parents 80dd44e + 0a3c9bb commit 603223a

File tree

10 files changed

+178
-25
lines changed

10 files changed

+178
-25
lines changed

Examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
<WarningLevel>4</WarningLevel>
5252
</PropertyGroup>
5353
<ItemGroup>
54-
<Reference Include="StreamExtended, Version=1.0.132.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
55-
<HintPath>..\..\packages\StreamExtended.1.0.132-beta\lib\net45\StreamExtended.dll</HintPath>
54+
<Reference Include="StreamExtended, Version=1.0.135.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
55+
<HintPath>..\..\packages\StreamExtended.1.0.135-beta\lib\net45\StreamExtended.dll</HintPath>
5656
</Reference>
5757
<Reference Include="System" />
5858
<Reference Include="System.Data" />
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="StreamExtended" version="1.0.132-beta" targetFramework="net45" />
3+
<package id="StreamExtended" version="1.0.135-beta" targetFramework="net45" />
44
</packages>

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
A light weight HTTP(S) proxy server written in C#
44

5-
<a href="https://ci.appveyor.com/project/justcoding121/titanium-web-proxy">![Build Status](https://ci.appveyor.com/api/projects/status/rvlxv8xgj0m7lkr4?svg=true)</a>
5+
<a href="https://ci.appveyor.com/project/justcoding121/titanium-web-proxy">![Build Status](https://ci.appveyor.com/api/projects/status/rvlxv8xgj0m7lkr4?svg=true)</a> [![Join the chat at https://gitter.im/Titanium-Web-Proxy/Lobby](https://badges.gitter.im/Titanium-Web-Proxy/Lobby.svg)](https://gitter.im/Titanium-Web-Proxy/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
66

77
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.
88

Titanium.Web.Proxy.sln.DotSettings

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<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">
22
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Titanium_002EWeb_002EProxy_002EExamples_002EWpf_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
3+
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EMBEDDED_ARRANGEMENT/@EntryValue">False</s:Boolean>
34
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
5+
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
6+
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
47
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_CASE_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
58
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_EMBEDDED_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
69
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
@@ -21,6 +24,11 @@
2124
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
2225
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
2326
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=dda2ffa1_002D435c_002D4111_002D88eb_002D1a7c93c382f0/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Property (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
27+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
28+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
29+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
30+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
2431
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
32+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
2533
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
2634
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

Titanium.Web.Proxy/Helpers/HttpWriter.cs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ internal class HttpWriter : CustomBinaryWriter
1717
private readonly char[] charBuffer;
1818

1919
private static readonly byte[] newLine = ProxyConstants.NewLine;
20-
20+
2121
private static readonly Encoder encoder = Encoding.ASCII.GetEncoder();
2222

2323
internal HttpWriter(Stream stream, int bufferSize) : base(stream)
@@ -33,18 +33,30 @@ public Task WriteLineAsync()
3333
return WriteAsync(newLine);
3434
}
3535

36-
public async Task WriteAsync(string value)
36+
public Task WriteAsync(string value)
3737
{
38-
int charCount = value.Length;
39-
value.CopyTo(0, charBuffer, 0, charCount);
38+
return WriteAsyncInternal(value, false);
39+
}
4040

41-
if (charCount < BufferSize)
41+
private Task WriteAsyncInternal(string value, bool addNewLine)
42+
{
43+
int newLineChars = addNewLine ? newLine.Length : 0;
44+
int charCount = value.Length;
45+
if (charCount < BufferSize - newLineChars)
4246
{
47+
value.CopyTo(0, charBuffer, 0, charCount);
48+
4349
var buffer = BufferPool.GetBuffer(BufferSize);
4450
try
4551
{
4652
int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true);
47-
await WriteAsync(buffer, 0, idx);
53+
if (newLineChars > 0)
54+
{
55+
Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
56+
idx += newLineChars;
57+
}
58+
59+
return WriteAsync(buffer, 0, idx);
4860
}
4961
finally
5062
{
@@ -53,16 +65,24 @@ public async Task WriteAsync(string value)
5365
}
5466
else
5567
{
56-
var buffer = new byte[charCount + 1];
68+
var charBuffer = new char[charCount];
69+
value.CopyTo(0, charBuffer, 0, charCount);
70+
71+
var buffer = new byte[charCount + newLineChars + 1];
5772
int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true);
58-
await WriteAsync(buffer, 0, idx);
73+
if (newLineChars > 0)
74+
{
75+
Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
76+
idx += newLineChars;
77+
}
78+
79+
return WriteAsync(buffer, 0, idx);
5980
}
6081
}
6182

62-
public async Task WriteLineAsync(string value)
83+
public Task WriteLineAsync(string value)
6384
{
64-
await WriteAsync(value);
65-
await WriteLineAsync();
85+
return WriteAsyncInternal(value, true);
6686
}
6787

6888
/// <summary>

Titanium.Web.Proxy/Helpers/Tcp.cs

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Runtime.InteropServices;
55
using System.Threading;
66
using System.Threading.Tasks;
7+
using StreamExtended.Helpers;
78
using Titanium.Web.Proxy.Extensions;
89
using Titanium.Web.Proxy.Network.Tcp;
910

@@ -109,15 +110,115 @@ internal static TcpRow GetTcpRowByLocalPort(IpVersion ipVersion, int localPort)
109110
/// <summary>
110111
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix
111112
/// Usefull for websocket requests
113+
/// Asynchronous Programming Model, which does not throw exceptions when the socket is closed
112114
/// </summary>
113115
/// <param name="clientStream"></param>
114116
/// <param name="serverStream"></param>
115117
/// <param name="bufferSize"></param>
116118
/// <param name="onDataSend"></param>
117119
/// <param name="onDataReceive"></param>
120+
/// <param name="exceptionFunc"></param>
118121
/// <returns></returns>
119-
internal static async Task SendRaw(Stream clientStream, Stream serverStream, int bufferSize,
120-
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive)
122+
internal static async Task SendRawApm(Stream clientStream, Stream serverStream, int bufferSize,
123+
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<Exception> exceptionFunc)
124+
{
125+
var tcs = new TaskCompletionSource<bool>();
126+
var cts = new CancellationTokenSource();
127+
cts.Token.Register(() => tcs.TrySetResult(true));
128+
129+
//Now async relay all server=>client & client=>server data
130+
byte[] clientBuffer = BufferPool.GetBuffer(bufferSize);
131+
byte[] serverBuffer = BufferPool.GetBuffer(bufferSize);
132+
try
133+
{
134+
BeginRead(clientStream, serverStream, clientBuffer, cts, onDataSend, exceptionFunc);
135+
BeginRead(serverStream, clientStream, serverBuffer, cts, onDataReceive, exceptionFunc);
136+
await tcs.Task;
137+
}
138+
finally
139+
{
140+
BufferPool.ReturnBuffer(clientBuffer);
141+
BufferPool.ReturnBuffer(serverBuffer);
142+
}
143+
}
144+
145+
private static void BeginRead(Stream inputStream, Stream outputStream, byte[] buffer, CancellationTokenSource cts, Action<byte[], int, int> onCopy, Action<Exception> exceptionFunc)
146+
{
147+
if (cts.IsCancellationRequested)
148+
{
149+
return;
150+
}
151+
152+
bool readFlag = false;
153+
var readCallback = (AsyncCallback)(ar =>
154+
{
155+
if (cts.IsCancellationRequested || readFlag)
156+
{
157+
return;
158+
}
159+
160+
readFlag = true;
161+
162+
try
163+
{
164+
int read = inputStream.EndRead(ar);
165+
if (read <= 0)
166+
{
167+
cts.Cancel();
168+
return;
169+
}
170+
171+
onCopy?.Invoke(buffer, 0, read);
172+
173+
var writeCallback = (AsyncCallback)(ar2 =>
174+
{
175+
if (cts.IsCancellationRequested)
176+
{
177+
return;
178+
}
179+
180+
try
181+
{
182+
outputStream.EndWrite(ar2);
183+
BeginRead(inputStream, outputStream, buffer, cts, onCopy, exceptionFunc);
184+
}
185+
catch (IOException ex)
186+
{
187+
cts.Cancel();
188+
exceptionFunc(ex);
189+
}
190+
});
191+
192+
outputStream.BeginWrite(buffer, 0, read, writeCallback, null);
193+
}
194+
catch (IOException ex)
195+
{
196+
cts.Cancel();
197+
exceptionFunc(ex);
198+
}
199+
});
200+
201+
var readResult = inputStream.BeginRead(buffer, 0, buffer.Length, readCallback, null);
202+
if (readResult.CompletedSynchronously)
203+
{
204+
readCallback(readResult);
205+
}
206+
}
207+
208+
/// <summary>
209+
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix
210+
/// Usefull for websocket requests
211+
/// Task-based Asynchronous Pattern
212+
/// </summary>
213+
/// <param name="clientStream"></param>
214+
/// <param name="serverStream"></param>
215+
/// <param name="bufferSize"></param>
216+
/// <param name="onDataSend"></param>
217+
/// <param name="onDataReceive"></param>
218+
/// <param name="exceptionFunc"></param>
219+
/// <returns></returns>
220+
internal static async Task SendRawTap(Stream clientStream, Stream serverStream, int bufferSize,
221+
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<Exception> exceptionFunc)
121222
{
122223
var cts = new CancellationTokenSource();
123224

Titanium.Web.Proxy/Network/Certificate/BCCertificateMaker.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.IO;
23
using System.Security.Cryptography.X509Certificates;
34
using System.Threading;
45
using Org.BouncyCastle.Asn1;
@@ -132,9 +133,14 @@ private static X509Certificate2 GenerateCertificate(string hostName,
132133
var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1,
133134
rsa.Exponent2, rsa.Coefficient);
134135

136+
#if NET45
135137
// Set private key onto certificate instance
136138
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
137139
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
140+
#else
141+
var x509Certificate = WithPrivateKey(certificate, rsaparams);
142+
x509Certificate.FriendlyName = subjectName;
143+
#endif
138144

139145
if (!doNotSetFriendlyName)
140146
{
@@ -151,6 +157,22 @@ private static X509Certificate2 GenerateCertificate(string hostName,
151157
return x509Certificate;
152158
}
153159

160+
private static X509Certificate2 WithPrivateKey(Org.BouncyCastle.X509.X509Certificate certificate, AsymmetricKeyParameter privateKey)
161+
{
162+
const string password = "password";
163+
var store = new Pkcs12Store();
164+
var entry = new X509CertificateEntry(certificate);
165+
store.SetCertificateEntry(certificate.SubjectDN.ToString(), entry);
166+
167+
store.SetKeyEntry(certificate.SubjectDN.ToString(), new AsymmetricKeyEntry(privateKey), new[] { entry });
168+
using (var ms = new MemoryStream())
169+
{
170+
store.Save(ms, password.ToCharArray(), new SecureRandom(new CryptoApiRandomGenerator()));
171+
172+
return new X509Certificate2(ms.ToArray(), password, X509KeyStorageFlags.Exportable);
173+
}
174+
}
175+
154176
/// <summary>
155177
/// Makes the certificate internal.
156178
/// </summary>

Titanium.Web.Proxy/ProxyServer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using StreamExtended.Network;
2+
using System;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Net;
@@ -7,7 +8,6 @@
78
using System.Security.Cryptography.X509Certificates;
89
using System.Threading;
910
using System.Threading.Tasks;
10-
using StreamExtended.Network;
1111
using Titanium.Web.Proxy.EventArguments;
1212
using Titanium.Web.Proxy.Extensions;
1313
using Titanium.Web.Proxy.Helpers;
@@ -438,7 +438,7 @@ public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType p
438438

439439
if (isHttp)
440440
{
441-
endPoint.IsSystemHttpsProxy = true;
441+
endPoint.IsSystemHttpProxy = true;
442442
}
443443

444444
if (isHttps)

Titanium.Web.Proxy/RequestHandler.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,10 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli
194194
((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
195195
}
196196

197-
await TcpHelper.SendRaw(clientStream, connection.Stream, BufferSize,
197+
await TcpHelper.SendRawApm(clientStream, connection.Stream, BufferSize,
198198
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
199-
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); });
199+
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
200+
ExceptionFunc);
200201
}
201202

202203
return;
@@ -441,9 +442,10 @@ private async Task<bool> HandleHttpSessionRequest(TcpClient client, string httpC
441442
await BeforeResponse.InvokeAsync(this, args, ExceptionFunc);
442443
}
443444

444-
await TcpHelper.SendRaw(clientStream, connection.Stream, BufferSize,
445+
await TcpHelper.SendRawApm(clientStream, connection.Stream, BufferSize,
445446
(buffer, offset, count) => { args.OnDataSent(buffer, offset, count); },
446-
(buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); });
447+
(buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); },
448+
ExceptionFunc);
447449

448450
args.Dispose();
449451
break;

Titanium.Web.Proxy/Titanium.Web.Proxy.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
<ItemGroup>
1515
<PackageReference Include="Portable.BouncyCastle" Version="1.8.1.3" />
16-
<PackageReference Include="StreamExtended" Version="1.0.132-beta" />
16+
<PackageReference Include="StreamExtended" Version="1.0.135-beta" />
1717
</ItemGroup>
1818

1919
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">

0 commit comments

Comments
 (0)