Beispiel #1
0
		public virtual void Add(WriteBatch writeBatch, Slice key, RavenJToken value, ushort? expectedVersion = null)
		{
            var stream = new BufferPoolMemoryStream(BufferPool);
            value.WriteTo(stream);
            stream.Position = 0;

			writeBatch.Add(key, stream, TableName, expectedVersion);
		}
Beispiel #2
0
		public virtual void Add(WriteBatch writeBatch, string key, byte[] value, ushort? expectedVersion = null)
		{
		    var stream = new BufferPoolMemoryStream(BufferPool);
            stream.Write(value, 0, value.Length);
		    stream.Position = 0;

			writeBatch.Add(key, stream, TableName, expectedVersion);
		}
Beispiel #3
0
        /// <summary>
        /// A function to compress and return the data parameter with possible context takeover support (reusing the DeflateStream).
        /// </summary>
        private byte[] Compress(byte[] data, int length)
        {
            if (compressorOutputStream == null)
            {
                compressorOutputStream = new BufferPoolMemoryStream();
            }
            compressorOutputStream.SetLength(0);

            if (compressorDeflateStream == null)
            {
                compressorDeflateStream           = new DeflateStream(compressorOutputStream, CompressionMode.Compress, this.Level, true, this.ClientMaxWindowBits);
                compressorDeflateStream.FlushMode = FlushType.Sync;
            }

            byte[] result = null;
            try
            {
                compressorDeflateStream.Write(data, 0, length);
                compressorDeflateStream.Flush();

                compressorOutputStream.Position = 0;

                // http://tools.ietf.org/html/rfc7692#section-7.2.1
                // Remove 4 octets (that are 0x00 0x00 0xff 0xff) from the tail end. After this step, the last octet of the compressed data contains (possibly part of) the DEFLATE header bits with the "BTYPE" bits set to 00.
                compressorOutputStream.SetLength(compressorOutputStream.Length - 4);

                result = compressorOutputStream.ToArray();
            }
            finally
            {
                if (this.ClientNoContextTakeover)
                {
                    compressorDeflateStream.Dispose();
                    compressorDeflateStream = null;
                }
            }

            return(result);
        }
        public void AddStreamField(System.IO.Stream stream, string fieldName, string fileName, string mimeType)
        {
            var header = new BufferPoolMemoryStream();

            header.WriteLine("--" + this.boundary);
            header.WriteLine("Content-Disposition: form-data; name=\"" + fieldName + "\"" + (!string.IsNullOrEmpty(fileName) ? "; filename=\"" + fileName + "\"" : string.Empty));
            // Set up Content-Type head for the form.
            if (!string.IsNullOrEmpty(mimeType))
            {
                header.WriteLine("Content-Type: " + mimeType);
            }
            header.WriteLine("Content-Length: " + stream.Length.ToString());
            header.WriteLine();
            header.Position = 0;

            var footer = new BufferPoolMemoryStream();

            footer.Write(HTTPRequest.EOL, 0, HTTPRequest.EOL.Length);
            footer.Position = 0;

            // all wrapped streams going to be disposed by the StreamList wrapper.
            var wrapper = new StreamList(header, stream, footer);

            try
            {
                if (this._length >= 0)
                {
                    this._length += wrapper.Length;
                }
            }
            catch
            {
                this._length = -1;
            }

            this.fields.Enqueue(wrapper);
        }
Beispiel #5
0
        public static byte[] EncodeCloseData(UInt16 code, string message)
        {
            //If there is a body, the first two bytes of the body MUST be a 2-byte unsigned integer
            // (in network byte order) representing a status code with value /code/ defined in Section 7.4 (http://tools.ietf.org/html/rfc6455#section-7.4). Following the 2-byte integer,
            // the body MAY contain UTF-8-encoded data with value /reason/, the interpretation of which is not defined by this specification.
            // This data is not necessarily human readable but may be useful for debugging or passing information relevant to the script that opened the connection.
            int msgLen = Encoding.UTF8.GetByteCount(message);

            using (var ms = new BufferPoolMemoryStream(2 + msgLen))
            {
                byte[] buff = BitConverter.GetBytes(code);
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(buff, 0, buff.Length);
                }

                ms.Write(buff, 0, buff.Length);

                buff = Encoding.UTF8.GetBytes(message);
                ms.Write(buff, 0, buff.Length);

                return(ms.ToArray());
            }
        }
Beispiel #6
0
        private string DecodeString(Stream stream)
        {
            byte   start        = (byte)stream.ReadByte();
            bool   rawString    = BufferHelper.ReadBit(start, 0) == 0;
            UInt32 stringLength = DecodeInteger(7, start, stream);

            if (rawString)
            {
                byte[] buffer = BufferPool.Get(stringLength, true);

                stream.Read(buffer, 0, (int)stringLength);

                BufferPool.Release(buffer);

                return(System.Text.Encoding.UTF8.GetString(buffer, 0, (int)stringLength));
            }
            else
            {
                var  node        = HuffmanEncoder.GetRoot();
                byte currentByte = (byte)stream.ReadByte();
                byte bitIdx      = 0; // 0..7

                using (BufferPoolMemoryStream bufferStream = new BufferPoolMemoryStream())
                {
                    do
                    {
                        byte bitValue = BufferHelper.ReadBit(currentByte, bitIdx);

                        if (++bitIdx > 7)
                        {
                            stringLength--;

                            if (stringLength > 0)
                            {
                                bitIdx      = 0;
                                currentByte = (byte)stream.ReadByte();
                            }
                        }

                        node = HuffmanEncoder.GetNext(node, bitValue);

                        if (node.Value != 0)
                        {
                            if (node.Value != HuffmanEncoder.EOS)
                            {
                                bufferStream.WriteByte((byte)node.Value);
                            }

                            node = HuffmanEncoder.GetRoot();
                        }
                    } while (stringLength > 0);

                    byte[] buffer = bufferStream.ToArray(true);

                    string result = System.Text.Encoding.UTF8.GetString(buffer, 0, (int)bufferStream.Length);

                    BufferPool.Release(buffer);

                    return(result);
                }
            }
        }
