Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
T
Titanium-Web-Proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Titanium-Web-Proxy
Commits
34a68075
Commit
34a68075
authored
May 20, 2016
by
justcoding121
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Asyncify fixes
parent
03205075
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
239 additions
and
218 deletions
+239
-218
ProxyTestController.cs
.../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
+4
-4
README.md
README.md
+3
-6
SessionEventArgs.cs
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+23
-16
HttpWebRequestExtensions.cs
Titanium.Web.Proxy/Extensions/HttpWebRequestExtensions.cs
+2
-1
HttpWebResponseExtensions.cs
Titanium.Web.Proxy/Extensions/HttpWebResponseExtensions.cs
+24
-6
StreamExtensions.cs
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
+10
-6
CertificateManager.cs
Titanium.Web.Proxy/Helpers/CertificateManager.cs
+94
-68
HttpWebClient.cs
Titanium.Web.Proxy/Http/HttpWebClient.cs
+12
-12
Response.cs
Titanium.Web.Proxy/Http/Response.cs
+3
-22
ProxyClient.cs
Titanium.Web.Proxy/Network/ProxyClient.cs
+1
-1
TcpConnectionManager.cs
Titanium.Web.Proxy/Network/TcpConnectionManager.cs
+21
-18
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+9
-6
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+12
-8
ResponseHandler.cs
Titanium.Web.Proxy/ResponseHandler.cs
+20
-43
Titanium.Web.Proxy.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+1
-1
No files found.
Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
View file @
34a68075
...
...
@@ -12,9 +12,9 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
public
void
StartProxy
()
{
ProxyServer
.
BeforeRequest
+=
OnRequest
;
ProxyServer
.
BeforeResponse
+=
OnResponse
;
ProxyServer
.
ServerCertificateValidationCallback
+=
OnCertificateValidation
;
//
ProxyServer.BeforeRequest += OnRequest;
//
ProxyServer.BeforeResponse += OnResponse;
//
ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
//Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning
...
...
@@ -112,7 +112,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
if
(
e
.
WebSession
.
Response
.
ResponseStatusCode
==
"200"
)
{
if
(
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
();
await
e
.
SetResponseBody
(
bodyBytes
);
...
...
README.md
View file @
34a68075
...
...
@@ -136,7 +136,7 @@ Sample request and response event handlers
{
if
(
e
.
WebSession
.
Response
.
ResponseStatusCode
==
"200"
)
{
if
(
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
();
await
e
.
SetResponseBody
(
bodyBytes
);
...
...
@@ -148,11 +148,8 @@ Sample request and response event handlers
}
}
/// <summary>
/// Allows overriding default certificate validation logic
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// Allows overriding default certificate validation logic
public
async
Task
OnCertificateValidation
(
object
sender
,
CertificateValidationEventArgs
e
)
{
//set IsValid to true/false based on Certificate Errors
...
...
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
34a68075
...
...
@@ -7,6 +7,7 @@ using Titanium.Web.Proxy.Http;
using
Titanium.Web.Proxy.Http.Responses
;
using
Titanium.Web.Proxy.Extensions
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Network
;
namespace
Titanium.Web.Proxy.EventArguments
{
...
...
@@ -86,14 +87,17 @@ namespace Titanium.Web.Proxy.EventArguments
await
this
.
Client
.
ClientStreamReader
.
CopyBytesToStream
(
requestBodyStream
,
WebSession
.
Request
.
ContentLength
).
ConfigureAwait
(
false
);
}
else
if
(
WebSession
.
Request
.
HttpVersion
.
ToLower
().
Trim
().
Equals
(
"http/1.0"
))
await
WebSession
.
ServerConnection
.
StreamReader
.
CopyBytesToStream
(
requestBodyStream
,
long
.
MaxValue
).
ConfigureAwait
(
false
);
}
WebSession
.
Request
.
RequestBody
=
await
GetDecompressedResponseBody
(
WebSession
.
Request
.
ContentEncoding
,
requestBodyStream
.
ToArray
()).
ConfigureAwait
(
false
);
}
}
//Now set the flag to true
//So that next time we can deliver body from cache
WebSession
.
Request
.
RequestBodyRead
=
true
;
//Now set the flag to true
//So that next time we can deliver body from cache
WebSession
.
Request
.
RequestBodyRead
=
true
;
}
}
/// <summary>
...
...
@@ -109,16 +113,18 @@ namespace Titanium.Web.Proxy.EventArguments
//If chuncked the read chunk by chunk until we hit chunk end symbol
if
(
WebSession
.
Response
.
IsChunked
)
{
await
WebSession
.
ProxyClient
.
Server
StreamReader
.
CopyBytesToStreamChunked
(
responseBodyStream
).
ConfigureAwait
(
false
);
await
WebSession
.
ServerConnection
.
StreamReader
.
CopyBytesToStreamChunked
(
responseBodyStream
).
ConfigureAwait
(
false
);
}
else
{
if
(
WebSession
.
Response
.
ContentLength
>
0
)
{
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await
WebSession
.
ProxyClient
.
Server
StreamReader
.
CopyBytesToStream
(
responseBodyStream
,
WebSession
.
Response
.
ContentLength
).
ConfigureAwait
(
false
);
await
WebSession
.
ServerConnection
.
StreamReader
.
CopyBytesToStream
(
responseBodyStream
,
WebSession
.
Response
.
ContentLength
).
ConfigureAwait
(
false
);
}
else
if
(
WebSession
.
Response
.
HttpVersion
.
ToLower
().
Trim
().
Equals
(
"http/1.0"
))
await
WebSession
.
ServerConnection
.
StreamReader
.
CopyBytesToStream
(
responseBodyStream
,
long
.
MaxValue
).
ConfigureAwait
(
false
);
}
WebSession
.
Response
.
ResponseBody
=
await
GetDecompressedResponseBody
(
WebSession
.
Response
.
ContentEncoding
,
responseBodyStream
.
ToArray
()).
ConfigureAwait
(
false
);
...
...
@@ -173,7 +179,11 @@ namespace Titanium.Web.Proxy.EventArguments
}
WebSession
.
Request
.
RequestBody
=
body
;
WebSession
.
Request
.
RequestBodyRead
=
true
;
if
(
WebSession
.
Request
.
IsChunked
==
false
)
WebSession
.
Request
.
ContentLength
=
body
.
Length
;
else
WebSession
.
Request
.
ContentLength
=
-
1
;
}
/// <summary>
...
...
@@ -191,13 +201,8 @@ namespace Titanium.Web.Proxy.EventArguments
await
ReadRequestBody
().
ConfigureAwait
(
false
);
}
WebSession
.
Request
.
RequestBody
=
WebSession
.
Request
.
Encoding
.
GetBytes
(
body
);
//If there is a content length header update it
if
(!
WebSession
.
Request
.
IsChunked
)
WebSession
.
Request
.
ContentLength
=
body
.
Length
;
await
SetRequestBody
(
WebSession
.
Request
.
Encoding
.
GetBytes
(
body
));
WebSession
.
Request
.
RequestBodyRead
=
true
;
}
/// <summary>
...
...
@@ -245,9 +250,10 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession
.
Response
.
ResponseBody
=
body
;
//If there is a content length header update it
if
(
!
WebSession
.
Response
.
IsChunked
)
if
(
WebSession
.
Response
.
IsChunked
==
false
)
WebSession
.
Response
.
ContentLength
=
body
.
Length
;
else
WebSession
.
Response
.
ContentLength
=
-
1
;
}
/// <summary>
...
...
@@ -266,8 +272,9 @@ namespace Titanium.Web.Proxy.EventArguments
}
var
bodyBytes
=
WebSession
.
Response
.
Encoding
.
GetBytes
(
body
);
await
SetResponseBody
(
bodyBytes
).
ConfigureAwait
(
false
);
}
}
private
async
Task
<
byte
[
]>
GetDecompressedResponseBody
(
string
encodingType
,
byte
[]
responseBodyStream
)
{
...
...
Titanium.Web.Proxy/Extensions/HttpWebRequestExtensions.cs
View file @
34a68075
...
...
@@ -15,7 +15,8 @@ namespace Titanium.Web.Proxy.Extensions
try
{
//return default if not specified
if
(
request
.
ContentType
==
null
)
return
Encoding
.
GetEncoding
(
"ISO-8859-1"
);
if
(
request
.
ContentType
==
null
)
return
Encoding
.
GetEncoding
(
"ISO-8859-1"
);
//extract the encoding by finding the charset
var
contentTypes
=
request
.
ContentType
.
Split
(
Constants
.
SemiColonSplit
);
...
...
Titanium.Web.Proxy/Extensions/HttpWebResponseExtensions.cs
View file @
34a68075
using
System.Text
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Extensions
{
public
static
class
HttpWebResponseExtensions
{
public
static
Encoding
GetResponseEncoding
(
this
Response
response
)
public
static
Encoding
GetResponse
Character
Encoding
(
this
Response
response
)
{
if
(
string
.
IsNullOrEmpty
(
response
.
CharacterSet
))
return
Encoding
.
GetEncoding
(
"ISO-8859-1"
);
try
{
return
Encoding
.
GetEncoding
(
response
.
CharacterSet
.
Replace
(
@""""
,
string
.
Empty
));
//return default if not specified
if
(
response
.
ContentType
==
null
)
return
Encoding
.
GetEncoding
(
"ISO-8859-1"
);
//extract the encoding by finding the charset
var
contentTypes
=
response
.
ContentType
.
Split
(
Constants
.
SemiColonSplit
);
foreach
(
var
contentType
in
contentTypes
)
{
var
encodingSplit
=
contentType
.
Split
(
'='
);
if
(
encodingSplit
.
Length
==
2
&&
encodingSplit
[
0
].
ToLower
().
Trim
()
==
"charset"
)
{
return
Encoding
.
GetEncoding
(
encodingSplit
[
1
]);
}
}
}
catch
{
return
Encoding
.
GetEncoding
(
"ISO-8859-1"
);
}
catch
{
//parsing errors
// ignored
}
//return default if not specified
return
Encoding
.
GetEncoding
(
"ISO-8859-1"
);
}
}
}
\ No newline at end of file
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
View file @
34a68075
...
...
@@ -20,25 +20,29 @@ namespace Titanium.Web.Proxy.Extensions
await
input
.
CopyToAsync
(
output
);
}
internal
static
async
Task
CopyBytesToStream
(
this
CustomBinaryReader
clientS
treamReader
,
Stream
stream
,
long
totalBytesToRead
)
internal
static
async
Task
CopyBytesToStream
(
this
CustomBinaryReader
s
treamReader
,
Stream
stream
,
long
totalBytesToRead
)
{
var
totalbytesRead
=
0
;
int
bytesToRead
;
long
bytesToRead
;
if
(
totalBytesToRead
<
Constants
.
BUFFER_SIZE
)
{
bytesToRead
=
(
int
)
totalBytesToRead
;
bytesToRead
=
totalBytesToRead
;
}
else
bytesToRead
=
Constants
.
BUFFER_SIZE
;
while
(
totalbytesRead
<
(
int
)
totalBytesToRead
)
while
(
totalbytesRead
<
totalBytesToRead
)
{
var
buffer
=
await
clientStreamReader
.
ReadBytesAsync
(
bytesToRead
);
var
buffer
=
await
streamReader
.
ReadBytesAsync
(
bytesToRead
);
if
(
buffer
.
Length
==
0
)
break
;
totalbytesRead
+=
buffer
.
Length
;
var
remainingBytes
=
(
int
)
totalBytesToRead
-
totalbytesRead
;
var
remainingBytes
=
totalBytesToRead
-
totalbytesRead
;
if
(
remainingBytes
<
bytesToRead
)
{
bytesToRead
=
remainingBytes
;
...
...
Titanium.Web.Proxy/Helpers/CertificateManager.cs
View file @
34a68075
...
...
@@ -4,15 +4,18 @@ using System.Diagnostics;
using
System.Collections.Generic
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Reflection
;
using
System.Threading.Tasks
;
using
System.Threading
;
namespace
Titanium.Web.Proxy.Helpers
{
public
class
CertificateManager
:
IDisposable
public
class
CertificateManager
:
IDisposable
{
private
const
string
CertCreateFormat
=
"-ss {0} -n \"CN={1}, O={2}\" -sky {3} -cy {4} -m 120 -a sha256 -eku 1.3.6.1.5.5.7.3.1 {5}"
;
private
readonly
IDictionary
<
string
,
X509Certificate2
>
_certificateCache
;
private
static
SemaphoreSlim
semaphoreLock
=
new
SemaphoreSlim
(
1
);
public
string
Issuer
{
get
;
private
set
;
}
public
string
RootCertificateName
{
get
;
private
set
;
}
...
...
@@ -35,10 +38,10 @@ namespace Titanium.Web.Proxy.Helpers
/// Attempts to move a self-signed certificate to the root store.
/// </summary>
/// <returns>true if succeeded, else false</returns>
public
bool
CreateTrustedRootCertificate
()
public
async
Task
<
bool
>
CreateTrustedRootCertificate
()
{
X509Certificate2
rootCertificate
=
CreateCertificate
(
RootStore
,
RootCertificateName
);
await
CreateCertificate
(
RootStore
,
RootCertificateName
);
return
rootCertificate
!=
null
;
}
...
...
@@ -46,9 +49,9 @@ namespace Titanium.Web.Proxy.Helpers
/// Attempts to remove the self-signed certificate from the root store.
/// </summary>
/// <returns>true if succeeded, else false</returns>
public
bool
DestroyTrustedRootCertificate
()
public
async
Task
<
bool
>
DestroyTrustedRootCertificate
()
{
return
DestroyCertificate
(
RootStore
,
RootCertificateName
);
return
await
DestroyCertificate
(
RootStore
,
RootCertificateName
);
}
public
X509Certificate2Collection
FindCertificates
(
string
certificateSubject
)
...
...
@@ -64,103 +67,126 @@ namespace Titanium.Web.Proxy.Helpers
discoveredCertificates
:
null
;
}
public
X509Certificate2
CreateCertificate
(
string
certificateName
)
public
async
Task
<
X509Certificate2
>
CreateCertificate
(
string
certificateName
)
{
return
CreateCertificate
(
MyStore
,
certificateName
);
return
await
CreateCertificate
(
MyStore
,
certificateName
);
}
protected
virtual
X509Certificate2
CreateCertificate
(
X509Store
store
,
string
certificateName
)
protected
async
virtual
Task
<
X509Certificate2
>
CreateCertificate
(
X509Store
store
,
string
certificateName
)
{
if
(
_certificateCache
.
ContainsKey
(
certificateName
))
return
_certificateCache
[
certificateName
];
lock
(
store
)
await
semaphoreLock
.
WaitAsync
();
X509Certificate2
certificate
=
null
;
try
{
X509Certificate2
certificate
=
null
;
try
{
store
.
Open
(
OpenFlags
.
ReadWrite
);
string
certificateSubject
=
string
.
Format
(
"CN={0}, O={1}"
,
certificateName
,
Issuer
);
store
.
Open
(
OpenFlags
.
ReadWrite
);
string
certificateSubject
=
string
.
Format
(
"CN={0}, O={1}"
,
certificateName
,
Issuer
);
var
certificates
=
FindCertificates
(
store
,
certificateSubject
);
var
certificates
=
FindCertificates
(
store
,
certificateSubject
);
if
(
certificates
!=
null
)
certificate
=
certificates
[
0
];
if
(
certificates
!=
null
)
certificate
=
certificates
[
0
];
if
(
certificate
==
null
)
{
string
[]
args
=
new
[]
{
if
(
certificate
==
null
)
{
string
[]
args
=
new
[]
{
GetCertificateCreateArgs
(
store
,
certificateName
)
};
CreateCertificate
(
args
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
await
CreateCertificate
(
args
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
return
certificates
!=
null
?
certificates
[
0
]
:
null
;
}
return
certificate
;
}
finally
{
store
.
Close
();
if
(
certificate
!=
null
&&
!
_certificateCache
.
ContainsKey
(
certificateName
))
_certificateCache
.
Add
(
certificateName
,
certificate
);
return
certificates
!=
null
?
certificates
[
0
]
:
null
;
}
store
.
Close
();
if
(
certificate
!=
null
&&
!
_certificateCache
.
ContainsKey
(
certificateName
))
_certificateCache
.
Add
(
certificateName
,
certificate
);
return
certificate
;
}
finally
{
semaphoreLock
.
Release
();
}
}
protected
virtual
void
CreateCertificate
(
string
[]
args
)
protected
virtual
Task
<
int
>
CreateCertificate
(
string
[]
args
)
{
using
(
var
process
=
new
Process
())
{
string
file
=
Path
.
Combine
(
Path
.
GetDirectoryName
(
Assembly
.
GetExecutingAssembly
().
Location
),
"makecert.exe"
);
if
(!
File
.
Exists
(
file
))
throw
new
Exception
(
"Unable to locate 'makecert.exe'."
);
// there is no non-generic TaskCompletionSource
var
tcs
=
new
TaskCompletionSource
<
int
>();
var
process
=
new
Process
();
process
.
StartInfo
.
Verb
=
"runas"
;
process
.
StartInfo
.
Arguments
=
args
!=
null
?
args
[
0
]
:
string
.
Empty
;
process
.
StartInfo
.
CreateNoWindow
=
true
;
process
.
StartInfo
.
UseShellExecute
=
false
;
process
.
StartInfo
.
FileName
=
file
;
string
file
=
Path
.
Combine
(
Path
.
GetDirectoryName
(
Assembly
.
GetExecutingAssembly
().
Location
),
"makecert.exe"
);
process
.
Start
();
process
.
WaitForExit
();
if
(!
File
.
Exists
(
file
))
throw
new
Exception
(
"Unable to locate 'makecert.exe'."
);
process
.
StartInfo
.
Verb
=
"runas"
;
process
.
StartInfo
.
Arguments
=
args
!=
null
?
args
[
0
]
:
string
.
Empty
;
process
.
StartInfo
.
CreateNoWindow
=
true
;
process
.
StartInfo
.
UseShellExecute
=
false
;
process
.
StartInfo
.
FileName
=
file
;
process
.
EnableRaisingEvents
=
true
;
process
.
Exited
+=
(
sender
,
processArgs
)
=>
{
tcs
.
SetResult
(
process
.
ExitCode
);
process
.
Dispose
();
};
bool
started
=
process
.
Start
();
if
(!
started
)
{
//you may allow for the process to be re-used (started = false)
//but I'm not sure about the guarantees of the Exited event in such a case
throw
new
InvalidOperationException
(
"Could not start process: "
+
process
);
}
return
tcs
.
Task
;
}
public
bool
DestroyCertificate
(
string
certificateName
)
public
async
Task
<
bool
>
DestroyCertificate
(
string
certificateName
)
{
return
DestroyCertificate
(
MyStore
,
certificateName
);
return
await
DestroyCertificate
(
MyStore
,
certificateName
);
}
protected
virtual
bool
DestroyCertificate
(
X509Store
store
,
string
certificateName
)
protected
virtual
async
Task
<
bool
>
DestroyCertificate
(
X509Store
store
,
string
certificateName
)
{
lock
(
store
)
await
semaphoreLock
.
WaitAsync
();
X509Certificate2Collection
certificates
=
null
;
try
{
X509Certificate2Collection
certificates
=
null
;
try
{
store
.
Open
(
OpenFlags
.
ReadWrite
);
string
certificateSubject
=
string
.
Format
(
"CN={0}, O={1}"
,
certificateName
,
Issuer
);
store
.
Open
(
OpenFlags
.
ReadWrite
);
string
certificateSubject
=
string
.
Format
(
"CN={0}, O={1}"
,
certificateName
,
Issuer
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
if
(
certificates
!=
null
)
{
store
.
RemoveRange
(
certificates
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
if
(
certificates
!=
null
)
{
store
.
RemoveRange
(
certificates
);
certificates
=
FindCertificates
(
store
,
certificateSubject
);
}
return
certificates
==
null
;
}
finally
store
.
Close
();
if
(
certificates
==
null
&&
_certificateCache
.
ContainsKey
(
certificateName
))
{
store
.
Close
();
if
(
certificates
==
null
&&
_certificateCache
.
ContainsKey
(
certificateName
))
{
_certificateCache
.
Remove
(
certificateName
);
}
_certificateCache
.
Remove
(
certificateName
);
}
return
certificates
==
null
;
}
finally
{
semaphoreLock
.
Release
();
}
}
protected
virtual
string
GetCertificateCreateArgs
(
X509Store
store
,
string
certificateName
)
...
...
Titanium.Web.Proxy/Http/HttpWebClient.cs
View file @
34a68075
...
...
@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Http
{
public
class
HttpWebSession
{
internal
TcpConnection
ProxyClient
{
get
;
set
;
}
internal
TcpConnection
ServerConnection
{
get
;
set
;
}
public
Request
Request
{
get
;
set
;
}
public
Response
Response
{
get
;
set
;
}
...
...
@@ -27,7 +27,7 @@ namespace Titanium.Web.Proxy.Http
internal
void
SetConnection
(
TcpConnection
Connection
)
{
Connection
.
LastAccess
=
DateTime
.
Now
;
ProxyClient
=
Connection
;
ServerConnection
=
Connection
;
}
internal
HttpWebSession
()
...
...
@@ -38,7 +38,7 @@ namespace Titanium.Web.Proxy.Http
internal
async
Task
SendRequest
()
{
Stream
stream
=
ProxyClient
.
Stream
;
Stream
stream
=
ServerConnection
.
Stream
;
StringBuilder
requestLines
=
new
StringBuilder
();
...
...
@@ -59,12 +59,12 @@ namespace Titanium.Web.Proxy.Http
string
request
=
requestLines
.
ToString
();
byte
[]
requestBytes
=
Encoding
.
ASCII
.
GetBytes
(
request
);
await
stream
.
WriteAsync
(
requestBytes
,
0
,
requestBytes
.
Length
);
stream
.
Flush
();
await
stream
.
FlushAsync
();
if
(
ProxyServer
.
Enable100ContinueBehaviour
)
if
(
this
.
Request
.
ExpectContinue
)
{
var
httpResult
=
(
await
ProxyClient
.
Server
StreamReader
.
ReadLineAsync
()).
Split
(
Constants
.
SpaceSplit
,
3
);
var
httpResult
=
(
await
ServerConnection
.
StreamReader
.
ReadLineAsync
()).
Split
(
Constants
.
SpaceSplit
,
3
);
var
responseStatusCode
=
httpResult
[
1
].
Trim
();
var
responseStatusDescription
=
httpResult
[
2
].
Trim
();
...
...
@@ -73,13 +73,13 @@ namespace Titanium.Web.Proxy.Http
&&
responseStatusDescription
.
ToLower
().
Equals
(
"continue"
))
{
this
.
Request
.
Is100Continue
=
true
;
await
ProxyClient
.
Server
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
await
ServerConnection
.
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
}
else
if
(
responseStatusCode
.
Equals
(
"417"
)
&&
responseStatusDescription
.
ToLower
().
Equals
(
"expectation failed"
))
{
this
.
Request
.
ExpectationFailed
=
true
;
await
ProxyClient
.
Server
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
await
ServerConnection
.
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
}
}
}
...
...
@@ -89,11 +89,11 @@ namespace Titanium.Web.Proxy.Http
//return if this is already read
if
(
this
.
Response
.
ResponseStatusCode
!=
null
)
return
;
var
httpResult
=
(
await
ProxyClient
.
Server
StreamReader
.
ReadLineAsync
()).
Split
(
Constants
.
SpaceSplit
,
3
);
var
httpResult
=
(
await
ServerConnection
.
StreamReader
.
ReadLineAsync
()).
Split
(
Constants
.
SpaceSplit
,
3
);
if
(
string
.
IsNullOrEmpty
(
httpResult
[
0
]))
{
await
ProxyClient
.
Server
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
await
ServerConnection
.
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
}
this
.
Response
.
HttpVersion
=
httpResult
[
0
].
Trim
();
...
...
@@ -106,7 +106,7 @@ namespace Titanium.Web.Proxy.Http
{
this
.
Response
.
Is100Continue
=
true
;
this
.
Response
.
ResponseStatusCode
=
null
;
await
ProxyClient
.
Server
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
await
ServerConnection
.
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
await
ReceiveResponse
();
return
;
}
...
...
@@ -115,12 +115,12 @@ namespace Titanium.Web.Proxy.Http
{
this
.
Response
.
ExpectationFailed
=
true
;
this
.
Response
.
ResponseStatusCode
=
null
;
await
ProxyClient
.
Server
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
await
ServerConnection
.
StreamReader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
await
ReceiveResponse
();
return
;
}
List
<
string
>
responseLines
=
await
ProxyClient
.
Server
StreamReader
.
ReadAllLinesAsync
().
ConfigureAwait
(
false
);
List
<
string
>
responseLines
=
await
ServerConnection
.
StreamReader
.
ReadAllLinesAsync
().
ConfigureAwait
(
false
);
for
(
int
index
=
0
;
index
<
responseLines
.
Count
;
++
index
)
{
...
...
Titanium.Web.Proxy/Http/Response.cs
View file @
34a68075
...
...
@@ -13,22 +13,9 @@ namespace Titanium.Web.Proxy.Http
public
string
ResponseStatusCode
{
get
;
set
;
}
public
string
ResponseStatusDescription
{
get
;
set
;
}
internal
Encoding
Encoding
{
get
{
return
this
.
GetResponseEncoding
();
}
}
internal
Encoding
Encoding
{
get
{
return
this
.
GetResponse
Character
Encoding
();
}
}
internal
string
CharacterSet
{
get
{
if
(
this
.
ContentType
.
Contains
(
";"
))
{
return
this
.
ContentType
.
Split
(
';'
)[
1
].
Substring
(
9
).
Trim
();
}
return
null
;
}
}
internal
string
ContentEncoding
{
get
...
...
@@ -69,13 +56,7 @@ namespace Titanium.Web.Proxy.Http
if
(
header
!=
null
)
{
if
(
header
.
Value
.
Contains
(
";"
))
{
return
header
.
Value
.
Split
(
';'
)[
0
].
Trim
();
}
else
return
header
.
Value
.
ToLower
().
Trim
();
return
header
.
Value
;
}
return
null
;
...
...
Titanium.Web.Proxy/
EventArguments
/ProxyClient.cs
→
Titanium.Web.Proxy/
Network
/ProxyClient.cs
View file @
34a68075
...
...
@@ -2,7 +2,7 @@
using
System.Net.Sockets
;
using
Titanium.Web.Proxy.Helpers
;
namespace
Titanium.Web.Proxy.
EventArguments
namespace
Titanium.Web.Proxy.
Network
{
/// <summary>
/// This class wraps Tcp connection to Server
...
...
Titanium.Web.Proxy/Network/TcpConnectionManager.cs
View file @
34a68075
...
...
@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy.Network
internal
Version
Version
{
get
;
set
;
}
internal
TcpClient
TcpClient
{
get
;
set
;
}
internal
CustomBinaryReader
S
erverS
treamReader
{
get
;
set
;
}
internal
CustomBinaryReader
StreamReader
{
get
;
set
;
}
internal
Stream
Stream
{
get
;
set
;
}
internal
DateTime
LastAccess
{
get
;
set
;
}
...
...
@@ -88,21 +88,25 @@ namespace Titanium.Web.Proxy.Network
client
=
new
TcpClient
(
ProxyServer
.
UpStreamHttpsProxy
.
HostName
,
ProxyServer
.
UpStreamHttpsProxy
.
Port
);
stream
=
(
Stream
)
client
.
GetStream
();
var
writer
=
new
StreamWriter
(
stream
,
Encoding
.
ASCII
,
Constants
.
BUFFER_SIZE
,
true
);
writer
.
WriteLine
(
string
.
Format
(
"CONNECT {0}:{1} {2}"
,
sessionArgs
.
WebSession
.
Request
.
RequestUri
.
Host
,
sessionArgs
.
WebSession
.
Request
.
RequestUri
.
Port
,
sessionArgs
.
WebSession
.
Request
.
HttpVersion
));
writer
.
WriteLine
(
string
.
Format
(
"Host: {0}:{1}"
,
sessionArgs
.
WebSession
.
Request
.
RequestUri
.
Host
,
sessionArgs
.
WebSession
.
Request
.
RequestUri
.
Port
));
writer
.
WriteLine
(
"Connection: Keep-Alive"
);
writer
.
WriteLine
();
writer
.
Flush
();
var
reader
=
new
CustomBinaryReader
(
stream
);
var
result
=
await
reader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
if
(!
result
.
ToLower
().
Contains
(
"200 connection established"
))
throw
new
Exception
(
"Upstream proxy failed to create a secure tunnel"
);
await
reader
.
ReadAllLinesAsync
().
ConfigureAwait
(
false
);
using
(
var
writer
=
new
StreamWriter
(
stream
,
Encoding
.
ASCII
,
Constants
.
BUFFER_SIZE
,
true
))
{
await
writer
.
WriteLineAsync
(
string
.
Format
(
"CONNECT {0}:{1} {2}"
,
sessionArgs
.
WebSession
.
Request
.
RequestUri
.
Host
,
sessionArgs
.
WebSession
.
Request
.
RequestUri
.
Port
,
sessionArgs
.
WebSession
.
Request
.
HttpVersion
));
await
writer
.
WriteLineAsync
(
string
.
Format
(
"Host: {0}:{1}"
,
sessionArgs
.
WebSession
.
Request
.
RequestUri
.
Host
,
sessionArgs
.
WebSession
.
Request
.
RequestUri
.
Port
));
await
writer
.
WriteLineAsync
(
"Connection: Keep-Alive"
);
await
writer
.
WriteLineAsync
();
await
writer
.
FlushAsync
();
writer
.
Close
();
}
using
(
var
reader
=
new
CustomBinaryReader
(
stream
))
{
var
result
=
await
reader
.
ReadLineAsync
().
ConfigureAwait
(
false
);
if
(!
result
.
ToLower
().
Contains
(
"200 connection established"
))
throw
new
Exception
(
"Upstream proxy failed to create a secure tunnel"
);
await
reader
.
ReadAllLinesAsync
().
ConfigureAwait
(
false
);
}
}
else
{
...
...
@@ -144,7 +148,7 @@ namespace Titanium.Web.Proxy.Network
port
=
port
,
IsSecure
=
isSecure
,
TcpClient
=
client
,
S
erverS
treamReader
=
new
CustomBinaryReader
(
stream
),
StreamReader
=
new
CustomBinaryReader
(
stream
),
Stream
=
stream
,
Version
=
version
};
...
...
@@ -178,6 +182,5 @@ namespace Titanium.Web.Proxy.Network
}
}
}
Titanium.Web.Proxy/ProxyServer.cs
View file @
34a68075
...
...
@@ -154,7 +154,7 @@ namespace Titanium.Web.Proxy
CertManager
=
new
CertificateManager
(
RootCertificateIssuerName
,
RootCertificateName
);
certTrusted
=
CertManager
.
CreateTrustedRootCertificate
();
certTrusted
=
CertManager
.
CreateTrustedRootCertificate
()
.
Result
;
foreach
(
var
endPoint
in
ProxyEndPoints
)
{
...
...
@@ -222,18 +222,21 @@ namespace Titanium.Web.Proxy
{
var
client
=
endPoint
.
listener
.
EndAcceptTcpClient
(
asyn
);
if
(
endPoint
.
GetType
()
==
typeof
(
TransparentProxyEndPoint
))
Task
.
Factory
.
StartNew
(()
=>
HandleClient
(
endPoint
as
TransparentProxyEndPoint
,
client
)
);
HandleClient
(
endPoint
as
TransparentProxyEndPoint
,
client
);
else
Task
.
Factory
.
StartNew
(()
=>
HandleClient
(
endPoint
as
ExplicitProxyEndPoint
,
client
)
);
HandleClient
(
endPoint
as
ExplicitProxyEndPoint
,
client
);
// Get the listener that handles the client request.
endPoint
.
listener
.
BeginAcceptTcpClient
(
OnAcceptConnection
,
endPoint
);
}
catch
catch
(
ObjectDisposedException
)
{
// ignored
// The listener was Stop()'d, disposing the underlying socket and
// triggering the completion of the callback. We're already exiting,
// so just return.
return
;
}
}
}
...
...
Titanium.Web.Proxy/RequestHandler.cs
View file @
34a68075
...
...
@@ -67,7 +67,7 @@ namespace Titanium.Web.Proxy
await
WriteConnectResponse
(
clientStreamWriter
,
httpVersion
).
ConfigureAwait
(
false
);
var
certificate
=
CertManager
.
CreateCertificate
(
httpRemoteUri
.
Host
);
var
certificate
=
await
CertManager
.
CreateCertificate
(
httpRemoteUri
.
Host
);
SslStream
sslStream
=
null
;
...
...
@@ -139,7 +139,7 @@ namespace Titanium.Web.Proxy
// certificate = CertManager.CreateCertificate(hostName);
//}
//else
certificate
=
CertManager
.
CreateCertificate
(
endPoint
.
GenericCertificateName
);
certificate
=
await
CertManager
.
CreateCertificate
(
endPoint
.
GenericCertificateName
);
try
{
...
...
@@ -281,13 +281,13 @@ namespace Titanium.Web.Proxy
{
WriteResponseStatus
(
args
.
WebSession
.
Response
.
HttpVersion
,
"100"
,
"Continue"
,
args
.
Client
.
ClientStreamWriter
);
a
rgs
.
Client
.
ClientStreamWriter
.
WriteLine
();
a
wait
args
.
Client
.
ClientStreamWriter
.
WriteLineAsync
();
}
else
if
(
args
.
WebSession
.
Request
.
ExpectationFailed
)
{
WriteResponseStatus
(
args
.
WebSession
.
Response
.
HttpVersion
,
"417"
,
"Expectation Failed"
,
args
.
Client
.
ClientStreamWriter
);
args
.
Client
.
ClientStreamWriter
.
WriteLine
();
await
args
.
Client
.
ClientStreamWriter
.
WriteLineAsync
();
}
if
(!
args
.
WebSession
.
Request
.
ExpectContinue
)
...
...
@@ -299,8 +299,14 @@ namespace Titanium.Web.Proxy
//If request was modified by user
if
(
args
.
WebSession
.
Request
.
RequestBodyRead
)
{
if
(
args
.
WebSession
.
Request
.
ContentEncoding
!=
null
)
{
args
.
WebSession
.
Request
.
RequestBody
=
await
GetCompressedResponseBody
(
args
.
WebSession
.
Request
.
ContentEncoding
,
args
.
WebSession
.
Request
.
RequestBody
);
}
args
.
WebSession
.
Request
.
ContentLength
=
args
.
WebSession
.
Request
.
RequestBody
.
Length
;
var
newStream
=
args
.
WebSession
.
ProxyClient
.
Stream
;
var
newStream
=
args
.
WebSession
.
ServerConnection
.
Stream
;
await
newStream
.
WriteAsync
(
args
.
WebSession
.
Request
.
RequestBody
,
0
,
args
.
WebSession
.
Request
.
RequestBody
.
Length
).
ConfigureAwait
(
false
);
}
else
...
...
@@ -392,7 +398,7 @@ namespace Titanium.Web.Proxy
private
static
async
Task
SendClientRequestBody
(
SessionEventArgs
args
)
{
// End the operation
var
postStream
=
args
.
WebSession
.
ProxyClient
.
Stream
;
var
postStream
=
args
.
WebSession
.
ServerConnection
.
Stream
;
if
(
args
.
WebSession
.
Request
.
ContentLength
>
0
)
{
...
...
@@ -466,8 +472,6 @@ namespace Titanium.Web.Proxy
if
(
sslPolicyErrors
==
SslPolicyErrors
.
None
)
return
true
;
Console
.
WriteLine
(
"Certificate error: {0}"
,
sslPolicyErrors
);
//By default
//do not allow this client to communicate with unauthenticated servers.
return
false
;
...
...
Titanium.Web.Proxy/ResponseHandler.cs
View file @
34a68075
...
...
@@ -24,7 +24,7 @@ namespace Titanium.Web.Proxy
try
{
if
(!
args
.
WebSession
.
Response
.
ResponseBodyRead
)
args
.
WebSession
.
Response
.
ResponseStream
=
args
.
WebSession
.
ProxyClient
.
Stream
;
args
.
WebSession
.
Response
.
ResponseStream
=
args
.
WebSession
.
ServerConnection
.
Stream
;
if
(
BeforeResponse
!=
null
&&
!
args
.
WebSession
.
Response
.
ResponseLocked
)
...
...
@@ -46,13 +46,13 @@ namespace Titanium.Web.Proxy
{
WriteResponseStatus
(
args
.
WebSession
.
Response
.
HttpVersion
,
"100"
,
"Continue"
,
args
.
Client
.
ClientStreamWriter
);
a
rgs
.
Client
.
ClientStreamWriter
.
WriteLine
();
a
wait
args
.
Client
.
ClientStreamWriter
.
WriteLineAsync
();
}
else
if
(
args
.
WebSession
.
Response
.
ExpectationFailed
)
{
WriteResponseStatus
(
args
.
WebSession
.
Response
.
HttpVersion
,
"417"
,
"Expectation Failed"
,
args
.
Client
.
ClientStreamWriter
);
a
rgs
.
Client
.
ClientStreamWriter
.
WriteLine
();
a
wait
args
.
Client
.
ClientStreamWriter
.
WriteLineAsync
();
}
WriteResponseStatus
(
args
.
WebSession
.
Response
.
HttpVersion
,
args
.
WebSession
.
Response
.
ResponseStatusCode
,
...
...
@@ -68,19 +68,20 @@ namespace Titanium.Web.Proxy
args
.
WebSession
.
Response
.
ResponseBody
=
await
GetCompressedResponseBody
(
contentEncoding
,
args
.
WebSession
.
Response
.
ResponseBody
).
ConfigureAwait
(
false
);
}
await
WriteResponseHeaders
(
args
.
Client
.
ClientStreamWriter
,
args
.
WebSession
.
Response
.
ResponseHeaders
,
args
.
WebSession
.
Response
.
ResponseBody
.
Length
,
isChunked
).
ConfigureAwait
(
false
);
await
WriteResponseBody
(
args
.
Client
.
ClientStream
,
args
.
WebSession
.
Response
.
ResponseBody
,
isChunked
).
ConfigureAwait
(
false
);
args
.
WebSession
.
Response
.
ContentLength
=
args
.
WebSession
.
Response
.
ResponseBody
.
Length
;
await
WriteResponseHeaders
(
args
.
Client
.
ClientStreamWriter
,
args
.
WebSession
.
Response
.
ResponseHeaders
).
ConfigureAwait
(
false
);
await
WriteResponseBody
(
args
.
Client
.
ClientStream
,
args
.
WebSession
.
Response
.
ResponseBody
,
isChunked
).
ConfigureAwait
(
false
);
}
else
{
WriteResponseHeaders
(
args
.
Client
.
ClientStreamWriter
,
args
.
WebSession
.
Response
.
ResponseHeaders
);
await
WriteResponseHeaders
(
args
.
Client
.
ClientStreamWriter
,
args
.
WebSession
.
Response
.
ResponseHeaders
);
if
(
args
.
WebSession
.
Response
.
IsChunked
||
args
.
WebSession
.
Response
.
ContentLength
>
0
)
await
WriteResponseBody
(
args
.
WebSession
.
ProxyClient
.
Server
StreamReader
,
args
.
Client
.
ClientStream
,
args
.
WebSession
.
Response
.
IsChunked
,
args
.
WebSession
.
Response
.
ContentLength
).
ConfigureAwait
(
false
);
if
(
args
.
WebSession
.
Response
.
IsChunked
||
args
.
WebSession
.
Response
.
ContentLength
>
0
||
args
.
WebSession
.
Response
.
HttpVersion
.
ToLower
().
Trim
()
==
"http/1.0"
)
await
WriteResponseBody
(
args
.
WebSession
.
ServerConnection
.
StreamReader
,
args
.
Client
.
ClientStream
,
args
.
WebSession
.
Response
.
IsChunked
,
args
.
WebSession
.
Response
.
ContentLength
).
ConfigureAwait
(
false
);
}
a
rgs
.
Client
.
ClientStream
.
Flush
();
a
wait
args
.
Client
.
ClientStream
.
FlushAsync
();
}
catch
...
...
@@ -104,10 +105,10 @@ namespace Titanium.Web.Proxy
private
static
void
WriteResponseStatus
(
string
version
,
string
code
,
string
description
,
StreamWriter
responseWriter
)
{
responseWriter
.
WriteLine
(
string
.
Format
(
"{0} {1} {2}"
,
version
,
code
,
description
));
responseWriter
.
WriteLine
Async
(
string
.
Format
(
"{0} {1} {2}"
,
version
,
code
,
description
));
}
private
static
void
WriteResponseHeaders
(
StreamWriter
responseWriter
,
List
<
HttpHeader
>
headers
)
private
static
async
Task
WriteResponseHeaders
(
StreamWriter
responseWriter
,
List
<
HttpHeader
>
headers
)
{
if
(
headers
!=
null
)
{
...
...
@@ -115,12 +116,12 @@ namespace Titanium.Web.Proxy
foreach
(
var
header
in
headers
)
{
responseWriter
.
WriteLine
(
header
.
ToString
());
await
responseWriter
.
WriteLineAsync
(
header
.
ToString
());
}
}
responseWriter
.
WriteLine
();
responseWriter
.
Flush
();
await
responseWriter
.
WriteLineAsync
();
await
responseWriter
.
FlushAsync
();
}
private
static
void
FixResponseProxyHeaders
(
List
<
HttpHeader
>
headers
)
{
...
...
@@ -141,34 +142,6 @@ namespace Titanium.Web.Proxy
headers
.
RemoveAll
(
x
=>
x
.
Name
.
ToLower
()
==
"proxy-connection"
);
}
private
static
async
Task
WriteResponseHeaders
(
StreamWriter
responseWriter
,
List
<
HttpHeader
>
headers
,
int
length
,
bool
isChunked
)
{
FixResponseProxyHeaders
(
headers
);
if
(!
isChunked
)
{
if
(
headers
.
Any
(
x
=>
x
.
Name
.
ToLower
()
==
"content-length"
)
==
false
)
{
headers
.
Add
(
new
HttpHeader
(
"Content-Length"
,
length
.
ToString
()));
}
}
if
(
headers
!=
null
)
{
foreach
(
var
header
in
headers
)
{
if
(!
isChunked
&&
header
.
Name
.
ToLower
()
==
"content-length"
)
header
.
Value
=
length
.
ToString
();
await
responseWriter
.
WriteLineAsync
(
header
.
ToString
()).
ConfigureAwait
(
false
);
}
}
await
responseWriter
.
WriteLineAsync
().
ConfigureAwait
(
false
);
await
responseWriter
.
FlushAsync
().
ConfigureAwait
(
false
);
}
private
static
async
Task
WriteResponseBody
(
Stream
clientStream
,
byte
[]
data
,
bool
isChunked
)
{
if
(!
isChunked
)
...
...
@@ -183,6 +156,10 @@ namespace Titanium.Web.Proxy
{
if
(!
isChunked
)
{
//http 1.0
if
(
ContentLength
==
-
1
)
ContentLength
=
long
.
MaxValue
;
int
bytesToRead
=
Constants
.
BUFFER_SIZE
;
if
(
ContentLength
<
Constants
.
BUFFER_SIZE
)
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
View file @
34a68075
...
...
@@ -61,7 +61,7 @@
<Compile
Include=
"Decompression\IDecompression.cs"
/>
<Compile
Include=
"Decompression\ZlibDecompression.cs"
/>
<Compile
Include=
"EventArguments\CertificateValidationEventArgs.cs"
/>
<Compile
Include=
"
EventArguments
\ProxyClient.cs"
/>
<Compile
Include=
"
Network
\ProxyClient.cs"
/>
<Compile
Include=
"Exceptions\BodyNotFoundException.cs"
/>
<Compile
Include=
"Extensions\HttpWebResponseExtensions.cs"
/>
<Compile
Include=
"Extensions\HttpWebRequestExtensions.cs"
/>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment