diff --git a/Proxy/HttpConnectProxy.cs b/Proxy/HttpConnectProxy.cs
index 741cabf..314da50 100644
--- a/Proxy/HttpConnectProxy.cs
+++ b/Proxy/HttpConnectProxy.cs
@@ -171,7 +171,7 @@ protected override void ProcessReceive(SocketAsyncEventArgs e)
return;
}
- int responseLength = prevMatched > 0 ? (e.Offset - prevMatched) : (e.Offset + result);
+ int responseLength = (prevMatched > 0 && result == e.Offset) ? (e.Offset - prevMatched) : result;
if (e.Offset + e.BytesTransferred > responseLength + m_LineSeparator.Length)
{
diff --git a/Test/HttpConnectProxyTest.cs b/Test/HttpConnectProxyTest.cs
index d1abadd..53784e3 100644
--- a/Test/HttpConnectProxyTest.cs
+++ b/Test/HttpConnectProxyTest.cs
@@ -14,48 +14,200 @@ namespace SuperSocket.ClientEngine.Test
public class HttpConnectProxyTest
{
[Fact]
- public void TestMatchSecondTime()
+ public void TestHttp10SimpleOnePacket()
{
- var server = CreateSimplyRespond(Encoding.ASCII.GetBytes("OK"));
- var proxyServer = CreateSimplyRespond(Encoding.ASCII.GetBytes("OK"));
+ SimulateHttpConnectProxy(
+ simulateResponseFromProxyServer: proxyServerPeer =>
+ {
+ SendLine(proxyServerPeer, "HTTP/1.0 200 Connection Established\r\n\r\n");
+ },
+ verifyCompletedEvent: e =>
+ {
+ Assert.Null(e.Exception);
+ Assert.True(e.Connected);
+ },
+ testCopyDataFromLeftToRight: SendAndReceiveHello
+ );
+ }
- ManualResetEvent wait = new ManualResetEvent(false);
+ [Fact]
+ public void TestHttp11SimpleOnePacket()
+ {
+ SimulateHttpConnectProxy(
+ simulateResponseFromProxyServer: proxyServerPeer =>
+ {
+ SendLine(proxyServerPeer, "HTTP/1.1 200 Connection Established\r\n\r\n");
+ },
+ verifyCompletedEvent: e =>
+ {
+ Assert.Null(e.Exception);
+ Assert.True(e.Connected);
+ },
+ testCopyDataFromLeftToRight: SendAndReceiveHello
+ );
+ }
+
+ [Fact]
+ public void TestHttp11ComplexOnePacket()
+ {
+ SimulateHttpConnectProxy(
+ simulateResponseFromProxyServer: proxyServerPeer =>
+ {
+ SendLine(proxyServerPeer,
+ "HTTP/1.1 200 Connection Established\r\n" +
+ "Proxy-agent: Apache/2.2.29 (Win32)\r\n" +
+ "\r\n"
+ );
+ },
+ verifyCompletedEvent: e =>
+ {
+ Assert.Null(e.Exception);
+ Assert.True(e.Connected);
+ },
+ testCopyDataFromLeftToRight: SendAndReceiveHello
+ );
+ }
+
+ [Fact]
+ public void TestHttp11ComplexMultiPacket()
+ {
+ SimulateHttpConnectProxy(
+ simulateResponseFromProxyServer: proxyServerPeer =>
+ {
+ // Actual response simulation from: Apache/2.2.29 (Win32)
+ SendLine(proxyServerPeer, "HTTP/1.1 200 Connection Established\r\n");
+
+ // This leads to "System.Exception: protocol error: more data has been received"
+ SendLine(proxyServerPeer, "Proxy-agent: Apache/2.2.29 (Win32)\r\n\r\n");
+ },
+ verifyCompletedEvent: e =>
+ {
+ Assert.Null(e.Exception);
+ Assert.True(e.Connected);
+ },
+ testCopyDataFromLeftToRight: SendAndReceiveHello
+ );
+ }
+
+ [Fact]
+ public void TestHttp11ComplexOnePacketAsForbidden()
+ {
+ SimulateHttpConnectProxy(
+ simulateResponseFromProxyServer: proxyServerPeer =>
+ {
+ // Actual 403 response simulation from: Apache/2.2.29 (Win32)
- var proxy = new HttpConnectProxy(proxyServer.LocalEndPoint);
- ProxyEventArgs eventArgs = null;
- proxy.Completed += (a, e) =>
+ // This leads to "System.Exception: protocol error: more data has been received"
+ SendLine(proxyServerPeer,
+ string.Join("\r\n",
+ "HTTP/1.1 403 Forbidden",
+ "Date: Tue, 24 Sep 2019 04:35:48 GMT",
+ "Content-Length: 216",
+ "Content-Type: text/html; charset=iso-8859-1",
+ "",
+ "",
+ "
",
+ "403 Forbidden",
+ "",
+ "Forbidden
",
+ "You don't have permission to access 192.168.2.181:7",
+ "on this server.
",
+ ""
+ )
+ );
+ },
+ verifyCompletedEvent: e =>
+ {
+ // This pattern is: NOT SUPPORTED FOR NOW!
+ Assert.NotNull(e.Exception);
+ Assert.Equal("protocol error: more data has been received", e.Exception.Message);
+ Assert.False(e.Connected);
+ },
+ testCopyDataFromLeftToRight: SendAndReceiveHello
+ );
+ }
+
+
+ void SimulateHttpConnectProxy(
+ Action simulateResponseFromProxyServer,
+ Action verifyCompletedEvent,
+ Action testCopyDataFromLeftToRight
+ )
+ {
+ var server = NewTcpPeer();
+ var proxyServer = NewTcpPeer();
+
+ var awaitAtProxyServer = proxyServer.AcceptAsync();
+
+ var proxy = new HttpConnectProxy(proxyServer.LocalEndPoint, 1024, null);
+ var eventArgs = (ProxyEventArgs)null;
+ var eventPulled = new ManualResetEvent(false);
+ proxy.Completed += (sender, e) =>
{
eventArgs = e;
- wait.Set();
+ eventPulled.Set();
};
+
proxy.Connect(server.LocalEndPoint);
- Assert.True(wait.WaitOne(5000));
- Assert.Null(eventArgs.Exception);
- Assert.True(eventArgs.Connected);
+ var proxyServerPeer = awaitAtProxyServer.GetAwaiter().GetResult();
+ Assert.Equal($"CONNECT 127.0.0.1:{((IPEndPoint)server.LocalEndPoint).Port} HTTP/1.1\r\n", ReadLine(proxyServerPeer));
+ Assert.Equal($"Host: 127.0.0.1:{((IPEndPoint)server.LocalEndPoint).Port}\r\n", ReadLine(proxyServerPeer));
+ Assert.Equal($"Proxy-Connection: Keep-Alive\r\n", ReadLine(proxyServerPeer));
+ Assert.Equal($"\r\n", ReadLine(proxyServerPeer));
+
+ simulateResponseFromProxyServer(proxyServerPeer);
+
+ Assert.True(eventPulled.WaitOne(5000));
+
+ // This verification needs to be ran on xUnit thread.
+ // Otherwise xUnit cannot identify which test is failure.
+ verifyCompletedEvent(eventArgs);
+
+ if (eventArgs.Connected)
+ {
+ testCopyDataFromLeftToRight?.Invoke(proxyServerPeer, eventArgs.Socket);
+ }
+ }
+
+ void SendAndReceiveHello(Socket left, Socket right)
+ {
+ var echoMessage = $"HELLO, it is {DateTime.Now.Ticks} now!\r\n";
+
+ SendLine(left, echoMessage);
+ Assert.Equal(echoMessage, ReadLine(right));
+ }
+
+ void SendLine(Socket socket, string line)
+ {
+ Thread.Sleep(100);
+ socket.Send(Encoding.ASCII.GetBytes(line));
}
- Socket CreateSimplyRespond(byte[] data)
+ string ReadLine(Socket socket)
{
- var socket = NewTcpLocalBound();
- Task.Run(
- () =>
+ byte[] lineBuff = new byte[1024];
+ int at = 0;
+ while (true)
+ {
+ int received = socket.Receive(lineBuff, at, 1, SocketFlags.None);
+ if (received < 0)
{
- var stream = socket.Accept();
- stream.Send(data);
- stream.Shutdown(SocketShutdown.Both);
- socket.Dispose();
+ break;
}
- );
-
- return socket;
+ if (lineBuff[at] == 10)
+ {
+ at++;
+ break;
+ }
+ at++;
+ }
+ return Encoding.ASCII.GetString(lineBuff, 0, at);
}
- Socket NewTcp() => new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
-
- Socket NewTcpLocalBound()
+ Socket NewTcpPeer()
{
- var socket = NewTcp();
+ var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
socket.Listen(1);
return socket;
diff --git a/Test/README.md b/Test/README.md
new file mode 100644
index 0000000..27996b5
--- /dev/null
+++ b/Test/README.md
@@ -0,0 +1,29 @@
+# Test
+
+Open `../global.json`, and remove `sdk`, and save like:
+
+```json
+{
+ "projects": [
+ ".", "SuperSocket.ClientEngine", "Test"
+ ]
+}
+```
+
+Launch test by `dotnet test`
+
+```bat
+D:\Proj\SuperSocket.ClientEngine\Test>dotnet test
+???????????????????????...
+???????????
+
+D:\Proj\SuperSocket.ClientEngine\Test\bin\Debug\netcoreapp1.0\Test.dll(.NETCoreApp,Version=v1.0) ??????
+Microsoft (R) Test Execution Command Line Tool Version 15.9.0
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Starting test execution, please wait...
+
+Total tests: 14. Passed: 14. Failed: 0. Skipped: 0.
+Test Run Successful.
+Test execution time: 4.7565 Seconds
+```
diff --git a/Test/SearchMarkTest.cs b/Test/SearchMarkTest.cs
index 593cec0..15eb4a9 100644
--- a/Test/SearchMarkTest.cs
+++ b/Test/SearchMarkTest.cs
@@ -74,5 +74,32 @@ public void TestMatchSecondTimeWithSearchMarkState()
Assert.Equal(0, second.SearchMark(0, second.Length, searchState));
}
}
+
+
+ [Fact]
+ public void Test()
+ {
+ byte[] first = Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n");
+ byte[] second = Encoding.ASCII.GetBytes("Proxy-agent: Apache/2.2.29 (Win32)\r\n\r\n");
+
+ byte[] mark = Encoding.ASCII.GetBytes("\r\n\r\n");
+
+ var searchState = new SearchMarkState(mark);
+
+ {
+ // -1 means: not matched, or partially matched.
+ Assert.Equal(-1, first.SearchMark(0, first.Length, searchState));
+
+ // Check if (1 <= searchState.Matched) in case of partial match.
+ }
+ {
+ var prevMatched = searchState.Matched;
+ Assert.Equal(prevMatched, 2);
+
+ // "\r\n\r\n" is matched on second buffer at second[34] to [37].
+ // So prevMatched should be ignored this time.
+ Assert.Equal(34, second.SearchMark(0, second.Length, searchState));
+ }
+ }
}
}