Commit 8bbfa308 authored by Honfika's avatar Honfika

HTTP/2 small refactoring

parent 42a5ee99
...@@ -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>
......
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 = 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;
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
...@@ -15,16 +15,6 @@ using Titanium.Web.Proxy.Http2.Hpack; ...@@ -15,16 +15,6 @@ using Titanium.Web.Proxy.Http2.Hpack;
namespace Titanium.Web.Proxy.Http2 namespace Titanium.Web.Proxy.Http2
{ {
[Flags]
internal enum Http2FrameFlag
{
Ack = 0x01,
EndStream = 0x01,
EndHeaders = 0x04,
Padded = 0x08,
Priority = 0x20,
}
internal class Http2Helper internal class Http2Helper
{ {
/// <summary> /// <summary>
...@@ -72,22 +62,29 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -72,22 +62,29 @@ namespace Titanium.Web.Proxy.Http2
int headerTableSize = 0; int headerTableSize = 0;
Decoder decoder = null; Decoder decoder = null;
var headerBuffer = new byte[9]; Http2FrameHeader frameHeader = new Http2FrameHeader();
frameHeader.Buffer = new byte[9];
byte[] buffer = null; byte[] buffer = null;
while (true) while (true)
{ {
int read = await forceRead(input, headerBuffer, 0, 9, cancellationToken); var frameHeaderBuffer = frameHeader.Buffer;
onCopy(headerBuffer, 0, read); int read = await forceRead(input, frameHeaderBuffer, 0, 9, cancellationToken);
onCopy(frameHeaderBuffer, 0, read);
if (read != 9) if (read != 9)
{ {
return; return;
} }
int length = (headerBuffer[0] << 16) + (headerBuffer[1] << 8) + headerBuffer[2]; int length = (frameHeaderBuffer[0] << 16) + (frameHeaderBuffer[1] << 8) + frameHeaderBuffer[2];
byte type = headerBuffer[3]; var type = (Http2FrameType)frameHeaderBuffer[3];
byte flags = headerBuffer[4]; var flags = (Http2FrameFlag)frameHeaderBuffer[4];
int streamId = ((headerBuffer[5] & 0x7f) << 24) + (headerBuffer[6] << 16) + (headerBuffer[7] << 8) + int streamId = ((frameHeaderBuffer[5] & 0x7f) << 24) + (frameHeaderBuffer[6] << 16) +
headerBuffer[8]; (frameHeaderBuffer[7] << 8) + frameHeaderBuffer[8];
frameHeader.Length = length;
frameHeader.Type = type;
frameHeader.Flags = flags;
frameHeader.StreamId = streamId;
if (buffer == null || buffer.Length < localSettings.MaxFrameSize) if (buffer == null || buffer.Length < localSettings.MaxFrameSize)
{ {
...@@ -105,33 +102,35 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -105,33 +102,35 @@ namespace Titanium.Web.Proxy.Http2
bool endStream = false; bool endStream = false;
SessionEventArgs args = null; SessionEventArgs args = null;
RequestResponseBase rr = null; RequestResponseBase rr;
if (type == 0 || type == 1) if (type == Http2FrameType.Data || type == Http2FrameType.Headers || type == Http2FrameType.PushPromise)
{ {
if (!sessions.TryGetValue(streamId, out args)) if (!sessions.TryGetValue(streamId, out args))
{ {
if (type == 0) if (type == Http2FrameType.Data)
{ {
throw new ProxyHttpException("HTTP Body data received before any header frame.", null, args); throw new ProxyHttpException("HTTP Body data received before any header frame.", null, args);
} }
if (!isClient) if (type == Http2FrameType.Headers && !isClient)
{ {
throw new ProxyHttpException("HTTP Response received before any Request header frame.", null, args); throw new ProxyHttpException("HTTP Response received before any Request header frame.", null, args);
} }
args = sessionFactory(); if (type == Http2FrameType.PushPromise && isClient)
sessions.TryAdd(streamId, args); {
throw new ProxyHttpException("HTTP Push promise received from the client.", null, args);
}
} }
rr = isClient ? (RequestResponseBase)args.HttpClient.Request : args.HttpClient.Response;
} }
//System.Diagnostics.Debug.WriteLine("CONN: " + connectionId + ", CLIENT: " + isClient + ", STREAM: " + streamId + ", TYPE: " + type); //System.Diagnostics.Debug.WriteLine("CONN: " + connectionId + ", CLIENT: " + isClient + ", STREAM: " + streamId + ", TYPE: " + type);
if (type == 0 /* data */) if (type == Http2FrameType.Data)
{ {
bool padded = (flags & (int)Http2FrameFlag.Padded) != 0; rr = isClient ? (RequestResponseBase)args.HttpClient.Request : args.HttpClient.Response;
bool endStreamFlag = (flags & (int)Http2FrameFlag.EndStream) != 0;
bool padded = (flags & Http2FrameFlag.Padded) != 0;
bool endStreamFlag = (flags & Http2FrameFlag.EndStream) != 0;
if (endStreamFlag) if (endStreamFlag)
{ {
endStream = true; endStream = true;
...@@ -197,16 +196,21 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -197,16 +196,21 @@ namespace Titanium.Web.Proxy.Http2
await rr.Http2BeforeHandlerTask; await rr.Http2BeforeHandlerTask;
} }
await sendBody(remoteSettings, rr, headerBuffer, buffer, output); if (args.IsPromise)
{
breakpoint();
}
await sendBody(remoteSettings, rr, frameHeader, buffer, output);
} }
} }
} }
else if (type == 1 /* headers */) else if (type == Http2FrameType.Headers/* || type == Http2FrameType.PushPromise*/)
{ {
bool endHeaders = (flags & (int)Http2FrameFlag.EndHeaders) != 0; bool endHeaders = (flags & Http2FrameFlag.EndHeaders) != 0;
bool padded = (flags & (int)Http2FrameFlag.Padded) != 0; bool padded = (flags & Http2FrameFlag.Padded) != 0;
bool priority = (flags & (int)Http2FrameFlag.Priority) != 0; bool priority = (flags & Http2FrameFlag.Priority) != 0;
bool endStreamFlag = (flags & (int)Http2FrameFlag.EndStream) != 0; bool endStreamFlag = (flags & Http2FrameFlag.EndStream) != 0;
if (endStreamFlag) if (endStreamFlag)
{ {
endStream = true; endStream = true;
...@@ -218,13 +222,45 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -218,13 +222,45 @@ namespace Titanium.Web.Proxy.Http2
offset = 1; offset = 1;
breakpoint(); breakpoint();
} }
if (priority) if (type == Http2FrameType.PushPromise)
{ {
var priorityData = ((long)buffer[offset++] << 32) + ((long)buffer[offset++] << 24) + int promisedStreamId = (buffer[offset++] << 24) + (buffer[offset++] << 16) + (buffer[offset++] << 8) + buffer[offset++];
(buffer[offset++] << 16) + (buffer[offset++] << 8) + buffer[offset++]; if (!sessions.TryGetValue(streamId, out args))
rr.Priority = priorityData; {
args = sessionFactory();
args.IsPromise = true;
sessions.TryAdd(streamId, args);
sessions.TryAdd(promisedStreamId, args);
}
System.Diagnostics.Debug.WriteLine("PROMISE STREAM: " + streamId + ", " + promisedStreamId +
", CONN: " + connectionId);
rr = args.HttpClient.Request;
if (isClient)
{
// push_promise from client???
breakpoint();
}
} }
else
{
if (!sessions.TryGetValue(streamId, out args))
{
args = sessionFactory();
sessions.TryAdd(streamId, args);
}
rr = isClient ? (RequestResponseBase)args.HttpClient.Request : args.HttpClient.Response;
if (priority)
{
var priorityData = ((long)buffer[offset++] << 32) + ((long)buffer[offset++] << 24) +
(buffer[offset++] << 16) + (buffer[offset++] << 8) + buffer[offset++];
rr.Priority = priorityData;
}
}
int dataLength = length - offset; int dataLength = length - offset;
if (padded) if (padded)
...@@ -252,9 +288,8 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -252,9 +288,8 @@ namespace Titanium.Web.Proxy.Http2
headerListener); headerListener);
decoder.EndHeaderBlock(); decoder.EndHeaderBlock();
if (isClient) if (rr is Request request)
{ {
var request = args.HttpClient.Request;
request.HttpVersion = HttpVersion.Version20; request.HttpVersion = HttpVersion.Version20;
request.Method = headerListener.Method; request.Method = headerListener.Method;
request.OriginalUrl = headerListener.Path; request.OriginalUrl = headerListener.Path;
...@@ -263,7 +298,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -263,7 +298,7 @@ namespace Titanium.Web.Proxy.Http2
} }
else else
{ {
var response = args.HttpClient.Response; var response = (Response)rr;
response.HttpVersion = HttpVersion.Version20; response.HttpVersion = HttpVersion.Version20;
int.TryParse(headerListener.Status, out int statusCode); int.TryParse(headerListener.Status, out int statusCode);
response.StatusCode = statusCode; response.StatusCode = statusCode;
...@@ -291,7 +326,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -291,7 +326,7 @@ namespace Titanium.Web.Proxy.Http2
{ {
rr.ReadHttp2BeforeHandlerTaskCompletionSource = null; rr.ReadHttp2BeforeHandlerTaskCompletionSource = null;
tcs.SetResult(true); tcs.SetResult(true);
await sendHeader(remoteSettings, headerBuffer, rr, endStream, output); await sendHeader(remoteSettings, frameHeader, rr, endStream, output, args.IsPromise);
} }
else else
{ {
...@@ -303,16 +338,12 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -303,16 +338,12 @@ namespace Titanium.Web.Proxy.Http2
sendPacket = false; sendPacket = false;
} }
else if (type == 5 /* push_promise */) else if (type == Http2FrameType.Continuation)
{
breakpoint();
}
else if (type == 9 /* continuation */)
{ {
// todo: implementing this type is mandatory for multi-part headers // todo: implementing this type is mandatory for multi-part headers
breakpoint(); breakpoint();
} }
else if (type == 4 /* settings */) else if (type == Http2FrameType.Settings)
{ {
if (length % 6 != 0) if (length % 6 != 0)
{ {
...@@ -339,7 +370,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -339,7 +370,7 @@ namespace Titanium.Web.Proxy.Http2
} }
} }
if (type == 3 /* rst_stream */) if (type == Http2FrameType.RstStream)
{ {
int errorCode = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; int errorCode = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
if (streamId == 0) if (streamId == 0)
...@@ -363,13 +394,14 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -363,13 +394,14 @@ namespace Titanium.Web.Proxy.Http2
if (!isClient && endStream) if (!isClient && endStream)
{ {
sessions.TryRemove(streamId, out _); sessions.TryRemove(streamId, out _);
//System.Diagnostics.Debug.WriteLine("REMOVED CONN: " + connectionId + ", CLIENT: " + isClient + ", STREAM: " + streamId + ", TYPE: " + type); System.Diagnostics.Debug.WriteLine("REMOVED CONN: " + connectionId + ", CLIENT: " + isClient + ", STREAM: " + streamId + ", TYPE: " + type);
} }
if (sendPacket) if (sendPacket)
{ {
// do not cancel the write operation // do not cancel the write operation
await output.WriteAsync(headerBuffer, 0, headerBuffer.Length /*, cancellationToken*/); var buf = frameHeader.CopyToBuffer();
await output.WriteAsync(buf, 0, buf.Length/*, cancellationToken*/);
await output.WriteAsync(buffer, 0, length /*, cancellationToken*/); await output.WriteAsync(buffer, 0, length /*, cancellationToken*/);
} }
...@@ -393,7 +425,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -393,7 +425,7 @@ namespace Titanium.Web.Proxy.Http2
; ;
} }
private static async Task sendHeader(Http2Settings settings, byte[] headerBuffer, RequestResponseBase rr, bool endStream, Stream output) private static async Task sendHeader(Http2Settings settings, Http2FrameHeader frameHeader, RequestResponseBase rr, bool endStream, Stream output, bool pushPromise)
{ {
var encoder = new Encoder(settings.HeaderTableSize); var encoder = new Encoder(settings.HeaderTableSize);
var ms = new MemoryStream(); var ms = new MemoryStream();
...@@ -430,36 +462,35 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -430,36 +462,35 @@ namespace Titanium.Web.Proxy.Http2
var data = ms.ToArray(); var data = ms.ToArray();
int newLength = data.Length; int newLength = data.Length;
headerBuffer[0] = (byte)((newLength >> 16) & 0xff); frameHeader.Length = newLength;
headerBuffer[1] = (byte)((newLength >> 8) & 0xff); frameHeader.Type = pushPromise ? Http2FrameType.PushPromise : Http2FrameType.Headers;
headerBuffer[2] = (byte)(newLength & 0xff);
headerBuffer[3] = 1; // type: header
int flags = (int)Http2FrameFlag.EndHeaders; var flags = Http2FrameFlag.EndHeaders;
if (endStream) if (endStream)
{ {
flags |= (int)Http2FrameFlag.EndStream; flags |= Http2FrameFlag.EndStream;
} }
if (rr.Priority.HasValue) if (rr.Priority.HasValue)
{ {
flags |= (int)Http2FrameFlag.Priority; flags |= Http2FrameFlag.Priority;
} }
headerBuffer[4] = (byte)flags; frameHeader.Flags = flags;
// clear the padding flag // clear the padding flag
//headerBuffer[4] = (byte)(flags & ~((int)Http2FrameFlag.Padded)); //headerBuffer[4] = (byte)(flags & ~((int)Http2FrameFlag.Padded));
// send the header // send the header
await output.WriteAsync(headerBuffer, 0, headerBuffer.Length /*, cancellationToken*/); var buf = frameHeader.CopyToBuffer();
await output.WriteAsync(buf, 0, buf.Length/*, cancellationToken*/);
await output.WriteAsync(data, 0, data.Length /*, cancellationToken*/); await output.WriteAsync(data, 0, data.Length /*, cancellationToken*/);
} }
private static async Task sendBody(Http2Settings settings, RequestResponseBase rr, byte[] headerBuffer, byte[] buffer, Stream output) private static async Task sendBody(Http2Settings settings, RequestResponseBase rr, Http2FrameHeader frameHeader, byte[] buffer, Stream output)
{ {
var body = rr.CompressBodyAndUpdateContentLength(); var body = rr.CompressBodyAndUpdateContentLength();
await sendHeader(settings, headerBuffer, rr, !(rr.HasBody && rr.IsBodyRead), output); await sendHeader(settings, frameHeader, rr, !(rr.HasBody && rr.IsBodyRead), output, false);
if (rr.HasBody && rr.IsBodyRead) if (rr.HasBody && rr.IsBodyRead)
{ {
...@@ -470,12 +501,12 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -470,12 +501,12 @@ namespace Titanium.Web.Proxy.Http2
Buffer.BlockCopy(body, pos, buffer, 0, bodyFrameLength); Buffer.BlockCopy(body, pos, buffer, 0, bodyFrameLength);
pos += bodyFrameLength; pos += bodyFrameLength;
headerBuffer[0] = (byte)((bodyFrameLength >> 16) & 0xff); frameHeader.Length = bodyFrameLength;
headerBuffer[1] = (byte)((bodyFrameLength >> 8) & 0xff); frameHeader.Type = Http2FrameType.Data;
headerBuffer[2] = (byte)(bodyFrameLength & 0xff); frameHeader.Flags = pos < body.Length ? (Http2FrameFlag)0 : Http2FrameFlag.EndStream;
headerBuffer[3] = 0; // type: data
headerBuffer[4] = pos < body.Length ? (byte)0 : (byte)(int)Http2FrameFlag.EndStream; var buf = frameHeader.CopyToBuffer();
await output.WriteAsync(headerBuffer, 0, headerBuffer.Length /*, cancellationToken*/); await output.WriteAsync(buf, 0, buf.Length/*, cancellationToken*/);
await output.WriteAsync(buffer, 0, bodyFrameLength /*, cancellationToken*/); await output.WriteAsync(buffer, 0, bodyFrameLength /*, cancellationToken*/);
} }
} }
......
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