Example #1
0
        protected byte[] DecodeStream(BufferPoolMemoryStream streamToDecode)
        {
            streamToDecode.Seek(0, SeekOrigin.Begin);

            // The cache stores the decoded data
            var encoding =
#if !BESTHTTP_DISABLE_CACHING
                IsFromCache ? null :
#endif
                GetHeaderValues("content-encoding");

#if !UNITY_WEBGL || UNITY_EDITOR
            Stream decoderStream = null;
#endif

            // Return early if there are no encoding used.
            if (encoding == null)
            {
                return(streamToDecode.ToArray());
            }
            else
            {
                switch (encoding[0])
                {
#if !UNITY_WEBGL || UNITY_EDITOR
                case "gzip": decoderStream = new Decompression.Zlib.GZipStream(streamToDecode, Decompression.Zlib.CompressionMode.Decompress); break;

                case "deflate": decoderStream = new Decompression.Zlib.DeflateStream(streamToDecode, Decompression.Zlib.CompressionMode.Decompress); break;
#endif
                //identity, utf-8, etc.
                default:
                    // Do not copy from one stream to an other, just return with the raw bytes
                    return(streamToDecode.ToArray());
                }
            }

#if !UNITY_WEBGL || UNITY_EDITOR
            using (var ms = new BufferPoolMemoryStream((int)streamToDecode.Length))
            {
                var buf       = BufferPool.Get(1024, true);
                int byteCount = 0;

                while ((byteCount = decoderStream.Read(buf, 0, buf.Length)) > 0)
                {
                    ms.Write(buf, 0, byteCount);
                }

                BufferPool.Release(buf);

                decoderStream.Dispose();
                return(ms.ToArray());
            }
#endif
        }
Example #2
0
        internal void AddData(Stream stream)
        {
            if (this.IsCompressed)
            {
                using (var decoderStream = new Decompression.Zlib.GZipStream(stream, Decompression.Zlib.CompressionMode.Decompress))
                {
                    using (var ms = new BufferPoolMemoryStream((int)stream.Length))
                    {
                        var buf       = BufferPool.Get(8 * 1024, true);
                        int byteCount = 0;

                        while ((byteCount = decoderStream.Read(buf, 0, buf.Length)) > 0)
                        {
                            ms.Write(buf, 0, byteCount);
                        }

                        BufferPool.Release(buf);

                        base.Data = ms.ToArray();
                    }
                }
            }
            else
            {
                base.Data = BufferPool.Get(stream.Length, false);
                stream.Read(base.Data, 0, (int)stream.Length);
            }
        }
        public RawFrameData Get()
        {
            if (Data == null)
            {
                Data = VariableSizedBufferPool.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 = BitConverter.GetBytes((Int32)this.GetHashCode());
                ms.Write(mask, 0, mask.Length);

                // 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));
            }
        }