Beispiel #7
0
        void OnResponse(int httpStatus, byte[] buffer, int bufferLength)
        {
            try
            {
                using (var ms = new BufferPoolMemoryStream())
                {
                    Stream = ms;

                    XHR_GetStatusLine(NativeId, OnBufferCallback);
                    XHR_GetResponseHeaders(NativeId, OnBufferCallback);

                    if (buffer != null && bufferLength > 0)
                    {
                        ms.Write(buffer, 0, bufferLength);
                    }

                    ms.Seek(0L, SeekOrigin.Begin);

                    var    internalBuffer = ms.GetBuffer();
                    string tmp            = System.Text.Encoding.UTF8.GetString(internalBuffer);
                    HTTPManager.Logger.Information(this.NativeId + " OnResponse - full response ", tmp);

                    SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler;
                    CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false);

                    CurrentRequest.Response.Receive(buffer != null && bufferLength > 0 ? (int)bufferLength : -1, true);

#if !BESTHTTP_DISABLE_COOKIES
                    if (CurrentRequest.IsCookiesEnabled)
                    {
                        BestHTTP.Cookies.CookieJar.Set(CurrentRequest.Response);
                    }
#endif
                }
            }
            catch (Exception e)
            {
                HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e);

                if (CurrentRequest != null)
                {
                    // Something gone bad, Response must be null!
                    CurrentRequest.Response = null;

                    switch (State)
                    {
                    case HTTPConnectionStates.AbortRequested:
                        CurrentRequest.State = HTTPRequestStates.Aborted;
                        break;

                    case HTTPConnectionStates.TimedOut:
                        CurrentRequest.State = HTTPRequestStates.TimedOut;
                        break;

                    default:
                        CurrentRequest.Exception = e;
                        CurrentRequest.State     = HTTPRequestStates.Error;
                        break;
                    }
                }
            }
            finally
            {
                Connections.Remove(NativeId);

                Stream = null;

                if (CurrentRequest != null)
                {
                    lock (HTTPManager.Locker)
                    {
                        State = HTTPConnectionStates.Closed;
                        if (CurrentRequest.State == HTTPRequestStates.Processing)
                        {
                            if (CurrentRequest.Response != null)
                            {
                                CurrentRequest.State = HTTPRequestStates.Finished;
                            }
                            else
                            {
                                CurrentRequest.State = HTTPRequestStates.Error;
                            }
                        }
                    }
                }

                LastProcessTime = DateTime.UtcNow;

                if (OnConnectionRecycled != null)
                {
                    RecycleNow();
                }

                XHR_Release(NativeId);
            }
        }
Beispiel #8
0
        public void Encode(HTTP2Stream context, HTTPRequest request, Queue <HTTP2FrameHeaderAndPayload> to, UInt32 streamId)
        {
            // Add usage of SETTINGS_MAX_HEADER_LIST_SIZE to be able to create a header and one or more continuation fragments
            // (https://httpwg.org/specs/rfc7540.html#SettingValues)

            using (BufferPoolMemoryStream bufferStream = new BufferPoolMemoryStream())
            {
                WriteHeader(bufferStream, ":method", HTTPRequest.MethodNames[(int)request.MethodType]);
                // add path
                WriteHeader(bufferStream, ":path", request.CurrentUri.PathAndQuery);
                // add authority
                WriteHeader(bufferStream, ":authority", request.CurrentUri.Authority);
                // add scheme
                WriteHeader(bufferStream, ":scheme", "https");

                //bool hasBody = false;

                // add other, regular headers
                request.EnumerateHeaders((header, values) =>
                {
                    if (header.Equals("connection", StringComparison.OrdinalIgnoreCase) ||
                        header.Equals("te", StringComparison.OrdinalIgnoreCase) ||
                        header.Equals("host", StringComparison.OrdinalIgnoreCase))
                    {
                        return;
                    }

                    //if (!hasBody)
                    //    hasBody = header.Equals("content-length", StringComparison.OrdinalIgnoreCase) && int.Parse(values[0]) > 0;

                    // https://httpwg.org/specs/rfc7540.html#HttpSequence
                    // The chunked transfer encoding defined in Section 4.1 of [RFC7230] MUST NOT be used in HTTP/2.
                    if (header.Equals("Transfer-Encoding", StringComparison.OrdinalIgnoreCase))
                    {
                        // error!
                        return;
                    }

                    // https://httpwg.org/specs/rfc7540.html#HttpHeaders
                    // Just as in HTTP/1.x, header field names are strings of ASCII characters that are compared in a case-insensitive fashion.
                    // However, header field names MUST be converted to lowercase prior to their encoding in HTTP/2.
                    // A request or response containing uppercase header field names MUST be treated as malformed
                    if (header.Any(Char.IsUpper))
                    {
                        header = header.ToLower();
                    }

                    for (int i = 0; i < values.Count; ++i)
                    {
                        WriteHeader(bufferStream, header, values[i]);

                        if (HTTPManager.Logger.Level <= Logger.Loglevels.Information)
                        {
                            HTTPManager.Logger.Information("HPACKEncoder", string.Format("[{0}] - Encode - Header({1}/{2}): '{3}': '{4}'", context.Id, i + 1, values.Count, header, values[i]), this.parent.Context, context.Context, request.Context);
                        }
                    }
                }, true);

                var upStreamInfo = request.GetUpStream();
                CreateHeaderFrames(to,
                                   streamId,
                                   bufferStream.ToArray(true),
                                   (UInt32)bufferStream.Length,
                                   upStreamInfo.Stream != null);
            }
        }
