示例#1
0
        /// <summary>
        /// Encodes the given string.
        /// This method should not be directly used since it allocates.
        /// EncodeInto is preferred.
        /// </summary>
        /// <param name="value">The value to encode</param>
        /// <param name="huffman">Controls the huffman encoding</param>
        public static byte[] Encode(string value, HuffmanStrategy huffman)
        {
            // Estimate the size of the buffer
            var asciiSize             = Encoding.ASCII.GetByteCount(value);
            var estimatedHeaderLength = IntEncoder.RequiredBytes(asciiSize, 0, 7);
            var estimatedBufferSize   = estimatedHeaderLength + asciiSize;

            while (true)
            {
                // Create a buffer with some headroom
                var buf = new byte[estimatedBufferSize + 16];
                // Try to serialize value in there
                var size = EncodeInto(
                    new ArraySegment <byte>(buf), value, asciiSize, huffman);
                if (size != -1)
                {
                    // Serialization was performed
                    // Trim the buffer in order to return it
                    if (size == buf.Length)
                    {
                        return(buf);
                    }
                    var newBuf = new byte[size];
                    Array.Copy(buf, 0, newBuf, 0, size);
                    return(newBuf);
                }
                else
                {
                    // Need more buffer space
                    estimatedBufferSize = (estimatedBufferSize + 2) * 2;
                }
            }
        }
示例#2
0
        /// <summary>
        /// 对给定的字符串进行编码。
        /// </summary>
        /// <param name="value"></param>
        /// <param name="huffman"></param>
        /// <returns></returns>
        public static byte[] Encode(string value, HuffmanStrategy huffman)
        {
            var asciiSize             = Encoding.ASCII.GetByteCount(value);
            var estimatedHeaderLength = IntEncoder.RequiredBytes(asciiSize, 0, 7);
            var estimatedBufferSize   = estimatedHeaderLength + asciiSize;

            while (true)
            {
                var buf = new byte[estimatedBufferSize + 16];

                var size = EncodeInto(
                    new ArraySegment <byte>(buf), value, asciiSize, huffman);
                if (size != -1)
                {
                    if (size == buf.Length)
                    {
                        return(buf);
                    }
                    var newBuf = new byte[size];
                    Array.Copy(buf, 0, newBuf, 0, size);
                    return(newBuf);
                }
                else
                {
                    estimatedBufferSize = (estimatedBufferSize + 2) * 2;
                }
            }
        }
示例#3
0
        /// <summary>
        /// Creates a new HPACK Encoder
        /// </summary>
        /// <param name="options">Encoder options</param>
        public Encoder(Options?options)
        {
            var dynamicTableSize = Defaults.DynamicTableSize;

            this._huffmanStrategy = HuffmanStrategy.IfSmaller;

            if (options.HasValue)
            {
                var opts = options.Value;
                if (opts.DynamicTableSize.HasValue)
                {
                    dynamicTableSize = opts.DynamicTableSize.Value;
                }
                if (opts.HuffmanStrategy.HasValue)
                {
                    this._huffmanStrategy = opts.HuffmanStrategy.Value;
                }
            }

            // TODO: If the size is not the default size we basically also need
            // to send a size update frame immediatly.
            // However this currently the obligation of the user of this class

            this._headerTable = new HeaderTable(dynamicTableSize);
        }
示例#4
0
            /// <summary>
            /// Creates a bidirectionally open stream,
            /// where the headers in both directions have already been sent but
            /// no data.
            /// </summary>
            public static async Task <Result> CreateConnectionAndStream(
                bool isServer,
                ILoggerProvider loggerProvider,
                IBufferedPipe iPipe, IBufferedPipe oPipe,
                Settings?localSettings          = null,
                Settings?remoteSettings         = null,
                HuffmanStrategy huffmanStrategy = HuffmanStrategy.Never)
            {
                var result = new Result();

                if (isServer)
                {
                    var r1 = await ServerStreamTests.StreamCreator.CreateConnectionAndStream(
                        StreamState.Open, loggerProvider,
                        iPipe, oPipe,
                        localSettings, remoteSettings,
                        huffmanStrategy);

                    // Headers have been received but not sent
                    await r1.stream.WriteHeadersAsync(DefaultStatusHeaders, false);

                    await oPipe.ReadAndDiscardHeaders(1u, false);

                    result.conn     = r1.conn;
                    result.hEncoder = r1.hEncoder;
                    result.stream   = r1.stream;
                }
                else
                {
                    var r1 = await ClientStreamTests.StreamCreator.CreateConnectionAndStream(
                        StreamState.Open, loggerProvider,
                        iPipe, oPipe,
                        localSettings, remoteSettings,
                        huffmanStrategy);

                    // Headers have been sent but not yet received
                    await iPipe.WriteHeaders(r1.hEncoder, 1u, false, DefaultStatusHeaders, true);

                    result.conn     = r1.conn;
                    result.hEncoder = r1.hEncoder;
                    result.stream   = r1.stream;
                }

                return(result);
            }
