/// <summary> /// Checks the message for the required headers based on its type, fills in the ones that are missing if any /// </summary> /// <param name="socket"></param> /// <param name="message"></param> internal protected void CheckHeaders(Socket socket, HttpMessage message) { /* * The following instructions must be followed exactly to adhere to the HTTP/1.1 specs as stated in RFC 2616. * * The following sending rules apply to Requests and Response messages. * * Requests: (Fields that must be set) * - Host * - User-Agent * - Content-Length (If Body) * - Content-Type (If Body) * * Responses: * - Server * - Content-Length (If Body) * - Content-Type (If Body) * * So let's check our headers out now, before we go any further * */ switch(message.Type) { case HttpMessageTypes.HttpRequest: this.CheckRequiredRequestHeaders(socket, (HttpRequest)message); break; case HttpMessageTypes.HttpResponse: this.CheckRequiredResponseHeaders((HttpResponse)message); break; }; }
/// <summary> /// Initializes a new instance of the X class /// </summary> /// <param name="message">The message being processed</param> /// <param name="justHeaders">A flag to indicated that only the message headers have been processed</param> /// <param name="bytes">The bytes that were just processed</param> /// <param name="totalBytes">The total number of bytes that have been processed</param> public HttpMessageProgressEventArgs(HttpMessage message, bool justHeaders, byte[] bytes, int totalBytes) : base() { _message = message; _justHeaders = justHeaders; _bytes = bytes; _totalBytes = totalBytes; }
/// <summary> /// Writes the message to the socket /// </summary> /// <param name="socket"></param> /// <param name="abortEvent"></param> /// <param name="message"></param> public void Write(Socket socket, ManualResetEvent abortEvent, HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // send the headers of the message this.SendHeaders(socket, message, onProgress, stateObject); // send the body of the message this.SendBody(socket, abortEvent, message, onProgress, stateObject); }
/// <summary> /// Writes the message's body to the socket /// </summary> /// <param name="socket"></param> /// <param name="message"></param> internal protected void SendBody(Socket socket, ManualResetEvent abortEvent, HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // bail if there is no message body if (message.Body == null) return; // bail if there is a message body, with no length if (message.Body.Length == 0) return; /* * In order to properly send the body, we must first determine if the transfer-encoding is chunked or not * */ if (HttpUtils.Contains(message.TransferEncoding, HttpTransferEncodings.Chunked)) { // chunked this.SendBodyChunked(socket, abortEvent, message, onProgress, stateObject); } else { // non-chunked this.SendBodyNonChunked(socket, abortEvent, message, onProgress, stateObject); } }
/// <summary> /// Initializes a new instance of the HttpMessageCancelEventArgs class /// </summary> /// <param name="message">The message context</param> /// <param name="cancel">A flag that indicates whether this event will be cancelled</param> public HttpMessageCancelEventArgs(HttpMessage message, bool cancel) : base(message) { _cancel = cancel; }
/// <summary> /// Initializes a new instance of the HttpResponse class /// </summary> /// <param name="message"></param> public HttpResponse(HttpMessage message) : base(message) { _statusLine = HttpStatusLine.Parse(base.FirstLine); // this.InitHeaders(); // no very bad!!! using a message as a constructor means we've received a message and we're creating a response around it, so it will already have headers!!! }
// /// <summary> // /// Returns the content length of the message // /// </summary> // private int GetContentLength(ref HttpMessage message) // { // // if the headers don't contain the content length header, the length is zero // if (!message.Headers.Contains(HttpHeaders.EntityHeaders.ContentLength)) // return 0; // // // retrieve the header // HttpHeader header = message.Headers[HttpHeaders.EntityHeaders.ContentLength]; // // try // { // // try and parse the content length // int contentLength = int.Parse(header.Value); // if (contentLength < 0) // contentLength = 0; // someone might be trying to screw us // return contentLength; // } // catch(Exception ex) // { // Debug.WriteLine(ex); // } // // // fall back on zero if there is a failure // return 0; // } /// <summary> /// Returns a byte array that contains the bytes forming the message body /// </summary> private byte[] ReconstructChunkedBodyIfNecessary(ref HttpMessage message) { byte[] buffer = null; // if the message was chunked if (message.IsChunked) { // reasemble the body from the chunks // this is where the chunked body class will come in if (_chunkedBody != null) buffer = _chunkedBody.ToByteArray(); } else { // if there is no content length specified if (message.ContentLength == 0) // bail out with no body return null; /* * --------------------- * | First Line | CRLF * --------------------- * | Header Lines | * ..................... * ..................... * ..................... * ..................... * | | CRLF CRLF * --------------------- * | Body | * --------------------- * */ // copy the bytes that make up the body into a temp buffer buffer = new byte[_receivedBytes.Length - _headerOffsetEnd]; Buffer.BlockCopy(_receivedBytes, _headerOffsetEnd, buffer, 0, buffer.Length); } return buffer; }
/// <summary> /// Tries to parse a trailer header for the message /// </summary> /// <returns></returns> private bool TryAndParseTrailerHeaderLine(ref HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // try and parse a token from the data HttpByteParserToken token = _parser.GetNextToken(); if (token == null) { // change state to processing the body _state = Processing.Finished; // we're done with the headers return true; } // the first line of any message is never empty if (token.IsEmpty) { // change state to processing the body _state = Processing.Finished; // we're done with the headers return true; } // success! this line is a header string line = token.ToString(HttpUtils.Encoding); // parse the line into a new header HttpHeader header = HttpHeader.Parse(line); // save the header in the chunked body trailers _chunkedBody.TrailerHeaders.Add(header); return true; }
/// <summary> /// Tries to parse a chunk size line for the message /// </summary> /// <returns></returns> private bool TryAndParseChunkSizeLine(ref HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // try and parse a token from the data HttpByteParserToken token = _parser.GetNextToken(); if (token == null) return false; // the first line of any message is never empty if (token.IsEmpty) return false; // success! this line is a header string line = token.ToString(HttpUtils.Encoding); // parse the size line HttpChunkSizeLine chunkSizeLine = HttpChunkSizeLine.Parse(line); // create a new chunk _chunk = new HttpChunk(chunkSizeLine, null); // change state to processing chunk data _state = Processing.ChunkData; return true; }
public void Write(Socket socket, ManualResetEvent abortEvent, HttpMessage message) { this.Write(socket, abortEvent, message, null, null); }
/// <summary> /// Initializes a new instance of the HttpMessageWriterAbortedException class /// </summary> /// <param name="message"></param> public HttpMessageWriterAbortedException(HttpMessage message) : base(string.Format("The writer was aborted while sending the message '{0}'.", message.ToString(false))) { _message = message; }
/// <summary> /// Sends the body of a non-chunked transfer-encoded message /// </summary> /// <param name="socket"></param> /// <param name="abortEvent"></param> /// <param name="message"></param> internal protected void SendBodyNonChunked(Socket socket, ManualResetEvent abortEvent, HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { int segmentNumber = 0; int totalBytesSent = 0; byte[] body = message.ToByteArray(false /* no headers */, true /* just the body */); while(true) { // if we have sent the entire message then we can abortEvent if (totalBytesSent == body.Length) break; // start out with a full segment size (this is just the buffer size by which we will send - 8k seems to be really common although i have no idea why other than that it's a power of 2...) int chunkSize = MAX_SEGMENT_SIZE_NONCHUNKED; // adjust how much we need to send to complete the message if (chunkSize > (body.Length - totalBytesSent)) chunkSize = (body.Length - totalBytesSent); // bump up the segment number segmentNumber++; // create the chunk to send byte[] bytes = new byte[chunkSize]; Buffer.BlockCopy(body, totalBytesSent /* offset */, bytes, 0, chunkSize); // try and send the segment int bytesSent = HttpUtils.SendBytes(socket, bytes); // update the stream position which is the total number of bytes received totalBytesSent += bytesSent; // notify the callback of our progress this.OnProgress(onProgress, this, new HttpMessageProgressEventArgs(message, false, bytes, totalBytesSent, stateObject)); // clear that buffer immediately bytes = null; // see if we have been instructed to abortEvent reading if (abortEvent != null) if (abortEvent.WaitOne(1, false)) throw new HttpMessageWriterAbortedException(message); } // while (true) }
/// <summary> /// Sends the body of a chunked transfer-encoded message /// </summary> /// <param name="socket"></param> /// <param name="abortEvent"></param> /// <param name="message"></param> internal protected void SendBodyChunked(Socket socket, ManualResetEvent abortEvent, HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { int segmentNumber = 0; int totalBytesSent = 0; byte[] body = message.ToByteArray(false /* no headers */, true /* just the body */); while(true) { // if we have sent the entire message then we can abortEvent if (totalBytesSent > body.Length) break; // start out with a full segment size (this is just the buffer size by which we will send - 8k seems to be really common although i have no idea why other than that it's a power of 2...) int chunkSize = MAX_SEGMENT_SIZE_CHUNKED; // adjust how much we need to send to complete the message if (chunkSize > (body.Length - totalBytesSent)) chunkSize = (body.Length - totalBytesSent); // bump up the segment number segmentNumber++; // create a chunk of data to send byte[] bytes = new byte[chunkSize]; Buffer.BlockCopy(body, totalBytesSent, bytes, 0, chunkSize); // create a chunk around the data HttpChunk chunk = new HttpChunk(bytes); // send the data int bytesSent = HttpUtils.SendBytes(socket, chunk.ToByteArray()); // if any data was sent if (bytesSent > 0) { // figure out how much as data, minus the chunks control chars int actualBytesSent = bytesSent - chunk.GetNonDataByteCount(); // if the chunksize is zero or less then we'll return what was sent if (chunkSize > 0) // all other times it'll not count the non data byte count in the chunk bytesSent = actualBytesSent; } // tally the number of bytes we have sent totalBytesSent += bytesSent; // notify the callback of our progress this.OnProgress(onProgress, this, new HttpMessageProgressEventArgs(message, false, bytes, totalBytesSent, stateObject)); // clear that buffer immediately bytes = null; // see if we have been instructed to abortEvent reading if (abortEvent != null) if (abortEvent.WaitOne(1, false)) throw new HttpMessageWriterAbortedException(message); } }
/// <summary> /// Tries to parse the first line for the message /// </summary> /// <returns></returns> private bool TryAndParseFirstLine(ref HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // try and parse a token from the data HttpByteParserToken token = _parser.GetNextToken(); if (token == null) return false; // the first line of any message is never empty if (token.IsEmpty) return false; // success! store the first line of the message message._firstLine = token.ToString(HttpUtils.Encoding); // save the offset into the data where the headers start _headerOffsetStart = _parser.Index; // change state to processing headers! _state = Processing.HeaderLine; return true; }
/// <summary> /// Initializes a new instance of the HttpRequest class /// </summary> /// <param name="message">The incoming message that will construct this request</param> public HttpRequest(HttpMessage message) : base(message) { _requestLine = HttpRequestLine.Parse(base.FirstLine); }
/// <summary> /// Tries to parse a header line for the message /// </summary> /// <returns></returns> private bool TryAndParseHeaderLine(ref HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // try and parse a token from the data HttpByteParserToken token = _parser.GetNextToken(); if (token == null) return false; // the first line of any message is never empty if (token.IsEmpty) { // save the offset into the data where the headers end _headerOffsetEnd = _parser.Index; // determine if the body is chunked, as we have all the headers now we can determine how the message is going to come in if (message.IsChunked) // change state to processing a chunk size line _state = Processing.ChunkSizeLine; else // change state to processing the body _state = Processing.Body; // notify the callback that we have received the headers this.OnProgress(onProgress, this, new HttpMessageProgressEventArgs(message, true, new byte[] {}, _receivedBytes.Length, stateObject)); // we're done with the headers return true; } // success! this line is a header string line = token.ToString(HttpUtils.Encoding); // parse the line into a new header HttpHeader header = HttpHeader.Parse(line); // save the header message.Headers.Add(header); return true; }
/// <summary> /// Sends the body of a chunked transfer-encoded message /// </summary> /// <param name="socket"></param> /// <param name="abortEvent"></param> /// <param name="message"></param> internal protected void SendBodyChunked(Socket socket, ManualResetEvent abortEvent, HttpMessage message, EventHandler <HttpMessageProgressEventArgs> onProgress, object stateObject) { int segmentNumber = 0; int totalBytesSent = 0; byte[] body = message.ToByteArray(false /* no headers */, true /* just the body */); while (true) { // if we have sent the entire message then we can abortEvent if (totalBytesSent > body.Length) { break; } // start out with a full segment size (this is just the buffer size by which we will send - 8k seems to be really common although i have no idea why other than that it's a power of 2...) int chunkSize = MAX_SEGMENT_SIZE_CHUNKED; // adjust how much we need to send to complete the message if (chunkSize > (body.Length - totalBytesSent)) { chunkSize = (body.Length - totalBytesSent); } // bump up the segment number segmentNumber++; // create a chunk of data to send byte[] bytes = new byte[chunkSize]; Buffer.BlockCopy(body, totalBytesSent, bytes, 0, chunkSize); // create a chunk around the data HttpChunk chunk = new HttpChunk(bytes); // send the data int bytesSent = HttpUtils.SendBytes(socket, chunk.ToByteArray()); // if any data was sent if (bytesSent > 0) { // figure out how much as data, minus the chunks control chars int actualBytesSent = bytesSent - chunk.GetNonDataByteCount(); // if the chunksize is zero or less then we'll return what was sent if (chunkSize > 0) { // all other times it'll not count the non data byte count in the chunk bytesSent = actualBytesSent; } } // tally the number of bytes we have sent totalBytesSent += bytesSent; // notify the callback of our progress this.OnProgress(onProgress, this, new HttpMessageProgressEventArgs(message, false, bytes, totalBytesSent, stateObject)); // clear that buffer immediately bytes = null; // see if we have been instructed to abortEvent reading if (abortEvent != null) { if (abortEvent.WaitOne(1, false)) { throw new HttpMessageWriterAbortedException(message); } } } }
/// <summary> /// Tries to parse some chunk data for the message /// </summary> /// <returns></returns> private bool TryAndParseChunkData(ref HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // get bytes, and update the index... int waitingOn = _chunk.Size; int received = _receivedBytes.Length - 2 - _parser.Index; // if we received enough data to pull in a chunk, do that now if (received >= waitingOn) { // copy the end of the data out as chunk data byte[] data = HttpUtils.Clone(_receivedBytes, _parser.Index, _chunk.Size); // bump the parser past the end of the next \r\n _parser.Index += _chunk.Size + 2; // create a new chunked body if (_chunkedBody == null) _chunkedBody = new HttpChunkedBody(); // assign the data to the chunk _chunk.Data = data; // todo: // add the chunk to the body _chunkedBody.Chunks.Add(_chunk); // if the chunk is empty, it's the last chunk bool empty = _chunk.IsEmpty; // destroy the chunk _chunk = null; if (empty) { // change state to processing chunk data _state = Processing.ChunkTrailerHeaderLine; } else { // go back to processing a chunk size line _state = Processing.ChunkSizeLine; } return true; } return false; }
/// <summary> /// Sends the body of a non-chunked transfer-encoded message /// </summary> /// <param name="socket"></param> /// <param name="abortEvent"></param> /// <param name="message"></param> internal protected void SendBodyNonChunked(Socket socket, ManualResetEvent abortEvent, HttpMessage message, EventHandler <HttpMessageProgressEventArgs> onProgress, object stateObject) { int segmentNumber = 0; int totalBytesSent = 0; byte[] body = message.ToByteArray(false /* no headers */, true /* just the body */); while (true) { // if we have sent the entire message then we can abortEvent if (totalBytesSent == body.Length) { break; } // start out with a full segment size (this is just the buffer size by which we will send - 8k seems to be really common although i have no idea why other than that it's a power of 2...) int chunkSize = MAX_SEGMENT_SIZE_NONCHUNKED; // adjust how much we need to send to complete the message if (chunkSize > (body.Length - totalBytesSent)) { chunkSize = (body.Length - totalBytesSent); } // bump up the segment number segmentNumber++; // create the chunk to send byte[] bytes = new byte[chunkSize]; Buffer.BlockCopy(body, totalBytesSent /* offset */, bytes, 0, chunkSize); // try and send the segment int bytesSent = HttpUtils.SendBytes(socket, bytes); // update the stream position which is the total number of bytes received totalBytesSent += bytesSent; // notify the callback of our progress this.OnProgress(onProgress, this, new HttpMessageProgressEventArgs(message, false, bytes, totalBytesSent, stateObject)); // clear that buffer immediately bytes = null; // see if we have been instructed to abortEvent reading if (abortEvent != null) { if (abortEvent.WaitOne(1, false)) { throw new HttpMessageWriterAbortedException(message); } } } // while (true) }
/// <summary> /// Tries to parse the body for the message /// </summary> /// <returns></returns> private bool TryAndParseBody(ref HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // determine the content length of the message's entity int contentLength = message.ContentLength; // the number of bytes we have received thus far would be determined by looking at the different between what we have and where the header's stopped int postedBytesLength = _receivedBytes.Length - _headerOffsetEnd; // we could have potentially received more than just one message so the extra data would be denoted by the difference between the content length and the body which we received int extraBytesReceived = postedBytesLength - contentLength; // if we received extra bytes that do not belong to this message if (extraBytesReceived > 0) { // create a new buffer to hold the exra _previouslyReceivedBytes = new byte[extraBytesReceived]; // copy the tail of the message off as extra bytes Buffer.BlockCopy(_receivedBytes, _headerOffsetEnd + contentLength, _previouslyReceivedBytes, 0, extraBytesReceived); // now shrink the bytes received to be exactly what comprises the bytes that make up this message byte[] tempBuffer = new byte[_headerOffsetEnd + contentLength]; Buffer.BlockCopy(_receivedBytes, 0, tempBuffer, 0, tempBuffer.Length); _receivedBytes = tempBuffer; } // if we've read in enough data to get the body, or there's no body coming if ( (postedBytesLength >= contentLength && contentLength > 0) || (contentLength == 0)) { // change state to finished _state = Processing.Finished; // we want to kick out of the processing loop return false; } // otherwise we simply need to wait on the socket to receive more data return false; }
/// <summary> /// Initializes a new instance of the HttpMessageWriterAbortedException class /// </summary> /// <param name="message"></param> public HttpMessageReaderAbortedException(HttpMessage message) : base(string.Format("The reader was aborted while receiving the message '{0}'.", message.ToString(false))) { _message = message; }
/// <summary> /// Reads an HttpMessage from the specified socket /// </summary> /// <param name="socket">The socket to read</param> /// <param name="abortEvent">An event to watch in case of an abort</param> /// <returns></returns> public virtual HttpMessage Read(Socket socket, ManualResetEvent abortEvent, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { if (socket == null) throw new ArgumentNullException("socket"); // setup our vars this.InitVars(); // create a new message HttpMessage message = new HttpMessage(); try { // while we've not finished reading the message while(!this.IsFinished(abortEvent)) { // keep receiving this.ReceiveData(socket, abortEvent, ref message, onProgress, stateObject); // keep processing this.ProcessData(abortEvent, ref message, onProgress, stateObject); } } catch(OperationAbortedException) { throw new HttpMessageReaderAbortedException(message); } // set the message body message._body = this.ReconstructChunkedBodyIfNecessary(ref message); // clean up our vars this.CleanupVars(); return message; }
/// <summary> /// Initializes a new instance of the HttpMessage class /// </summary> /// <param name="message"></param> public HttpMessage(HttpMessage message) { _firstLine = message.FirstLine; _headers = new HttpHeaderCollection(); foreach(HttpHeader header in message.Headers) _headers.Add(new HttpHeader(header.Name, header.Value)); this.Body = message.Body; }
/// <summary> /// Waits for data to become available for reading on the socket, then reads it into the main buffer for processing /// </summary> private void ReceiveData(Socket socket, ManualResetEvent abortEvent, ref HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { /* * Data Receiving Phase * This will receive data from the socket until we have enough data to process. * */ // wait for some bytes to become available int bytesAvailable = 0; byte[] bytesReceived = new byte[0]; // if we don't have data left over from last time try and go with just that first if (_previouslyReceivedBytes == null) { // ok there was nothing left over, so we're back to receiving again bytesAvailable = HttpUtils.WaitForAvailableBytes(socket, abortEvent); // receive as many bytes as we can (a chunk as it were) bytesReceived = HttpUtils.ReceiveBytes(socket, bytesAvailable, MAX_BUFFER_LENGTH); } // if there was anything left over from last time if (_previouslyReceivedBytes != null) { // combine the previous with this chunk bytesReceived = HttpUtils.Combine(_previouslyReceivedBytes, bytesReceived); Debug.WriteLine(string.Format("Retaining '{0}' bytes from last message.", bytesReceived.Length)); // reset the previous buffer _previouslyReceivedBytes = null; } // if we have a previous message buffer if (_receivedBytes != null) { // combine what we had, with what we just received byte[] buffer = HttpUtils.Combine(_receivedBytes, bytesReceived); _receivedBytes = buffer; } else /* * this is the first part of a new message * */ // otherwise just start with the chunk _receivedBytes = bytesReceived; // if we have received the headers if (this.IsPastHeaders()) // notify the callback that data has been received this.OnProgress(onProgress, this, new HttpMessageProgressEventArgs(message, false, bytesReceived, _receivedBytes.Length, stateObject)); }
/// <summary> /// Initializes a new instance of the HttpMessageEventArgs class /// </summary> /// <param name="message">The message context</param> public HttpMessageEventArgs(HttpMessage message) : base() { _context = message; }
/// <summary> /// Processes the raw data, either reading headers or chunked body data /// </summary> private void ProcessData(ManualResetEvent abortEvent, ref HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // at each pass in the processing phase set the buffer to be parsed by the parser to the buffer into which we received bytes // the previous parsing phase may have failed simply because we did not have enough data yet to fullfill the current state's needs // so we'll update what we have received and carry on from the last spot, just hopefully with more data... :P _parser.SetBuffer(_receivedBytes); bool stillParsing = true; // enter a processing loop and keep trying to parse data based on our current state until the phase fails while(stillParsing) { // look at our current state to determine what data we should be trying to process and parse switch(_state) { case Processing.FirstLine: stillParsing = this.TryAndParseFirstLine(ref message, onProgress, stateObject); break; case Processing.HeaderLine: stillParsing = this.TryAndParseHeaderLine(ref message, onProgress, stateObject); break; case Processing.ChunkSizeLine: stillParsing = this.TryAndParseChunkSizeLine(ref message, onProgress, stateObject); break; case Processing.ChunkData: stillParsing = this.TryAndParseChunkData(ref message, onProgress, stateObject); break; case Processing.ChunkTrailerHeaderLine: stillParsing = this.TryAndParseTrailerHeaderLine(ref message, onProgress, stateObject); break; case Processing.Body: stillParsing = this.TryAndParseBody(ref message, onProgress, stateObject); break; case Processing.Finished: Debug.Assert(false, "We should be out of the processing loop if our state is 'Finished'."); break; }; // and in case any of the methods change the state to finished, but think they are still processing if (stillParsing) // jump in and check so we don't get stuck in an endless loop of parsing stillParsing = !this.IsFinished(abortEvent); } }
/// <summary> /// Writes the message's headers to the socket /// </summary> /// <param name="socket"></param> /// <param name="message"></param> internal protected int SendHeaders(Socket socket, HttpMessage message, EventHandler<HttpMessageProgressEventArgs> onProgress, object stateObject) { // check the headers of the message this.CheckHeaders(socket, message); /* * The only thing we can reliably send at the start of every message are the headers * Technically we should be carefull how many bytes the headers are, but at this point * until i see it fail, i'm gonna take it easy on the paranoia factor, sometimes i can only defend so much before i'm wasting time... :P * * So send the headers * */ byte[] bytes = message.ToByteArray(true /* get just the headers */, false /* do not include the body */); // send the header bytes int bytesSent = HttpUtils.SendBytes(socket, bytes); // notify the callback that we have sent the headers this.OnProgress(onProgress, this, new HttpMessageProgressEventArgs(message, true, bytes, bytesSent, stateObject)); return bytesSent; }