示例#1
0
    // Perform the HTTP hit and return the result!
    public HttpResponse GetResponse()
    {
        //
        // Step 1: Connect to the remote server
        //
        Socket httpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
                                       ProtocolType.Tcp);

        string serverName, requestUri;
        int    serverPort;

        if (proxyPort > 0)
        {
            serverName = proxyHost;
            serverPort = proxyPort;
            requestUri = fRequestUri; // Use the full URI
        }
        else
        {
            serverName = fHost;
            serverPort = fHostPort;
            requestUri = fResource; // Use just the resource
        }

        IPAddress serverAddress;

        try {
            serverAddress = IPAddress.Parse(serverName);
        }
        catch (Exception) {
            // Hostname didn't parse as an IP address; try to resolve it
            IPHostEntry !entry     = (!)Dns.Resolve(serverName);
            IPAddress[] !addresses = (!)entry.AddressList;

            if (addresses.Length == 0)
            {
                throw new Exception("Couldn't resolve host name");
            }

            serverAddress = addresses[0];
        }

        IPEndPoint serverEndpoint = new IPEndPoint(serverAddress, serverPort);

        // Connect to the remote server
        httpSocket.Connect(serverEndpoint);

        //
        // Step 2: Formulate and transmit the request line and any supporting data
        //
        // Send the request line
        string requestLine = fMethod + " " + requestUri + " HTTP/1.1\r\nHost: " +
                             fHost + "\r\nConnection: Close\r\n";

        // Send an indication of the request data content type and size, if appropriate
        if (fRequestData != null)
        {
            requestLine += "Content-Length: " + fRequestData.Length + "\r\n" +
                           "Content-Type: " + fContentType + "\r\n";
        }

        // Add any user-specified headers
        foreach (string !headerName in fRequestHeaders.Keys)
        {
            requestLine += headerName + ": " + fRequestHeaders[headerName] + "\r\n";
        }

        requestLine += "\r\n";
        byte[] requestBytes = Encoding.ASCII.GetBytes(requestLine);
        httpSocket.Send(requestBytes);

        // Send any request data
        if (fRequestData != null)
        {
            httpSocket.Send(fRequestData);
        }

        // Signal we're done sending
        httpSocket.Shutdown(SocketShutdown.Send);

        //
        // Step 3: Parse the response line and headers
        //

        // Pump the response into an HttpHeadersParser
        ByteBuffer        scratchBuffer = new ByteBuffer();
        HttpHeadersParser headerParser = new HttpHeadersParser(scratchBuffer);
        bool         doneWithout100Continue = false, doneWithHeaders = false;
        int          nextWritePos = 0;
        HttpResponse response = null;

        do
        {
            while (!doneWithHeaders)
            {
                // Make 2K available for each read
                scratchBuffer.EnsureSize(nextWritePos + readIncrementSize);
                int numReadBytes = httpSocket.Receive(scratchBuffer.UnderlyingBuffer, nextWritePos,
                                                      scratchBuffer.Size - nextWritePos, SocketFlags.None);

                if (numReadBytes == 0)
                {
                    // We ran out of data before we finished parsing headers.
                    throw new Exception("Unexpected end of data");
                }

                nextWritePos += numReadBytes;

                if (headerParser.Pump(nextWritePos))
                {
                    // Finished parsing headers!
                    response        = headerParser.GetResponse();
                    doneWithHeaders = true;
                }
            }

            if (response == null)
            {
                throw new Exception("HTTP response data unexpectedly null");
            }

            // Special case: if we see a 100-continue, we want to carefully start
            // over at the next chunk of data!
            if (response.StatusCode == 100)
            {
                // Switch to a new scratch buffer that will contain the data
                // following the 100-continue.
                int        remainderBeginning = headerParser.BodyDataOffset;
                int        remainderLength    = nextWritePos - remainderBeginning;
                ByteBuffer newScratch         = new ByteBuffer();
                ByteBuffer oldScratch         = scratchBuffer;

                // Reset parsing by creating a new parser
                headerParser = new HttpHeadersParser(newScratch);

                // Switch over!
                scratchBuffer = newScratch;
                nextWritePos  = remainderLength;

                if (remainderLength > 0)
                {
                    // Copy the beginning of the next data chunk into newScratch
                    newScratch.EnsureSize(remainderLength);
                    Buffer.BlockCopy(oldScratch.UnderlyingBuffer, remainderBeginning,
                                     newScratch.UnderlyingBuffer, 0, remainderLength);

                    // Make sure the fragment we were already holding gets pumped in
                    if (headerParser.Pump(nextWritePos))
                    {
                        // Interestingly, we're already done
                        response = headerParser.GetResponse();
                        if (response == null)
                        {
                            throw new Exception("HTTP response data unexpectedly null");
                        }
                        Debug.Assert(response.StatusCode != 100);
                        doneWithout100Continue = true;
                    }
                    else
                    {
                        // Go back and carry on
                        doneWithHeaders = false;
                    }
                }
                else
                {
                    // The chunk we were chewing on exactly contained the 100-continue,
                    // so we definitely need to go back and read some more...
                    doneWithHeaders = false;
                }
            }
            else
            {
                // The response was not 100-continue, so we're all done
                doneWithout100Continue = true;
            }
        }while (!doneWithout100Continue);

        //
        // Step 4: Process the body data
        //
        // Now figure out how we're going to deal with the actual body
        // of the response. We almost certainly already have an initial
        // chunk of the body data in the scratchBuffer, since we read the
        // headers in 2K chunks
        //
        int bodyScratchBeginning = headerParser.BodyDataOffset;
        int scratchBodyLength    = nextWritePos - bodyScratchBeginning;

        string transferEncoding = response.GetHeader("Transfer-Encoding");

        if (transferEncoding != null &&
            transferEncoding.ToLower().Equals("chunked"))
        {
            // Chunked encoding is special: the beginning of the body data
            // already specifies some chunk information. Run this through our
            // chunk decoder to straighten it out.
            byte[] initialBodyChunk           = scratchBuffer.TrimAndCopy(bodyScratchBeginning, scratchBodyLength);
            ChunkedEncodingParser chunkParser = new ChunkedEncodingParser(initialBodyChunk, httpSocket);
            ByteBuffer            bodyData    = chunkParser.Run();
            response.BodyData = bodyData.TrimAndCopy(bodyData.Size);
        }
        else
        {
            ByteBuffer bodyBuffer;

            // Non-chunked encoding. First, move the beginning of the body
            // data to a new ByteBuffer for sanity.
            // Separate out any initial body data into a new ByteBuffer, for sanity
            int bodySize = (response.ContentLength > 0) ? response.ContentLength : bodySizeGuess;

            // Hard to imagine how this would happen...
            if (bodySize < scratchBodyLength)
            {
                bodySize = scratchBodyLength;
            }

            bodyBuffer = new ByteBuffer(bodySize);
            Buffer.BlockCopy(scratchBuffer.UnderlyingBuffer, bodyScratchBeginning,
                             bodyBuffer.UnderlyingBuffer, 0, scratchBodyLength);
            int nextBodyPos = scratchBodyLength;

            if (response.ContentLength > 0)
            {
                // Read until we've gotten exactly the expected number of bytes
                int bodyDataLeft = response.ContentLength - scratchBodyLength;

                while (bodyDataLeft > 0)
                {
                    int numReadBytes = httpSocket.Receive(bodyBuffer.UnderlyingBuffer, nextBodyPos,
                                                          bodyDataLeft, SocketFlags.None);

                    if (numReadBytes == 0)
                    {
                        throw new Exception("Connection closed unexpectedly");
                    }

                    nextBodyPos  += numReadBytes;
                    bodyDataLeft -= numReadBytes;
                }
            }
            else
            {
                // No indicated ContentLength; Just read until the connection gets closed!
                int numReadBytes = 0;

                // Read until the remote side closes
                do
                {
                    bodyBuffer.EnsureSize(bodyBuffer.Size + readIncrementSize);
                    numReadBytes = httpSocket.Receive(bodyBuffer.UnderlyingBuffer, nextBodyPos,
                                                      bodyBuffer.Size - nextBodyPos, SocketFlags.None);
                    nextBodyPos += numReadBytes;
                }while (numReadBytes > 0);
            }

            response.BodyData = bodyBuffer.TrimAndCopy(nextBodyPos);
        }

        // All done!
        httpSocket.Close();

        return(response);
    }
示例#2
0
 public MessageValueHandler()
 {
     _httpHeadersParser    = new HttpHeadersParser();
     _httpStatusLineParser = new HttpStatusLineParser();
     _httpMessageParser    = new HttpMessageParser();
 }