示例#5
0
        public Encoder(Options?options)
        {
            var dynamicTableSize = Defaults.DynamicTableSize;

            this._huffmanStrategy = HuffmanStrategy.IfSmaller;

            if (options.HasValue)
            {
                var opts = options.Value;
                if (opts.DynamicTableSize.HasValue)
                {
                    dynamicTableSize = opts.DynamicTableSize.Value;
                }
                if (opts.HuffmanStrategy.HasValue)
                {
                    this._huffmanStrategy = opts.HuffmanStrategy.Value;
                }
            }
            this._headerTable = new HeaderTable(dynamicTableSize);
        }
示例#6
0
        public static async Task <Connection> BuildEstablishedConnection(
            bool isServer,
            IBufferedPipe inputStream,
            IBufferedPipe outputStream,
            ILoggerProvider loggerProvider,
            Func <IStream, bool> streamListener = null,
            Settings?localSettings          = null,
            Settings?remoteSettings         = null,
            HuffmanStrategy huffmanStrategy = HuffmanStrategy.Never)
        {
            ILogger logger = null;

            if (loggerProvider != null)
            {
                logger = loggerProvider.CreateLogger("http2Con");
            }
            if (streamListener == null)
            {
                streamListener = (s) => false;
            }

            var config = new ConnectionConfigurationBuilder(isServer)
                         .UseStreamListener(streamListener)
                         .UseHuffmanStrategy(huffmanStrategy)
                         .UseSettings(localSettings ?? Settings.Default)
                         .Build();

            var conn = new Connection(
                config, inputStream, outputStream,
                new Connection.Options
            {
                Logger = logger,
            });

            await PerformHandshakes(
                conn,
                inputStream, outputStream,
                remoteSettings);

            return(conn);
        }
        public static async Task <Result> CreateClientConnectionAndStream(
            StreamState state,
            ILoggerProvider loggerProvider,
            IBufferedPipe iPipe, IBufferedPipe oPipe,
            Settings?localSettings          = null,
            Settings?remoteSettings         = null,
            HuffmanStrategy huffmanStrategy = HuffmanStrategy.Never)
        {
            if (state == StreamState.Idle)
            {
                throw new Exception("Not supported");
            }

            var hEncoder = new Encoder();
            var conn     = await Http2ConnectionUtils.BuildEstablishedConnection(
                false, iPipe, oPipe, loggerProvider, null,
                localSettings : localSettings,
                remoteSettings : remoteSettings,
                huffmanStrategy : huffmanStrategy);

            var endOfStream = false;

            if (state == StreamState.HalfClosedLocal ||
                state == StreamState.Closed)
            {
                endOfStream = true;
            }
            var stream = await conn.CreateStreamAsync(
                DefaultGetHeaders, endOfStream : endOfStream);

            await oPipe.ReadAndDiscardHeaders(1u, endOfStream);

            if (state == StreamState.HalfClosedRemote ||
                state == StreamState.Closed)
            {
                var outBuf = new byte[Settings.Default.MaxFrameSize];
                var result = hEncoder.EncodeInto(
                    new ArraySegment <byte>(outBuf),
                    DefaultStatusHeaders);
                await iPipe.WriteFrameHeaderWithTimeout(
                    new FrameHeader
                {
                    Type  = FrameType.Headers,
                    Flags = (byte)(HeadersFrameFlags.EndOfHeaders |
                                   HeadersFrameFlags.EndOfStream),
                    StreamId = 1u,
                    Length   = result.UsedBytes,
                });

                await iPipe.WriteAsync(new ArraySegment <byte>(outBuf, 0, result.UsedBytes));

                var readHeadersTask = stream.ReadHeadersAsync();
                var combined        = await Task.WhenAny(readHeadersTask, Task.Delay(
                                                             Http2ReadableStreamTestExtensions.ReadTimeout));

                Assert.True(readHeadersTask == combined, "Expected to receive headers");
                var headers = await readHeadersTask;
                Assert.True(headers.SequenceEqual(DefaultStatusHeaders));
            }
            else if (state == StreamState.Reset)
            {
                await iPipe.WriteResetStream(1u, ErrorCode.Cancel);
            }

            return(new Result
            {
                hEncoder = hEncoder,
                conn = conn,
                stream = stream,
            });
        }
        public static async Task <Result> CreateServerConnectionAndStream(HeaderField[] getHeaders,
                                                                          StreamState state,
                                                                          ILoggerProvider loggerProvider,
                                                                          IBufferedPipe iPipe, IBufferedPipe oPipe,
                                                                          Settings?localSettings          = null,
                                                                          Settings?remoteSettings         = null,
                                                                          HuffmanStrategy huffmanStrategy = HuffmanStrategy.Never)
        {
            IStream stream      = null;
            var     handlerDone = new SemaphoreSlim(0);

            if (state == StreamState.Idle)
            {
                throw new Exception("Not supported");
            }

            Func <IStream, bool> listener = (s) =>
            {
                Task.Run(async() =>
                {
                    stream = s;
                    try
                    {
                        await s.ReadHeadersAsync();
                        if (state == StreamState.Reset)
                        {
                            s.Cancel();
                            return;
                        }

                        if (state == StreamState.HalfClosedRemote ||
                            state == StreamState.Closed)
                        {
                            await s.ReadAllToArrayWithTimeout();
                        }

                        if (state == StreamState.HalfClosedLocal ||
                            state == StreamState.Closed)
                        {
                            await s.WriteHeadersAsync(
                                DefaultStatusHeaders, true);
                        }
                    }
                    finally
                    {
                        handlerDone.Release();
                    }
                });
                return(true);
            };
            var conn = await Http2ConnectionUtils.BuildEstablishedConnection(
                true, iPipe, oPipe, loggerProvider, listener,
                localSettings : localSettings,
                remoteSettings : remoteSettings,
                huffmanStrategy : huffmanStrategy);

            var hEncoder = new Encoder();

            await iPipe.WriteHeaders(
                hEncoder, 1, false, getHeaders);

            if (state == StreamState.HalfClosedRemote ||
                state == StreamState.Closed)
            {
                await iPipe.WriteData(1u, 0, endOfStream : true);
            }

            var ok = await handlerDone.WaitAsync(
                Http2ReadableStreamTestExtensions.ReadTimeout);

            if (!ok)
            {
                throw new Exception("Stream handler did not finish");
            }

            if (state == StreamState.HalfClosedLocal ||
                state == StreamState.Closed)
            {
                // Consume the sent headers and data
                await oPipe.ReadAndDiscardHeaders(1u, true);
            }
            else if (state == StreamState.Reset)
            {
                // Consume the sent reset frame
                await oPipe.AssertResetStreamReception(1, ErrorCode.Cancel);
            }

            return(new Result
            {
                conn = conn,
                stream = stream,
                hEncoder = hEncoder,
            });
        }
 /// <summary>
 /// Configures the strategy for applying huffman encoding on outgoing
 /// headers.
 /// </summary>
 public ConnectionConfigurationBuilder UseHuffmanStrategy(HuffmanStrategy strategy)
 {
     this.huffmanStrategy = strategy;
     return(this);
 }
