// 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); }
public MessageValueHandler() { _httpHeadersParser = new HttpHeadersParser(); _httpStatusLineParser = new HttpStatusLineParser(); _httpMessageParser = new HttpMessageParser(); }