Commit 60feeb00 authored by Honfika's avatar Honfika

http/2: using some default settings from specification, process the settings frame

process the rst_stream frame
parent 66c41133
...@@ -39,14 +39,19 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -39,14 +39,19 @@ namespace Titanium.Web.Proxy.Http2
CancellationTokenSource cancellationTokenSource, Guid connectionId, CancellationTokenSource cancellationTokenSource, Guid connectionId,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
var clientSettings = new Http2Settings();
var serverSettings = new Http2Settings();
;
var sessions = new ConcurrentDictionary<int, SessionEventArgs>(); var sessions = new ConcurrentDictionary<int, SessionEventArgs>();
// Now async relay all server=>client & client=>server data // Now async relay all server=>client & client=>server data
var sendRelay = var sendRelay =
copyHttp2FrameAsync(clientStream, serverStream, onDataSend, sessionFactory, sessions, onBeforeRequest, copyHttp2FrameAsync(clientStream, serverStream, onDataSend, clientSettings, serverSettings,
sessionFactory, sessions, onBeforeRequest,
bufferSize, connectionId, true, cancellationTokenSource.Token, exceptionFunc); bufferSize, connectionId, true, cancellationTokenSource.Token, exceptionFunc);
var receiveRelay = var receiveRelay =
copyHttp2FrameAsync(serverStream, clientStream, onDataReceive, sessionFactory, sessions, onBeforeResponse, copyHttp2FrameAsync(serverStream, clientStream, onDataReceive, serverSettings, clientSettings,
sessionFactory, sessions, onBeforeResponse,
bufferSize, connectionId, false, cancellationTokenSource.Token, exceptionFunc); bufferSize, connectionId, false, cancellationTokenSource.Token, exceptionFunc);
await Task.WhenAny(sendRelay, receiveRelay); await Task.WhenAny(sendRelay, receiveRelay);
...@@ -56,15 +61,17 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -56,15 +61,17 @@ namespace Titanium.Web.Proxy.Http2
} }
private static async Task copyHttp2FrameAsync(Stream input, Stream output, Action<byte[], int, int> onCopy, private static async Task copyHttp2FrameAsync(Stream input, Stream output, Action<byte[], int, int> onCopy,
Http2Settings localSettings, Http2Settings remoteSettings,
Func<SessionEventArgs> sessionFactory, ConcurrentDictionary<int, SessionEventArgs> sessions, Func<SessionEventArgs> sessionFactory, ConcurrentDictionary<int, SessionEventArgs> sessions,
Func<SessionEventArgs, Task> onBeforeRequestResponse, Func<SessionEventArgs, Task> onBeforeRequestResponse,
int bufferSize, Guid connectionId, bool isClient, CancellationToken cancellationToken, int bufferSize, Guid connectionId, bool isClient, CancellationToken cancellationToken,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
var decoder = new Decoder(8192, 4096 * 16); int headerTableSize = 0;
Decoder decoder = null;
var headerBuffer = new byte[9]; var headerBuffer = new byte[9];
var buffer = new byte[32768]; byte[] buffer = null;
while (true) while (true)
{ {
int read = await forceRead(input, headerBuffer, 0, 9, cancellationToken); int read = await forceRead(input, headerBuffer, 0, 9, cancellationToken);
...@@ -80,6 +87,11 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -80,6 +87,11 @@ namespace Titanium.Web.Proxy.Http2
int streamId = ((headerBuffer[5] & 0x7f) << 24) + (headerBuffer[6] << 16) + (headerBuffer[7] << 8) + int streamId = ((headerBuffer[5] & 0x7f) << 24) + (headerBuffer[6] << 16) + (headerBuffer[7] << 8) +
headerBuffer[8]; headerBuffer[8];
if (buffer == null || buffer.Length < localSettings.MaxFrameSize)
{
buffer = new byte[localSettings.MaxFrameSize];
}
read = await forceRead(input, buffer, 0, length, cancellationToken); read = await forceRead(input, buffer, 0, length, cancellationToken);
onCopy(buffer, 0, read); onCopy(buffer, 0, read);
if (read != length) if (read != length)
...@@ -145,7 +157,7 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -145,7 +157,7 @@ namespace Titanium.Web.Proxy.Http2
} }
} }
} }
else if (type == 1 /*headers*/) else if (type == 1 /* headers */)
{ {
bool endHeaders = (flags & (int)Http2FrameFlag.EndHeaders) != 0; bool endHeaders = (flags & (int)Http2FrameFlag.EndHeaders) != 0;
bool padded = (flags & (int)Http2FrameFlag.Padded) != 0; bool padded = (flags & (int)Http2FrameFlag.Padded) != 0;
...@@ -181,13 +193,18 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -181,13 +193,18 @@ namespace Titanium.Web.Proxy.Http2
}); });
try try
{ {
lock (decoder) // recreate the decoder when new value is bigger
// should we recreate when smaller, too?
if (decoder == null || headerTableSize < localSettings.HeaderTableSize)
{ {
decoder.Decode(new BinaryReader(new MemoryStream(buffer, offset, dataLength)), headerTableSize = localSettings.HeaderTableSize;
headerListener); decoder = new Decoder(8192, headerTableSize);
decoder.EndHeaderBlock();
} }
decoder.Decode(new BinaryReader(new MemoryStream(buffer, offset, dataLength)),
headerListener);
decoder.EndHeaderBlock();
if (isClient) if (isClient)
{ {
var request = args.HttpClient.Request; var request = args.HttpClient.Request;
...@@ -225,6 +242,53 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -225,6 +242,53 @@ namespace Titanium.Web.Proxy.Http2
rr.Locked = true; rr.Locked = true;
} }
} }
else if (type == 4 /* settings */)
{
if (length % 6 != 0)
{
// https://httpwg.org/specs/rfc7540.html#SETTINGS
// 6.5. SETTINGS
// A SETTINGS frame with a length other than a multiple of 6 octets MUST be treated as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR
throw new ProxyHttpException("Invalid settings length", null, null);
}
int pos = 0;
while (pos < length)
{
int identifier = (buffer[pos++] << 8) + buffer[pos++];
int value = (buffer[pos++] << 24) + (buffer[pos++] << 16) + (buffer[pos++] << 8) + buffer[pos++];
if (identifier == 1 /*SETTINGS_HEADER_TABLE_SIZE*/)
{
//System.Diagnostics.Debug.WriteLine("HEADER SIZE CONN: " + connectionId + ", CLIENT: " + isClient + ", value: " + value);
remoteSettings.HeaderTableSize = value;
}
else if (identifier == 5 /*SETTINGS_MAX_FRAME_SIZE*/)
{
remoteSettings.MaxFrameSize = value;
}
}
}
if (type == 3 /* rst_stream */)
{
int errorCode = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
if (streamId == 0)
{
// connection error
exceptionFunc(new ProxyHttpException("HTTP/2 connection error. Error code: " + errorCode, null, args));
return;
}
else
{
// stream error
sessions.TryRemove(streamId, out _);
if (errorCode != 8 /*cancel*/)
{
exceptionFunc(new ProxyHttpException("HTTP/2 stream error. Error code: " + errorCode, null, args));
}
}
}
if (!isClient && endStream) if (!isClient && endStream)
{ {
...@@ -269,6 +333,14 @@ namespace Titanium.Web.Proxy.Http2 ...@@ -269,6 +333,14 @@ namespace Titanium.Web.Proxy.Http2
return totalRead; return totalRead;
} }
class Http2Settings
{
public int HeaderTableSize { get; set; } = 4096;
public int MaxFrameSize { get; set; } = 16384;
}
class MyHeaderListener : IHeaderListener class MyHeaderListener : IHeaderListener
{ {
private readonly Action<string, string> addHeaderFunc; private readonly Action<string, string> addHeaderFunc;
......
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