Beispiel #9
0
        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
        protected void ReadChunked(Stream stream)
        {
            BeginReceiveStreamFragments();

            string contentLengthHeader    = GetFirstHeaderValue("Content-Length");
            bool   hasContentLengthHeader = !string.IsNullOrEmpty(contentLengthHeader);
            int    realLength             = 0;

            if (hasContentLengthHeader)
            {
                hasContentLengthHeader = int.TryParse(contentLengthHeader, out realLength);
            }

            if (HTTPManager.Logger.Level == Logger.Loglevels.All)
            {
                VerboseLogging(string.Format("ReadChunked - hasContentLengthHeader: {0}, contentLengthHeader: {1} realLength: {2:N0}", hasContentLengthHeader.ToString(), contentLengthHeader, realLength));
            }

            using (var output = new BufferPoolMemoryStream())
            {
                int chunkLength = ReadChunkLength(stream);

                if (HTTPManager.Logger.Level == Logger.Loglevels.All)
                {
                    VerboseLogging(string.Format("chunkLength: {0:N0}", chunkLength));
                }

                byte[] buffer = BufferPool.Get(Mathf.NextPowerOfTwo(chunkLength), true);

                int contentLength = 0;

                // Progress report:
                long Downloaded          = 0;
                long DownloadLength      = hasContentLengthHeader ? realLength : chunkLength;
                bool sendProgressChanged = this.baseRequest.OnDownloadProgress != null && (this.IsSuccess
#if !BESTHTTP_DISABLE_CACHING
                                                                                           || this.IsFromCache
#endif
                                                                                           );

                if (sendProgressChanged)
                {
                    RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.baseRequest, RequestEvents.DownloadProgress, Downloaded, DownloadLength));
                }

                string encoding =
#if !BESTHTTP_DISABLE_CACHING
                    IsFromCache ? null :