Example #4
0
        public DecompressedData Decompress(byte[] data, int offset, int count, bool forceDecompress = false, bool dataCanBeLarger = false)
        {
            if (decompressorInputStream == null)
            {
                decompressorInputStream = new BufferPoolMemoryStream(count);
            }

            if (data != null)
            {
                decompressorInputStream.Write(data, offset, count);
            }

            if (!forceDecompress && decompressorInputStream.Length < MinLengthToDecompress)
            {
                return(new DecompressedData(null, 0));
            }

            decompressorInputStream.Position = 0;

            if (decompressorGZipStream == null)
            {
                decompressorGZipStream = new Zlib.GZipStream(decompressorInputStream,
                                                             Zlib.CompressionMode.Decompress,
                                                             Zlib.CompressionLevel.Default,
                                                             true);
                decompressorGZipStream.FlushMode = Zlib.FlushType.Sync;
            }

            if (decompressorOutputStream == null)
            {
                decompressorOutputStream = new BufferPoolMemoryStream();
            }
            decompressorOutputStream.SetLength(0);

            byte[] copyBuffer = BufferPool.Get(1024, true);

            int readCount;
            int sumReadCount = 0;

            while ((readCount = decompressorGZipStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0)
            {
                decompressorOutputStream.Write(copyBuffer, 0, readCount);
                sumReadCount += readCount;
            }

            BufferPool.Release(copyBuffer);

            // If no read is done (returned with any data) don't zero out the input stream, as it would delete any not yet used data.
            if (sumReadCount > 0)
            {
                decompressorGZipStream.SetLength(0);
            }

            byte[] result = decompressorOutputStream.ToArray(dataCanBeLarger);

            return(new DecompressedData(result, dataCanBeLarger ? (int)decompressorOutputStream.Length : result.Length));
        }
Example #5
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!
                    }

                    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]));
                        }
                    }
                }, true);

                byte[] requestBody = request.GetEntityBody();
                CreateHeaderFrames(to,
                                   streamId,
                                   bufferStream.ToArray(true),
                                   (UInt32)bufferStream.Length,
                                   requestBody != null && requestBody.Length > 0 /*hasBody*/);
            }
        }
        private byte[] Decompress(byte[] data, int offset, int count, bool forceDecompress = false)
        {
            if (decompressorInputStream == null)
            {
                decompressorInputStream = new BufferPoolMemoryStream(count);
            }

            if (data != null)
            {
                decompressorInputStream.Write(data, offset, count);
            }

            if (!forceDecompress && decompressorInputStream.Length < MinLengthToDecompress)
            {
                return(null);
            }

            decompressorInputStream.Position = 0;

            if (decompressorGZipStream == null)
            {
                decompressorGZipStream = new Decompression.Zlib.GZipStream(decompressorInputStream,
                                                                           Decompression.Zlib.CompressionMode.Decompress,
                                                                           Decompression.Zlib.CompressionLevel.Default,
                                                                           true);
                decompressorGZipStream.FlushMode = Decompression.Zlib.FlushType.Sync;
            }

            if (decompressorOutputStream == null)
            {
                decompressorOutputStream = new BufferPoolMemoryStream();
            }
            decompressorOutputStream.SetLength(0);

            byte[] copyBuffer = VariableSizedBufferPool.Get(1024, true);

            int readCount;

            while ((readCount = decompressorGZipStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0)
            {
                decompressorOutputStream.Write(copyBuffer, 0, readCount);
            }

            VariableSizedBufferPool.Release(copyBuffer);

            decompressorGZipStream.SetLength(0);

            // TODO: be able to use a larger array
            byte[] result = decompressorOutputStream.ToArray();

            return(result);
        }
Example #7
0
        /// <summary>
        /// A function to decompress and return the data parameter with possible context takeover support (reusing the DeflateStream).
        /// </summary>
        private byte[] Decompress(byte[] data, int length)
        {
            if (decompressorInputStream == null)
            {
                decompressorInputStream = new BufferPoolMemoryStream(length + 4);
            }

            decompressorInputStream.Write(data, 0, length);

            // http://tools.ietf.org/html/rfc7692#section-7.2.2
            // Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the payload of the message.
            decompressorInputStream.Write(PerMessageCompression.Trailer, 0, PerMessageCompression.Trailer.Length);

            decompressorInputStream.Position = 0;

            if (decompressorDeflateStream == null)
            {
                decompressorDeflateStream           = new DeflateStream(decompressorInputStream, CompressionMode.Decompress, CompressionLevel.Default, true, this.ServerMaxWindowBits);
                decompressorDeflateStream.FlushMode = FlushType.Sync;
            }

            if (decompressorOutputStream == null)
            {
                decompressorOutputStream = new BufferPoolMemoryStream();
            }
            decompressorOutputStream.SetLength(0);

            byte[] copyBuffer = BufferPool.Get(1024, true);
            int    readCount;

            while ((readCount = decompressorDeflateStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0)
            {
                decompressorOutputStream.Write(copyBuffer, 0, readCount);
            }

            BufferPool.Release(copyBuffer);

            decompressorDeflateStream.SetLength(0);

            byte[] result = decompressorOutputStream.ToArray();

            if (this.ServerNoContextTakeover)
            {
                decompressorDeflateStream.Dispose();
                decompressorDeflateStream = null;
            }

            return(result);
        }
Example #8
0
        public override byte[] GetData()
        {
            if (CachedData != null)
            {
                return(CachedData);
            }

            using (var ms = new BufferPoolMemoryStream())
            {
                for (int i = 0; i < Fields.Count; ++i)
                {
                    HTTPFieldData field = Fields[i];

                    // Set the boundary
                    ms.WriteLine("--" + Boundary);

                    // Set up Content-Disposition header to our form with the name
                    ms.WriteLine("Content-Disposition: form-data; name=\"" + field.Name + "\"" + (!string.IsNullOrEmpty(field.FileName) ? "; filename=\"" + field.FileName + "\"" : string.Empty));

                    // Set up Content-Type head for the form.
                    if (!string.IsNullOrEmpty(field.MimeType))
                    {
                        ms.WriteLine("Content-Type: " + field.MimeType);
                    }

                    ms.WriteLine("Content-Length: " + field.Payload.Length.ToString());
                    ms.WriteLine();

                    // Write the actual data to the MemoryStream
                    ms.Write(field.Payload, 0, field.Payload.Length);

                    ms.Write(HTTPRequest.EOL, 0, HTTPRequest.EOL.Length);
                }

                // Write out the trailing boundary
                ms.WriteLine("--" + Boundary + "--");

                IsChanged = false;

                // Set the RawData of our request
                return(CachedData = ms.ToArray());
            }
        }
Example #9
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);
        }
Example #10
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());
            }
        }
Example #11
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);
                }
            }
        }
Example #12
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);
            }
        }
Example #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));
            }
        }