public void PrefetchedBytes_ReadHeaderAndSkipBody() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET / HTTP/1.1", "Host: www.example.org", "Content-Length: 3", "" ); sample.AppendBody("ABC"); // insert 0-length body message in the middle of sequence // In prefetched bytes processing, 0-length body handling is mistakable. sample.AppendHeader( "HEAD / HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); sample.AppendHeader( "CONNECT www.example.org:443 HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); // The body length should be in range of 'tiny' length. // "prefetched bytes" occurs when the body is 'tiny' length for simple body. // In this case, whole three messages are on one memory block. Debug.Assert(sample.SampleWriterPosition < ComponentFactory.MemoryBlockCache.MemoryBlockSize); // ACT & ASSERT int counter = 0; int actualMessageCount = TestReadHeaderAndSkipBody(sample, (request) => { switch (counter) { case 0: Assert.Equal("GET", request.Method); Assert.Equal(3, request.ContentLength); break; case 1: Assert.Equal("HEAD", request.Method); Assert.Equal(0, request.ContentLength); break; case 2: Assert.Equal("CONNECT", request.Method); Assert.Equal(0, request.ContentLength); break; } ++counter; }); Assert.Equal(3, actualMessageCount); sample.AssertAllSampleBytesRead(); } }
public void PrefetchedBytes_ReadHeaderAndSkipBody() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "HTTP/1.1 200 OK", "Content-Length: 7", "" ); sample.AppendBody("1234567"); // insert 0-length body message in the middle of sequence // In prefetched bytes processing, 0-length body handling is mistakable. sample.AppendHeader( "HTTP/1.1 302 Found", "" ); sample.AppendBody(MessageSample.EmptyBody); sample.AppendHeader( "HTTP/1.1 404 Not Found", "Content-Length: 0", "" ); sample.AppendBody(MessageSample.EmptyBody); // The body length should be in range of 'tiny' length. // "prefetched bytes" occurs when the body is 'tiny' length for simple body. // In this case, whole three messages are on one memory block. Debug.Assert(sample.SampleWriterPosition < ComponentFactory.MemoryBlockCache.MemoryBlockSize); // ACT & ASSERT int counter = 0; int actualMessageCount = TestReadHeaderAndSkipBody(sample, (response) => { switch (counter) { case 0: Assert.Equal(200, response.StatusCode); Assert.Equal(7, response.ContentLength); break; case 1: Assert.Equal(302, response.StatusCode); Assert.Equal(0, response.ContentLength); break; case 2: Assert.Equal(404, response.StatusCode); Assert.Equal(0, response.ContentLength); break; } ++counter; }); Assert.Equal(3, actualMessageCount); sample.AssertAllSampleBytesRead(); } }
public void Modification_remove() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET / HTTP/1.1", "Host: www.example.org", "Proxy-Authorization: dXNlcjpwYXNz", "" ); sample.AppendBody(MessageSample.EmptyBody); string expectedOutput = string.Join( MessageSample.CRLF, // separator "GET / HTTP/1.1", "Host: www.example.org", "", MessageSample.EmptyBody ); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { // remove the existing field request.AddModification( request.ProxyAuthorizationSpan, (modifier) => true // remove ); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualTo(expectedOutput); } }
public void Modification_append() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET / HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); string expectedOutput = string.Join( MessageSample.CRLF, // separator "GET / HTTP/1.1", "Host: www.example.org", "X-Test: dummy", "", MessageSample.EmptyBody ); Func <Modifier, bool> handler = (modifier) => { modifier.WriteASCIIString("X-Test: dummy", appendCRLF: true); return(true); }; // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { // append a field request.AddModification(request.EndOfHeaderFields, handler); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualTo(expectedOutput); } }
public void Body_Tiny() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "PUT / HTTP/1.1", "Host: www.example.org:80", "Content-Length: 5", "" ); sample.AppendBody("12345"); // The body length should be in range of 'tiny' length. // That is, the whole message must be stored in one memory block. // In a Request object, 'tiny' length body is stored in the rest of header's memory block. Debug.Assert(sample.SampleWriterPosition < ComponentFactory.MemoryBlockCache.MemoryBlockSize); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { Assert.Equal(5, request.ContentLength); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void Modification_suppressModification() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET http://www.example.org/test/index.html?abc=def HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { request.AddModification( request.RequestTargetSpan, (modifier) => { modifier.WriteASCIIString(request.TargetUri.PathAndQuery); return(true); } ); }, request: null, suppressModification: true); // suppressModification! Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void RequestTarget_AuthorityForm_without_port() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "CONNECT www.example.org HTTP/1.1", "Host: www.example.org:400", // dare to give different value from request-target for test "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { // HostEndPoint and Host are derived from the request-target. Assert.Equal(new DnsEndPoint("www.example.org", 443), request.HostEndPoint); Assert.Equal("www.example.org:443", request.Host); // HostSpan is the span of the actual Host field. Assert.Equal(new Span(34, 61), request.HostSpan); // TargetUri is null for request-target of authority-form Assert.Equal(null, request.TargetUri); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void RequestTarget_AbsoluteForm() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET http://www.example.org/abc/def?ghij=kl HTTP/1.1", "Host: test.example.org:123", // dare to give different value from request-target for test "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { // HostEndPoint and Host are derived from the request-target. Assert.Equal(new DnsEndPoint("www.example.org", 80), request.HostEndPoint); Assert.Equal("www.example.org:80", request.Host); // HostSpan is the span of the actual Host field. Assert.Equal(new Span(53, 81), request.HostSpan); Assert.Equal(new Uri("http://www.example.org/abc/def?ghij=kl"), request.TargetUri); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void Modification_change() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET http://www.example.org/test/index.html?abc=def HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); string expectedOutput = string.Join( MessageSample.CRLF, // separator "GET /test/index.html?abc=def HTTP/1.1", "Host: www.example.org", "", MessageSample.EmptyBody ); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { // change the existing field request.AddModification( request.RequestTargetSpan, (modifier) => { modifier.WriteASCIIString(request.TargetUri.PathAndQuery); return(true); } ); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualTo(expectedOutput); } }
public void Header_Large() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "HTTP/1.1 200 OK" ); for (int i = 0; i < 50; ++i) { sample.AppendText($"X-Test-{i}: 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", appendCRLF: true); } sample.AppendHeader( "" ); // This sample header to be tested must consume more than two memory blocks. Debug.Assert(ComponentFactory.MemoryBlockCache.MemoryBlockSize < sample.SampleWriterPosition); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (response) => { Assert.Equal(200, response.StatusCode); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void Modification_append_samepoint() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET / HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); // keep the order of X-Test-1, X-Test-2 and X-Test-3. string expectedOutput = string.Join( MessageSample.CRLF, // separator "GET / HTTP/1.1", "Host: www.example.org", "X-Test-1: dummy", "X-Test-2: dummy", "X-Test-3: dummy", "", MessageSample.EmptyBody ); Func <Modifier, bool> handler1 = (modifier) => { modifier.WriteASCIIString("X-Test-1: dummy", appendCRLF: true); return(true); }; Func <Modifier, bool> handler2 = (modifier) => { modifier.WriteASCIIString("X-Test-2: dummy", appendCRLF: true); return(true); }; Func <Modifier, bool> handler3 = (modifier) => { modifier.WriteASCIIString("X-Test-3: dummy", appendCRLF: true); return(true); }; // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { Span span = request.EndOfHeaderFields; Debug.Assert(span.Length == 0); // add at the same point request.AddModification(span, handler1); request.AddModification(span, handler2); request.AddModification(span, handler3); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualTo(expectedOutput); } }
public void StatusCode_TooShortDigits() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "HTTP/1.1 20 Invalid Status Code", // status-code: too short digits! "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT HttpException exception = Assert.Throws <HttpException>( () => TestReadAndWrite(sample) ); Assert.Equal(HttpStatusCode.BadGateway, exception.HttpStatusCode); } }
public void Version_LowerCaseHTTPName() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "http/1.1 200 OK", // HTTP-version: invalid! "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT HttpException exception = Assert.Throws <HttpException>( () => TestReadAndWrite(sample) ); Assert.Equal(HttpStatusCode.BadGateway, exception.HttpStatusCode); } }
public void Version_InvalidDigits() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET / HTTP/1.1.2", // HTTP-version: invalid! "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT HttpException actual = Assert.Throws <HttpException>( () => TestReadAndWrite(sample) ); Assert.Equal(HttpStatusCode.BadRequest, actual.HttpStatusCode); } }
public void StatusCode_407() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "HTTP/1.1 407 Proxy Authentication Required", "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (response) => { Assert.Equal(407, response.StatusCode); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void SkipBody() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "HTTP/1.1 200 OK", "Content-Length: 7", "" ); sample.AppendBody("ABCDEFG"); // ACT & ASSERT int actualMessageCount = TestReadHeaderAndSkipBody(sample, (response) => { Assert.Equal(7, response.ContentLength); }); Assert.Equal(1, actualMessageCount); sample.AssertAllSampleBytesRead(); } }
public void Version_10() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "HTTP/1.0 200 OK", "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (response) => { Assert.Equal(new Version(1, 0), response.Version); Assert.Equal(200, response.StatusCode); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void Method_CONNECT() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "CONNECT www.example.org:443 HTTP/1.1", "Host: www.example.org:443", "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { Assert.Equal("CONNECT", request.Method); Assert.Equal(true, request.IsConnectMethod); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void Simple() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET / HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { Assert.Equal("GET", request.Method); Assert.Equal(new Version(1, 1), request.Version); Assert.Equal("www.example.org:80", request.Host); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void Method_POST() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "POST / HTTP/1.1", "Host: www.example.org", "Content-Length: 3", "" ); sample.AppendBody("123"); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { Assert.Equal("POST", request.Method); Assert.Equal("www.example.org:80", request.Host); Assert.Equal(3, request.ContentLength); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void Modification_cancel() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET / HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { request.AddModification( request.HostSpan, (modifier) => false // cancel modification! ); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void RequestTarget_AsteriskForm() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "OPTIONS * HTTP/1.1", "Host: www.example.org:80", "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { Assert.Equal(new DnsEndPoint("www.example.org", 80), request.HostEndPoint); Assert.Equal("www.example.org:80", request.Host); Assert.Equal(new Span(20, 46), request.HostSpan); // TargetUri is null for request-target of asterisk-form Assert.Equal(null, request.TargetUri); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void RequestTarget_OriginForm() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET /abc/def?ghi=kl HTTP/1.1", // origin-form "Host: www.example.org:81", "" ); sample.AppendBody(MessageSample.EmptyBody); // ACT & ASSERT int actualMessageCount = TestReadAndWrite(sample, (request) => { Assert.Equal(new DnsEndPoint("www.example.org", 81), request.HostEndPoint); Assert.Equal("www.example.org:81", request.Host); Assert.Equal(new Span(30, 56), request.HostSpan); // TargetUri is null for request-target of origin-form Assert.Equal(null, request.TargetUri); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void Redirect_Tiny() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "HTTP/1.1 200 OK", "Content-Length: 10", "" ); sample.AppendBody("0123456789"); // The body length should be in range of 'tiny' length. // That is, the whole message must be stored in one memory block. // In a Response object, 'tiny' length body is stored in the rest of header's memory block. Debug.Assert(sample.SampleWriterPosition < ComponentFactory.MemoryBlockCache.MemoryBlockSize); // ACT & ASSERT int actualMessageCount = TestReadHeaderAndRedirect(sample, (response) => { Assert.Equal(10, response.ContentLength); }); Assert.Equal(1, actualMessageCount); sample.AssertOutputEqualToSample(); } }
public void PrefetchedBytes_InputReconnect() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "GET / HTTP/1.1", "Host: www.example.org", "Content-Length: 3", "" ); sample.AppendBody("ABC"); sample.AppendHeader( "HEAD / HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); long thirdMessageOffset = sample.SampleWriterPosition; sample.AppendHeader( "CONNECT www.example.org:443 HTTP/1.1", "Host: www.example.org", "" ); sample.AppendBody(MessageSample.EmptyBody); // The body length should be in range of 'tiny' length. // "prefetched bytes" occurs when the body is 'tiny' length for simple body. // In this case, whole three messages are on one memory block. Debug.Assert(sample.SampleWriterPosition < ComponentFactory.MemoryBlockCache.MemoryBlockSize); // the second message will be skipped string expectedOutput = string.Join( MessageSample.CRLF, // separator "GET / HTTP/1.1", "Host: www.example.org", "Content-Length: 3", "", "ABCCONNECT www.example.org:443 HTTP/1.1", // Note no CRLF at the end of the body "Host: www.example.org", "", MessageSample.EmptyBody ); // ACT & ASSERT int counter = 0; int actualMessageCount = TestReadAndWrite(sample, (request) => { switch (counter) { case 0: Assert.Equal("GET", request.Method); Assert.Equal(3, request.ContentLength); // move to the third message // It causes InputReconnected event. sample.ChangeSampleReaderPosition(thirdMessageOffset); break; case 1: // the third message instead of the second message Assert.Equal("CONNECT", request.Method); Assert.Equal(0, request.ContentLength); break; } ++counter; }); Assert.Equal(2, actualMessageCount); sample.AssertOutputEqualTo(expectedOutput); } }
public void PrefetchedBytes_InputReconnect() { using (MessageSample sample = CreateMessageSample()) { // ARRANGE sample.AppendHeader( "HTTP/1.1 200 OK", "Content-Length: 7", "" ); sample.AppendBody("1234567"); sample.AppendHeader( "HTTP/1.1 302 Found", "" ); sample.AppendBody(MessageSample.EmptyBody); long thirdMessageOffset = sample.SampleWriterPosition; sample.AppendHeader( "HTTP/1.1 404 Not Found", "Content-Length: 0", "" ); sample.AppendBody(MessageSample.EmptyBody); // The body length should be in range of 'tiny' length. // "prefetched bytes" occurs when the body is 'tiny' length for simple body. // In this case, whole three messages are on one memory block. Debug.Assert(sample.SampleWriterPosition < ComponentFactory.MemoryBlockCache.MemoryBlockSize); // the second message will be skipped string expectedOutput = string.Join( MessageSample.CRLF, // separator "HTTP/1.1 200 OK", "Content-Length: 7", "", "1234567HTTP/1.1 404 Not Found", // Note no CRLF at the end of the body "Content-Length: 0", "", MessageSample.EmptyBody ); // ACT & ASSERT int counter = 0; int actualMessageCount = TestReadAndWrite(sample, (response) => { switch (counter) { case 0: Assert.Equal(200, response.StatusCode); Assert.Equal(7, response.ContentLength); // move to the third message // It causes InputReconnected event. sample.ChangeSampleReaderPosition(thirdMessageOffset); break; case 1: // the third message instead of the second message Assert.Equal(404, response.StatusCode); Assert.Equal(0, response.ContentLength); break; } ++counter; }); Assert.Equal(2, actualMessageCount); sample.AssertOutputEqualTo(expectedOutput); } }