示例#10
0
        /// <summary>
        /// Encodes the given string into the target buffer
        /// </summary>
        /// <param name="buf">The buffer into which the string should be serialized</param>
        /// <param name="value">The value to encode</param>
        /// <param name="valueByteLen">
        /// The length of the string in bytes in non-huffman-encoded form.
        /// This can be retrieved through the GetByteLength method.
        /// </param>
        /// <param name="huffman">Controls the huffman encoding</param>
        /// <returns>
        /// The number of bytes that were required to encode the value.
        /// -1 if the value did not fit into the buffer.
        /// </returns>
        public static int EncodeInto(
            ArraySegment <byte> buf,
            string value, int valueByteLen, HuffmanStrategy huffman)
        {
            var offset = buf.Offset;
            var free   = buf.Count;

            // Fast check for free space. Doesn't need to be exact
            if (free < 1 + valueByteLen)
            {
                return(-1);
            }

            var encodedByteLen       = valueByteLen;
            var requiredHuffmanBytes = 0;
            var useHuffman           = huffman == HuffmanStrategy.Always;

            byte[] huffmanInputBuf = null;

            // Check if the string should be reencoded with huffman encoding
            if (huffman == HuffmanStrategy.Always || huffman == HuffmanStrategy.IfSmaller)
            {
                huffmanInputBuf      = Encoding.ASCII.GetBytes(value);
                requiredHuffmanBytes = Huffman.EncodedLength(
                    new ArraySegment <byte>(huffmanInputBuf));
                if (huffman == HuffmanStrategy.IfSmaller && requiredHuffmanBytes < encodedByteLen)
                {
                    useHuffman = true;
                }
            }

            if (useHuffman)
            {
                encodedByteLen = requiredHuffmanBytes;
            }

            // Write the required length to the target buffer
            var prefixContent = useHuffman ? (byte)0x80 : (byte)0;
            var used          = IntEncoder.EncodeInto(
                new ArraySegment <byte>(buf.Array, offset, free),
                encodedByteLen, prefixContent, 7);

            if (used == -1)
            {
                return(-1);            // Couldn't write length
            }
            offset += used;
            free   -= used;

            if (useHuffman)
            {
                if (free < requiredHuffmanBytes)
                {
                    return(-1);
                }
                // Use the huffman encoder to write bytes to target buffer
                used = Huffman.EncodeInto(
                    new ArraySegment <byte>(buf.Array, offset, free),
                    new ArraySegment <byte>(huffmanInputBuf));
                if (used == -1)
                {
                    return(-1);            // Couldn't write length
                }
                offset += used;
            }
            else
            {
                if (free < valueByteLen)
                {
                    return(-1);
                }
                // Use ASCII encoder to write bytes to target buffer
                used = Encoding.ASCII.GetBytes(
                    value, 0, value.Length, buf.Array, offset);
                offset += used;
            }

            // Return the number amount of used bytes
            return(offset - buf.Offset);
        }
