Esempio n. 1
0
        private void SendGZipResponse(HeyHttpResponse response)
        {
            using (MemoryStream compressedStream = new MemoryStream())
            {
                using (GZipStream gzipStream = new GZipStream(compressedStream, CompressionMode.Compress))
                {
                    response.ContentStream.CopyTo(gzipStream);

                    // Not sure why, but gzip stream must be close before getting the data.
                    gzipStream.Close();

                    byte[] bytes = compressedStream.ToArray();

                    // Send response headers.
                    response.Headers.Add("Content-Encoding: gzip");
                    response.Headers.Add("Content-Length: " + bytes.Length);
                    response.Headers.Add("Content-Type: " + GetMediaType(request.Path));
                    response.CopyHeadersTo(networkStream);

                    //compressedStream.CopyTo(networkStream);
                    //networkStream.Flush();

                    // For debugging.
                    networkStream.Write(bytes, 0, bytes.Length);
                    logger.WriteBodyLine(BitConverter.ToString(bytes));
                }
            }
        }
Esempio n. 2
0
        private void ForkSendResponse(HeyHttpResponse response)
        {
            // Fork gzip response.
            if (request.QueryStringHasTrueValue("gzip"))
            {
                SendGZipResponse(response);
                return;
            }

            // Fork slow response.
            int delayInMilliseconds;

            if (request.QueryStringHas("slow", out delayInMilliseconds))
            {
                SendSlowResponse(response, delayInMilliseconds, HttpResponseOption.Slow);
                return;
            }

            // Fork slow response with ReadLine pauses.
            if (request.QueryStringHasTrueValue("pause"))
            {
                SendSlowResponse(response, 0, HttpResponseOption.Pause);
                return;
            }

            // Fork chunked response.
            if (request.QueryStringHasTrueValue("chunked"))
            {
                SendChunkedResponse(response);
                return;
            }

            // Send normal response.
            SendSlowResponse(response, 0, HttpResponseOption.None);
        }
Esempio n. 3
0
        private static void Reply407AndClose(HeyLogger logger, NetworkStream clientStream)
        {
            HeyHttpResponse response = new HeyHttpResponse(logger);

            response.Version = "HTTP/1.0";
            response.Status  = "407 Proxy Authentication Required";
            response.Headers.Add("Proxy-agent: Netscape-Proxy/1.1");
            response.Headers.Add("Proxy-Authenticate: Basic realm=\"WallyWorld\"");
            response.Headers.Add("Content-Length: 0");
            response.Headers.Add("Connection: close");
            response.CopyHeadersTo(clientStream);
            clientStream.Close();
        }
Esempio n. 4
0
        private void AcceptCore()
        {
            requestsCount = 0;
            keepAlive     = true;
            while (keepAlive)
            {
                using (logFile = new HttpLogFile(clientSocket.RemoteEndPoint, requestsCount + 1))
                {
                    ReadRequestHeaders();
                    ReadRequestContent();

                    keepAlive = request.IsKeepAlive;

                    HeyHttpResponse response = new HeyHttpResponse(logger)
                    {
                        Status      = "200 OK",
                        IsKeepAlive = request.IsKeepAlive
                    };
                    SendResponse(response);
                    requestsCount++;
                }
            }
        }
