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
6a186156
Unverified
Commit
6a186156
authored
Apr 29, 2019
by
honfika
Committed by
GitHub
Apr 29, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #587 from honfika/master
allow to modify HTTP/2 header
parents
29ffc5b9
0c88f3f1
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
399 additions
and
83 deletions
+399
-83
MainWindow.xaml.cs
examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
+16
-3
SessionEventArgs.cs
src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+19
-0
RequestResponseBase.cs
src/Titanium.Web.Proxy/Http/RequestResponseBase.cs
+11
-0
Encoder.cs
src/Titanium.Web.Proxy/Http2/Hpack/Encoder.cs
+7
-6
StaticTable.cs
src/Titanium.Web.Proxy/Http2/Hpack/StaticTable.cs
+6
-6
Http2FrameFlag.cs
src/Titanium.Web.Proxy/Http2/Http2FrameFlag.cs
+14
-0
Http2FrameHeader.cs
src/Titanium.Web.Proxy/Http2/Http2FrameHeader.cs
+32
-0
Http2FrameType.cs
src/Titanium.Web.Proxy/Http2/Http2FrameType.cs
+16
-0
Http2Helper.cs
src/Titanium.Web.Proxy/Http2/Http2Helper.cs
+270
-66
HttpHeader.cs
src/Titanium.Web.Proxy/Models/HttpHeader.cs
+8
-1
ResponseHandler.cs
src/Titanium.Web.Proxy/ResponseHandler.cs
+0
-1
No files found.
examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
View file @
6a186156
...
@@ -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
)
{
{
...
...
src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
6a186156
...
@@ -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
{
{
...
...
src/Titanium.Web.Proxy/Http/RequestResponseBase.cs
View file @
6a186156
...
@@ -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>
...
...
src/Titanium.Web.Proxy/Http2/Hpack/Encoder.cs
View file @
6a186156
...
@@ -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
.
Nam
e
)
&&
Equals
(
value
,
e
.
Value
))
if
(
e
.
Hash
==
h
&&
name
.
Equals
(
e
.
Name
,
StringComparison
.
OrdinalIgnoreCas
e
)
&&
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
.
Nam
e
))
if
(
e
.
Hash
==
h
&&
name
.
Equals
(
e
.
Name
,
StringComparison
.
OrdinalIgnoreCas
e
))
{
{
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
;
...
...
src/Titanium.Web.Proxy/Http2/Hpack/StaticTable.cs
View file @
6a186156
...
@@ -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
=
C
reateMap
();
private
static
readonly
Dictionary
<
string
,
int
>
staticIndexByName
=
c
reateMap
();
/// <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
.
Nam
e
))
if
(!
name
.
Equals
(
entry
.
Name
,
StringComparison
.
OrdinalIgnoreCas
e
))
{
{
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
>
C
reateMap
()
private
static
Dictionary
<
string
,
int
>
c
reateMap
()
{
{
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
;
}
}
...
...
src/Titanium.Web.Proxy/Http2/Http2FrameFlag.cs
0 → 100644
View file @
6a186156
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
src/Titanium.Web.Proxy/Http2/Http2FrameHeader.cs
0 → 100644
View file @
6a186156
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
;
}
}
}
src/Titanium.Web.Proxy/Http2/Http2FrameType.cs
0 → 100644
View file @
6a186156
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
src/Titanium.Web.Proxy/Http2/Http2Helper.cs
View file @
6a186156
...
@@ -2,10 +2,12 @@
...
@@ -2,10 +2,12 @@
using
System
;
using
System
;
using
System.Collections.Concurrent
;
using
System.Collections.Concurrent
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Diagnostics
;
using
System.IO
;
using
System.IO
;
using
System.Net
;
using
System.Net
;
using
System.Threading
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Compression
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Exceptions
;
using
Titanium.Web.Proxy.Exceptions
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Http
;
...
@@ -13,16 +15,6 @@ using Titanium.Web.Proxy.Http2.Hpack;
...
@@ -13,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>
...
@@ -70,22 +62,29 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -70,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
];
var
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
)
{
{
...
@@ -99,70 +98,71 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -99,70 +98,71 @@ namespace Titanium.Web.Proxy.Http2
return
;
return
;
}
}
bool
sendPacket
=
true
;
bool
endStream
=
false
;
bool
endStream
=
false
;
SessionEventArgs
args
=
null
;
SessionEventArgs
args
=
null
;
RequestResponseBase
rr
=
null
;
RequestResponseBase
rr
=
null
;
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);
//}
if
(
type
==
Http2FrameType
.
PushPromise
&&
isClient
)
{
{
throw
new
ProxyHttpException
(
"HTTP
Response received before any Request header frame
."
,
null
,
args
);
throw
new
ProxyHttpException
(
"HTTP
Push promise received from the client
."
,
null
,
args
);
}
}
args
=
sessionFactory
();
sessions
.
TryAdd
(
streamId
,
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
&&
args
!=
null
)
{
{
bool
endStreamFlag
=
(
flags
&
(
int
)
Http2FrameFlag
.
EndStream
)
!=
0
;
rr
=
isClient
?
(
RequestResponseBase
)
args
.
HttpClient
.
Request
:
args
.
HttpClient
.
Response
;
bool
padded
=
(
flags
&
Http2FrameFlag
.
Padded
)
!=
0
;
bool
endStreamFlag
=
(
flags
&
Http2FrameFlag
.
EndStream
)
!=
0
;
if
(
endStreamFlag
)
if
(
endStreamFlag
)
{
{
endStream
=
true
;
endStream
=
true
;
}
}
if
(
rr
.
Http2IgnoreBodyFrames
)
{
sendPacket
=
false
;
}
if
(
rr
.
ReadHttp2BodyTaskCompletionSource
!=
null
)
if
(
rr
.
ReadHttp2BodyTaskCompletionSource
!=
null
)
{
{
// Get body method was called in the "before" event handler
// Get body method was called in the "before" event handler
var
data
=
rr
.
Http2BodyData
;
var
data
=
rr
.
Http2BodyData
;
data
.
Write
(
buffer
,
0
,
length
);
int
offset
=
0
;
if
(
padded
)
if
(
endStream
)
{
{
rr
.
Body
=
data
.
ToArray
();
offset
++;
rr
.
IsBodyRead
=
true
;
length
--;
length
-=
buffer
[
0
];
var
tcs
=
rr
.
ReadHttp2BodyTaskCompletionSource
;
rr
.
ReadHttp2BodyTaskCompletionSource
=
null
;
if
(!
tcs
.
Task
.
IsCompleted
)
{
tcs
.
SetResult
(
true
);
}
rr
.
Http2BodyData
=
null
;
}
}
}
data
.
Write
(
buffer
,
offset
,
length
);
}
}
}
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
;
...
@@ -172,12 +172,47 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -172,12 +172,47 @@ namespace Titanium.Web.Proxy.Http2
if
(
padded
)
if
(
padded
)
{
{
offset
=
1
;
offset
=
1
;
breakpoint
();
}
}
if
(
priority
)
if
(
type
==
Http2FrameType
.
PushPromise
)
{
{
offset
+=
5
;
int
promisedStreamId
=
(
buffer
[
offset
++]
<<
24
)
+
(
buffer
[
offset
++]
<<
16
)
+
(
buffer
[
offset
++]
<<
8
)
+
buffer
[
offset
++];
if
(!
sessions
.
TryGetValue
(
streamId
,
out
args
))
{
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
)
...
@@ -205,17 +240,17 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -205,17 +240,17 @@ namespace Titanium.Web.Proxy.Http2
headerListener
);
headerListener
);
decoder
.
EndHeaderBlock
();
decoder
.
EndHeaderBlock
();
if
(
isClien
t
)
if
(
rr
is
Request
reques
t
)
{
{
var
request
=
args
.
HttpClient
.
Request
;
request
.
HttpVersion
=
HttpVersion
.
Version20
;
request
.
HttpVersion
=
HttpVersion
.
Version20
;
request
.
Method
=
headerListener
.
Method
;
request
.
Method
=
headerListener
.
Method
;
request
.
OriginalUrl
=
headerListener
.
Status
;
request
.
OriginalUrl
=
headerListener
.
Path
;
request
.
RequestUri
=
headerListener
.
GetUri
();
request
.
RequestUri
=
headerListener
.
GetUri
();
}
}
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
;
...
@@ -226,23 +261,41 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -226,23 +261,41 @@ namespace Titanium.Web.Proxy.Http2
exceptionFunc
(
new
ProxyHttpException
(
"Failed to decode HTTP/2 headers"
,
ex
,
args
));
exceptionFunc
(
new
ProxyHttpException
(
"Failed to decode HTTP/2 headers"
,
ex
,
args
));
}
}
if
(!
endHeaders
)
{
breakpoint
();
}
if
(
endHeaders
)
if
(
endHeaders
)
{
{
var
tcs
=
new
TaskCompletionSource
<
bool
>();
var
tcs
=
new
TaskCompletionSource
<
bool
>();
rr
.
ReadHttp2BeforeHandlerTaskCompletionSource
=
tcs
;
rr
.
ReadHttp2BeforeHandlerTaskCompletionSource
=
tcs
;
var
handler
=
onBeforeRequestResponse
(
args
);
var
handler
=
onBeforeRequestResponse
(
args
);
rr
.
Http2BeforeHandlerTask
=
handler
;
if
(
handler
==
await
Task
.
WhenAny
(
tcs
.
Task
,
handler
))
if
(
handler
==
await
Task
.
WhenAny
(
tcs
.
Task
,
handler
))
{
{
rr
.
ReadHttp2BeforeHandlerTaskCompletionSource
=
null
;
rr
.
ReadHttp2BeforeHandlerTaskCompletionSource
=
null
;
tcs
.
SetResult
(
true
);
tcs
.
SetResult
(
true
);
await
sendHeader
(
remoteSettings
,
frameHeader
,
rr
,
endStream
,
output
,
args
.
IsPromise
);
}
else
{
rr
.
Http2IgnoreBodyFrames
=
true
;
}
}
rr
.
Locked
=
true
;
rr
.
Locked
=
true
;
}
}
sendPacket
=
false
;
}
}
else
if
(
type
==
4
/* settings */
)
else
if
(
type
==
Http2FrameType
.
Continuation
)
{
// todo: implementing this type is mandatory for multi-part headers
breakpoint
();
}
else
if
(
type
==
Http2FrameType
.
Settings
)
{
{
if
(
length
%
6
!=
0
)
if
(
length
%
6
!=
0
)
{
{
...
@@ -269,7 +322,7 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -269,7 +322,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
)
...
@@ -290,15 +343,68 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -290,15 +343,68 @@ namespace Titanium.Web.Proxy.Http2
}
}
}
}
if
(
endStream
&&
rr
.
ReadHttp2BodyTaskCompletionSource
!=
null
)
{
if
(!
rr
.
BodyAvailable
)
{
var
data
=
rr
.
Http2BodyData
;
var
body
=
data
.
ToArray
();
if
(
rr
.
ContentEncoding
!=
null
)
{
using
(
var
ms
=
new
MemoryStream
())
{
using
(
var
zip
=
DecompressionFactory
.
Create
(
rr
.
ContentEncoding
,
new
MemoryStream
(
body
)))
{
zip
.
CopyTo
(
ms
);
}
body
=
ms
.
ToArray
();
}
}
rr
.
Body
=
body
;
}
rr
.
IsBodyRead
=
true
;
var
tcs
=
rr
.
ReadHttp2BodyTaskCompletionSource
;
rr
.
ReadHttp2BodyTaskCompletionSource
=
null
;
if
(!
tcs
.
Task
.
IsCompleted
)
{
tcs
.
SetResult
(
true
);
}
rr
.
Http2BodyData
=
null
;
if
(
rr
.
Http2BeforeHandlerTask
!=
null
)
{
await
rr
.
Http2BeforeHandlerTask
;
}
if
(
args
.
IsPromise
)
{
breakpoint
();
}
await
sendBody
(
remoteSettings
,
rr
,
frameHeader
,
buffer
,
output
);
}
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
);
}
}
// do not cancel the write operation
if
(
sendPacket
)
await
output
.
WriteAsync
(
headerBuffer
,
0
,
headerBuffer
.
Length
/*, cancellationToken*/
);
{
await
output
.
WriteAsync
(
buffer
,
0
,
length
/*, cancellationToken*/
);
// do not cancel the write operation
var
buf
=
frameHeader
.
CopyToBuffer
();
await
output
.
WriteAsync
(
buf
,
0
,
buf
.
Length
/*, cancellationToken*/
);
await
output
.
WriteAsync
(
buffer
,
0
,
length
/*, cancellationToken*/
);
}
if
(
cancellationToken
.
IsCancellationRequested
)
if
(
cancellationToken
.
IsCancellationRequested
)
{
{
...
@@ -313,6 +419,104 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -313,6 +419,104 @@ namespace Titanium.Web.Proxy.Http2
}
}
}
}
[
Conditional
(
"DEBUG"
)]
private
static
void
breakpoint
()
{
// when this method is called something received which is not yet implemented
;
}
private
static
async
Task
sendHeader
(
Http2Settings
settings
,
Http2FrameHeader
frameHeader
,
RequestResponseBase
rr
,
bool
endStream
,
Stream
output
,
bool
pushPromise
)
{
var
encoder
=
new
Encoder
(
settings
.
HeaderTableSize
);
var
ms
=
new
MemoryStream
();
var
writer
=
new
BinaryWriter
(
ms
);
if
(
rr
.
Priority
.
HasValue
)
{
long
p
=
rr
.
Priority
.
Value
;
writer
.
Write
((
byte
)((
p
>>
32
)
&
0xff
));
writer
.
Write
((
byte
)((
p
>>
24
)
&
0xff
));
writer
.
Write
((
byte
)((
p
>>
16
)
&
0xff
));
writer
.
Write
((
byte
)((
p
>>
8
)
&
0xff
));
writer
.
Write
((
byte
)(
p
&
0xff
));
}
if
(
rr
is
Request
request
)
{
encoder
.
EncodeHeader
(
writer
,
":method"
,
request
.
Method
);
encoder
.
EncodeHeader
(
writer
,
":authority"
,
request
.
RequestUri
.
Host
);
encoder
.
EncodeHeader
(
writer
,
":scheme"
,
request
.
RequestUri
.
Scheme
);
encoder
.
EncodeHeader
(
writer
,
":path"
,
request
.
RequestUriString
,
false
,
HpackUtil
.
IndexType
.
None
,
false
);
}
else
{
var
response
=
(
Response
)
rr
;
encoder
.
EncodeHeader
(
writer
,
":status"
,
response
.
StatusCode
.
ToString
());
}
foreach
(
var
header
in
rr
.
Headers
)
{
encoder
.
EncodeHeader
(
writer
,
header
.
Name
.
ToLower
(),
header
.
Value
);
}
var
data
=
ms
.
ToArray
();
int
newLength
=
data
.
Length
;
frameHeader
.
Length
=
newLength
;
frameHeader
.
Type
=
pushPromise
?
Http2FrameType
.
PushPromise
:
Http2FrameType
.
Headers
;
var
flags
=
Http2FrameFlag
.
EndHeaders
;
if
(
endStream
)
{
flags
|=
Http2FrameFlag
.
EndStream
;
}
if
(
rr
.
Priority
.
HasValue
)
{
flags
|=
Http2FrameFlag
.
Priority
;
}
frameHeader
.
Flags
=
flags
;
// clear the padding flag
//headerBuffer[4] = (byte)(flags & ~((int)Http2FrameFlag.Padded));
// send the header
var
buf
=
frameHeader
.
CopyToBuffer
();
await
output
.
WriteAsync
(
buf
,
0
,
buf
.
Length
/*, cancellationToken*/
);
await
output
.
WriteAsync
(
data
,
0
,
data
.
Length
/*, cancellationToken*/
);
}
private
static
async
Task
sendBody
(
Http2Settings
settings
,
RequestResponseBase
rr
,
Http2FrameHeader
frameHeader
,
byte
[]
buffer
,
Stream
output
)
{
var
body
=
rr
.
CompressBodyAndUpdateContentLength
();
await
sendHeader
(
settings
,
frameHeader
,
rr
,
!(
rr
.
HasBody
&&
rr
.
IsBodyRead
),
output
,
false
);
if
(
rr
.
HasBody
&&
rr
.
IsBodyRead
)
{
int
pos
=
0
;
while
(
pos
<
body
.
Length
)
{
int
bodyFrameLength
=
Math
.
Min
(
buffer
.
Length
,
body
.
Length
-
pos
);
Buffer
.
BlockCopy
(
body
,
pos
,
buffer
,
0
,
bodyFrameLength
);
pos
+=
bodyFrameLength
;
frameHeader
.
Length
=
bodyFrameLength
;
frameHeader
.
Type
=
Http2FrameType
.
Data
;
frameHeader
.
Flags
=
pos
<
body
.
Length
?
(
Http2FrameFlag
)
0
:
Http2FrameFlag
.
EndStream
;
var
buf
=
frameHeader
.
CopyToBuffer
();
await
output
.
WriteAsync
(
buf
,
0
,
buf
.
Length
/*, cancellationToken*/
);
await
output
.
WriteAsync
(
buffer
,
0
,
bodyFrameLength
/*, cancellationToken*/
);
}
}
else
{
;
}
}
private
static
async
Task
<
int
>
forceRead
(
Stream
input
,
byte
[]
buffer
,
int
offset
,
int
bytesToRead
,
private
static
async
Task
<
int
>
forceRead
(
Stream
input
,
byte
[]
buffer
,
int
offset
,
int
bytesToRead
,
CancellationToken
cancellationToken
)
CancellationToken
cancellationToken
)
{
{
...
...
src/Titanium.Web.Proxy/Models/HttpHeader.cs
View file @
6a186156
...
@@ -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>
...
...
src/Titanium.Web.Proxy/ResponseHandler.cs
View file @
6a186156
...
@@ -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>
...
...
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