示例#11
0
        /// <summary>
        /// 将给定的字符串编码到目标缓冲区中
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="value"></param>
        /// <param name="valueByteLen"></param>
        /// <param name="huffman"></param>
        /// <returns></returns>
        public static int EncodeInto(
            ArraySegment <byte> buf,
            string value, int valueByteLen, HuffmanStrategy huffman)
        {
            var offset = buf.Offset;
            var free   = buf.Count;

            if (free < 1 + valueByteLen)
            {
                return(-1);
            }

            var encodedByteLen       = valueByteLen;
            var requiredHuffmanBytes = 0;
            var useHuffman           = huffman == HuffmanStrategy.Always;

            byte[] huffmanInputBuf = null;


            if (huffman == HuffmanStrategy.Always || huffman == HuffmanStrategy.IfSmaller)
            {
                huffmanInputBuf      = Encoding.ASCII.GetBytes(value);
                requiredHuffmanBytes = Huffman.EncodedLength(
                    new ArraySegment <byte>(huffmanInputBuf));
                if (huffman == HuffmanStrategy.IfSmaller && requiredHuffmanBytes < encodedByteLen)
                {
                    useHuffman = true;
                }
            }

            if (useHuffman)
            {
                encodedByteLen = requiredHuffmanBytes;
            }


            var prefixContent = useHuffman ? (byte)0x80 : (byte)0;
            var used          = IntEncoder.EncodeInto(
                new ArraySegment <byte>(buf.Array, offset, free),
                encodedByteLen, prefixContent, 7);

            if (used == -1)
            {
                return(-1);
            }
            offset += used;
            free   -= used;

            if (useHuffman)
            {
                if (free < requiredHuffmanBytes)
                {
                    return(-1);
                }

                used = Huffman.EncodeInto(
                    new ArraySegment <byte>(buf.Array, offset, free),
                    new ArraySegment <byte>(huffmanInputBuf));
                if (used == -1)
                {
                    return(-1);
                }
                offset += used;
            }
            else
            {
                if (free < valueByteLen)
                {
                    return(-1);
                }

                used = Encoding.ASCII.GetBytes(
                    value, 0, value.Length, buf.Array, offset);
                offset += used;
            }

            return(offset - buf.Offset);
        }