Esempio n. 5
0
        private static void OnAccept(IAsyncResult asyncResult)
        {
            HeyLogger logger         = new HeyLogger();
            Socket    socketListener = asyncResult.AsyncState as Socket;
            Socket    client         = socketListener.EndAccept(asyncResult);

            socketListener.BeginAccept(OnAccept, asyncResult.AsyncState);

            try
            {
                HeyHttpRequest request = new HeyHttpRequest(logger);

                // Client got connected.
                logger.WriteLine(String.Format("Connected: {0}", client.RemoteEndPoint));

                // Read HTTP headers.
                NetworkStream clientStream = new NetworkStream(client);
                MemoryStream  tempStream   = new MemoryStream();
                request.ReadHeaders(clientStream, tempStream);

                // Authentication.
                if (settings.AuthenticationRequired && !IsAuthenticated(request))
                {
                    Reply407AndClose(logger, clientStream);
                    return;
                }

                // Find server host name.
                logger.WriteLine(String.Format("Trying to connect to {0}", request.Host));

                // Get IP address for the given server hostname.
                IPHostEntry hostEntry = Dns.GetHostEntry(request.Host);
                if (hostEntry.AddressList.Length == 0)
                {
                    throw new Exception("Unknow server hostname.");
                }
                IPAddress address = hostEntry.AddressList[0];

                // Connect to server.
                Socket server = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                server.Connect(new IPEndPoint(address, request.Port));

                logger.WriteLine(String.Format("Connected to {0}", request.Host));

                // Send cached data to server.
                NetworkStream serverStream = new NetworkStream(server);

                // When using the CONNECT method, proxy must send a HTTP response to client.
                // See "Tunneling TCP based protocols through Web proxy servers" internet-draft.
                if (request.Method == "CONNECT")
                {
                    HeyHttpResponse response = new HeyHttpResponse(logger);
                    response.Status = "200 Connection established";
                    response.Headers.Add("Proxy-agent: Happy-Sockets-Proxy/1.0");
                    response.CopyHeadersTo(clientStream);
                }
                else
                {
                    // Only forward headers when it is not a CONNECT request.
                    tempStream.Seek(0, SeekOrigin.Begin);
                    tempStream.CopyTo(serverStream);
                }

                // Forward data.
                ParameterizedThreadStart serverToClientStart = new ParameterizedThreadStart(ForwardData);
                Thread serverToClientThread = new Thread(serverToClientStart);
                serverToClientThread.Start(
                    new StreamsPair
                {
                    StreamA = serverStream,
                    StreamB = clientStream,
                    Label   = "server to client",
                    Logger  = logger
                });

                ParameterizedThreadStart clientToServerStart = new ParameterizedThreadStart(ForwardData);
                Thread clientToServerThread = new Thread(clientToServerStart);
                clientToServerThread.Start(
                    new StreamsPair
                {
                    StreamA = clientStream,
                    StreamB = serverStream,
                    Label   = "client to server",
                    Logger  = logger
                });

                //serverToClientThread.Join();
                //clientToServerThread.Join();

                // TODO: make sure streams do not go out of scope.
                // TODO: wait until threads end and ensure connections are close.
            }
            catch (SocketException ex)
            {
                WriteLine(ConsoleColor.Red, ex.Message);
                logger.WriteLine(ex.ToString());

                // We couldn't connect to the  server, terminate connection with the client.
                client.Close();
            }
            catch (IOException ex)
            {
                WriteLine(ConsoleColor.Red, ex.Message);
                logger.WriteLine(ex.ToString());

                // The client closed the connection, terminate the connection with the server.
                client.Close();
            }
            catch (Exception ex)
            {
                WriteLine(ConsoleColor.Red, ex.Message);
                logger.WriteLine(ex.ToString());
            }
        }
Esempio n. 6
0
        private void SendChunkedResponse(HeyHttpResponse response)
        {
            Stream stream = response.ContentStream;

            // Send headers.
            response.Headers.Add("Transfer-Encoding: chunked");
            response.Headers.Add("Trailer: \"Foo\"");
            response.Headers.Add("Content-Type: " + GetMediaType(request.Path));
            response.CopyHeadersTo(networkStream);

            // Send a random amount of bytes.
            Random random = new Random();

            int bytesRead = 0;

            while (bytesRead < stream.Length)
            {
                byte[] buffer;
                int    chunkSize = random.Next(1, MaxChunckSize);

                if (stream.Length - bytesRead < chunkSize)
                {
                    chunkSize = (int)stream.Length - bytesRead;
                }

                // Send chunck size and CRLF.
                string chunkSizeString = String.Format("{0:X}\r\n", chunkSize);
                buffer = Encoding.ASCII.GetBytes(chunkSizeString);
                networkStream.Write(buffer, 0, buffer.Length);

                // Send chunck.
                buffer = new byte[chunkSize];
                int localBytesRead = stream.Read(buffer, 0, buffer.Length);
                networkStream.Write(buffer, 0, localBytesRead);

                // Send CRLF.
                buffer = Encoding.ASCII.GetBytes("\r\n");
                networkStream.Write(buffer, 0, buffer.Length);

                networkStream.Flush();

                bytesRead += localBytesRead;
                logger.WriteBodyLine(
                    String.Format("Chunk of {0,13:N0} bytes, {1,13:N0} of {2,13:N0} bytes sent.",
                                  chunkSize,
                                  bytesRead,
                                  stream.Length));

                Thread.Sleep(100);
            }

            StringBuilder theEnd = new StringBuilder();

            // Add last chunk.
            theEnd.Append("0\r\n");

            // Add trailer.
            theEnd.Append("Foo: Bar\r\n");

            // Add last CRLF.
            theEnd.Append("\r\n");

            byte[] lastChunkBuffer = Encoding.ASCII.GetBytes(theEnd.ToString());
            networkStream.Write(lastChunkBuffer, 0, lastChunkBuffer.Length);
        }
