Unverified Commit 7a44e456 authored by honfika's avatar honfika Committed by GitHub

Merge pull request #348 from justcoding121/develop

beta|develop
parents 83377610 46a0b972
...@@ -28,8 +28,7 @@ namespace Titanium.Web.Proxy.Examples.Basic.Helpers ...@@ -28,8 +28,7 @@ namespace Titanium.Web.Proxy.Examples.Basic.Helpers
var consoleHandle = GetStdHandle(STD_INPUT_HANDLE); var consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
// get current console mode // get current console mode
uint consoleMode; if (!GetConsoleMode(consoleHandle, out uint consoleMode))
if (!GetConsoleMode(consoleHandle, out consoleMode))
{ {
// ERROR: Unable to get console mode. // ERROR: Unable to get console mode.
return false; return false;
......
...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public SessionListItem SelectedSession public SessionListItem SelectedSession
{ {
get { return selectedSession; } get => selectedSession;
set set
{ {
if (value != selectedSession) if (value != selectedSession)
...@@ -44,8 +44,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -44,8 +44,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public int ClientConnectionCount public int ClientConnectionCount
{ {
get { return (int)GetValue(ClientConnectionCountProperty); } get => (int)GetValue(ClientConnectionCountProperty);
set { SetValue(ClientConnectionCountProperty, value); } set => SetValue(ClientConnectionCountProperty, value);
} }
public static readonly DependencyProperty ServerConnectionCountProperty = DependencyProperty.Register( public static readonly DependencyProperty ServerConnectionCountProperty = DependencyProperty.Register(
...@@ -53,8 +53,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -53,8 +53,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public int ServerConnectionCount public int ServerConnectionCount
{ {
get { return (int)GetValue(ServerConnectionCountProperty); } get => (int)GetValue(ServerConnectionCountProperty);
set { SetValue(ServerConnectionCountProperty, value); } set => SetValue(ServerConnectionCountProperty, value);
} }
private readonly Dictionary<HttpWebClient, SessionListItem> sessionDictionary = new Dictionary<HttpWebClient, SessionListItem>(); private readonly Dictionary<HttpWebClient, SessionListItem> sessionDictionary = new Dictionary<HttpWebClient, SessionListItem>();
...@@ -107,8 +107,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -107,8 +107,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{ {
await Dispatcher.InvokeAsync(() => await Dispatcher.InvokeAsync(() =>
{ {
SessionListItem item; if (sessionDictionary.TryGetValue(e.WebSession, out var item))
if (sessionDictionary.TryGetValue(e.WebSession, out item))
{ {
item.Update(); item.Update();
} }
...@@ -135,8 +134,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -135,8 +134,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
SessionListItem item = null; SessionListItem item = null;
await Dispatcher.InvokeAsync(() => await Dispatcher.InvokeAsync(() =>
{ {
SessionListItem item2; if (sessionDictionary.TryGetValue(e.WebSession, out var item2))
if (sessionDictionary.TryGetValue(e.WebSession, out item2))
{ {
item2.Update(); item2.Update();
item = item2; item = item2;
...@@ -177,8 +175,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -177,8 +175,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
e.DataReceived += (sender, args) => e.DataReceived += (sender, args) =>
{ {
var session = (SessionEventArgs)sender; var session = (SessionEventArgs)sender;
SessionListItem li; if (sessionDictionary.TryGetValue(session.WebSession, out var li))
if (sessionDictionary.TryGetValue(session.WebSession, out li))
{ {
li.ReceivedDataCount += args.Count; li.ReceivedDataCount += args.Count;
} }
...@@ -187,8 +184,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -187,8 +184,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
e.DataSent += (sender, args) => e.DataSent += (sender, args) =>
{ {
var session = (SessionEventArgs)sender; var session = (SessionEventArgs)sender;
SessionListItem li; if (sessionDictionary.TryGetValue(session.WebSession, out var li))
if (sessionDictionary.TryGetValue(session.WebSession, out li))
{ {
li.SentDataCount += args.Count; li.SentDataCount += args.Count;
} }
......
...@@ -26,50 +26,50 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -26,50 +26,50 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public string StatusCode public string StatusCode
{ {
get { return statusCode; } get => statusCode;
set { SetField(ref statusCode, value);} set => SetField(ref statusCode, value);
} }
public string Protocol public string Protocol
{ {
get { return protocol; } get => protocol;
set { SetField(ref protocol, value); } set => SetField(ref protocol, value);
} }
public string Host public string Host
{ {
get { return host; } get => host;
set { SetField(ref host, value); } set => SetField(ref host, value);
} }
public string Url public string Url
{ {
get { return url; } get => url;
set { SetField(ref url, value); } set => SetField(ref url, value);
} }
public long BodySize public long BodySize
{ {
get { return bodySize; } get => bodySize;
set { SetField(ref bodySize, value); } set => SetField(ref bodySize, value);
} }
public string Process public string Process
{ {
get { return process; } get => process;
set { SetField(ref process, value); } set => SetField(ref process, value);
} }
public long ReceivedDataCount public long ReceivedDataCount
{ {
get { return receivedDataCount; } get => receivedDataCount;
set { SetField(ref receivedDataCount, value); } set => SetField(ref receivedDataCount, value);
} }
public long SentDataCount public long SentDataCount
{ {
get { return sentDataCount; } get => sentDataCount;
set { SetField(ref sentDataCount, value); } set => SetField(ref sentDataCount, value);
} }
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
......
...@@ -51,8 +51,8 @@ ...@@ -51,8 +51,8 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="StreamExtended, Version=1.0.81.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL"> <Reference Include="StreamExtended, Version=1.0.110.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.81\lib\net45\StreamExtended.dll</HintPath> <HintPath>..\..\packages\StreamExtended.1.0.110-beta\lib\net45\StreamExtended.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="StreamExtended" version="1.0.81" targetFramework="net45" /> <package id="StreamExtended" version="1.0.110-beta" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
using System;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.EventArguments
{
public class MultipartRequestPartSentEventArgs : EventArgs
{
public string Boundary { get; }
public HeaderCollection Headers { get; }
public MultipartRequestPartSentEventArgs(string boundary, HeaderCollection headers)
{
Boundary = boundary;
Headers = headers;
}
}
}
\ No newline at end of file
...@@ -4,7 +4,6 @@ using System.IO; ...@@ -4,7 +4,6 @@ using System.IO;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Decompression; using Titanium.Web.Proxy.Decompression;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
...@@ -42,6 +41,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -42,6 +41,8 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
internal ProxyClient ProxyClient { get; set; } internal ProxyClient ProxyClient { get; set; }
internal bool HasMulipartEventSubscribers => MultipartRequestPartSent != null;
/// <summary> /// <summary>
/// Returns a unique Id for this request/response session /// Returns a unique Id for this request/response session
/// same as RequestId of WebSession /// same as RequestId of WebSession
...@@ -53,7 +54,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -53,7 +54,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public bool ReRequest public bool ReRequest
{ {
get { return reRequest; } get => reRequest;
set set
{ {
if (WebSession.Response.StatusCode == 0) if (WebSession.Response.StatusCode == 0)
...@@ -90,6 +91,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -90,6 +91,11 @@ namespace Titanium.Web.Proxy.EventArguments
public event EventHandler<DataEventArgs> DataReceived; public event EventHandler<DataEventArgs> DataReceived;
/// <summary>
/// Occurs when multipart request part sent.
/// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent;
public ProxyEndPoint LocalEndPoint; public ProxyEndPoint LocalEndPoint;
/// <summary> /// <summary>
...@@ -189,6 +195,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -189,6 +195,11 @@ namespace Titanium.Web.Proxy.EventArguments
DataReceived?.Invoke(this, new DataEventArgs(buffer, offset, count)); DataReceived?.Invoke(this, new DataEventArgs(buffer, offset, count));
} }
internal void OnMultipartRequestPartSent(string boundary, HeaderCollection headers)
{
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary, headers));
}
/// <summary> /// <summary>
/// Read response body as byte[] for current response /// Read response body as byte[] for current response
/// </summary> /// </summary>
......
using System.Net; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
......
using System.Text;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Extensions
{
/// <summary>
/// Extensions on HttpWebSession object
/// </summary>
internal static class HttpWebRequestExtensions
{
/// <summary>
/// parse the character encoding of request from request headers
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
internal static Encoding GetEncoding(this Request request)
{
return HttpHelper.GetEncodingFromContentType(request.ContentType);
}
}
}
using System.Text;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Extensions
{
internal static class HttpWebResponseExtensions
{
/// <summary>
/// Gets the character encoding of response from response headers
/// </summary>
/// <param name="response"></param>
/// <returns></returns>
internal static Encoding GetResponseCharacterEncoding(this Response response)
{
return HttpHelper.GetEncodingFromContentType(response.ContentType);
}
}
}
using StreamExtended.Network; using System;
using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
...@@ -17,6 +17,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -17,6 +17,7 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="input"></param> /// <param name="input"></param>
/// <param name="output"></param> /// <param name="output"></param>
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="bufferSize"></param>
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, int bufferSize) internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, int bufferSize)
{ {
byte[] buffer = new byte[bufferSize]; byte[] buffer = new byte[bufferSize];
......
using System.Net.Sockets; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Helpers;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
......
...@@ -8,7 +8,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -8,7 +8,12 @@ namespace Titanium.Web.Proxy.Helpers
{ {
private static readonly Encoding defaultEncoding = Encoding.GetEncoding("ISO-8859-1"); private static readonly Encoding defaultEncoding = Encoding.GetEncoding("ISO-8859-1");
public static Encoding GetEncodingFromContentType(string contentType) /// <summary>
/// Gets the character encoding of request/response from content-type header
/// </summary>
/// <param name="contentType"></param>
/// <returns></returns>
internal static Encoding GetEncodingFromContentType(string contentType)
{ {
try try
{ {
...@@ -22,10 +27,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -22,10 +27,10 @@ namespace Titanium.Web.Proxy.Helpers
var parameters = contentType.Split(ProxyConstants.SemiColonSplit); var parameters = contentType.Split(ProxyConstants.SemiColonSplit);
foreach (string parameter in parameters) foreach (string parameter in parameters)
{ {
var encodingSplit = parameter.Split(ProxyConstants.EqualSplit, 2); var split = parameter.Split(ProxyConstants.EqualSplit, 2);
if (encodingSplit.Length == 2 && encodingSplit[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase)) if (split.Length == 2 && split[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase))
{ {
string value = encodingSplit[1]; string value = split[1];
if (value.Equals("x-user-defined", StringComparison.OrdinalIgnoreCase)) if (value.Equals("x-user-defined", StringComparison.OrdinalIgnoreCase))
{ {
//todo: what is this? //todo: what is this?
...@@ -51,6 +56,32 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -51,6 +56,32 @@ namespace Titanium.Web.Proxy.Helpers
return defaultEncoding; return defaultEncoding;
} }
internal static string GetBoundaryFromContentType(string contentType)
{
if (contentType != null)
{
//extract the boundary
var parameters = contentType.Split(ProxyConstants.SemiColonSplit);
foreach (string parameter in parameters)
{
var split = parameter.Split(ProxyConstants.EqualSplit, 2);
if (split.Length == 2 && split[0].Trim().Equals("boundary", StringComparison.CurrentCultureIgnoreCase))
{
string value = split[1];
if (value.Length > 2 && value[0] == '"' && value[value.Length - 1] == '"')
{
value = value.Substring(1, value.Length - 2);
}
return value;
}
}
}
//return null if not specified
return null;
}
/// <summary> /// <summary>
/// Tries to get root domain from a given hostname /// Tries to get root domain from a given hostname
/// Adapted from below answer /// Adapted from below answer
......
using StreamExtended.Network; using System;
using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
......
...@@ -54,9 +54,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -54,9 +54,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
localhost = Dns.GetHostEntry(Dns.GetHostName()); localhost = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress; if (IPAddress.TryParse(hostName, out var ipAddress))
if (IPAddress.TryParse(hostName, out ipAddress))
isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress)); isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress));
if (!isLocalhost) if (!isLocalhost)
......
...@@ -81,8 +81,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -81,8 +81,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public ExternalProxy GetProxy(Uri destination) public ExternalProxy GetProxy(Uri destination)
{ {
IList<string> proxies; if (GetAutoProxies(destination, out var proxies))
if (GetAutoProxies(destination, out proxies))
{ {
if (proxies == null) if (proxies == null)
{ {
......
...@@ -227,8 +227,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -227,8 +227,7 @@ namespace Titanium.Web.Proxy.Http
internal string GetHeaderValueOrNull(string headerName) internal string GetHeaderValueOrNull(string headerName)
{ {
HttpHeader header; if (Headers.TryGetValue(headerName, out var header))
if (Headers.TryGetValue(headerName, out header))
{ {
return header.Value; return header.Value;
} }
...@@ -238,8 +237,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -238,8 +237,7 @@ namespace Titanium.Web.Proxy.Http
internal string SetOrAddHeaderValue(string headerName, string value) internal string SetOrAddHeaderValue(string headerName, string value)
{ {
HttpHeader header; if (Headers.TryGetValue(headerName, out var header))
if (Headers.TryGetValue(headerName, out header))
{ {
header.Value = value; header.Value = value;
} }
......
using StreamExtended.Network; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -25,7 +26,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -25,7 +26,7 @@ namespace Titanium.Web.Proxy.Http
{ {
nonUniqueResponseHeaders[newHeader.Name].Add(newHeader); nonUniqueResponseHeaders[newHeader.Name].Add(newHeader);
} }
//if header is alread in unique header collection then move both to non-unique collection //if header is already in unique header collection then move both to non-unique collection
else if (headers.ContainsKey(newHeader.Name)) else if (headers.ContainsKey(newHeader.Name))
{ {
var existing = headers[newHeader.Name]; var existing = headers[newHeader.Name];
...@@ -46,5 +47,17 @@ namespace Titanium.Web.Proxy.Http ...@@ -46,5 +47,17 @@ namespace Titanium.Web.Proxy.Http
} }
} }
} }
/// <summary>
/// Increase size of buffer and copy existing content to new buffer
/// </summary>
/// <param name="buffer"></param>
/// <param name="size"></param>
private static void ResizeBuffer(ref byte[] buffer, long size)
{
var newBuffer = new byte[size];
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
buffer = newBuffer;
}
} }
} }
...@@ -136,10 +136,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -136,10 +136,7 @@ namespace Titanium.Web.Proxy.Http
{ {
string httpStatus = await ServerConnection.StreamReader.ReadLineAsync(); string httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
Version version; Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode, out string responseStatusDescription);
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 == (int)HttpStatusCode.Continue if (responseStatusCode == (int)HttpStatusCode.Continue
...@@ -180,10 +177,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -180,10 +177,7 @@ namespace Titanium.Web.Proxy.Http
httpStatus = await ServerConnection.StreamReader.ReadLineAsync(); httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
} }
Version version; Response.ParseResponseLine(httpStatus, out var version, out int statusCode, out string statusDescription);
int statusCode;
string statusDescription;
Response.ParseResponseLine(httpStatus, out version, out statusCode, out statusDescription);
Response.HttpVersion = version; Response.HttpVersion = version;
Response.StatusCode = statusCode; Response.StatusCode = statusCode;
......
...@@ -3,6 +3,7 @@ using System.ComponentModel; ...@@ -3,6 +3,7 @@ using System.ComponentModel;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -66,14 +67,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -66,14 +67,8 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public string Host public string Host
{ {
get get => Headers.GetHeaderValueOrNull("host");
{ set => Headers.SetOrAddHeaderValue("host", value);
return Headers.GetHeaderValueOrNull("host");
}
set
{
Headers.SetOrAddHeaderValue("host", value);
}
} }
/// <summary> /// <summary>
...@@ -95,8 +90,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -95,8 +90,7 @@ namespace Titanium.Web.Proxy.Http
return -1; return -1;
} }
long contentLen; long.TryParse(headerValue, out long contentLen);
long.TryParse(headerValue, out contentLen);
if (contentLen >= 0) if (contentLen >= 0)
{ {
return contentLen; return contentLen;
...@@ -123,14 +117,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -123,14 +117,8 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public string ContentType public string ContentType
{ {
get get => Headers.GetHeaderValueOrNull("content-type");
{ set => Headers.SetOrAddHeaderValue("content-type", value);
return Headers.GetHeaderValueOrNull("content-type");
}
set
{
Headers.SetOrAddHeaderValue("content-type", value);
}
} }
/// <summary> /// <summary>
...@@ -169,6 +157,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -169,6 +157,8 @@ namespace Titanium.Web.Proxy.Http
} }
} }
public bool IsMultipartFormData => ContentType?.StartsWith("multipart/form-data") == true;
/// <summary> /// <summary>
/// Request Url /// Request Url
/// </summary> /// </summary>
...@@ -177,7 +167,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -177,7 +167,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Encoding for this request /// Encoding for this request
/// </summary> /// </summary>
public Encoding Encoding => this.GetEncoding(); public Encoding Encoding => HttpHelper.GetEncodingFromContentType(ContentType);
/// <summary> /// <summary>
/// Terminates the underlying Tcp Connection to client after current request /// Terminates the underlying Tcp Connection to client after current request
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.ComponentModel; using System.ComponentModel;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -36,7 +37,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -36,7 +37,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Encoding used in response /// Encoding used in response
/// </summary> /// </summary>
public Encoding Encoding => this.GetResponseCharacterEncoding(); public Encoding Encoding => HttpHelper.GetEncodingFromContentType(ContentType);
/// <summary> /// <summary>
/// Content encoding for this response /// Content encoding for this response
...@@ -118,8 +119,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -118,8 +119,7 @@ namespace Titanium.Web.Proxy.Http
return -1; return -1;
} }
long contentLen; long.TryParse(headerValue, out long contentLen);
long.TryParse(headerValue, out contentLen);
if (contentLen >= 0) if (contentLen >= 0)
{ {
return contentLen; return contentLen;
......
...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public string UserName public string UserName
{ {
get { return UseDefaultCredentials ? defaultCredentials.Value.UserName : userName; } get => UseDefaultCredentials ? defaultCredentials.Value.UserName : userName;
set set
{ {
userName = value; userName = value;
...@@ -45,7 +45,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -45,7 +45,7 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public string Password public string Password
{ {
get { return UseDefaultCredentials ? defaultCredentials.Value.Password : password; } get => UseDefaultCredentials ? defaultCredentials.Value.Password : password;
set set
{ {
password = value; password = value;
......
using System; using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1;
......
...@@ -7,7 +7,6 @@ using System.Linq; ...@@ -7,7 +7,6 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network.Certificate; using Titanium.Web.Proxy.Network.Certificate;
...@@ -36,7 +35,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -36,7 +35,7 @@ namespace Titanium.Web.Proxy.Network
{ {
internal CertificateEngine Engine internal CertificateEngine Engine
{ {
get { return engine; } get => engine;
set set
{ {
//For Mono (or Non-Windows) only Bouncy Castle is supported //For Mono (or Non-Windows) only Bouncy Castle is supported
...@@ -83,7 +82,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -83,7 +82,7 @@ namespace Titanium.Web.Proxy.Network
internal string Issuer internal string Issuer
{ {
get { return issuer ?? defaultRootCertificateIssuer; } get => issuer ?? defaultRootCertificateIssuer;
set set
{ {
issuer = value; issuer = value;
...@@ -93,7 +92,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -93,7 +92,7 @@ namespace Titanium.Web.Proxy.Network
internal string RootCertificateName internal string RootCertificateName
{ {
get { return rootCertificateName ?? defaultRootRootCertificateName; } get => rootCertificateName ?? defaultRootRootCertificateName;
set set
{ {
rootCertificateName = value; rootCertificateName = value;
...@@ -103,7 +102,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -103,7 +102,7 @@ namespace Titanium.Web.Proxy.Network
internal X509Certificate2 RootCertificate internal X509Certificate2 RootCertificate
{ {
get { return rootCertificate; } get => rootCertificate;
set set
{ {
ClearRootCertificate(); ClearRootCertificate();
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
using System; using System;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Network.Tcp namespace Titanium.Web.Proxy.Network.Tcp
......
...@@ -5,7 +5,6 @@ using System.Collections.Concurrent; ...@@ -5,7 +5,6 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Principal; using System.Security.Principal;
using System.Threading.Tasks; using System.Threading.Tasks;
......
using StreamExtended.Network; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
...@@ -8,8 +7,8 @@ using System.Security.Authentication; ...@@ -8,8 +7,8 @@ using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Helpers.WinHttp; using Titanium.Web.Proxy.Helpers.WinHttp;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -84,8 +83,8 @@ namespace Titanium.Web.Proxy ...@@ -84,8 +83,8 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public X509Certificate2 RootCertificate public X509Certificate2 RootCertificate
{ {
get { return CertificateManager.RootCertificate; } get => CertificateManager.RootCertificate;
set { CertificateManager.RootCertificate = value; } set => CertificateManager.RootCertificate = value;
} }
/// <summary> /// <summary>
...@@ -94,8 +93,8 @@ namespace Titanium.Web.Proxy ...@@ -94,8 +93,8 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public string RootCertificateIssuerName public string RootCertificateIssuerName
{ {
get { return CertificateManager.Issuer; } get => CertificateManager.Issuer;
set { CertificateManager.Issuer = value; } set => CertificateManager.Issuer = value;
} }
/// <summary> /// <summary>
...@@ -107,8 +106,8 @@ namespace Titanium.Web.Proxy ...@@ -107,8 +106,8 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public string RootCertificateName public string RootCertificateName
{ {
get { return CertificateManager.RootCertificateName; } get => CertificateManager.RootCertificateName;
set { CertificateManager.RootCertificateName = value; } set => CertificateManager.RootCertificateName = value;
} }
/// <summary> /// <summary>
...@@ -118,7 +117,7 @@ namespace Titanium.Web.Proxy ...@@ -118,7 +117,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public bool TrustRootCertificate public bool TrustRootCertificate
{ {
get { return trustRootCertificate; } get => trustRootCertificate;
set set
{ {
trustRootCertificate = value; trustRootCertificate = value;
...@@ -136,8 +135,8 @@ namespace Titanium.Web.Proxy ...@@ -136,8 +135,8 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public CertificateEngine CertificateEngine public CertificateEngine CertificateEngine
{ {
get { return CertificateManager.Engine; } get => CertificateManager.Engine;
set { CertificateManager.Engine = value; } set => CertificateManager.Engine = value;
} }
/// <summary> /// <summary>
...@@ -242,8 +241,8 @@ namespace Titanium.Web.Proxy ...@@ -242,8 +241,8 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public Action<Exception> ExceptionFunc public Action<Exception> ExceptionFunc
{ {
get { return exceptionFunc ?? defaultExceptionFunc.Value; } get => exceptionFunc ?? defaultExceptionFunc.Value;
set { exceptionFunc = value; } set => exceptionFunc = value;
} }
/// <summary> /// <summary>
......
using StreamExtended; using System;
using StreamExtended.Network;
using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
...@@ -8,12 +6,16 @@ using System.Net.Security; ...@@ -8,12 +6,16 @@ using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication; using System.Security.Authentication;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended;
using StreamExtended.Helpers;
using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
...@@ -53,10 +55,7 @@ namespace Titanium.Web.Proxy ...@@ -53,10 +55,7 @@ namespace Titanium.Web.Proxy
return; return;
} }
string httpMethod; Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, out var version);
string httpUrl;
Version version;
Request.ParseRequestLine(httpCmd, out httpMethod, out httpUrl, out version);
httpRemoteUri = httpMethod == "CONNECT" ? new Uri("http://" + httpUrl) : new Uri(httpUrl); httpRemoteUri = httpMethod == "CONNECT" ? new Uri("http://" + httpUrl) : new Uri(httpUrl);
...@@ -236,7 +235,12 @@ namespace Titanium.Web.Proxy ...@@ -236,7 +235,12 @@ namespace Titanium.Web.Proxy
var sslStream = new SslStream(clientStream); var sslStream = new SslStream(clientStream);
clientStream = new CustomBufferedStream(sslStream, BufferSize); clientStream = new CustomBufferedStream(sslStream, BufferSize);
string sniHostName = clientSslHelloInfo.Extensions?.FirstOrDefault(x => x.Name == "server_name")?.Data; string sniHostName = null;
if (clientSslHelloInfo.Extensions != null && clientSslHelloInfo.Extensions.TryGetValue("server_name", out var serverNameExtension))
{
sniHostName = serverNameExtension.Data;
}
string certName = HttpHelper.GetWildCardDomainName(sniHostName ?? endPoint.GenericCertificateName); string certName = HttpHelper.GetWildCardDomainName(sniHostName ?? endPoint.GenericCertificateName);
var certificate = CertificateManager.CreateCertificate(certName, false); var certificate = CertificateManager.CreateCertificate(certName, false);
...@@ -306,10 +310,7 @@ namespace Titanium.Web.Proxy ...@@ -306,10 +310,7 @@ namespace Titanium.Web.Proxy
try try
{ {
string httpMethod; Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, out var version);
string httpUrl;
Version version;
Request.ParseRequestLine(httpCmd, out httpMethod, out httpUrl, out version);
//Read the request headers in to unique and non-unique header collections //Read the request headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.Headers); await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.Headers);
...@@ -390,10 +391,7 @@ namespace Titanium.Web.Proxy ...@@ -390,10 +391,7 @@ namespace Titanium.Web.Proxy
await connection.Stream.WriteAsync(requestBytes, 0, requestBytes.Length); await connection.Stream.WriteAsync(requestBytes, 0, requestBytes.Length);
string httpStatus = await connection.StreamReader.ReadLineAsync(); string httpStatus = await connection.StreamReader.ReadLineAsync();
Version responseVersion; Response.ParseResponseLine(httpStatus, out var responseVersion, out int responseStatusCode, out string responseStatusDescription);
int responseStatusCode;
string responseStatusDescription;
Response.ParseResponseLine(httpStatus, out responseVersion, out responseStatusCode, out responseStatusDescription);
args.WebSession.Response.HttpVersion = responseVersion; args.WebSession.Response.HttpVersion = responseVersion;
args.WebSession.Response.StatusCode = responseStatusCode; args.WebSession.Response.StatusCode = responseStatusCode;
args.WebSession.Response.StatusDescription = responseStatusDescription; args.WebSession.Response.StatusDescription = responseStatusDescription;
...@@ -466,11 +464,12 @@ namespace Titanium.Web.Proxy ...@@ -466,11 +464,12 @@ namespace Titanium.Web.Proxy
try try
{ {
args.WebSession.Request.RequestLocked = true; var request = args.WebSession.Request;
request.RequestLocked = true;
//if expect continue is enabled then send the headers first //if expect continue is enabled then send the headers first
//and see if server would return 100 conitinue //and see if server would return 100 conitinue
if (args.WebSession.Request.ExpectContinue) if (request.ExpectContinue)
{ {
args.WebSession.SetConnection(connection); args.WebSession.SetConnection(connection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour); await args.WebSession.SendRequest(Enable100ContinueBehaviour);
...@@ -479,12 +478,12 @@ namespace Titanium.Web.Proxy ...@@ -479,12 +478,12 @@ namespace Titanium.Web.Proxy
//If 100 continue was the response inform that to the client //If 100 continue was the response inform that to the client
if (Enable100ContinueBehaviour) if (Enable100ContinueBehaviour)
{ {
if (args.WebSession.Request.Is100Continue) if (request.Is100Continue)
{ {
await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.Continue, "Continue"); await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.Continue, "Continue");
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
else if (args.WebSession.Request.ExpectationFailed) else if (request.ExpectationFailed)
{ {
await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed"); await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed");
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
...@@ -492,38 +491,37 @@ namespace Titanium.Web.Proxy ...@@ -492,38 +491,37 @@ namespace Titanium.Web.Proxy
} }
//If expect continue is not enabled then set the connectio and send request headers //If expect continue is not enabled then set the connectio and send request headers
if (!args.WebSession.Request.ExpectContinue) if (!request.ExpectContinue)
{ {
args.WebSession.SetConnection(connection); args.WebSession.SetConnection(connection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour); await args.WebSession.SendRequest(Enable100ContinueBehaviour);
} }
//check if content-length is > 0 //check if content-length is > 0
if (args.WebSession.Request.ContentLength > 0) if (request.ContentLength > 0)
{ {
//If request was modified by user //If request was modified by user
if (args.WebSession.Request.IsBodyRead) if (request.IsBodyRead)
{ {
if (args.WebSession.Request.ContentEncoding != null) if (request.ContentEncoding != null)
{ {
args.WebSession.Request.Body = request.Body = await GetCompressedResponseBody(request.ContentEncoding, request.Body);
await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.Body);
} }
var body = args.WebSession.Request.Body; var body = request.Body;
//chunked send is not supported as of now //chunked send is not supported as of now
args.WebSession.Request.ContentLength = body.Length; request.ContentLength = body.Length;
var newStream = args.WebSession.ServerConnection.Stream; var newStream = args.WebSession.ServerConnection.Stream;
await newStream.WriteAsync(body, 0, body.Length); await newStream.WriteAsync(body, 0, body.Length);
} }
else else
{ {
if (!args.WebSession.Request.ExpectationFailed) if (!request.ExpectationFailed)
{ {
//If its a post/put/patch request, then read the client html body and send it to server //If its a post/put/patch request, then read the client html body and send it to server
if (args.WebSession.Request.HasBody) if (request.HasBody)
{ {
await SendClientRequestBody(args); await SendClientRequestBody(args);
} }
...@@ -532,7 +530,7 @@ namespace Titanium.Web.Proxy ...@@ -532,7 +530,7 @@ namespace Titanium.Web.Proxy
} }
//If not expectation failed response was returned by server then parse response //If not expectation failed response was returned by server then parse response
if (!args.WebSession.Request.ExpectationFailed) if (!request.ExpectationFailed)
{ {
disposed = await HandleHttpSessionResponse(args); disposed = await HandleHttpSessionResponse(args);
...@@ -632,9 +630,36 @@ namespace Titanium.Web.Proxy ...@@ -632,9 +630,36 @@ namespace Titanium.Web.Proxy
//send the request body bytes to server //send the request body bytes to server
if (args.WebSession.Request.ContentLength > 0) if (args.WebSession.Request.ContentLength > 0)
{
var request = args.WebSession.Request;
if (args.HasMulipartEventSubscribers && request.IsMultipartFormData)
{
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
long bytesToSend = args.WebSession.Request.ContentLength;
while (bytesToSend > 0)
{
long read = await SendUntilBoundaryAsync(args.ProxyClient.ClientStream, postStream, bytesToSend, boundary);
if (read == 0)
{
break;
}
bytesToSend -= read;
if (bytesToSend > 0)
{
var headers = new HeaderCollection();
var stream = new CustomBufferedPeekStream(args.ProxyClient.ClientStream);
await HeaderParser.ReadHeaders(new CustomBinaryReader(stream, BufferSize), headers);
args.OnMultipartRequestPartSent(boundary, headers);
}
}
}
else
{ {
await args.ProxyClient.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength); await args.ProxyClient.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength);
} }
}
//Need to revist, find any potential bugs //Need to revist, find any potential bugs
//send the request body bytes to server in chunks //send the request body bytes to server in chunks
else if (args.WebSession.Request.IsChunked) else if (args.WebSession.Request.IsChunked)
...@@ -642,5 +667,68 @@ namespace Titanium.Web.Proxy ...@@ -642,5 +667,68 @@ namespace Titanium.Web.Proxy
await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(postStream); await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(postStream);
} }
} }
/// <summary>
/// Read a line from the byte stream
/// </summary>
/// <returns></returns>
public async Task<long> SendUntilBoundaryAsync(CustomBufferedStream inputStream, CustomBufferedStream outputStream, long totalBytesToRead, string boundary)
{
int bufferDataLength = 0;
// try to use the thread static buffer
var buffer = BufferPool.GetBuffer(BufferSize);
int boundaryLength = boundary.Length + 4;
long bytesRead = 0;
while (bytesRead < totalBytesToRead && (inputStream.DataAvailable || await inputStream.FillBufferAsync()))
{
byte newChar = inputStream.ReadByteFromBuffer();
buffer[bufferDataLength] = newChar;
bufferDataLength++;
bytesRead++;
if (bufferDataLength >= boundaryLength)
{
int startIdx = bufferDataLength - boundaryLength;
if (buffer[startIdx] == '-' && buffer[startIdx + 1] == '-'
&& buffer[bufferDataLength - 2] == '\r' && buffer[bufferDataLength - 1] == '\n')
{
startIdx += 2;
bool ok = true;
for (int i = 0; i < boundary.Length; i++)
{
if (buffer[startIdx + i] != boundary[i])
{
ok = false;
break;
}
}
if (ok)
{
break;
}
}
}
if (bufferDataLength == buffer.Length)
{
//boundary is not longer than 70 bytes accounrding to the specification, so keeping the last 100 (minimum 74) bytes is enough
const int bytesToKeep = 100;
await outputStream.WriteAsync(buffer, 0, buffer.Length - bytesToKeep);
Buffer.BlockCopy(buffer, buffer.Length - bytesToKeep, buffer, 0, bytesToKeep);
bufferDataLength = bytesToKeep;
}
}
if (bytesRead > 0)
{
await outputStream.WriteAsync(buffer, 0, bufferDataLength);
}
return bytesRead;
}
} }
} }
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.8.1.3" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.1.3" />
<PackageReference Include="StreamExtended" Version="1.0.81" /> <PackageReference Include="StreamExtended" Version="1.0.110-beta" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright> <copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright>
<tags></tags> <tags></tags>
<dependencies> <dependencies>
<dependency id="StreamExtended" version="1.0.81" /> <dependency id="StreamExtended" version="1.0.110-beta" />
<dependency id="Portable.BouncyCastle" version="1.8.1.3" /> <dependency id="Portable.BouncyCastle" version="1.8.1.3" />
</dependencies> </dependencies>
</metadata> </metadata>
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Portable.BouncyCastle" version="1.8.1.3" targetFramework="net45" /> <package id="Portable.BouncyCastle" version="1.8.1.3" targetFramework="net45" />
<package id="StreamExtended" version="1.0.81" targetFramework="net45" /> <package id="StreamExtended" version="1.0.110-beta" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
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