#endif
                    GetFirstHeaderValue("content-encoding");
                bool gzipped = !string.IsNullOrEmpty(encoding) && encoding == "gzip";

                Decompression.GZipDecompressor decompressor = gzipped ? new Decompression.GZipDecompressor(256) : null;

                while (chunkLength != 0)
                {
                    if (this.baseRequest.IsCancellationRequested)
                    {
                        return;
                    }

                    // To avoid more GC garbage we use only one buffer, and resize only if the next chunk doesn't fit.
                    if (buffer.Length < chunkLength)
                    {
                        BufferPool.Resize(ref buffer, chunkLength, true);
                    }

                    int readBytes = 0;

                    // Fill up the buffer
                    do
                    {
                        int bytes = stream.Read(buffer, readBytes, chunkLength - readBytes);
                        if (bytes <= 0)
                        {
                            throw ExceptionHelper.ServerClosedTCPStream();
                        }

                        readBytes += bytes;

                        // Progress report:
                        // Placing reporting inside this cycle will report progress much more frequent
                        Downloaded += bytes;

                        if (sendProgressChanged)
                        {
                            RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.baseRequest, RequestEvents.DownloadProgress, Downloaded, DownloadLength));
                        }
                    } while (readBytes < chunkLength);

                    if (baseRequest.UseStreaming)
                    {
                        if (gzipped)
                        {
                            var decompressed = decompressor.Decompress(buffer, 0, readBytes, false, true);
                            if (decompressed.Data != null)
                            {
                                FeedStreamFragment(decompressed.Data, 0, decompressed.Length);
                            }
                        }
                        else
                        {
                            FeedStreamFragment(buffer, 0, readBytes);
                        }
                    }
                    else
                    {
                        output.Write(buffer, 0, readBytes);
                    }

                    // Every chunk data has a trailing CRLF
                    ReadTo(stream, LF);

                    contentLength += readBytes;

                    // read the next chunk's length
                    chunkLength = ReadChunkLength(stream);

                    if (HTTPManager.Logger.Level == Logger.Loglevels.All)
                    {
                        VerboseLogging(string.Format("chunkLength: {0:N0}", chunkLength));
                    }

                    if (!hasContentLengthHeader && sendProgressChanged)
                    {
                        RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.baseRequest, RequestEvents.DownloadProgress, Downloaded, DownloadLength));
                    }
                }

                BufferPool.Release(buffer);

                if (baseRequest.UseStreaming)
                {
                    if (gzipped)
                    {
                        var decompressed = decompressor.Decompress(null, 0, 0, true, true);
                        if (decompressed.Data != null)
                        {
                            FeedStreamFragment(decompressed.Data, 0, decompressed.Length);
                        }
                    }

                    FlushRemainingFragmentBuffer();
                }

                // Read the trailing headers or the CRLF
                ReadHeaders(stream);

                // HTTP servers sometimes use compression (gzip) or deflate methods to optimize transmission.
                // How both chunked and gzip encoding interact is dictated by the two-staged encoding of HTTP:
                //  first the content stream is encoded as (Content-Encoding: gzip), after which the resulting byte stream is encoded for transfer using another encoder (Transfer-Encoding: chunked).
                //  This means that in case both compression and chunked encoding are enabled, the chunk encoding itself is not compressed, and the data in each chunk should not be compressed individually.
                //  The remote endpoint can decode the incoming stream by first decoding it with the Transfer-Encoding, followed by the specified Content-Encoding.
                // It would be a better implementation when the chunk would be decododed on-the-fly. Becouse now the whole stream must be downloaded, and then decoded. It needs more memory.
                if (!baseRequest.UseStreaming)
                {
                    this.Data = DecodeStream(output);
                }

                if (decompressor != null)
                {
                    decompressor.Dispose();
                }
            }
        }
        // No transfer-encoding just raw bytes.
        internal void ReadRaw(Stream stream, long contentLength)
        {
            BeginReceiveStreamFragments();

            // Progress report:
            baseRequest.DownloadLength          = contentLength;
            baseRequest.DownloadProgressChanged = this.IsSuccess
#if !BESTHTTP_DISABLE_CACHING
                                                  || this.IsFromCache
#endif
            ;

            if (HTTPManager.Logger.Level == Logger.Loglevels.All)
            {
                VerboseLogging(string.Format("ReadRaw - contentLength: {0:N0}", contentLength));
            }

            string encoding =
#if !BESTHTTP_DISABLE_CACHING
                IsFromCache ? null :
#endif
                GetFirstHeaderValue("content-encoding");
            bool gzipped = !string.IsNullOrEmpty(encoding) && encoding == "gzip";
            if (!baseRequest.UseStreaming && contentLength > 2147483646)
            {
                throw new OverflowException("You have to use STREAMING to download files bigger than 2GB!");
            }

            using (var output = new BufferPoolMemoryStream(baseRequest.UseStreaming ? 0 : (int)contentLength))
            {
                byte[] buffer    = VariableSizedBufferPool.Get(Math.Max(baseRequest.StreamFragmentSize, MinBufferSize), true);
                int    readBytes = 0;

                while (contentLength > 0)
                {
                    readBytes = 0;

                    do
                    {
                        int readbuffer = (int)Math.Min(2147483646, (uint)contentLength);
                        int bytes      = stream.Read(buffer, readBytes, Math.Min(readbuffer, buffer.Length - readBytes));

                        if (bytes <= 0)
                        {
                            throw ExceptionHelper.ServerClosedTCPStream();
                        }

                        readBytes     += bytes;
                        contentLength -= bytes;

                        // Progress report:
                        baseRequest.Downloaded += bytes;
                        baseRequest.DownloadProgressChanged = this.IsSuccess
#if !BESTHTTP_DISABLE_CACHING
                                                              || this.IsFromCache
#endif
                        ;
                    } while (readBytes < buffer.Length && contentLength > 0);

                    if (baseRequest.UseStreaming)
                    {
                        if (gzipped)
                        {
                            var decompressed = Decompress(buffer, 0, readBytes);
                            if (decompressed != null)
                            {
                                FeedStreamFragment(decompressed, 0, decompressed.Length);
                            }
                        }
                        else
                        {
                            FeedStreamFragment(buffer, 0, readBytes);
                        }
                    }
                    else
                    {
                        output.Write(buffer, 0, readBytes);
                    }
                }
                ;

                VariableSizedBufferPool.Release(buffer);

                if (baseRequest.UseStreaming)
                {
                    if (gzipped)
                    {
                        var decompressed = Decompress(null, 0, 0, true);
                        if (decompressed != null)
                        {
                            FeedStreamFragment(decompressed, 0, decompressed.Length);
                        }
                    }

                    FlushRemainingFragmentBuffer();
                }

                if (!baseRequest.UseStreaming)
                {
                    this.Data = DecodeStream(output);
                }
            }

            CloseDecompressors();
        }
        public BufferSegment EncodeMessage(Message message)
        {
            var memBuffer = BufferPool.Get(256, true);
            var stream    = new BufferPoolMemoryStream(memBuffer, 0, memBuffer.Length, true, true, false, true);

            // Write 5 bytes for placeholder for length prefix
            stream.WriteByte(0);
            stream.WriteByte(0);
            stream.WriteByte(0);
            stream.WriteByte(0);
            stream.WriteByte(0);

            var bufferWriter = new BufferPoolBufferWriter(stream);
            var writer       = new MessagePackWriter(bufferWriter);

            switch (message.type)
            {
            case MessageTypes.StreamItem:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1
                // [2, Headers, InvocationId, Item]

                writer.WriteArrayHeader(4);

                writer.Write(2);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);
                WriteValue(ref writer, bufferWriter, message.item);

                break;

            case MessageTypes.Completion:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1
                // [3, Headers, InvocationId, ResultKind, Result?]

                byte resultKind = (byte)(!string.IsNullOrEmpty(message.error) ? /*error*/ 1 : message.result != null ? /*non-void*/ 3 : /*void*/ 2);

                writer.WriteArrayHeader(resultKind == 2 ? 4 : 5);

                writer.Write(3);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);
                writer.Write(resultKind);

                if (resultKind == 1)     // error
                {
                    WriteString(ref writer, message.error);
                }
                else if (resultKind == 3)     // non-void
                {
                    WriteValue(ref writer, bufferWriter, message.result);
                }

                break;

            case MessageTypes.Invocation:
            // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1
            // [1, Headers, InvocationId, NonBlocking, Target, [Arguments], [StreamIds]]

            case MessageTypes.StreamInvocation:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1
                // [4, Headers, InvocationId, Target, [Arguments], [StreamIds]]

                writer.WriteArrayHeader(message.streamIds != null ? 6 : 5);

                writer.Write((int)message.type);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);
                WriteString(ref writer, message.target);
                writer.WriteArrayHeader(message.arguments != null ? message.arguments.Length : 0);
                if (message.arguments != null)
                {
                    for (int i = 0; i < message.arguments.Length; ++i)
                    {
                        WriteValue(ref writer, bufferWriter, message.arguments[i]);
                    }
                }

                if (message.streamIds != null)
                {
                    writer.WriteArrayHeader(message.streamIds.Length);

                    for (int i = 0; i < message.streamIds.Length; ++i)
                    {
                        WriteValue(ref writer, bufferWriter, message.streamIds[i]);
                    }
                }

                break;

            case MessageTypes.CancelInvocation:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1
                // [5, Headers, InvocationId]

                writer.WriteArrayHeader(3);

                writer.Write(5);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);

                break;

            case MessageTypes.Ping:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1
                // [6]

                writer.WriteArrayHeader(1);
                writer.Write(6);

                break;

            case MessageTypes.Close:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1
                // [7, Error, AllowReconnect?]

                writer.WriteArrayHeader(string.IsNullOrEmpty(message.error) ? 1 : 2);

                writer.Write(7);
                if (!string.IsNullOrEmpty(message.error))
                {
                    WriteString(ref writer, message.error);
                }

                break;
            }

            writer.Flush();

            // get how much bytes got written to the buffer. This includes the 5 placeholder bytes too.
            int length = (int)stream.Position;

            // this is the length without the 5 placeholder bytes
            int contentLength = length - 5;

            // get the stream's internal buffer. We set the releaseBuffer flag to false, so we can use it safely.
            var buffer = stream.GetBuffer();

            // add varint length prefix
            byte prefixBytes = GetRequiredBytesForLengthPrefix(contentLength);

            WriteLengthAsVarInt(buffer, 5 - prefixBytes, contentLength);

            // return with the final segment
            return(new BufferSegment(buffer, 5 - prefixBytes, contentLength + prefixBytes));
        }
 public BufferPoolBufferWriter(BufferPoolMemoryStream stream)
 {
     this.underlyingStream = stream;
     this.last             = BufferSegment.Empty;
 }