Esempio n. 7
0
        private void SendSlowResponse(
            HeyHttpResponse response,
            int delayInMilliseconds,
            HttpResponseOption option)
        {
            Stream stream = response.ContentStream;

            long idleLength;

            if (!request.QueryStringHas("idleLength", out idleLength))
            {
                idleLength = -1;
            }

            // Calculate positions.

            long totalBytesRead = 0;
            long firstPosition  = 0;

            if (response.FirstPosition > 0)
            {
                firstPosition   = response.FirstPosition;
                stream.Position = firstPosition;
            }

            long length = response.ContentStream.Length;

            if (response.LastPosition > 0)
            {
                length = response.LastPosition - firstPosition + 1;
            }

            // Send headers.
            response.Headers.Add("Content-Length: " + length);
            response.Headers.Add("Content-Type: " + GetMediaType(request.Path));
            response.CopyHeadersTo(networkStream);

            // Server must not send a message body when using HEAD method (RFC 7231 4.3.2 HEAD).
            if (request.IsHeadMethod)
            {
                return;
            }

            // Calulate buffer size.
            int bufferLength;

            if (!request.QueryStringHas("bufferLength", out bufferLength))
            {
                bufferLength = 1000000; // 1 MB
            }
            byte[] buffer = new byte[bufferLength];

            while (totalBytesRead < length)
            {
                long remainingBytes = length - totalBytesRead;
                int  loopLength     = (int)Math.Min(bufferLength, remainingBytes);
                int  localBytesRead = stream.Read(buffer, 0, loopLength);

                if (option == HttpResponseOption.Pause)
                {
                    BlockThreadUntilEnterIsPressed();
                }

                if (option == HttpResponseOption.Slow)
                {
                    Thread.Sleep(delayInMilliseconds);
                }

                networkStream.Write(buffer, 0, localBytesRead);
                networkStream.Flush();

                totalBytesRead += localBytesRead;
                logger.WriteBodyLine(String.Format(
                                         "{0,13:N0} of {1,13:N0} bytes sent.",
                                         totalBytesRead,
                                         length));

                if (idleLength >= 0 && totalBytesRead >= idleLength)
                {
                    BlockThreadUntilClientIsDisconnected();
                }
            }

            logger.WriteBodyLine("Response completed!\r\n");
        }
