/// <summary>
        /// Performs the actual writing of data to the socket. Used by all other Write* methods.
        /// </summary>
        /// <param name="socket">The <see cref="AsyncSocket"/> to write the data to</param>
        /// <param name="bytes">The bytes to write to the socket</param>
        /// <param name="timeout">The socket write timeout value</param>
        /// <param name="tag">The tag that will identify the write operation (can be referenced in the socket's DidWrite event)</param>
        /// <param name="disconnectAfterWriting">Indicates if the server should disconnect the socket after writing the data</param>
        /// <param name="requestComplete">Indicates if the request is complete once the data is written</param>
        protected void FinalWrite(AsyncSocket socket, byte[] bytes, int timeout, long tag, bool disconnectAfterWriting, bool requestComplete)
        {
            Data data = new Data(bytes);

            Log(data);

            // give any custom readers the change to modify the output before we send it (especially useful for WebSockets that need to frame their data)
            if (this.requestReader != null)
            {
                this.requestReader.BeforeResponse(ref bytes);
            }

            // if we are done sending stuff back (all responses and callbacks), we need to initiate an orderly shutdown
            if (!disconnectAfterWriting && requestComplete)
            {
                OrderlySocketShutdown();
            }

            // send the data
            socket.Write(bytes, timeout, tag);

            // if we are the ones disconnecting, do it now. (usually if we are sending back an error response)
            // if not, we can just leave the socket alone
            if (disconnectAfterWriting)
            {
                OnSocketUsageComplete(socket);
                socket.CloseAfterWriting();
            }
        }