Beispiel #13
0
        public unsafe RawFrameData Get()
        {
            if (Data == null)
            {
                Data = BufferPool.NoData;
            }

            using (var ms = new BufferPoolMemoryStream(this.DataLength + 9))
            {
                // For the complete documentation for this section see:
                // http://tools.ietf.org/html/rfc6455#section-5.2

                // Write the header
                ms.WriteByte(this.Header);

                // The length of the "Payload data", in bytes: if 0-125, that is the payload length.  If 126, the following 2 bytes interpreted as a
                // 16-bit unsigned integer are the payload length.  If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the
                // most significant bit MUST be 0) are the payload length.  Multibyte length quantities are expressed in network byte order.
                if (this.DataLength < 126)
                {
                    ms.WriteByte((byte)(0x80 | (byte)this.DataLength));
                }
                else if (this.DataLength < UInt16.MaxValue)
                {
                    ms.WriteByte((byte)(0x80 | 126));
                    byte[] len = BitConverter.GetBytes((UInt16)this.DataLength);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(len, 0, len.Length);
                    }

                    ms.Write(len, 0, len.Length);
                }
                else
                {
                    ms.WriteByte((byte)(0x80 | 127));
                    byte[] len = BitConverter.GetBytes((UInt64)this.DataLength);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(len, 0, len.Length);
                    }

                    ms.Write(len, 0, len.Length);
                }

                // All frames sent from the client to the server are masked by a 32-bit value that is contained within the frame.  This field is
                // present if the mask bit is set to 1 and is absent if the mask bit is set to 0.
                // If the data is being sent by the client, the frame(s) MUST be masked.
                byte[] mask = BufferPool.Get(4, true);

                int hash = this.GetHashCode();

                mask[0] = (byte)((hash >> 24) & 0xFF);
                mask[1] = (byte)((hash >> 16) & 0xFF);
                mask[2] = (byte)((hash >> 8) & 0xFF);
                mask[3] = (byte)(hash & 0xFF);

                ms.Write(mask, 0, 4);

                // Do the masking.
                fixed(byte *pData = Data, pmask = mask)
                {
                    // Here, instead of byte by byte, we reinterpret cast the data as uints and apply the masking so.
                    // This way, we can mask 4 bytes in one cycle, instead of just 1
                    int localLength = this.DataLength / 4;

                    if (localLength > 0)
                    {
                        uint *upData = (uint *)pData;
                        uint  umask  = *(uint *)pmask;

                        unchecked
                        {
                            for (int i = 0; i < localLength; ++i)
                            {
                                upData[i] = upData[i] ^ umask;
                            }
                        }
                    }

                    // Because data might not be exactly dividable by 4, we have to mask the remaining 0..3 too.
                    int from = localLength * 4;

                    localLength = from + this.DataLength % 4;
                    for (int i = from; i < localLength; ++i)
                    {
                        pData[i] = (byte)(pData[i] ^ pmask[i % 4]);
                    }
                }

                BufferPool.Release(mask);

                ms.Write(Data, 0, DataLength);

                return(new RawFrameData(ms.ToArray(true), (int)ms.Length));
            }
        }
        public RawFrameData Get()
        {
            if (Data == null)
            {
                Data = BufferPool.NoData;
            }

            using (var ms = new BufferPoolMemoryStream(this.DataLength + 9))
            {
                // For the complete documentation for this section see:
                // http://tools.ietf.org/html/rfc6455#section-5.2

                // Write the header
                ms.WriteByte(this.Header);

                // The length of the "Payload data", in bytes: if 0-125, that is the payload length.  If 126, the following 2 bytes interpreted as a
                // 16-bit unsigned integer are the payload length.  If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the
                // most significant bit MUST be 0) are the payload length.  Multibyte length quantities are expressed in network byte order.
                if (this.DataLength < 126)
                {
                    ms.WriteByte((byte)(0x80 | (byte)this.DataLength));
                }
                else if (this.DataLength < UInt16.MaxValue)
                {
                    ms.WriteByte((byte)(0x80 | 126));
                    byte[] len = BitConverter.GetBytes((UInt16)this.DataLength);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(len, 0, len.Length);
                    }

                    ms.Write(len, 0, len.Length);
                }
                else
                {
                    ms.WriteByte((byte)(0x80 | 127));
                    byte[] len = BitConverter.GetBytes((UInt64)this.DataLength);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(len, 0, len.Length);
                    }

                    ms.Write(len, 0, len.Length);
                }

                // All frames sent from the client to the server are masked by a 32-bit value that is contained within the frame.  This field is
                // present if the mask bit is set to 1 and is absent if the mask bit is set to 0.
                // If the data is being sent by the client, the frame(s) MUST be masked.
                byte[] mask = BufferPool.Get(4, true);

                int hash = this.GetHashCode();

                mask[0] = (byte)((hash >> 24) & 0xFF);
                mask[1] = (byte)((hash >> 16) & 0xFF);
                mask[2] = (byte)((hash >> 8) & 0xFF);
                mask[3] = (byte)(hash & 0xFF);

                ms.Write(mask, 0, 4);

                BufferPool.Release(mask);

                // Do the masking.
                for (int i = 0; i < this.DataLength; ++i)
                {
                    ms.WriteByte((byte)(Data[i] ^ mask[i % 4]));
                }

                return(new RawFrameData(ms.ToArray(true), (int)ms.Length));
            }
        }