Esempio n. 8
0
        private void SendResponse(HeyHttpResponse response)
        {
            try
            {
                // Set default content.
                response.ContentStream = CreateDefaultResponseStream();

                // Do authentication.
                HeyHttpAuthentication.AddAuthenticateHeaderIfNeeded(request, response);

                // Custom headers.
                if (request.QueryStringHasTrueValue("custom"))
                {
                    response.Headers.Add("X-Header: Value1");
                    response.Headers.Add("X-Header: Value2");
                    response.Headers.Add("X-Header: Value3");
                }

                // Show the 'Save As' dialog.
                string filename;
                if (request.QueryStringHas("filename", out filename))
                {
                    response.Headers.Add(String.Format("Content-Disposition: Attachment; filename={0}", filename));
                }

                // 201 Created for AtomPub.
                if (request.QueryStringHasTrueValue("create"))
                {
                    response.Status = "201 Created";
                    response.Headers.Add("Location: http://heyhttp.org/Data/first-post.atom");
                    response.Headers.Add("Content-Location: http://heyhttp.org/Data/first-post.atom");
                    response.Headers.Add("ETag: \"e180ee84f0671b1\"");
                }

                // 301 Moved Permanently.
                string redirect;
                if (request.QueryStringHas("redirect", out redirect))
                {
                    response.Status = "301 Moved Permanently";

                    response.Headers.Add("Location: " + redirect);

                    // Do not send any data back.
                    request.Path = "";
                }

                // 503 Service Unavailable, retry after N seconds.
                if (request.QueryStringHasTrueValue("retry"))
                {
                    if (!retryReceivedBefore)
                    {
                        response.Status = "503 Service Unavailable";
                        response.Headers.Add("Retry-After: 5");
                        response.ContentStream = GetStringStream(String.Empty);
                    }
                    retryReceivedBefore = !retryReceivedBefore;
                }

                // Set cache headers.
                if (request.QueryStringHasTrueValue("cache"))
                {
                    response.Headers.Add("Cache-Control: max-age=3600"); // One hour (60 minutes * 60 seconds)

                    //additionalHeaders.Add("Expires: " + DateTime.UtcNow.AddHours(24).ToString("R"));
                }
                else if (request.QueryStringHasTrueValue("nocache"))
                {
                    response.Headers.Add("Cache-Control: no-cache"); // HTTP 1.1
                }

                // Set cookie header.
                if (request.QueryStringHasTrueValue("setcookie"))
                {
                    response.Headers.Add("Set-Cookie: sessionTestCookie=X");
                    response.Headers.Add("Set-Cookie: persistentTestCookie=Y; expires=Wednesday, 09-Nov-2020 23:12:40 GMT");
                    response.Headers.Add("Set-Cookie: httpOnlyTestCookie=X; expires=Wednesday, 09-Nov-2020 23:12:40 GMT; HttpOnly");
                    response.Headers.Add("Set-Cookie: subdomainTestCookie=ghi; expires=Wednesday, 09-Nov-2020 23:12:40 GMT; domain=foo.heyhttp.org");
                    response.Headers.Add("Set-Cookie: slashEndingCookie=b; expires=Wednesday, 09-Nov-2020 23:12:40 GMT; domain=heyhttp.org; path=/foo/");
                    response.Headers.Add("Set-Cookie: nonSlashEndingCookie=a; expires=Wednesday, 09-Nov-2020 23:12:40 GMT; domain=heyhttp.org; path=/foo");
                }

                // Return an specific HTTP status.
                int requestedStatus;
                if (request.QueryStringHas("status", out requestedStatus))
                {
                    response.Status = String.Format("{0} Foo Foo Bar", requestedStatus);
                }

                // Set arbitrary header.
                string headerName;
                string headerValue;
                if (request.QueryStringHas("name", out headerName) && request.QueryStringHas("value", out headerValue))
                {
                    response.Headers.Add(String.Format("{0}: {1}", headerName, headerValue));
                }

                // Introduce a long delay.
                int delay;
                if (request.QueryStringHas("delay", out delay))
                {
                    Thread.Sleep(delay);
                }

                // Get trace content stream.
                if (request.QueryStringHasTrueValue("trace"))
                {
                    response.ContentStream = GetTraceStream();

                    // Do not send any data back.
                    request.Path = "";
                }


                // Get file.
                if (!String.IsNullOrEmpty(request.Path) &&
                    !String.IsNullOrEmpty(response.Status) &&
                    request.Path != "/" &&
                    !response.Status.StartsWith("404"))
                {
                    try
                    {
                        response.ContentStream = GetFileStream();
                    }
                    catch (FileNotFoundException ex)
                    {
                        response.Status        = "404 Not Found";
                        response.ContentStream = GetStringStream(ex.Message);
                    }
                    catch (DirectoryNotFoundException ex)
                    {
                        response.Status        = "404 Not Found";
                        response.ContentStream = GetStringStream(ex.Message);
                    }
                }

                // Create a response of the length given.
                long sizeInBytes;
                if (request.QueryStringHas("length", out sizeInBytes))
                {
                    response.ContentStream = CreateBigStream(sizeInBytes);
                }

                // This check must be done after the response coontent has been selected.
                string eTag;
                bool   useETag = request.QueryStringHas("etag", out eTag);
                if (useETag)
                {
                    //// 304 Not Modified with ETag.
                    //if (request.GetHeader("If-None-Match") == eTag)
                    //{
                    //    response.Status = "304 Not Modified";
                    //    response.ContentStream = GetStringStream(String.Empty);
                    //}
                    //else
                    //{
                    //}

                    // This 'if' is to manually change the flow when debugging.
                    if (DateTime.Now.Second == -1)
                    {
                        eTag                   = String.Empty;
                        response.Status        = "404 NOT FOUND";
                        response.ContentStream = GetStringStream("Oops!");
                    }
                    else
                    {
                        response.Headers.Add(String.Format("ETag: \"{0}\"", eTag));
                        response.Headers.Add("Accept-Ranges: bytes");
                    }
                }

                bool useLastModified = request.QueryStringHasTrueValue("lastModified");
                if (useLastModified)
                {
                    response.Headers.Add("Last-Modified: Thu, 21 Aug 2014 21:34:57 GMT");
                    response.Headers.Add("Accept-Ranges: bytes");
                }

                HttpRangeHeader rangeHeader = request.GetRange();
                if (rangeHeader != null && (useETag || useLastModified))
                {
                    response.Status = "206 Partial Content";
                    response.Headers.Add(rangeHeader.GetContentRange(response.ContentStream.Length));
                    response.FirstPosition = rangeHeader.FirstPosition;
                    response.LastPosition  = rangeHeader.LastPosition;
                }

                ForkSendResponse(response);
            }
            finally
            {
                // 'finally' block is executed even if a 'try' or 'catch' block contains a 'return'.
                if (response.ContentStream != null)
                {
                    response.ContentStream.Dispose();
                }
            }
        }
