Unverified Commit 6a186156 authored by honfika's avatar honfika Committed by GitHub

Merge pull request #587 from honfika/master

allow to modify HTTP/2 header
parents 29ffc5b9 0c88f3f1
...@@ -152,6 +152,11 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -152,6 +152,11 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private async Task ProxyServer_BeforeRequest(object sender, SessionEventArgs e) private async Task ProxyServer_BeforeRequest(object sender, SessionEventArgs e)
{ {
if (e.HttpClient.ConnectRequest?.TunnelType != TunnelType.Http2)
{
return;
}
SessionListItem item = null; SessionListItem item = null;
await Dispatcher.InvokeAsync(() => { item = addSession(e); }); await Dispatcher.InvokeAsync(() => { item = addSession(e); });
...@@ -159,6 +164,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -159,6 +164,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
//{ //{
//} //}
//if (!e.HttpClient.Request.RequestUri.ToString().Contains("/mail/u/"))
// return;
if (e.HttpClient.Request.HasBody) if (e.HttpClient.Request.HasBody)
{ {
e.HttpClient.Request.KeepBody = true; e.HttpClient.Request.KeepBody = true;
...@@ -168,6 +176,11 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -168,6 +176,11 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private async Task ProxyServer_BeforeResponse(object sender, SessionEventArgs e) private async Task ProxyServer_BeforeResponse(object sender, SessionEventArgs e)
{ {
if (e.HttpClient.ConnectRequest?.TunnelType != TunnelType.Http2)
{
return;
}
SessionListItem item = null; SessionListItem item = null;
await Dispatcher.InvokeAsync(() => await Dispatcher.InvokeAsync(() =>
{ {
...@@ -177,9 +190,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -177,9 +190,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
} }
}); });
//if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Http2) //e.HttpClient.Response.Headers.AddHeader("X-Titanium-Header", "HTTP/2 works");
//{
//} //e.SetResponseBody(Encoding.ASCII.GetBytes("TITANIUMMMM!!!!"));
if (item != null) if (item != null)
{ {
......
...@@ -28,6 +28,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -28,6 +28,11 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
private bool reRequest; private bool reRequest;
/// <summary>
/// Is this session a HTTP/2 promise?
/// </summary>
public bool IsPromise { get; internal set; }
/// <summary> /// <summary>
/// Constructor to initialize the proxy /// Constructor to initialize the proxy
/// </summary> /// </summary>
...@@ -91,6 +96,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -91,6 +96,9 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (request.HttpVersion == HttpHeader.Version20) if (request.HttpVersion == HttpHeader.Version20)
{ {
// do not send to the remote endpoint
request.Http2IgnoreBodyFrames = true;
request.Http2BodyData = new MemoryStream(); request.Http2BodyData = new MemoryStream();
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
...@@ -100,6 +108,10 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -100,6 +108,10 @@ namespace Titanium.Web.Proxy.EventArguments
request.ReadHttp2BeforeHandlerTaskCompletionSource.SetResult(true); request.ReadHttp2BeforeHandlerTaskCompletionSource.SetResult(true);
await tcs.Task; await tcs.Task;
// Now set the flag to true
// So that next time we can deliver body from cache
request.IsBodyRead = true;
} }
else else
{ {
...@@ -157,6 +169,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -157,6 +169,9 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (response.HttpVersion == HttpHeader.Version20) if (response.HttpVersion == HttpHeader.Version20)
{ {
// do not send to the remote endpoint
response.Http2IgnoreBodyFrames = true;
response.Http2BodyData = new MemoryStream(); response.Http2BodyData = new MemoryStream();
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
...@@ -166,6 +181,10 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -166,6 +181,10 @@ namespace Titanium.Web.Proxy.EventArguments
response.ReadHttp2BeforeHandlerTaskCompletionSource.SetResult(true); response.ReadHttp2BeforeHandlerTaskCompletionSource.SetResult(true);
await tcs.Task; await tcs.Task;
// Now set the flag to true
// So that next time we can deliver body from cache
response.IsBodyRead = true;
} }
else else
{ {
......
...@@ -56,6 +56,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -56,6 +56,15 @@ namespace Titanium.Web.Proxy.Http
internal MemoryStream Http2BodyData; internal MemoryStream Http2BodyData;
internal bool Http2IgnoreBodyFrames;
internal Task Http2BeforeHandlerTask;
/// <summary>
/// Priority used only in HTTP/2
/// </summary>
internal long? Priority;
/// <summary> /// <summary>
/// Keeps the body data after the session is finished. /// Keeps the body data after the session is finished.
/// </summary> /// </summary>
...@@ -201,6 +210,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -201,6 +210,8 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
internal bool Locked { get; set; } internal bool Locked { get; set; }
internal bool BodyAvailable => BodyInternal != null;
internal abstract void EnsureBodyAvailable(bool throwWhenNotReadYet = true); internal abstract void EnsureBodyAvailable(bool throwWhenNotReadYet = true);
/// <summary> /// <summary>
......
...@@ -61,7 +61,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -61,7 +61,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
/// <param name="value">Value.</param> /// <param name="value">Value.</param>
/// <param name="sensitive">If set to <c>true</c> sensitive.</param> /// <param name="sensitive">If set to <c>true</c> sensitive.</param>
public void EncodeHeader(BinaryWriter output, string name, string value, bool sensitive = false) /// <param name="indexType">Index type.</param>
/// <param name="useStaticName">Use static name.</param>
public void EncodeHeader(BinaryWriter output, string name, string value, bool sensitive = false, HpackUtil.IndexType indexType = HpackUtil.IndexType.Incremental, bool useStaticName = true)
{ {
// If the header value is sensitive then it must never be indexed // If the header value is sensitive then it must never be indexed
if (sensitive) if (sensitive)
...@@ -116,10 +118,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -116,10 +118,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack
} }
else else
{ {
int nameIndex = getNameIndex(name); int nameIndex = useStaticName ? getNameIndex(name) : -1;
ensureCapacity(headerSize); ensureCapacity(headerSize);
var indexType = HpackUtil.IndexType.Incremental;
encodeLiteral(output, name, value, indexType, nameIndex); encodeLiteral(output, name, value, indexType, nameIndex);
add(name, value); add(name, value);
} }
...@@ -309,7 +310,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -309,7 +310,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int i = index(h); int i = index(h);
for (var e = headerFields[i]; e != null; e = e.Next) for (var e = headerFields[i]; e != null; e = e.Next)
{ {
if (e.Hash == h && Equals(name, e.Name) && Equals(value, e.Value)) if (e.Hash == h && name.Equals(e.Name, StringComparison.OrdinalIgnoreCase) && Equals(value, e.Value))
{ {
return e; return e;
} }
...@@ -336,7 +337,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -336,7 +337,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int index = -1; int index = -1;
for (var e = headerFields[i]; e != null; e = e.Next) for (var e = headerFields[i]; e != null; e = e.Next)
{ {
if (e.Hash == h && HpackUtil.Equals(name, e.Name)) if (e.Hash == h && name.Equals(e.Name, StringComparison.OrdinalIgnoreCase))
{ {
index = e.Index; index = e.Index;
break; break;
...@@ -513,7 +514,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -513,7 +514,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="value">Value.</param> /// <param name="value">Value.</param>
/// <param name="index">Index.</param> /// <param name="index">Index.</param>
/// <param name="next">Next.</param> /// <param name="next">Next.</param>
public HeaderEntry(int hash, string name, string value, int index, HeaderEntry next) : base(name, value) public HeaderEntry(int hash, string name, string value, int index, HeaderEntry next) : base(name, value, true)
{ {
Index = index; Index = index;
Hash = hash; Hash = hash;
......
...@@ -154,7 +154,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -154,7 +154,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
new HttpHeader("WWW-Authenticate", string.Empty) new HttpHeader("WWW-Authenticate", string.Empty)
}; };
private static readonly Dictionary<string, int> staticIndexByName = CreateMap(); private static readonly Dictionary<string, int> staticIndexByName = createMap();
/// <summary> /// <summary>
/// The number of header fields in the static table. /// The number of header fields in the static table.
...@@ -180,12 +180,12 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -180,12 +180,12 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="name">Name.</param> /// <param name="name">Name.</param>
public static int GetIndex(string name) public static int GetIndex(string name)
{ {
if (!staticIndexByName.ContainsKey(name)) if (!staticIndexByName.TryGetValue(name, out int index))
{ {
return -1; return -1;
} }
return staticIndexByName[name]; return index;
} }
/// <summary> /// <summary>
...@@ -207,7 +207,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -207,7 +207,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
while (index <= Length) while (index <= Length)
{ {
var entry = Get(index); var entry = Get(index);
if (!HpackUtil.Equals(name, entry.Name)) if (!name.Equals(entry.Name, StringComparison.OrdinalIgnoreCase))
{ {
break; break;
} }
...@@ -227,7 +227,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -227,7 +227,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// create a map of header name to index value to allow quick lookup /// create a map of header name to index value to allow quick lookup
/// </summary> /// </summary>
/// <returns>The map.</returns> /// <returns>The map.</returns>
private static Dictionary<string, int> CreateMap() private static Dictionary<string, int> createMap()
{ {
int length = staticTable.Count; int length = staticTable.Count;
var ret = new Dictionary<string, int>(length); var ret = new Dictionary<string, int>(length);
...@@ -237,7 +237,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack ...@@ -237,7 +237,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
for (int index = length; index > 0; index--) for (int index = length; index > 0; index--)
{ {
var entry = Get(index); var entry = Get(index);
string name = entry.Name; string name = entry.Name.ToLower();
ret[name] = index; ret[name] = index;
} }
......
using System;
namespace Titanium.Web.Proxy.Http2
{
[Flags]
internal enum Http2FrameFlag : byte
{
Ack = 0x01,
EndStream = 0x01,
EndHeaders = 0x04,
Padded = 0x08,
Priority = 0x20,
}
}
\ No newline at end of file
namespace Titanium.Web.Proxy.Http2
{
internal class Http2FrameHeader
{
public int Length;
public Http2FrameType Type;
public Http2FrameFlag Flags;
public int StreamId;
public byte[] Buffer;
public byte[] CopyToBuffer()
{
int length = Length;
var buf = /*new byte[9];*/Buffer;
buf[0] = (byte)((length >> 16) & 0xff);
buf[1] = (byte)((length >> 8) & 0xff);
buf[2] = (byte)(length & 0xff);
buf[3] = (byte)Type;
buf[4] = (byte)Flags;
int streamId = StreamId;
//buf[5] = (byte)((streamId >> 24) & 0xff);
//buf[6] = (byte)((streamId >> 16) & 0xff);
//buf[7] = (byte)((streamId >> 8) & 0xff);
//buf[8] = (byte)(streamId & 0xff);
return buf;
}
}
}
namespace Titanium.Web.Proxy.Http2
{
internal enum Http2FrameType : byte
{
Data = 0x00,
Headers = 0x01,
Priority = 0x02,
RstStream = 0x03,
Settings = 0x04,
PushPromise = 0x05,
Ping = 0x06,
GoAway = 0x07,
WindowUpdate = 0x08,
Continuation = 0x09,
}
}
\ No newline at end of file
This diff is collapsed.
...@@ -35,13 +35,20 @@ namespace Titanium.Web.Proxy.Models ...@@ -35,13 +35,20 @@ namespace Titanium.Web.Proxy.Models
{ {
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
{ {
throw new Exception("Name cannot be null"); throw new Exception("Name cannot be null or empty");
} }
Name = name.Trim(); Name = name.Trim();
Value = value.Trim(); Value = value.Trim();
} }
protected HttpHeader(string name, string value, bool headerEntry)
{
// special header entry created in inherited class with empty name
Name = name.Trim();
Value = value.Trim();
}
/// <summary> /// <summary>
/// Header Name. /// Header Name.
/// </summary> /// </summary>
......
...@@ -125,7 +125,6 @@ namespace Titanium.Web.Proxy ...@@ -125,7 +125,6 @@ namespace Titanium.Web.Proxy
} }
args.TimeLine["Response Sent"] = DateTime.Now; args.TimeLine["Response Sent"] = DateTime.Now;
} }
/// <summary> /// <summary>
......
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