Beispiel #15
0
        private IEnumerator <IAsyncResult> PerformOperationImpl(IAccountIdentifier identifier, string accountName, TableProtocolHead tableProtocolHead, RequestStartedCallback requestStartedCallback, CheckPermissionDelegate checkPermissionCallback, QueryRowCommandPropertiesAvailableCallback queryRowCommandPropertiesAvailableCallback, Dictionary <string, string> continuationToken, ContinuationTokenAvailableCallback continuationTokenAvailableCallback, bool operationIsConditional, RequestContext requestContext, AsyncIteratorContext <NoResults> context)
        {
            TimeSpan maxValue = TimeSpan.MaxValue;

            if (VersioningHelper.CompareVersions(tableProtocolHead.RequestRestVersion, "2016-05-31") >= 0)
            {
                maxValue = tableProtocolHead.operationContext.RemainingTimeout();
            }
            IAsyncResult workerThread = this.authorizationManager.BeginCheckAccess(identifier, accountName, null, null, PermissionLevel.Owner, maxValue, context.GetResumeCallback(), context.GetResumeState("AuthorizationManager.BeginCheckAccess"));

            yield return(workerThread);

            this.authorizationManager.EndCheckAccess(workerThread);
            using (Stream bufferPoolMemoryStream = new BufferPoolMemoryStream(65536))
            {
                bool flag = false;
                if (tableProtocolHead.ShouldReadRequestBody)
                {
                    long requestContentLength = tableProtocolHead.RequestContentLength;
                    if (requestContentLength > (long)4194304)
                    {
                        IStringDataEventStream verbose = Logger <IRestProtocolHeadLogger> .Instance.Verbose;
                        object[] objArray = new object[] { requestContentLength, 4194304 };
                        verbose.Log("Content-Length is out of range. Content-Length={0} MaxRequestBodySize={1}", objArray);
                        if (!tableProtocolHead.IsBatchRequest())
                        {
                            if (VersioningHelper.CompareVersions(tableProtocolHead.RequestRestVersion, "2013-08-15") < 0)
                            {
                                throw new RequestTooLargeException();
                            }
                            throw new RequestEntityTooLargeException(new long?((long)4194304));
                        }
                        flag = true;
                        requestContentLength = (long)102400;
                    }
                    else if (requestContentLength == (long)-1)
                    {
                        requestContentLength = (long)4194305;
                    }
                    Logger <IRestProtocolHeadLogger> .Instance.Verbose.Log("Started asynchronously reading from input stream");

                    if (VersioningHelper.CompareVersions(tableProtocolHead.RequestRestVersion, "2016-05-31") >= 0)
                    {
                        maxValue = tableProtocolHead.operationContext.RemainingTimeout();
                    }
                    workerThread = AsyncStreamCopy.BeginAsyncStreamCopy(tableProtocolHead.RequestStream, bufferPoolMemoryStream, requestContentLength, 65536, maxValue, context.GetResumeCallback(), context.GetResumeState("BeginAsyncStreamCopy"));
                    yield return(workerThread);

                    long num = AsyncStreamCopy.EndAsyncStreamCopy(workerThread);
                    bufferPoolMemoryStream.Seek((long)0, SeekOrigin.Begin);
                    tableProtocolHead.RequestStream = bufferPoolMemoryStream;
                    Logger <IRestProtocolHeadLogger> .Instance.Verbose.Log("Finished reading from input stream");

                    workerThread = AsyncHelpers.BeginSwitchToWorkerThread(context.GetResumeCallback(), context.GetResumeState("PerformOperationImpl"));
                    yield return(workerThread);

                    AsyncHelpers.EndSwitchToWorkerThread(workerThread);
                    Logger <IRestProtocolHeadLogger> .Instance.Verbose.Log("Switched to a WorkerThread");

                    if (num > (long)4194304)
                    {
                        IStringDataEventStream stringDataEventStream = Logger <IRestProtocolHeadLogger> .Instance.Verbose;
                        object[] requestContentLength1 = new object[] { tableProtocolHead.RequestContentLength, 4194304, num };
                        stringDataEventStream.Log("Request body is too long. Content-Length={0} MaxRequestBodySize={1} BytesRead={2}", requestContentLength1);
                        if (!tableProtocolHead.IsBatchRequest())
                        {
                            if (VersioningHelper.CompareVersions(tableProtocolHead.RequestRestVersion, "2013-08-15") < 0)
                            {
                                throw new RequestTooLargeException();
                            }
                            throw new RequestEntityTooLargeException(new long?((long)4194304));
                        }
                        flag = true;
                    }
                }
                if (!flag)
                {
                    this.DispatchRequestToAstoria(identifier, tableProtocolHead, requestStartedCallback, checkPermissionCallback, queryRowCommandPropertiesAvailableCallback, continuationToken, continuationTokenAvailableCallback, operationIsConditional);
                }
                else
                {
                    Logger <IRestProtocolHeadLogger> .Instance.Error.Log("Batch request body is too long. Sending error batch response now.");

                    if (VersioningHelper.CompareVersions(tableProtocolHead.RequestRestVersion, "2013-08-15") >= 0)
                    {
                        throw new RequestEntityTooLargeException(new long?((long)4194304));
                    }
                    string            str = tableProtocolHead.ReadContentIdFromRequest(100, 102400);
                    TableProtocolHead tableProtocolHead1 = tableProtocolHead;
                    string            str1 = str;
                    if (str1 == null)
                    {
                        str1 = "1";
                    }
                    tableProtocolHead1.SendBatchRequestTooLargeResponse(str1);
                }
            }
        }