Esempio n. 9
0
        public static void AddAuthenticateHeaderIfNeeded(HeyHttpRequest request, HeyHttpResponse response)
        {
            // Basic Server:
            // GET --------->
            //
            // <--------- 401 Unauthorized
            //            WWW-Authenticate: Basic realm="xyz"
            //
            // GET --------->
            // Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
            //
            // <--------- 200 OK

            // Basic Proxy:
            //
            // GET --------->
            // <-------- 407 Proxy Authentication Required
            //           Proxy-Authenticate: Basic realm="xyz"
            //
            // GET --------->
            // Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
            //
            // <--------- 200 OK

            // NTLM:
            //
            // GET --------->
            // <--------- 401 Unauthorized
            // negotiate message --------->
            // <--------- challenge message
            // authenticate message --------->
            // <--------- 200 OK
            if (IsNtlmMessage(request, NtlmMessageType.NegotiatieMessage))
            {
                response.Status = "401 Unauthorized";
                response.Headers.Add("WWW-Authenticate: NTLM TlRMTVNTUAACAAAADgAOADgAAAAFgomiBTEwAGt4s6QAAAAAAAAAAPwA/ABGAAAABgLwIwAAAA9SAEUARABNAE8ATgBEAAIADgBSAEUARABNAE8ATgBEAAEAHgBXAEkATgAtAEQARgBHADEAMABFADIAOABLADEANgAEADQAcgBlAGQAbQBvAG4AZAAuAGMAbwByAHAALgBtAGkAYwByAG8AcwBvAGYAdAAuAGMAbwBtAAMAVABXAEkATgAtAEQARgBHADEAMABFADIAOABLADEANgAuAHIAZQBkAG0AbwBuAGQALgBjAG8AcgBwAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAFACQAYwBvAHIAcAAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ABwAIADJX/d1Cjc0BAAAAAA==");
            }

            // Require basic access authentication.
            if (request.QueryStringHasTrueValue("basic") && !IsAuthorizationValid(request))
            {
                response.Status = "401 Unauthorized";
                response.Headers.Add("WWW-Authenticate: Basic realm=\"Secure Area\"");
                request.Path = "";
            }

            // Require digest access authentication.
            if (request.QueryStringHasTrueValue("digest") && !IsAuthorizationValid(request))
            {
                response.Status = "401 Unauthorized";
                response.Headers.Add(String.Format(
                                         "WWW-Authenticate: Digest realm=\"{0}\", qop=\"{1}\", nonce=\"{2}\", opaque=\"{3}\"",
                                         digestRealm,
                                         digestQop,
                                         digestNonce,
                                         digestOpaque));
                request.Path = "";
            }

            // Require NTLM credentials.
            if (request.QueryStringHasTrueValue("negotiate") && !IsAuthorizationValid(request))
            {
                response.Status = "401 Unauthorized";
                response.Headers.Add("WWW-Authenticate: Negotiate");
                request.Path = "";
            }

            // NTLM authentication.
            if (request.QueryStringHasTrueValue("ntlm") && !IsAuthorizationValid(request))
            {
                response.Status = "401 Unauthorized";
                response.Headers.Add("WWW-Authenticate: NTLM");
                request.Path = "";
            }
        }