Beispiel #16
0
        protected void ReadUnknownSize(Stream stream)
        {
            // Progress report:
            long Downloaded          = 0;
            long DownloadLength      = 0;
            bool sendProgressChanged = this.baseRequest.OnDownloadProgress != null && (this.IsSuccess
#if !BESTHTTP_DISABLE_CACHING
                                                                                       || this.IsFromCache
#endif
                                                                                       );

            string encoding =
#if !BESTHTTP_DISABLE_CACHING
                IsFromCache ? null :
#endif
                GetFirstHeaderValue("content-encoding");
            bool gzipped = !string.IsNullOrEmpty(encoding) && encoding == "gzip";

            Decompression.GZipDecompressor decompressor = gzipped ? new Decompression.GZipDecompressor(256) : null;

            using (var output = new BufferPoolMemoryStream())
            {
                byte[] buffer = BufferPool.Get(Math.Max(baseRequest.StreamFragmentSize, MinBufferSize), false);

                if (HTTPManager.Logger.Level == Logger.Loglevels.All)
                {
                    VerboseLogging(string.Format("ReadUnknownSize - buffer size: {0:N0}", buffer.Length));
                }

                int readBytes = 0;
                int bytes     = 0;
                do
                {
                    readBytes = 0;

                    do
                    {
                        if (this.baseRequest.IsCancellationRequested)
                        {
                            return;
                        }

                        bytes = 0;

#if !NETFX_CORE || UNITY_EDITOR
                        NetworkStream networkStream = stream as NetworkStream;
                        // If we have the good-old NetworkStream, than we can use the DataAvailable property. On WP8 platforms, these are omitted... :/
                        if (networkStream != null && baseRequest.EnableSafeReadOnUnknownContentLength)
                        {
                            for (int i = readBytes; i < buffer.Length && networkStream.DataAvailable; ++i)
                            {
                                int read = stream.ReadByte();
                                if (read >= 0)
                                {
                                    buffer[i] = (byte)read;
                                    bytes++;
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                        else // This will be good anyway, but a little slower.
#endif
                        {
                            bytes = stream.Read(buffer, readBytes, buffer.Length - readBytes);
                        }

                        readBytes += bytes;

                        // Progress report:
                        Downloaded    += bytes;
                        DownloadLength = Downloaded;

                        if (sendProgressChanged)
                        {
                            RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.baseRequest, RequestEvents.DownloadProgress, Downloaded, DownloadLength));
                        }
                    } while (readBytes < buffer.Length && bytes > 0);

                    if (baseRequest.UseStreaming)
                    {
                        if (gzipped)
                        {
                            var decompressed = decompressor.Decompress(buffer, 0, readBytes, false, true);
                            if (decompressed.Data != null)
                            {
                                FeedStreamFragment(decompressed.Data, 0, decompressed.Length);
                            }
                        }
                        else
                        {
                            FeedStreamFragment(buffer, 0, readBytes);
                        }
                    }
                    else
                    {
                        output.Write(buffer, 0, readBytes);
                    }
                } while (bytes > 0);

                BufferPool.Release(buffer);

                if (baseRequest.UseStreaming)
                {
                    if (gzipped)
                    {
                        var decompressed = decompressor.Decompress(null, 0, 0, true, true);
                        if (decompressed.Data != null)
                        {
                            FeedStreamFragment(decompressed.Data, 0, decompressed.Length);
                        }
                    }

                    FlushRemainingFragmentBuffer();
                }

                if (!baseRequest.UseStreaming)
                {
                    this.Data = DecodeStream(output);
                }
            }

            if (decompressor != null)
            {
                decompressor.Dispose();
            }
        }
        void OnResponse(int httpStatus, byte[] buffer, int bufferLength)
        {
            HTTPConnectionStates proposedConnectionState = HTTPConnectionStates.Processing;
            bool resendRequest = false;

            try
            {
                if (this.CurrentRequest.IsCancellationRequested)
                {
                    return;
                }

                using (var ms = new BufferPoolMemoryStream())
                {
                    Stream = ms;

                    XHR_GetStatusLine(NativeId, OnBufferCallback);
                    XHR_GetResponseHeaders(NativeId, OnBufferCallback);

                    if (buffer != null && bufferLength > 0)
                    {
                        ms.Write(buffer, 0, bufferLength);
                    }

                    ms.Seek(0L, SeekOrigin.Begin);

                    var    internalBuffer = ms.GetBuffer();
                    string tmp            = System.Text.Encoding.UTF8.GetString(internalBuffer);
                    HTTPManager.Logger.Information(this.NativeId + " OnResponse - full response ", tmp, this.Context);

                    SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler;
                    CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false);

                    CurrentRequest.Response.Receive(buffer != null && bufferLength > 0 ? (int)bufferLength : -1, true);

                    KeepAliveHeader keepAlive = null;
                    ConnectionHelper.HandleResponse(this.ToString(), this.CurrentRequest, out resendRequest, out proposedConnectionState, ref keepAlive);
                }
            }
            catch (Exception e)
            {
                HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e, this.Context);

                if (this.ShutdownType == ShutdownTypes.Immediate)
                {
                    return;
                }

#if !BESTHTTP_DISABLE_CACHING
                if (this.CurrentRequest.UseStreaming)
                {
                    HTTPCacheService.DeleteEntity(this.CurrentRequest.CurrentUri);
                }
#endif

                // Something gone bad, Response must be null!
                this.CurrentRequest.Response = null;

                if (!this.CurrentRequest.IsCancellationRequested)
                {
                    this.CurrentRequest.Exception = e;
                    this.CurrentRequest.State     = HTTPRequestStates.Error;
                }

                proposedConnectionState = HTTPConnectionStates.Closed;
            }
            finally
            {
                // Exit ASAP
                if (this.ShutdownType != ShutdownTypes.Immediate)
                {
                    if (this.CurrentRequest.IsCancellationRequested)
                    {
                        // we don't know what stage the request is cancelled, we can't safely reuse the tcp channel.
                        proposedConnectionState = HTTPConnectionStates.Closed;

                        this.CurrentRequest.Response = null;

                        this.CurrentRequest.State = this.CurrentRequest.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted;
                    }
                    else if (resendRequest)
                    {
                        RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.CurrentRequest, RequestEvents.Resend));
                    }
                    else if (this.CurrentRequest.Response != null && this.CurrentRequest.Response.IsUpgraded)
                    {
                        proposedConnectionState = HTTPConnectionStates.WaitForProtocolShutdown;
                    }
                    else if (this.CurrentRequest.State == HTTPRequestStates.Processing)
                    {
                        if (this.CurrentRequest.Response != null)
                        {
                            this.CurrentRequest.State = HTTPRequestStates.Finished;
                        }
                        else
                        {
                            this.CurrentRequest.Exception = new Exception(string.Format("[{0}] Remote server closed the connection before sending response header! Previous request state: {1}. Connection state: {2}",
                                                                                        this.ToString(),
                                                                                        this.CurrentRequest.State.ToString(),
                                                                                        this.State.ToString()));
                            this.CurrentRequest.State = HTTPRequestStates.Error;

                            proposedConnectionState = HTTPConnectionStates.Closed;
                        }
                    }

                    this.CurrentRequest = null;

                    if (proposedConnectionState == HTTPConnectionStates.Processing)
                    {
                        proposedConnectionState = HTTPConnectionStates.Closed;
                    }

                    ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, proposedConnectionState));
                }
            }
        }
Beispiel #18
0
        // No transfer-encoding just raw bytes.
        internal void ReadRaw(Stream stream, long contentLength)
        {
            BeginReceiveStreamFragments();

            // Progress report:
            long downloaded          = 0;
            long downloadLength      = contentLength;
            bool sendProgressChanged = this.baseRequest.OnDownloadProgress != null && (this.IsSuccess
#if !BESTHTTP_DISABLE_CACHING
                                                                                       || this.IsFromCache
#endif
                                                                                       );

            if (sendProgressChanged)
            {
                RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.baseRequest, RequestEvents.DownloadProgress, downloaded, downloadLength));
            }

            if (HTTPManager.Logger.Level == Logger.Loglevels.All)
            {
                VerboseLogging(string.Format("ReadRaw - contentLength: {0:N0}", contentLength));
            }

            string encoding =
#if !BESTHTTP_DISABLE_CACHING
                IsFromCache ? null :
#endif
                GetFirstHeaderValue("content-encoding");
            bool gzipped = !string.IsNullOrEmpty(encoding) && encoding == "gzip";

            Decompression.GZipDecompressor decompressor = gzipped ? new Decompression.GZipDecompressor(256) : null;

            if (!baseRequest.UseStreaming && contentLength > 2147483646)
            {
                throw new OverflowException("You have to use STREAMING to download files bigger than 2GB!");
            }

            using (var output = new BufferPoolMemoryStream(baseRequest.UseStreaming ? 0 : (int)contentLength))
            {
                // Because of the last parameter, buffer's size can be larger than the requested but there's no reason to use
                //  an exact sized one if there's an larger one available in the pool. Later we will use the whole buffer.
                byte[] buffer    = BufferPool.Get(Math.Max(baseRequest.StreamFragmentSize, MinBufferSize), true);
                int    readBytes = 0;

                while (contentLength > 0)
                {
                    if (this.baseRequest.IsCancellationRequested)
                    {
                        return;
                    }

                    readBytes = 0;

                    do
                    {
                        // tryToReadCount contain how much bytes we want to read in once. We try to read the buffer fully in once,
                        //  but with a limit of the remaining contentLength.
                        int tryToReadCount = (int)Math.Min(Math.Min(int.MaxValue, contentLength), buffer.Length - readBytes);

                        int bytes = stream.Read(buffer, readBytes, tryToReadCount);

                        if (bytes <= 0)
                        {
                            throw ExceptionHelper.ServerClosedTCPStream();
                        }

                        readBytes     += bytes;
                        contentLength -= bytes;

                        // Progress report:
                        if (sendProgressChanged)
                        {
                            downloaded += bytes;
                            RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.baseRequest, RequestEvents.DownloadProgress, downloaded, downloadLength));
                        }
                    } while (readBytes < buffer.Length && contentLength > 0);

                    if (baseRequest.UseStreaming)
                    {
                        if (gzipped)
                        {
                            var decompressed = decompressor.Decompress(buffer, 0, readBytes, false, true);
                            if (decompressed.Data != null)
                            {
                                FeedStreamFragment(decompressed.Data, 0, decompressed.Length);
                            }
                        }
                        else
                        {
                            FeedStreamFragment(buffer, 0, readBytes);
                        }
                    }
                    else
                    {
                        output.Write(buffer, 0, readBytes);
                    }
                }
                ;

                BufferPool.Release(buffer);

                if (baseRequest.UseStreaming)
                {
                    if (gzipped)
                    {
                        var decompressed = decompressor.Decompress(null, 0, 0, true, true);
                        if (decompressed.Data != null)
                        {
                            FeedStreamFragment(decompressed.Data, 0, decompressed.Length);
                        }
                    }

                    FlushRemainingFragmentBuffer();
                }

                if (!baseRequest.UseStreaming)
                {
                    this.Data = DecodeStream(output);
                }
            }

            if (decompressor != null)
            {
                decompressor.Dispose();
            }
        }
Beispiel #19
0
        protected override void ThreadFunc()
        {
            // XmlHttpRequest setup

            this.NativeId = XHR_Create(HTTPRequest.MethodNames[(byte)CurrentRequest.MethodType],
                                       CurrentRequest.CurrentUri.OriginalString,
                                       CurrentRequest.Credentials != null ? CurrentRequest.Credentials.UserName : null,
                                       CurrentRequest.Credentials != null ? CurrentRequest.Credentials.Password : null,
                                       CurrentRequest.WithCredentials ? 1 : 0);
            Connections.Add(NativeId, this);

            CurrentRequest.EnumerateHeaders((header, values) =>
            {
                if (!header.Equals("Content-Length"))
                {
                    for (int i = 0; i < values.Count; ++i)
                    {
                        XHR_SetRequestHeader(NativeId, header, values[i]);
                    }
                }
            }, /*callBeforeSendCallback:*/ true);

            XHR_SetResponseHandler(NativeId, WebGLConnection.OnResponse, WebGLConnection.OnError, WebGLConnection.OnTimeout, WebGLConnection.OnAborted);
            // Setting OnUploadProgress result in an addEventListener("progress", ...) call making the request non-simple.
            // https://forum.unity.com/threads/best-http-released.200006/page-49#post-3696220
            XHR_SetProgressHandler(NativeId,
                                   CurrentRequest.OnDownloadProgress == null ? (OnWebGLProgressDelegate)null : WebGLConnection.OnDownloadProgress,
                                   CurrentRequest.OnUploadProgress == null ? (OnWebGLProgressDelegate)null : WebGLConnection.OnUploadProgress);

            XHR_SetTimeout(NativeId, (uint)(CurrentRequest.ConnectTimeout.TotalMilliseconds + CurrentRequest.Timeout.TotalMilliseconds));

            byte[] body              = CurrentRequest.GetEntityBody();
            int    length            = 0;
            bool   releaseBodyBuffer = false;

            if (body == null)
            {
                var upStreamInfo = CurrentRequest.GetUpStream();
                if (upStreamInfo.Stream != null)
                {
                    var internalBuffer = BufferPool.Get(upStreamInfo.Length > 0 ? upStreamInfo.Length : HTTPRequest.UploadChunkSize, true);
                    using (BufferPoolMemoryStream ms = new BufferPoolMemoryStream(internalBuffer, 0, internalBuffer.Length, true, true, false, true))
                    {
                        var buffer    = BufferPool.Get(HTTPRequest.UploadChunkSize, true);
                        int readCount = -1;
                        while ((readCount = upStreamInfo.Stream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            ms.Write(buffer, 0, readCount);
                        }

                        BufferPool.Release(buffer);

                        length = (int)ms.Position;
                        body   = ms.GetBuffer();

                        releaseBodyBuffer = true;
                    }
                }
            }
            else
            {
                length = body.Length;

                XHR_Send(NativeId, body, length);
            }

            XHR_Send(NativeId, body, length);

            if (releaseBodyBuffer)
            {
                BufferPool.Release(body);
            }
        }