diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b2d6788 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: CI + +on: + push: + branches: + - master + - github-workflow +jobs: + Build: + runs-on: ubuntu-latest + + steps: + - name: Checkout latest + uses: actions/checkout@v2 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.* + + - name: Install dependencies + run: dotnet restore + + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Upload Artificats + uses: actions/upload-artifact@v4 + with: + name: TestBinaries + path: HttpTwo.Tests/**/bin/* + + UnitTest: + name: Run Unit Tests + needs: Build + runs-on: windows-latest + steps: + - name: Checkout latest + uses: actions/checkout@v2 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: "20.x" + + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 6.* + + - uses: actions/download-artifact@v4 + with: + name: TestBinaries + path: HttpTwo.Tests + + - name: Install dependencies + working-directory: ./HttpTwo.Tests/node-http2 + run: npm install + + - name: Unit Test (.NET 6.0) + if: ${{ !cancelled() }} + run: dotnet test --verbosity normal --logger "trx;LogFileName=net6.trx" --results-directory coverage bin/Release/net6.0/HttpTwo.Tests.dll + working-directory: ./HttpTwo.Tests + + - name: Unit Test (.NET Framework 4.6.2) + if: ${{ !cancelled() }} + run: dotnet test --verbosity normal --logger "trx;LogFileName=net462.trx" --results-directory coverage bin/Release/net462/HttpTwo.Tests.dll + working-directory: ./HttpTwo.Tests + + - uses: dorny/test-reporter@v1 + if: success() || failure() # run this step even if previous step failed + with: + name: Test Result + path: HttpTwo.Tests/coverage/*.trx + reporter: dotnet-trx diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7270bb9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Release to NuGet + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout latest + uses: actions/checkout@v2 + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 8.* + + - name: Version Output + run: echo ${{ github.event.release.tag_name }} # Output: 1.1.2 + + - name: Build + run: dotnet build --configuration Release + + - name: Generate package + run: dotnet pack --no-build -c Release -o out /p:Version=${{ github.event.release.tag_name }} + + - name: Publish + run: dotnet nuget push ./out/*.nupkg --api-key ${{secrets.NUGET_API_KEY}} --source https://api.nuget.org/v3/index.json --skip-duplicate + \ No newline at end of file diff --git a/HttpTwo.HPack/Decoder.cs b/HttpTwo.HPack/Decoder.cs deleted file mode 100644 index 1695491..0000000 --- a/HttpTwo.HPack/Decoder.cs +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -using System; -using System.IO; - -namespace HttpTwo.HPack -{ - public class Decoder - { - private static IOException DECOMPRESSION_EXCEPTION = new IOException("decompression failure"); - - private static byte[] EMPTY = { }; - - private DynamicTable dynamicTable; - - private int maxHeaderSize; - private int maxDynamicTableSize; - private int encoderMaxDynamicTableSize; - private bool maxDynamicTableSizeChangeRequired; - - private long headerSize; - private State state; - private HPackUtil.IndexType indexType; - private int index; - private bool huffmanEncoded; - private int skipLength; - private int nameLength; - private int valueLength; - private byte[] name; - - public enum State - { - READ_HEADER_REPRESENTATION, - READ_MAX_DYNAMIC_TABLE_SIZE, - READ_INDEXED_HEADER, - READ_INDEXED_HEADER_NAME, - READ_LITERAL_HEADER_NAME_LENGTH_PREFIX, - READ_LITERAL_HEADER_NAME_LENGTH, - READ_LITERAL_HEADER_NAME, - SKIP_LITERAL_HEADER_NAME, - READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX, - READ_LITERAL_HEADER_VALUE_LENGTH, - READ_LITERAL_HEADER_VALUE, - SKIP_LITERAL_HEADER_VALUE - } - - /// - /// Initializes a new instance of the class. - /// - /// Max header size. - /// Max header table size. - public Decoder(int maxHeaderSize, int maxHeaderTableSize) - { - this.dynamicTable = new DynamicTable(maxHeaderTableSize); - this.maxHeaderSize = maxHeaderSize; - this.maxDynamicTableSize = maxHeaderTableSize; - this.encoderMaxDynamicTableSize = maxHeaderTableSize; - this.maxDynamicTableSizeChangeRequired = false; - this.Reset(); - } - - private void Reset() - { - this.headerSize = 0; - this.state = State.READ_HEADER_REPRESENTATION; - this.indexType = HPackUtil.IndexType.NONE; - } - - /// - /// Decode the header block into header fields. - /// - /// Input. - /// Header listener. - public void Decode(BinaryReader input, AddHeaderDelegate addHeaderDelegate) - { - while(input.BaseStream.Length - input.BaseStream.Position > 0) { - switch(this.state) { - case State.READ_HEADER_REPRESENTATION: - sbyte b = input.ReadSByte(); - if (maxDynamicTableSizeChangeRequired && (b & 0xE0) != 0x20) { - // Encoder MUST signal maximum dynamic table size change - throw new IOException("max dynamic table size change required"); - } - if (b < 0) { - // Indexed Header Field - index = b & 0x7F; - if (index == 0) { - throw new IOException("illegal index value (" + index + ")"); - } else if (index == 0x7F) { - state = State.READ_INDEXED_HEADER; - } else { - this.IndexHeader(index, addHeaderDelegate); - } - } else if ((b & 0x40) == 0x40) { - // Literal Header Field with Incremental Indexing - indexType = HPackUtil.IndexType.INCREMENTAL; - index = b & 0x3F; - if (index == 0) { - state = State.READ_LITERAL_HEADER_NAME_LENGTH_PREFIX; - } else if (index == 0x3F) { - state = State.READ_INDEXED_HEADER_NAME; - } else { - // Index was stored as the prefix - this.ReadName(index); - state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; - } - } else if ((b & 0x20) == 0x20) { - // Dynamic Table Size Update - index = b & 0x1F; - if (index == 0x1F) { - state = State.READ_MAX_DYNAMIC_TABLE_SIZE; - } else { - this.SetDynamicTableSize(index); - state = State.READ_HEADER_REPRESENTATION; - } - } else { - // Literal Header Field without Indexing / never Indexed - indexType = ((b & 0x10) == 0x10) ? HPackUtil.IndexType.NEVER : HPackUtil.IndexType.NONE; - index = b & 0x0F; - if (index == 0) { - state = State.READ_LITERAL_HEADER_NAME_LENGTH_PREFIX; - } else if (index == 0x0F) { - state = State.READ_INDEXED_HEADER_NAME; - } else { - // Index was stored as the prefix - this.ReadName(index); - state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; - } - } - break; - - case State.READ_MAX_DYNAMIC_TABLE_SIZE: - int maxSize = Decoder.DecodeULE128(input); - if (maxSize == -1) { - return; - } - - // Check for numerical overflow - if (maxSize > int.MaxValue - index) { - throw DECOMPRESSION_EXCEPTION; - } - - this.SetDynamicTableSize(index + maxSize); - state = State.READ_HEADER_REPRESENTATION; - break; - - case State.READ_INDEXED_HEADER: - int headerIndex = Decoder.DecodeULE128(input); - if (headerIndex == -1) { - return; - } - - // Check for numerical overflow - if (headerIndex > int.MaxValue - index) { - throw DECOMPRESSION_EXCEPTION; - } - - this.IndexHeader(index + headerIndex, addHeaderDelegate); - state = State.READ_HEADER_REPRESENTATION; - break; - - case State.READ_INDEXED_HEADER_NAME: - // Header Name matches an entry in the Header Table - int nameIndex = Decoder.DecodeULE128(input); - if (nameIndex == -1) { - return; - } - - // Check for numerical overflow - if (nameIndex > int.MaxValue - index) { - throw DECOMPRESSION_EXCEPTION; - } - - this.ReadName(index + nameIndex); - state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; - break; - - case State.READ_LITERAL_HEADER_NAME_LENGTH_PREFIX: - b = input.ReadSByte(); - huffmanEncoded = (b & 0x80) == 0x80; - index = b & 0x7F; - if (index == 0x7f) { - state = State.READ_LITERAL_HEADER_NAME_LENGTH; - } else { - nameLength = index; - - // Disallow empty names -- they cannot be represented in HTTP/1.x - if (nameLength == 0) { - throw DECOMPRESSION_EXCEPTION; - } - - // Check name length against max header size - if (this.ExceedsMaxHeaderSize(nameLength)) { - if (indexType == HPackUtil.IndexType.NONE) { - // Name is unused so skip bytes - name = EMPTY; - this.skipLength = nameLength; - state = State.SKIP_LITERAL_HEADER_NAME; - break; - } - - // Check name length against max dynamic table size - if (nameLength + HeaderField.HEADER_ENTRY_OVERHEAD > this.dynamicTable.Capacity) { - this.dynamicTable.Clear(); - name = EMPTY; - this.skipLength = nameLength; - state = State.SKIP_LITERAL_HEADER_NAME; - break; - } - } - state = State.READ_LITERAL_HEADER_NAME; - } - break; - - case State.READ_LITERAL_HEADER_NAME_LENGTH: - // Header Name is a Literal String - nameLength = Decoder.DecodeULE128(input); - if (nameLength == -1) { - return; - } - - // Check for numerical overflow - if (nameLength > int.MaxValue - index) { - throw DECOMPRESSION_EXCEPTION; - } - nameLength += index; - - // Check name length against max header size - if (this.ExceedsMaxHeaderSize(nameLength)) { - if (indexType == HPackUtil.IndexType.NONE) { - // Name is unused so skip bytes - name = EMPTY; - this.skipLength = nameLength; - state = State.SKIP_LITERAL_HEADER_NAME; - break; - } - - // Check name length against max dynamic table size - if (nameLength + HeaderField.HEADER_ENTRY_OVERHEAD > this.dynamicTable.Capacity) { - this.dynamicTable.Clear(); - name = EMPTY; - this.skipLength = nameLength; - state = State.SKIP_LITERAL_HEADER_NAME; - break; - } - } - state = State.READ_LITERAL_HEADER_NAME; - break; - - case State.READ_LITERAL_HEADER_NAME: - // Wait until entire name is readable - if (input.BaseStream.Length - input.BaseStream.Position < nameLength) { - return; - } - - name = this.ReadStringLiteral(input, nameLength); - state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; - break; - - case State.SKIP_LITERAL_HEADER_NAME: - - this.skipLength -= (int)input.BaseStream.Seek(this.skipLength, SeekOrigin.Current); - if (this.skipLength < 0) { - this.skipLength = 0; - } - if (this.skipLength == 0) { - state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; - } - break; - - case State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX: - b = input.ReadSByte(); - huffmanEncoded = (b & 0x80) == 0x80; - index = b & 0x7F; - if (index == 0x7f) { - state = State.READ_LITERAL_HEADER_VALUE_LENGTH; - } else { - this.valueLength = index; - - // Check new header size against max header size - long newHeaderSize1 = (long)nameLength + (long)this.valueLength; - if (this.ExceedsMaxHeaderSize(newHeaderSize1)) { - // truncation will be reported during endHeaderBlock - headerSize = maxHeaderSize + 1; - - if (indexType == HPackUtil.IndexType.NONE) { - // Value is unused so skip bytes - state = State.SKIP_LITERAL_HEADER_VALUE; - break; - } - - // Check new header size against max dynamic table size - if (newHeaderSize1 + HeaderField.HEADER_ENTRY_OVERHEAD > this.dynamicTable.Capacity) { - this.dynamicTable.Clear(); - state = State.SKIP_LITERAL_HEADER_VALUE; - break; - } - } - - if (this.valueLength == 0) { - this.InsertHeader(addHeaderDelegate, name, EMPTY, indexType); - state = State.READ_HEADER_REPRESENTATION; - } else { - state = State.READ_LITERAL_HEADER_VALUE; - } - } - break; - - case State.READ_LITERAL_HEADER_VALUE_LENGTH: - // Header Value is a Literal String - this.valueLength = Decoder.DecodeULE128(input); - if (this.valueLength == -1) { - return; - } - - // Check for numerical overflow - if (this.valueLength > int.MaxValue - index) { - throw DECOMPRESSION_EXCEPTION; - } - this.valueLength += index; - - // Check new header size against max header size - long newHeaderSize2 = (long)nameLength + (long)this.valueLength; - if (newHeaderSize2 + headerSize > maxHeaderSize) { - // truncation will be reported during endHeaderBlock - headerSize = maxHeaderSize + 1; - - if (indexType == HPackUtil.IndexType.NONE) { - // Value is unused so skip bytes - state = State.SKIP_LITERAL_HEADER_VALUE; - break; - } - - // Check new header size against max dynamic table size - if (newHeaderSize2 + HeaderField.HEADER_ENTRY_OVERHEAD > this.dynamicTable.Capacity) { - this.dynamicTable.Clear(); - state = State.SKIP_LITERAL_HEADER_VALUE; - break; - } - } - state = State.READ_LITERAL_HEADER_VALUE; - break; - - case State.READ_LITERAL_HEADER_VALUE: - // Wait until entire value is readable - if (input.BaseStream.Length - input.BaseStream.Position < this.valueLength) { - return; - } - - byte[] value = this.ReadStringLiteral(input, this.valueLength); - this.InsertHeader(addHeaderDelegate, name, value, indexType); - state = State.READ_HEADER_REPRESENTATION; - break; - - case State.SKIP_LITERAL_HEADER_VALUE: - this.valueLength -= (int)input.BaseStream.Seek(this.valueLength, SeekOrigin.Current); - if (this.valueLength < 0) { - this.valueLength = 0; - } - if (this.valueLength == 0) { - state = State.READ_HEADER_REPRESENTATION; - } - break; - - default: - throw new Exception("should not reach here"); - } - } - } - - /// - /// End the current header block. Returns if the header field has been truncated. - /// This must be called after the header block has been completely decoded. - /// - /// true, if header block was ended, false otherwise. - public bool EndHeaderBlock() - { - bool truncated = headerSize > maxHeaderSize; - this.Reset(); - return truncated; - } - - /// - /// Set the maximum table size. - /// If this is below the maximum size of the dynamic table used by the encoder, - /// the beginning of the next header block MUST signal this change. - /// - /// Max header table size. - public void SetMaxHeaderTableSize(int maxHeaderTableSize) - { - maxDynamicTableSize = maxHeaderTableSize; - if (maxDynamicTableSize < encoderMaxDynamicTableSize) { - // decoder requires less space than encoder - // encoder MUST signal this change - this.maxDynamicTableSizeChangeRequired = true; - this.dynamicTable.SetCapacity(maxDynamicTableSize); - } - } - - /// - /// Return the maximum table size. - /// This is the maximum size allowed by both the encoder and the decoder. - /// - /// The max header table size. - public int GetMaxHeaderTableSize() - { - return this.dynamicTable.Capacity; - } - - /// - /// Return the number of header fields in the dynamic table. - /// Exposed for testing. - /// - int Length() - { - return this.dynamicTable.Length(); - } - - /// - /// Return the size of the dynamic table. - /// Exposed for testing. - /// - int Size() - { - return this.dynamicTable.Size; - } - - /// - /// Return the header field at the given index. - /// Exposed for testing. - /// - /// The header field. - /// Index. - HeaderField GetHeaderField(int index) - { - return this.dynamicTable.GetEntry(index + 1); - } - - private void SetDynamicTableSize(int dynamicTableSize) - { - if (dynamicTableSize > this.maxDynamicTableSize) { - throw new IOException("invalid max dynamic table size"); - } - this.encoderMaxDynamicTableSize = dynamicTableSize; - this.maxDynamicTableSizeChangeRequired = false; - this.dynamicTable.SetCapacity(dynamicTableSize); - } - - private void ReadName(int index) - { - if (index <= StaticTable.Length) { - HeaderField headerField = StaticTable.GetEntry(index); - name = headerField.Name; - } else if (index - StaticTable.Length <= this.dynamicTable.Length()) { - HeaderField headerField = this.dynamicTable.GetEntry(index - StaticTable.Length); - name = headerField.Name; - } else { - throw new IOException("illegal index value (" + index + ")"); - } - } - - private void IndexHeader(int index, AddHeaderDelegate addHeaderDelegate) - { - if (index <= StaticTable.Length) { - HeaderField headerField = StaticTable.GetEntry(index); - this.AddHeader(addHeaderDelegate, headerField.Name, headerField.Value, false); - } else if (index - StaticTable.Length <= this.dynamicTable.Length()) { - HeaderField headerField = this.dynamicTable.GetEntry(index - StaticTable.Length); - this.AddHeader(addHeaderDelegate, headerField.Name, headerField.Value, false); - } else { - throw new IOException("illegal index value (" + index + ")"); - } - } - - private void InsertHeader(AddHeaderDelegate addHeaderDelegate, byte[] name, byte[] value, HPackUtil.IndexType indexType) - { - this.AddHeader(addHeaderDelegate, name, value, indexType == HPackUtil.IndexType.NEVER); - - switch(indexType) { - case HPackUtil.IndexType.NONE: - case HPackUtil.IndexType.NEVER: - break; - - case HPackUtil.IndexType.INCREMENTAL: - this.dynamicTable.Add(new HeaderField(name, value)); - break; - - default: - throw new Exception("should not reach here"); - } - } - - private void AddHeader(AddHeaderDelegate addHeaderDelegate, byte[] name, byte[] value, bool sensitive) - { - if (name.Length == 0) { - throw new ArgumentException("name is empty"); - } - long newSize = headerSize + name.Length + value.Length; - if (newSize <= maxHeaderSize) { - addHeaderDelegate (name, value, sensitive); - headerSize = (int)newSize; - } else { - // truncation will be reported during endHeaderBlock - headerSize = maxHeaderSize + 1; - } - } - - private bool ExceedsMaxHeaderSize(long size) - { - // Check new header size against max header size - if (size + headerSize <= maxHeaderSize) { - return false; - } - - // truncation will be reported during endHeaderBlock - headerSize = maxHeaderSize + 1; - return true; - } - - private byte[] ReadStringLiteral(BinaryReader input, int length) - { - byte[] buf = new byte[length]; - int lengthToRead = length; - if (input.BaseStream.Length - input.BaseStream.Position < length) { - lengthToRead = (int)input.BaseStream.Length - (int)input.BaseStream.Position; - } - int readBytes = input.Read(buf, 0, lengthToRead); - if (readBytes != length) { - throw DECOMPRESSION_EXCEPTION; - } - - if (huffmanEncoded) { - return Huffman.DECODER.Decode(buf); - } else { - return buf; - } - } - - // Unsigned Little Endian Base 128 Variable-Length Integer Encoding - private static int DecodeULE128(BinaryReader input) - { - long markedPosition = input.BaseStream.Position; - int result = 0; - int shift = 0; - while(shift < 32) { - if (input.BaseStream.Length - input.BaseStream.Position == 0) { - // Buffer does not contain entire integer, - // reset reader index and return -1. - input.BaseStream.Position = markedPosition; - return -1; - } - sbyte b = input.ReadSByte(); - if (shift == 28 && (b & 0xF8) != 0) { - break; - } - result |= (b & 0x7F) << shift; - if ((b & 0x80) == 0) { - return result; - } - shift += 7; - } - // Value exceeds Integer.MAX_VALUE - input.BaseStream.Position = markedPosition; - throw DECOMPRESSION_EXCEPTION; - } - } -} \ No newline at end of file diff --git a/HttpTwo.HPack/DynamicTable.cs b/HttpTwo.HPack/DynamicTable.cs deleted file mode 100644 index a13a1e2..0000000 --- a/HttpTwo.HPack/DynamicTable.cs +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -using System; - -namespace HttpTwo.HPack -{ - public class DynamicTable - { - // a circular queue of header fields - HeaderField[] headerFields; - int head; - int tail; - private int size; - private int capacity = -1; - // ensure setCapacity creates the array - - public int Capacity { get { return this.capacity; } } - - public int Size { get { return this.size; } } - - /// - /// Creates a new dynamic table with the specified initial capacity. - /// - /// Initial capacity. - public DynamicTable(int initialCapacity) - { - this.SetCapacity(initialCapacity); - } - - /// - /// Return the number of header fields in the dynamic table. - /// - public int Length() - { - int length; - if (head < tail) { - length = headerFields.Length - tail + head; - } else { - length = head - tail; - } - return length; - } - - /// - /// Return the current size of the dynamic table. - /// This is the sum of the size of the entries. - /// - /// The size. - public int GetSize() - { - return this.size; - } - - /// - /// Return the maximum allowable size of the dynamic table. - /// - /// The capacity. - public int GetCapacity() - { - return capacity; - } - - /// - /// Return the header field at the given index. - /// The first and newest entry is always at index 1, - /// and the oldest entry is at the index length(). - /// - /// The entry. - /// Index. - public HeaderField GetEntry(int index) - { - if (index <= 0 || index > this.Length()) { - throw new IndexOutOfRangeException(); - } - int i = head - index; - if (i < 0) { - return headerFields[i + headerFields.Length]; - } else { - return headerFields[i]; - } - } - - /// - /// Add the header field to the dynamic table. - /// Entries are evicted from the dynamic table until the size of the table - /// and the new header field is less than or equal to the table's capacity. - /// If the size of the new entry is larger than the table's capacity, - /// the dynamic table will be cleared. - /// - /// Header. - public void Add(HeaderField header) - { - int headerSize = header.Size; - if (headerSize > capacity) { - this.Clear(); - return; - } - while(size + headerSize > capacity) { - this.Remove(); - } - headerFields[head++] = header; - size += header.Size; - if (head == headerFields.Length) { - head = 0; - } - } - - /// - /// Remove and return the oldest header field from the dynamic table. - /// - public HeaderField Remove() - { - HeaderField removed = headerFields[tail]; - if (removed == null) { - return null; - } - size -= removed.Size; - headerFields[tail++] = null; - if (tail == headerFields.Length) { - tail = 0; - } - return removed; - } - - /// - /// Remove all entries from the dynamic table. - /// - public void Clear() - { - while(tail != head) { - headerFields[tail++] = null; - if (tail == headerFields.Length) { - tail = 0; - } - } - head = 0; - tail = 0; - size = 0; - } - - /// - /// Set the maximum size of the dynamic table. - /// Entries are evicted from the dynamic table until the size of the table - /// is less than or equal to the maximum size. - /// - /// Capacity. - public void SetCapacity(int capacity) - { - if (capacity < 0) { - throw new ArgumentException("Illegal Capacity: " + capacity); - } - - // initially capacity will be -1 so init won't return here - if (this.capacity == capacity) { - return; - } - this.capacity = capacity; - - if (capacity == 0) { - this.Clear(); - } else { - // initially size will be 0 so remove won't be called - while(size > capacity) { - this.Remove(); - } - } - - int maxEntries = capacity / HeaderField.HEADER_ENTRY_OVERHEAD; - if (capacity % HeaderField.HEADER_ENTRY_OVERHEAD != 0) { - maxEntries++; - } - - // check if capacity change requires us to reallocate the array - if (headerFields != null && headerFields.Length == maxEntries) { - return; - } - - HeaderField[] tmp = new HeaderField[maxEntries]; - - // initially length will be 0 so there will be no copy - int len = this.Length(); - int cursor = tail; - for(int i = 0; i < len; i++) { - HeaderField entry = headerFields[cursor++]; - tmp[i] = entry; - if (cursor == headerFields.Length) { - cursor = 0; - } - } - - this.tail = 0; - this.head = tail + len; - this.headerFields = tmp; - } - } -} \ No newline at end of file diff --git a/HttpTwo.HPack/Encoder.cs b/HttpTwo.HPack/Encoder.cs deleted file mode 100644 index 480414a..0000000 --- a/HttpTwo.HPack/Encoder.cs +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -using System; -using System.IO; -using System.Text; - -namespace HttpTwo.HPack -{ - public class Encoder - { - private static int BUCKET_SIZE = 17; - private static byte[] EMPTY = { }; - - // for testing - private bool useIndexing; - private bool forceHuffmanOn; - private bool forceHuffmanOff; - - // a linked hash map of header fields - private HeaderEntry[] headerFields = new HeaderEntry[BUCKET_SIZE]; - private HeaderEntry head = new HeaderEntry(-1, EMPTY, EMPTY, int.MaxValue, null); - private int size; - private int capacity; - - /// - /// Initializes a new instance of the class. - /// - /// Max header table size. - public Encoder(int maxHeaderTableSize) - { - this.Init(maxHeaderTableSize, true, false, false); - } - - /// - /// Initializes a new instance of the class. - /// for testing only. - /// - /// Max header table size. - /// If set to true use indexing. - /// If set to true force huffman on. - /// If set to true force huffman off. - public Encoder(int maxHeaderTableSize, bool useIndexing, bool forceHuffmanOn, bool forceHuffmanOff) - { - this.Init(maxHeaderTableSize, useIndexing, forceHuffmanOn, forceHuffmanOff); - } - - private void Init(int maxHeaderTableSize, bool useIndexing, bool forceHuffmanOn, bool forceHuffmanOff) - { - if (maxHeaderTableSize < 0) { - throw new ArgumentException("Illegal Capacity: " + maxHeaderTableSize); - } - this.useIndexing = useIndexing; - this.forceHuffmanOn = forceHuffmanOn; - this.forceHuffmanOff = forceHuffmanOff; - this.capacity = maxHeaderTableSize; - head.Before = head.After = head; - } - - public void EncodeHeader(BinaryWriter output, string name, string value) - { - this.EncodeHeader(output, name, value, false); - } - - public void EncodeHeader(BinaryWriter output, string name, string value, bool sensitive) - { - this.EncodeHeader(output, Encoding.UTF8.GetBytes(name), Encoding.UTF8.GetBytes(value), sensitive); - } - - public void EncodeHeader(BinaryWriter output, byte[] name, byte[] value) - { - this.EncodeHeader(output, name, value, false); - } - - /// - /// Encode the header field into the header block. - /// - /// Output. - /// Name. - /// Value. - /// If set to true sensitive. - public void EncodeHeader(BinaryWriter output, byte[] name, byte[] value, bool sensitive) - { - // If the header value is sensitive then it must never be indexed - if (sensitive) { - int nameIndex = this.GetNameIndex(name); - this.EncodeLiteral(output, name, value, HPackUtil.IndexType.NEVER, nameIndex); - return; - } - - // If the peer will only use the static table - if (this.capacity == 0) { - int staticTableIndex = StaticTable.GetIndex(name, value); - if (staticTableIndex == -1) { - int nameIndex = StaticTable.GetIndex(name); - this.EncodeLiteral(output, name, value, HPackUtil.IndexType.NONE, nameIndex); - } else { - Encoder.EncodeInteger(output, 0x80, 7, staticTableIndex); - } - return; - } - - int headerSize = HeaderField.SizeOf(name, value); - - // If the headerSize is greater than the max table size then it must be encoded literally - if (headerSize > this.capacity) { - int nameIndex = this.GetNameIndex(name); - this.EncodeLiteral(output, name, value, HPackUtil.IndexType.NONE, nameIndex); - return; - } - - HeaderEntry headerField = this.GetEntry(name, value); - if (headerField != null) { - int index = this.GetIndex(headerField.Index) + StaticTable.Length; - // Section 6.1. Indexed Header Field Representation - Encoder.EncodeInteger(output, 0x80, 7, index); - } else { - int staticTableIndex = StaticTable.GetIndex(name, value); - if (staticTableIndex != -1) { - // Section 6.1. Indexed Header Field Representation - Encoder.EncodeInteger(output, 0x80, 7, staticTableIndex); - } else { - int nameIndex = this.GetNameIndex(name); - if (useIndexing) { - this.EnsureCapacity(headerSize); - } - var indexType = useIndexing ? HPackUtil.IndexType.INCREMENTAL : HPackUtil.IndexType.NONE; - this.EncodeLiteral(output, name, value, indexType, nameIndex); - if (useIndexing) { - this.Add(name, value); - } - } - } - } - - /// - /// Set the maximum table size. - /// - /// Output. - /// Max header table size. - public void SetMaxHeaderTableSize(BinaryWriter output, int maxHeaderTableSize) - { - if (maxHeaderTableSize < 0) { - throw new ArgumentException("Illegal Capacity: " + maxHeaderTableSize); - } - if (this.capacity == maxHeaderTableSize) { - return; - } - this.capacity = maxHeaderTableSize; - this.EnsureCapacity(0); - Encoder.EncodeInteger(output, 0x20, 5, maxHeaderTableSize); - } - - /// - /// Return the maximum table size. - /// - /// The max header table size. - public int GetMaxHeaderTableSize() - { - return this.capacity; - } - - /// - /// Encode integer according to Section 5.1. - /// - /// Output. - /// Mask. - /// N. - /// The index. - private static void EncodeInteger(BinaryWriter output, int mask, int n, int i) - { - if (n < 0 || n > 8) { - throw new ArgumentException("N: " + n); - } - int nbits = 0xFF >> (8 - n); - if (i < nbits) { - output.Write((byte)(mask | i)); - } else { - output.Write((byte)(mask | nbits)); - int length = i - nbits; - while(true) { - if ((length & ~0x7F) == 0) { - output.Write((byte)length); - return; - } else { - output.Write((byte)((length & 0x7F) | 0x80)); - length >>= 7; - } - } - } - } - - /// - /// Encode string literal according to Section 5.2. - /// - /// Output. - /// String literal. - private void EncodeStringLiteral(BinaryWriter output, byte[] stringLiteral) - { - int huffmanLength = Huffman.ENCODER.GetEncodedLength(stringLiteral); - if ((huffmanLength < stringLiteral.Length && !forceHuffmanOff) || forceHuffmanOn) { - Encoder.EncodeInteger(output, 0x80, 7, huffmanLength); - Huffman.ENCODER.Encode(output, stringLiteral); - } else { - Encoder.EncodeInteger(output, 0x00, 7, stringLiteral.Length); - output.Write(stringLiteral, 0, stringLiteral.Length); - } - } - - /// - /// Encode literal header field according to Section 6.2. - /// - /// Output. - /// Name. - /// Value. - /// Index type. - /// Name index. - private void EncodeLiteral(BinaryWriter output, byte[] name, byte[] value, HPackUtil.IndexType indexType, int nameIndex) - { - int mask; - int prefixBits; - switch(indexType) { - case HPackUtil.IndexType.INCREMENTAL: - mask = 0x40; - prefixBits = 6; - break; - - case HPackUtil.IndexType.NONE: - mask = 0x00; - prefixBits = 4; - break; - - case HPackUtil.IndexType.NEVER: - mask = 0x10; - prefixBits = 4; - break; - - default: - throw new Exception("should not reach here"); - } - Encoder.EncodeInteger(output, mask, prefixBits, nameIndex == -1 ? 0 : nameIndex); - if (nameIndex == -1) { - this.EncodeStringLiteral(output, name); - } - this.EncodeStringLiteral(output, value); - } - - private int GetNameIndex(byte[] name) - { - int index = StaticTable.GetIndex(name); - if (index == -1) { - index = this.GetIndex(name); - if (index >= 0) { - index += StaticTable.Length; - } - } - return index; - } - - /// - /// Ensure that the dynamic table has enough room to hold 'headerSize' more bytes. - /// Removes the oldest entry from the dynamic table until sufficient space is available. - /// - /// Header size. - private void EnsureCapacity(int headerSize) - { - while(this.size + headerSize > this.capacity) { - int index = this.Length(); - if (index == 0) { - break; - } - this.Remove(); - } - } - - /// - /// Return the number of header fields in the dynamic table. - /// Exposed for testing. - /// - int Length() - { - return this.size == 0 ? 0 : this.head.After.Index - this.head.Before.Index + 1; - } - - /// - /// Return the size of the dynamic table. - /// Exposed for testing. - /// - /// The size. - int GetSize() - { - return this.size; - } - - /// - /// Return the header field at the given index. - /// Exposed for testing. - /// - /// The header field. - /// Index. - HeaderField GetHeaderField(int index) - { - HeaderEntry entry = head; - while(index-- >= 0) { - entry = entry.Before; - } - return entry; - } - - /// - /// Returns the header entry with the lowest index value for the header field. - /// Returns null if header field is not in the dynamic table. - /// - /// The entry. - /// Name. - /// Value. - private HeaderEntry GetEntry(byte[] name, byte[] value) - { - if (this.Length() == 0 || name == null || value == null) { - return null; - } - int h = Encoder.Hash(name); - int i = Encoder.Index(h); - for(HeaderEntry e = headerFields[i]; e != null; e = e.Next) { - if (e.Hash == h && HPackUtil.Equals(name, e.Name) && HPackUtil.Equals(value, e.Value)) { - return e; - } - } - return null; - } - - /// - /// Returns the lowest index value for the header field name in the dynamic table. - /// Returns -1 if the header field name is not in the dynamic table. - /// - /// The index. - /// Name. - private int GetIndex(byte[] name) - { - if (this.Length() == 0 || name == null) { - return -1; - } - int h = Encoder.Hash(name); - int i = Encoder.Index(h); - int index = -1; - for(HeaderEntry e = headerFields[i]; e != null; e = e.Next) { - if (e.Hash == h && HPackUtil.Equals(name, e.Name)) { - index = e.Index; - break; - } - } - return this.GetIndex(index); - } - - /// - /// Compute the index into the dynamic table given the index in the header entry. - /// - /// The index. - /// Index. - private int GetIndex(int index) - { - if (index == -1) { - return index; - } - return index - head.Before.Index + 1; - } - - /// - /// Add the header field to the dynamic table. - /// Entries are evicted from the dynamic table until the size of the table - /// and the new header field is less than the table's capacity. - /// If the size of the new entry is larger than the table's capacity, - /// the dynamic table will be cleared. - /// - /// Name. - /// Value. - private void Add(byte[] name, byte[] value) - { - int headerSize = HeaderField.SizeOf(name, value); - - // Clear the table if the header field size is larger than the capacity. - if (headerSize > this.capacity) { - this.Clear(); - return; - } - - // Evict oldest entries until we have enough capacity. - while(this.size + headerSize > this.capacity) { - this.Remove(); - } - - // Copy name and value that modifications of original do not affect the dynamic table. - name.CopyTo(name, 0); - value.CopyTo(value, 0); - - int h = Encoder.Hash(name); - int i = Encoder.Index(h); - HeaderEntry old = headerFields[i]; - HeaderEntry e = new HeaderEntry(h, name, value, head.Before.Index - 1, old); - headerFields[i] = e; - e.AddBefore(head); - this.size += headerSize; - } - - /// - /// Remove and return the oldest header field from the dynamic table. - /// - private HeaderField Remove() - { - if (this.size == 0) { - return null; - } - HeaderEntry eldest = head.After; - int h = eldest.Hash; - int i = Encoder.Index(h); - HeaderEntry prev = headerFields[i]; - HeaderEntry e = prev; - while(e != null) { - HeaderEntry next = e.Next; - if (e == eldest) { - if (prev == eldest) { - headerFields[i] = next; - } else { - prev.Next = next; - } - eldest.Remove(); - this.size -= eldest.Size; - return eldest; - } - prev = e; - e = next; - } - return null; - } - - /// - /// Remove all entries from the dynamic table. - /// - private void Clear() - { - for(int i = 0; i < headerFields.Length; i++) { - headerFields[i] = null; - } - head.Before = head.After = head; - this.size = 0; - } - - /// - /// Returns the hash code for the given header field name. - /// - /// true if hash name; otherwise, false. - /// Name. - private static int Hash(byte[] name) - { - int h = 0; - for(int i = 0; i < name.Length; i++) { - h = 31 * h + name[i]; - } - if (h > 0) { - return h; - } else if (h == int.MinValue) { - return int.MaxValue; - } else { - return -h; - } - } - - /// - /// Returns the index into the hash table for the hash code h. - /// - /// The height. - private static int Index(int h) - { - return h % BUCKET_SIZE; - } - - /// - /// A linked hash map HeaderField entry. - /// - private class HeaderEntry : HeaderField - { - // These fields comprise the doubly linked list used for iteration. - private HeaderEntry before, after; - - // These fields comprise the chained list for header fields with the same hash. - private HeaderEntry next; - private int hash; - - // This is used to compute the index in the dynamic table. - private int index; - - public HeaderEntry Before { get { return this.before; } set { this.before = value; } } - - public HeaderEntry After { get { return this.after; } set { this.after = value; } } - - public HeaderEntry Next { get { return this.next; } set { this.next = value; } } - - public int Hash { get { return this.hash; } } - - public int Index { get { return this.index; } } - - /// - /// Creates new entry. - /// - /// Hash. - /// Name. - /// Value. - /// Index. - /// Next. - public HeaderEntry(int hash, byte[] name, byte[] value, int index, HeaderEntry next) : base(name, value) - { - this.index = index; - this.hash = hash; - this.next = next; - } - - /// - /// Removes this entry from the linked list. - /// - public void Remove() - { - before.after = after; - after.before = before; - } - - /// - /// Inserts this entry before the specified existing entry in the list. - /// - /// Existing entry. - public void AddBefore(HeaderEntry existingEntry) - { - after = existingEntry; - before = existingEntry.before; - before.after = this; - after.before = this; - } - } - } -} \ No newline at end of file diff --git a/HttpTwo.HPack/HPackUtil.cs b/HttpTwo.HPack/HPackUtil.cs deleted file mode 100644 index a6677fa..0000000 --- a/HttpTwo.HPack/HPackUtil.cs +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -using System; -using System.Text; - -namespace HttpTwo.HPack -{ - public class HPackUtil - { - /// - /// A string compare that doesn't leak timing information. - /// - /// S1. - /// S2. - public static bool Equals(byte[] s1, byte[] s2) - { - if (s1.Length != s2.Length) { - return false; - } - int c = 0; - for(int i = 0; i < s1.Length; i++) { - c |= (s1[i] ^ s2[i]); - } - return c == 0; - } - - /// - /// Checks that the specified object reference is not {@code null}. - /// - /// The non null. - /// Object. - public static object RequireNonNull(object obj) - { - if (obj == null) { - throw new NullReferenceException(); - } - return obj; - } - - // Section 6.2. Literal Header Field Representation - public enum IndexType - { - INCREMENTAL, - // Section 6.2.1. Literal Header Field with Incremental Indexing - NONE, - // Section 6.2.2. Literal Header Field without Indexing - NEVER - // Section 6.2.3. Literal Header Field never Indexed - } - - // Appendix B: Huffman Codes - // http://tools.ietf.org/html/rfc7541#appendix-B - public static int[] HUFFMAN_CODES = { - 0x1ff8, - 0x7fffd8, - 0xfffffe2, - 0xfffffe3, - 0xfffffe4, - 0xfffffe5, - 0xfffffe6, - 0xfffffe7, - 0xfffffe8, - 0xffffea, - 0x3ffffffc, - 0xfffffe9, - 0xfffffea, - 0x3ffffffd, - 0xfffffeb, - 0xfffffec, - 0xfffffed, - 0xfffffee, - 0xfffffef, - 0xffffff0, - 0xffffff1, - 0xffffff2, - 0x3ffffffe, - 0xffffff3, - 0xffffff4, - 0xffffff5, - 0xffffff6, - 0xffffff7, - 0xffffff8, - 0xffffff9, - 0xffffffa, - 0xffffffb, - 0x14, - 0x3f8, - 0x3f9, - 0xffa, - 0x1ff9, - 0x15, - 0xf8, - 0x7fa, - 0x3fa, - 0x3fb, - 0xf9, - 0x7fb, - 0xfa, - 0x16, - 0x17, - 0x18, - 0x0, - 0x1, - 0x2, - 0x19, - 0x1a, - 0x1b, - 0x1c, - 0x1d, - 0x1e, - 0x1f, - 0x5c, - 0xfb, - 0x7ffc, - 0x20, - 0xffb, - 0x3fc, - 0x1ffa, - 0x21, - 0x5d, - 0x5e, - 0x5f, - 0x60, - 0x61, - 0x62, - 0x63, - 0x64, - 0x65, - 0x66, - 0x67, - 0x68, - 0x69, - 0x6a, - 0x6b, - 0x6c, - 0x6d, - 0x6e, - 0x6f, - 0x70, - 0x71, - 0x72, - 0xfc, - 0x73, - 0xfd, - 0x1ffb, - 0x7fff0, - 0x1ffc, - 0x3ffc, - 0x22, - 0x7ffd, - 0x3, - 0x23, - 0x4, - 0x24, - 0x5, - 0x25, - 0x26, - 0x27, - 0x6, - 0x74, - 0x75, - 0x28, - 0x29, - 0x2a, - 0x7, - 0x2b, - 0x76, - 0x2c, - 0x8, - 0x9, - 0x2d, - 0x77, - 0x78, - 0x79, - 0x7a, - 0x7b, - 0x7ffe, - 0x7fc, - 0x3ffd, - 0x1ffd, - 0xffffffc, - 0xfffe6, - 0x3fffd2, - 0xfffe7, - 0xfffe8, - 0x3fffd3, - 0x3fffd4, - 0x3fffd5, - 0x7fffd9, - 0x3fffd6, - 0x7fffda, - 0x7fffdb, - 0x7fffdc, - 0x7fffdd, - 0x7fffde, - 0xffffeb, - 0x7fffdf, - 0xffffec, - 0xffffed, - 0x3fffd7, - 0x7fffe0, - 0xffffee, - 0x7fffe1, - 0x7fffe2, - 0x7fffe3, - 0x7fffe4, - 0x1fffdc, - 0x3fffd8, - 0x7fffe5, - 0x3fffd9, - 0x7fffe6, - 0x7fffe7, - 0xffffef, - 0x3fffda, - 0x1fffdd, - 0xfffe9, - 0x3fffdb, - 0x3fffdc, - 0x7fffe8, - 0x7fffe9, - 0x1fffde, - 0x7fffea, - 0x3fffdd, - 0x3fffde, - 0xfffff0, - 0x1fffdf, - 0x3fffdf, - 0x7fffeb, - 0x7fffec, - 0x1fffe0, - 0x1fffe1, - 0x3fffe0, - 0x1fffe2, - 0x7fffed, - 0x3fffe1, - 0x7fffee, - 0x7fffef, - 0xfffea, - 0x3fffe2, - 0x3fffe3, - 0x3fffe4, - 0x7ffff0, - 0x3fffe5, - 0x3fffe6, - 0x7ffff1, - 0x3ffffe0, - 0x3ffffe1, - 0xfffeb, - 0x7fff1, - 0x3fffe7, - 0x7ffff2, - 0x3fffe8, - 0x1ffffec, - 0x3ffffe2, - 0x3ffffe3, - 0x3ffffe4, - 0x7ffffde, - 0x7ffffdf, - 0x3ffffe5, - 0xfffff1, - 0x1ffffed, - 0x7fff2, - 0x1fffe3, - 0x3ffffe6, - 0x7ffffe0, - 0x7ffffe1, - 0x3ffffe7, - 0x7ffffe2, - 0xfffff2, - 0x1fffe4, - 0x1fffe5, - 0x3ffffe8, - 0x3ffffe9, - 0xffffffd, - 0x7ffffe3, - 0x7ffffe4, - 0x7ffffe5, - 0xfffec, - 0xfffff3, - 0xfffed, - 0x1fffe6, - 0x3fffe9, - 0x1fffe7, - 0x1fffe8, - 0x7ffff3, - 0x3fffea, - 0x3fffeb, - 0x1ffffee, - 0x1ffffef, - 0xfffff4, - 0xfffff5, - 0x3ffffea, - 0x7ffff4, - 0x3ffffeb, - 0x7ffffe6, - 0x3ffffec, - 0x3ffffed, - 0x7ffffe7, - 0x7ffffe8, - 0x7ffffe9, - 0x7ffffea, - 0x7ffffeb, - 0xffffffe, - 0x7ffffec, - 0x7ffffed, - 0x7ffffee, - 0x7ffffef, - 0x7fffff0, - 0x3ffffee, - 0x3fffffff // EOS - }; - - public static byte[] HUFFMAN_CODE_LENGTHS = { - 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, - 28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, - 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10, - 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, - 15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, - 6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28, - 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23, - 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24, - 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23, - 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23, - 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25, - 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27, - 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, - 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26, - 30 // EOS - }; - - public static int HUFFMAN_EOS = 256; - - private HPackUtil() - { - // utility class - } - } -} \ No newline at end of file diff --git a/HttpTwo.HPack/HeaderDelegate.cs b/HttpTwo.HPack/HeaderDelegate.cs deleted file mode 100644 index e0c7bc1..0000000 --- a/HttpTwo.HPack/HeaderDelegate.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -namespace HttpTwo.HPack -{ - /// - /// EmitHeader is called by the decoder during header field emission. - /// The name and value byte arrays must not be modified. - /// - /// Name. - /// Value. - /// If set to true sensitive. - public delegate void AddHeaderDelegate (byte[] name, byte[] value, bool sensitive); -} \ No newline at end of file diff --git a/HttpTwo.HPack/HeaderField.cs b/HttpTwo.HPack/HeaderField.cs deleted file mode 100644 index 395178d..0000000 --- a/HttpTwo.HPack/HeaderField.cs +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -using System; -using System.Text; - -namespace HttpTwo.HPack -{ - public class HeaderField : IComparable - { - private byte[] name; - private byte[] value; - - // Section 4.1. Calculating Table Size - // The additional 32 octets account for an estimated - // overhead associated with the structure. - public static int HEADER_ENTRY_OVERHEAD = 32; - - public byte[] Name { get { return this.name; } } - - public byte[] Value { get { return this.value; } } - - public int Size { get { return this.name.Length + this.value.Length + HEADER_ENTRY_OVERHEAD; } } - - // This constructor can only be used if name and value are ISO-8859-1 encoded. - public HeaderField(string name, string value) - { - this.name = Encoding.UTF8.GetBytes(name); - this.value = Encoding.UTF8.GetBytes(value); - } - - public HeaderField(byte[] name, byte[] value) - { - this.name = (byte[])HPackUtil.RequireNonNull(name); - this.value = (byte[])HPackUtil.RequireNonNull(value); - } - - public static int SizeOf(byte[] name, byte[] value) - { - return name.Length + value.Length + HEADER_ENTRY_OVERHEAD; - } - - public int CompareTo(HeaderField anotherHeaderField) - { - int ret = this.CompareTo(name, anotherHeaderField.name); - if (ret == 0) { - ret = this.CompareTo(value, anotherHeaderField.value); - } - return ret; - } - - private int CompareTo(byte[] s1, byte[] s2) - { - int len1 = s1.Length; - int len2 = s2.Length; - int lim = Math.Min(len1, len2); - - int k = 0; - while(k < lim) { - byte b1 = s1[k]; - byte b2 = s2[k]; - if (b1 != b2) { - return b1 - b2; - } - k++; - } - return len1 - len2; - } - - public override bool Equals(Object obj) - { - if (obj == this) { - return true; - } - if (!(obj is HeaderField)) { - return false; - } - HeaderField other = (HeaderField)obj; - bool nameEquals = HPackUtil.Equals(name, other.name); - bool valueEquals = HPackUtil.Equals(value, other.value); - return nameEquals && valueEquals; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - public override String ToString() - { - return String.Format("{0}: {1}", Encoding.UTF8.GetString(this.name), Encoding.UTF8.GetString(this.value)); - } - } -} \ No newline at end of file diff --git a/HttpTwo.HPack/HttpTwo.HPack.csproj b/HttpTwo.HPack/HttpTwo.HPack.csproj deleted file mode 100644 index 8b2f48a..0000000 --- a/HttpTwo.HPack/HttpTwo.HPack.csproj +++ /dev/null @@ -1,47 +0,0 @@ - - - - Debug - AnyCPU - {C75A83BB-7EF1-4CDB-8C52-9E6E3E11341D} - Library - HttpTwo.HPack - HttpTwo.HPack - v4.5 - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - full - true - bin\Release - prompt - 4 - false - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/HttpTwo.HPack/Huffman.cs b/HttpTwo.HPack/Huffman.cs deleted file mode 100644 index 38d10e6..0000000 --- a/HttpTwo.HPack/Huffman.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -namespace HttpTwo.HPack -{ - public class Huffman - { - /// - /// Huffman Decoder - /// - public static readonly HuffmanDecoder DECODER = new HuffmanDecoder(HPackUtil.HUFFMAN_CODES, HPackUtil.HUFFMAN_CODE_LENGTHS); - - /// - /// Huffman Encoder - /// - public static readonly HuffmanEncoder ENCODER = new HuffmanEncoder(HPackUtil.HUFFMAN_CODES, HPackUtil.HUFFMAN_CODE_LENGTHS); - - private Huffman() - { - // utility class - } - } -} diff --git a/HttpTwo.HPack/HuffmanDecoder.cs b/HttpTwo.HPack/HuffmanDecoder.cs deleted file mode 100644 index d2c397a..0000000 --- a/HttpTwo.HPack/HuffmanDecoder.cs +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -using System; -using System.IO; - -namespace HttpTwo.HPack -{ - public class HuffmanDecoder - { - private static IOException EOS_DECODED = new IOException("EOS Decoded"); - private static IOException INVALID_PADDING = new IOException("Invalid Padding"); - - private Node root; - - /// - /// Creates a new Huffman decoder with the specified Huffman coding. - /// - /// the Huffman codes indexed by symbol - /// the length of each Huffman code - public HuffmanDecoder(int[] codes, byte[] lengths) - { - if (codes.Length != 257 || codes.Length != lengths.Length) { - throw new ArgumentException("invalid Huffman coding"); - } - this.root = BuildTree(codes, lengths); - } - - /// - /// Decompresses the given Huffman coded string literal. - /// - /// the string literal to be decoded - /// the output stream for the compressed data - /// throws IOException if an I/O error occurs. In particular, an IOException may be thrown if the output stream has been closed. - public byte[] Decode(byte[] buf) - { - using(var baos = new MemoryStream()) { - Node node = root; - int current = 0; - int bits = 0; - for(int i = 0; i < buf.Length; i++) { - int b = buf[i] & 0xFF; - current = (current << 8) | b; - bits += 8; - while(bits >= 8) { - int c = (current >> (bits - 8)) & 0xFF; - node = node.Children[c]; - bits -= node.Bits; - if (node.IsTerminal()) { - if (node.Symbol == HPackUtil.HUFFMAN_EOS) { - throw EOS_DECODED; - } - baos.Write(new byte[] { (byte)node.Symbol }, 0, 1); - node = root; - } - } - } - - while(bits > 0) { - int c = (current << (8 - bits)) & 0xFF; - node = node.Children[c]; - if (node.IsTerminal() && node.Bits <= bits) { - bits -= node.Bits; - baos.Write(new byte[] { (byte)node.Symbol }, 0, 1); - node = root; - } else { - break; - } - } - - // Section 5.2. String Literal Representation - // Padding not corresponding to the most significant bits of the code - // for the EOS symbol (0xFF) MUST be treated as a decoding error. - int mask = (1 << bits) - 1; - if ((current & mask) != mask) { - throw INVALID_PADDING; - } - - return baos.ToArray(); - } - } - - public class Node - { - private int symbol; - // terminal nodes have a symbol - private int bits; - // number of bits matched by the node - private Node[] children; - // internal nodes have children - - public int Symbol { get { return this.symbol; } } - - public int Bits { get { return this.bits; } } - - public Node[] Children { get { return this.children; } } - - /// - /// Initializes a new instance of the class. - /// - public Node() - { - symbol = 0; - bits = 8; - children = new Node[256]; - } - - /// - /// Initializes a new instance of the class. - /// - /// the symbol the node represents - /// the number of bits matched by this node - public Node(int symbol, int bits) - { - //assert(bits > 0 && bits <= 8); - this.symbol = symbol; - this.bits = bits; - children = null; - } - - public bool IsTerminal() - { - return children == null; - } - } - - private static Node BuildTree(int[] codes, byte[] lengths) - { - Node root = new Node(); - for(int i = 0; i < codes.Length; i++) { - Insert(root, i, codes[i], lengths[i]); - } - return root; - } - - private static void Insert(Node root, int symbol, int code, byte length) - { - // traverse tree using the most significant bytes of code - Node current = root; - while(length > 8) { - if (current.IsTerminal()) { - throw new InvalidDataException("invalid Huffman code: prefix not unique"); - } - length -= 8; - int i = (code >> length) & 0xFF; - if (current.Children[i] == null) { - current.Children[i] = new Node(); - } - current = current.Children[i]; - } - - Node terminal = new Node(symbol, length); - int shift = 8 - length; - int start = (code << shift) & 0xFF; - int end = 1 << shift; - for(int i = start; i < start + end; i++) { - current.Children[i] = terminal; - } - } - } -} \ No newline at end of file diff --git a/HttpTwo.HPack/HuffmanEncoder.cs b/HttpTwo.HPack/HuffmanEncoder.cs deleted file mode 100644 index cc12ae6..0000000 --- a/HttpTwo.HPack/HuffmanEncoder.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -using System; -using System.IO; - -namespace HttpTwo.HPack -{ - public class HuffmanEncoder - { - private int[] codes; - private byte[] lengths; - - /// - /// Creates a new Huffman encoder with the specified Huffman coding. - /// - /// the Huffman codes indexed by symbol - /// the length of each Huffman code - public HuffmanEncoder(int[] codes, byte[] lengths) - { - this.codes = codes; - this.lengths = lengths; - } - - /// - /// Compresses the input string literal using the Huffman coding. - /// - /// the output stream for the compressed data - /// the string literal to be Huffman encoded - /// if an I/O error occurs. - /// - public void Encode(BinaryWriter output, byte[] data) - { - this.Encode(output, data, 0, data.Length); - } - - /// - /// Compresses the input string literal using the Huffman coding. - /// - /// the output stream for the compressed data - /// the string literal to be Huffman encoded - /// the start offset in the data - /// the number of bytes to encode - /// if an I/O error occurs. In particular, an IOException may be thrown if the output stream has been closed. - public void Encode(BinaryWriter output, byte[] data, int off, int len) - { - if (output == null) { - throw new NullReferenceException("out"); - } else if (data == null) { - throw new NullReferenceException("data"); - } else if (off < 0 || len < 0 || (off + len) < 0 || off > data.Length || (off + len) > data.Length) { - throw new IndexOutOfRangeException(); - } else if (len == 0) { - return; - } - - long current = 0; - int n = 0; - - for(int i = 0; i < len; i++) { - int b = data[off + i] & 0xFF; - uint code = (uint)this.codes[b]; - int nbits = lengths[b]; - - current <<= nbits; - current |= code; - n += nbits; - - while(n >= 8) { - n -= 8; - output.Write(((byte)(current >> n))); - } - } - - if (n > 0) { - current <<= (8 - n); - current |= (uint)(0xFF >> n); // this should be EOS symbol - output.Write((byte)current); - } - } - - /// - /// Returns the number of bytes required to Huffman encode the input string literal. - /// - /// the number of bytes required to Huffman encode data - /// the string literal to be Huffman encoded - public int GetEncodedLength(byte[] data) - { - if (data == null) { - throw new NullReferenceException("data"); - } - long len = 0; - foreach(byte b in data) { - len += lengths[b & 0xFF]; - } - return (int)((len + 7) >> 3); - } - } -} \ No newline at end of file diff --git a/HttpTwo.HPack/Properties/AssemblyInfo.cs b/HttpTwo.HPack/Properties/AssemblyInfo.cs deleted file mode 100644 index 818d11d..0000000 --- a/HttpTwo.HPack/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle ("HPack")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("redth")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion ("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] - diff --git a/HttpTwo.HPack/StaticTable.cs b/HttpTwo.HPack/StaticTable.cs deleted file mode 100644 index 0fb7235..0000000 --- a/HttpTwo.HPack/StaticTable.cs +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2015 Ringo Leese - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections; -using System.Text; - -namespace HttpTwo.HPack -{ - public static class StaticTable - { - /// - /// The static table - /// Appendix A: Static Table - /// - /// - private static List STATIC_TABLE = new List() { - /* 1 */new HeaderField(":authority", String.Empty), - /* 2 */new HeaderField(":method", "GET"), - /* 3 */new HeaderField(":method", "POST"), - /* 4 */new HeaderField(":path", "/"), - /* 5 */new HeaderField(":path", "/index.html"), - /* 6 */new HeaderField(":scheme", "http"), - /* 7 */new HeaderField(":scheme", "https"), - /* 8 */new HeaderField(":status", "200"), - /* 9 */new HeaderField(":status", "204"), - /* 10 */new HeaderField(":status", "206"), - /* 11 */new HeaderField(":status", "304"), - /* 12 */new HeaderField(":status", "400"), - /* 13 */new HeaderField(":status", "404"), - /* 14 */new HeaderField(":status", "500"), - /* 15 */new HeaderField("accept-charset", String.Empty), - /* 16 */new HeaderField("accept-encoding", "gzip, deflate"), - /* 17 */new HeaderField("accept-language", String.Empty), - /* 18 */new HeaderField("accept-ranges", String.Empty), - /* 19 */new HeaderField("accept", String.Empty), - /* 20 */new HeaderField("access-control-allow-origin", String.Empty), - /* 21 */new HeaderField("age", String.Empty), - /* 22 */new HeaderField("allow", String.Empty), - /* 23 */new HeaderField("authorization", String.Empty), - /* 24 */new HeaderField("cache-control", String.Empty), - /* 25 */new HeaderField("content-disposition", String.Empty), - /* 26 */new HeaderField("content-encoding", String.Empty), - /* 27 */new HeaderField("content-language", String.Empty), - /* 28 */new HeaderField("content-length", String.Empty), - /* 29 */new HeaderField("content-location", String.Empty), - /* 30 */new HeaderField("content-range", String.Empty), - /* 31 */new HeaderField("content-type", String.Empty), - /* 32 */new HeaderField("cookie", String.Empty), - /* 33 */new HeaderField("date", String.Empty), - /* 34 */new HeaderField("etag", String.Empty), - /* 35 */new HeaderField("expect", String.Empty), - /* 36 */new HeaderField("expires", String.Empty), - /* 37 */new HeaderField("from", String.Empty), - /* 38 */new HeaderField("host", String.Empty), - /* 39 */new HeaderField("if-match", String.Empty), - /* 40 */new HeaderField("if-modified-since", String.Empty), - /* 41 */new HeaderField("if-none-match", String.Empty), - /* 42 */new HeaderField("if-range", String.Empty), - /* 43 */new HeaderField("if-unmodified-since", String.Empty), - /* 44 */new HeaderField("last-modified", String.Empty), - /* 45 */new HeaderField("link", String.Empty), - /* 46 */new HeaderField("location", String.Empty), - /* 47 */new HeaderField("max-forwards", String.Empty), - /* 48 */new HeaderField("proxy-authenticate", String.Empty), - /* 49 */new HeaderField("proxy-authorization", String.Empty), - /* 50 */new HeaderField("range", String.Empty), - /* 51 */new HeaderField("referer", String.Empty), - /* 52 */new HeaderField("refresh", String.Empty), - /* 53 */new HeaderField("retry-after", String.Empty), - /* 54 */new HeaderField("server", String.Empty), - /* 55 */new HeaderField("set-cookie", String.Empty), - /* 56 */new HeaderField("strict-transport-security", String.Empty), - /* 57 */new HeaderField("transfer-encoding", String.Empty), - /* 58 */new HeaderField("user-agent", String.Empty), - /* 59 */new HeaderField("vary", String.Empty), - /* 60 */new HeaderField("via", String.Empty), - /* 61 */new HeaderField("www-authenticate", String.Empty) - }; - - private static Dictionary STATIC_INDEX_BY_NAME = CreateMap(); - - /// - /// The number of header fields in the static table. - /// - /// The length. - public static int Length { get { return STATIC_TABLE.Count; } } - - /// - /// Return the header field at the given index value. - /// - /// The entry. - /// Index. - public static HeaderField GetEntry(int index) - { - return STATIC_TABLE[index - 1]; - } - - /// - /// Returns the lowest index value for the given header field name in the static table. - /// Returns -1 if the header field name is not in the static table. - /// - /// The index. - /// Name. - public static int GetIndex(byte[] name) - { - string nameString = Encoding.UTF8.GetString(name); - if (!STATIC_INDEX_BY_NAME.ContainsKey(nameString)) { - return -1; - } - return STATIC_INDEX_BY_NAME[nameString]; - } - - /// - /// Returns the index value for the given header field in the static table. - /// Returns -1 if the header field is not in the static table. - /// - /// The index. - /// Name. - /// Value. - public static int GetIndex(byte[] name, byte[] value) - { - int index = GetIndex(name); - if (index == -1) { - return -1; - } - - // Note this assumes all entries for a given header field are sequential. - while(index <= StaticTable.Length) { - HeaderField entry = GetEntry(index); - if (!HPackUtil.Equals(name, entry.Name)) { - break; - } - if (HPackUtil.Equals(value, entry.Value)) { - return index; - } - index++; - } - - return -1; - } - - /// - /// create a map of header name to index value to allow quick lookup - /// - /// The map. - private static Dictionary CreateMap() - { - int length = STATIC_TABLE.Count; - var ret = new Dictionary(length); - - // Iterate through the static table in reverse order to - // save the smallest index for a given name in the map. - for(int index = length; index > 0; index--) { - HeaderField entry = GetEntry(index); - string name = Encoding.UTF8.GetString(entry.Name); - ret[name] = index; - } - return ret; - } - } -} \ No newline at end of file diff --git a/HttpTwo.Tests/HttpTests.cs b/HttpTwo.Tests/HttpTests.cs index a63ed57..4115d7c 100644 --- a/HttpTwo.Tests/HttpTests.cs +++ b/HttpTwo.Tests/HttpTests.cs @@ -3,6 +3,7 @@ using System.Net.Http; using System.Collections.Specialized; using System.Threading; +using System.Threading.Tasks; using HttpTwo.Internal; namespace HttpTwo.Tests @@ -14,7 +15,7 @@ public class HttpTests NodeHttp2Runner node; - [TestFixtureSetUp] + [OneTimeSetUp] public void Setup () { // Setup logger @@ -30,7 +31,7 @@ public void Setup () } } - [TestFixtureTearDown] + [OneTimeTearDown] public void Teardown () { if (UseInternalHttpRunner) @@ -38,31 +39,33 @@ public void Teardown () } [Test] - public async void Get_Single_Html_Page () + public async Task Get_Single_Html_Page () { var http2MsgHandler = new Http2MessageHandler (); var http = new HttpClient (http2MsgHandler); var data = await http.GetStringAsync ("http://localhost:8999/index.html"); - Assert.IsNotNullOrEmpty (data); + Assert.IsNotNull (data); + Assert.IsNotEmpty(data); Assert.IsTrue (data.Contains ("Hello World")); } //[Test] - public async void Get_Single_Html_Page_Https () + public async Task Get_Single_Html_Page_Https () { var http2MsgHandler = new Http2MessageHandler (); var http = new HttpClient (http2MsgHandler); var data = await http.GetStringAsync ("https://localhost:8999/index.html"); - Assert.IsNotNullOrEmpty (data); + Assert.IsNotNull (data); + Assert.IsNotEmpty(data); Assert.IsTrue (data.Contains ("Hello World")); } [Test] - public async void Get_Multiple_Html_Pages () + public async Task Get_Multiple_Html_Pages () { var http2MsgHandler = new Http2MessageHandler (); var http = new HttpClient (http2MsgHandler); @@ -70,14 +73,15 @@ public async void Get_Multiple_Html_Pages () for (int i = 0; i < 3; i++) { var data = await http.GetStringAsync ("http://localhost:8999/index.html"); - Assert.IsNotNullOrEmpty (data); + Assert.IsNotNull (data); + Assert.IsNotEmpty(data); Assert.IsTrue (data.Contains ("Hello World")); } } [Test] - public async void Settings_Disable_Push_Promise () + public async Task Settings_Disable_Push_Promise () { var url = new Uri ("http://localhost:8999/index.html"); var settings = new Http2ConnectionSettings (url) { DisablePushPromise = true }; @@ -109,7 +113,7 @@ public async void Settings_Disable_Push_Promise () [Test] - public async void Get_Send_Headers_With_Continuation () + public async Task Get_Send_Headers_With_Continuation () { var uri = new Uri ("http://localhost:8999/index.html"); var http = new Http2Client (uri); @@ -123,12 +127,13 @@ public async void Get_Send_Headers_With_Continuation () var data = System.Text.Encoding.ASCII.GetString (response.Body); - Assert.IsNotNullOrEmpty (data); + Assert.IsNotNull (data); + Assert.IsNotEmpty(data); Assert.IsTrue (data.Contains ("Hello World")); } [Test] - public async void Ping () + public async Task Ping () { var uri = new Uri ("http://localhost:8999/index.html"); var http = new Http2Client (uri); @@ -144,7 +149,7 @@ public async void Ping () } [Test] - public async void GoAway () + public async Task GoAway () { var uri = new Uri ("http://localhost:8999/index.html"); var http = new Http2Client (uri); diff --git a/HttpTwo.Tests/HttpTwo.Tests.csproj b/HttpTwo.Tests/HttpTwo.Tests.csproj index bda0989..14914cb 100644 --- a/HttpTwo.Tests/HttpTwo.Tests.csproj +++ b/HttpTwo.Tests/HttpTwo.Tests.csproj @@ -1,57 +1,46 @@ - - - - Debug - AnyCPU - {DD2208F9-C8C1-430A-AB17-5A657733A029} - Library - HttpTwo.Tests - HttpTwo.Tests - v4.5 - - - true - full - false - bin\Debug - DEBUG;TRACE; - prompt - 4 - false - - - true - bin\Release - prompt - 4 - false - TRACE; - - - - - ..\packages\NUnit.2.6.4\lib\nunit.framework.dll - - - - - - - - - - - - - - - - {DC2F25D9-75BE-4C67-B41D-84B738A16B9F} - HttpTwo - - - {C75A83BB-7EF1-4CDB-8C52-9E6E3E11341D} - HttpTwo.HPack - - - \ No newline at end of file + + + Debug + AnyCPU + {DD2208F9-C8C1-430A-AB17-5A657733A029} + Library + HttpTwo.Tests + HttpTwo.Tests + net6.0;net462 + false + + + true + full + false + bin\Debug + DEBUG;TRACE; + prompt + 4 + false + + + true + bin\Release + prompt + 4 + false + TRACE; + + + + + + + + + + + + + + {DC2F25D9-75BE-4C67-B41D-84B738A16B9F} + HttpTwo + + + diff --git a/HttpTwo.Tests/NodeHttp2Runner.cs b/HttpTwo.Tests/NodeHttp2Runner.cs index 4f39739..6172780 100644 --- a/HttpTwo.Tests/NodeHttp2Runner.cs +++ b/HttpTwo.Tests/NodeHttp2Runner.cs @@ -17,7 +17,7 @@ public void StartServer () return; // HTTP2_PLAIN=true HTTP2_LOG=trace HTTP2_LOG_DATA=1 node ./example/server.js - var scriptPath = Path.Combine (AppDomain.CurrentDomain.BaseDirectory, "..", "..", "node-http2", "example", "server.js"); + var scriptPath = Path.Combine (AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "node-http2", "example", "server.js"); process = new Process (); process.StartInfo = new ProcessStartInfo ("node", "\"" + scriptPath + "\""); diff --git a/HttpTwo.Tests/node-http2/package-lock.json b/HttpTwo.Tests/node-http2/package-lock.json new file mode 100644 index 0000000..cf591c3 --- /dev/null +++ b/HttpTwo.Tests/node-http2/package-lock.json @@ -0,0 +1,2780 @@ +{ + "name": "http2", + "version": "3.2.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "http2", + "version": "3.2.0", + "license": "MIT", + "dependencies": { + "bunyan": "^1.5.1" + }, + "devDependencies": { + "bunyan": "*", + "chai": "*", + "docco": "*", + "istanbul": "*", + "mocha": "*" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "dev": true, + "engines": [ + "node >=0.10.0" + ], + "bin": { + "bunyan": "bin/bunyan" + }, + "optionalDependencies": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/docco": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/docco/-/docco-0.9.1.tgz", + "integrity": "sha512-B1jUzcAc4AmicWUnmPTxUGM2lqJ11X4DiLUXyhzUVb7A1NNGTSNo3LtGzhHMyRAuJyxrHwHiOCDGuE/n9xRhmA==", + "dev": true, + "dependencies": { + "commander": "~ 8.3.0", + "fs-extra": "~ 10.0.0", + "highlight.js": "~ 11.3.1", + "marked": "~ 4.0.3", + "underscore": "~ 1.13.1" + }, + "bin": { + "docco": "bin/docco" + }, + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.14.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "dev": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.3.1.tgz", + "integrity": "sha512-PUhCRnPjLtiLHZAQ5A/Dt5F8cWZeMyj9KRsACsWT+OD6OP0x6dp5OmT5jdx0JgEyPxPZZIPQpRN2TciUT7occw==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha512-nMtdn4hvK0HjUlzr1DrKSUY8ychprt8dzHOgY2KXsIhHu5PuQQEOTM27gV9Xblyon7aUH/TSFIjRHEODF/FRPg==", + "deprecated": "This module is no longer maintained, try this instead:\n npm i nyc\nVisit https://istanbul.js.org/integrations for other alternatives.", + "dev": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/marked": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.19.tgz", + "integrity": "sha512-rgQF/OxOiLcvgUAj1Q1tAf4Bgxn5h5JZTp04Fx4XUkVhs7B+7YA9JEWJhJpoO8eJt8MkZMwqLCNeNqj1bCREZQ==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", + "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dev": true, + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "dev": true, + "optional": true, + "dependencies": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true, + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "dev": true, + "optional": true, + "bin": { + "ncp": "bin/ncp" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true + }, + "node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "dev": true, + "optional": true, + "dependencies": { + "glob": "^6.0.1" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "dev": true, + "optional": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "dev": true, + "optional": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "optional": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "dev": true, + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "docco": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/docco/-/docco-0.9.1.tgz", + "integrity": "sha512-B1jUzcAc4AmicWUnmPTxUGM2lqJ11X4DiLUXyhzUVb7A1NNGTSNo3LtGzhHMyRAuJyxrHwHiOCDGuE/n9xRhmA==", + "dev": true, + "requires": { + "commander": "~ 8.3.0", + "fs-extra": "~ 10.0.0", + "highlight.js": "~ 11.3.1", + "marked": "~ 4.0.3", + "underscore": "~ 1.13.1" + } + }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "fs-extra": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "highlight.js": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.3.1.tgz", + "integrity": "sha512-PUhCRnPjLtiLHZAQ5A/Dt5F8cWZeMyj9KRsACsWT+OD6OP0x6dp5OmT5jdx0JgEyPxPZZIPQpRN2TciUT7occw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha512-nMtdn4hvK0HjUlzr1DrKSUY8ychprt8dzHOgY2KXsIhHu5PuQQEOTM27gV9Xblyon7aUH/TSFIjRHEODF/FRPg==", + "dev": true, + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + } + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, + "marked": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.19.tgz", + "integrity": "sha512-rgQF/OxOiLcvgUAj1Q1tAf4Bgxn5h5JZTp04Fx4XUkVhs7B+7YA9JEWJhJpoO8eJt8MkZMwqLCNeNqj1bCREZQ==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "mocha": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", + "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dev": true, + "optional": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "dev": true, + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + } + }, + "nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true, + "optional": true + }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "dev": true, + "optional": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "dev": true, + "optional": true, + "requires": { + "glob": "^6.0.1" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "dev": true, + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "dev": true, + "optional": true + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true + }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/HttpTwo.Tests/packages.config b/HttpTwo.Tests/packages.config deleted file mode 100644 index c714ef3..0000000 --- a/HttpTwo.Tests/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/HttpTwo.nuspec b/HttpTwo.nuspec index 1afe1bd..8b81eaa 100644 --- a/HttpTwo.nuspec +++ b/HttpTwo.nuspec @@ -4,24 +4,23 @@ HttpTwo $version$ HttpTwo - Redth - Redth - http://www.apache.org/licenses/LICENSE-2.0.txt - https://github.com/Redth/HttpTwo - https://raw.githubusercontent.com/Redth/HttpTwo/master/HttpTwo-Icon.png + Redth,Neio,silentdth + Redth,Neio,silentdth + https://github.com/neio/HttpTwo + https://raw.githubusercontent.com/neio/HttpTwo/master/HttpTwo-Icon.png false HTTP/2 C# Client Side Implementation HTTP/2 C# Client Side Implementation - 2012-2016 Redth + 2012-2022 Redth,Neio,silentdth en-US HTTP, HTTP2, HTTP/2, HTTPTWO, Http Two, HTTP 2, HTTP.2, HTTP_2, HTTP-2 - - + + diff --git a/HttpTwo.sln b/HttpTwo.sln index 210aa90..193d339 100644 --- a/HttpTwo.sln +++ b/HttpTwo.sln @@ -1,8 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpTwo.HPack", "HttpTwo.HPack\HttpTwo.HPack.csproj", "{C75A83BB-7EF1-4CDB-8C52-9E6E3E11341D}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpTwo", "HttpTwo\HttpTwo.csproj", "{DC2F25D9-75BE-4C67-B41D-84B738A16B9F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpTwo.Tests", "HttpTwo.Tests\HttpTwo.Tests.csproj", "{DD2208F9-C8C1-430A-AB17-5A657733A029}" @@ -13,10 +11,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C75A83BB-7EF1-4CDB-8C52-9E6E3E11341D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C75A83BB-7EF1-4CDB-8C52-9E6E3E11341D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C75A83BB-7EF1-4CDB-8C52-9E6E3E11341D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C75A83BB-7EF1-4CDB-8C52-9E6E3E11341D}.Release|Any CPU.Build.0 = Release|Any CPU {DC2F25D9-75BE-4C67-B41D-84B738A16B9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC2F25D9-75BE-4C67-B41D-84B738A16B9F}.Debug|Any CPU.Build.0 = Debug|Any CPU {DC2F25D9-75BE-4C67-B41D-84B738A16B9F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -26,4 +20,7 @@ Global {DD2208F9-C8C1-430A-AB17-5A657733A029}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD2208F9-C8C1-430A-AB17-5A657733A029}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection EndGlobal diff --git a/HttpTwo/Frames/Frame.cs b/HttpTwo/Frames/Frame.cs index 468001f..409b17d 100644 --- a/HttpTwo/Frames/Frame.cs +++ b/HttpTwo/Frames/Frame.cs @@ -71,11 +71,11 @@ public uint Length { public abstract FrameType Type { get; } - public virtual byte Flags { get; protected set; } = 0x0; + public virtual byte Flags { get; protected set; } public virtual uint StreamIdentifier { get; set; } - public virtual IEnumerable Payload { get; } + public virtual IEnumerable Payload { get { return null; } } uint? payloadLength; public uint PayloadLength { diff --git a/HttpTwo/Frames/HeadersFrame.cs b/HttpTwo/Frames/HeadersFrame.cs index 6bcf15a..3c100b5 100644 --- a/HttpTwo/Frames/HeadersFrame.cs +++ b/HttpTwo/Frames/HeadersFrame.cs @@ -46,7 +46,7 @@ public ushort Weight { public bool EndHeaders { get;set; } public bool Priority { get;set; } public byte[] HeaderBlockFragment { get; set; } - public uint StreamDependency { get; set; } = 0; + public uint StreamDependency { get; set; } public override FrameType Type { get { return FrameType.Headers; } diff --git a/HttpTwo/Frames/PriorityFrame.cs b/HttpTwo/Frames/PriorityFrame.cs index f89eb5d..a8d7bb4 100644 --- a/HttpTwo/Frames/PriorityFrame.cs +++ b/HttpTwo/Frames/PriorityFrame.cs @@ -27,7 +27,7 @@ public ushort Weight { } } - public uint StreamDependency { get; set; } = 0; + public uint StreamDependency { get; set; } // type=0x1 public override FrameType Type { diff --git a/HttpTwo/Frames/PushPromiseFrame.cs b/HttpTwo/Frames/PushPromiseFrame.cs index 5e0e90a..2a19b43 100644 --- a/HttpTwo/Frames/PushPromiseFrame.cs +++ b/HttpTwo/Frames/PushPromiseFrame.cs @@ -30,7 +30,7 @@ public ushort PadLength { public byte[] HeaderBlockFragment { get;set; } - public uint StreamDependency { get; set; } = 0; + public uint StreamDependency { get; set; } public override FrameType Type { get { return FrameType.PushPromise; } diff --git a/HttpTwo/Http2Client.cs b/HttpTwo/Http2Client.cs index e109f6a..3fa9e22 100644 --- a/HttpTwo/Http2Client.cs +++ b/HttpTwo/Http2Client.cs @@ -1,307 +1,333 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; -using HttpTwo.Internal; - -namespace HttpTwo -{ - public class Http2Client - { - readonly Http2Connection connection; - readonly IStreamManager streamManager; - IFlowControlManager flowControlManager; - - public Http2Client (Uri uri, X509CertificateCollection certificates = null, IStreamManager streamManager = null, IFlowControlManager flowControlManager = null) - : this (new Http2ConnectionSettings (uri, certificates), streamManager, flowControlManager) - { - } - - public Http2Client (string url, X509CertificateCollection certificates = null, IStreamManager streamManager = null, IFlowControlManager flowControlManager = null) - : this (new Http2ConnectionSettings (url, certificates), streamManager, flowControlManager) - { - } - - public Http2Client (Http2ConnectionSettings connectionSettings, IStreamManager streamManager = null, IFlowControlManager flowControlManager = null) - { - this.flowControlManager = flowControlManager ?? new FlowControlManager (); - this.streamManager = streamManager ?? new StreamManager (this.flowControlManager); - this.ConnectionSettings = connectionSettings; - - connection = new Http2Connection (ConnectionSettings, this.streamManager, this.flowControlManager); - } - - public Http2ConnectionSettings ConnectionSettings { get; private set; } - - public IStreamManager StreamManager { get { return streamManager; } } - public IFlowControlManager FlowControlManager { get { return flowControlManager; } } - - public async Task Connect () - { - await connection.Connect ().ConfigureAwait (false); - } - - public async Task Post (Uri uri, NameValueCollection headers = null, byte[] data = null) - { - return await Send (uri, HttpMethod.Post, headers, data).ConfigureAwait (false); - } - - public async Task Post (Uri uri, NameValueCollection headers = null, Stream data = null) - { - return await Send (uri, HttpMethod.Post, headers, data).ConfigureAwait (false); - } - - public async Task Send (Uri uri, HttpMethod method, NameValueCollection headers = null, byte[] data = null) - { - MemoryStream ms = null; - - if (data != null) - ms = new MemoryStream (data); - - return await Send (new CancellationToken (), uri, method, headers, ms).ConfigureAwait (false); - } - - public async Task Send (Uri uri, HttpMethod method, NameValueCollection headers = null, Stream data = null) - { - return await Send (new CancellationToken (), uri, method, headers, data).ConfigureAwait (false); - } - - public async Task Send (CancellationToken cancelToken, Uri uri, HttpMethod method, NameValueCollection headers = null, Stream data = null) - { - var semaphoreClose = new SemaphoreSlim(0); - - await connection.Connect ().ConfigureAwait (false); - - var stream = await streamManager.Get ().ConfigureAwait (false); - stream.OnFrameReceived += async (frame) => - { - // Check for an end of stream state - if (stream.State == StreamState.HalfClosedRemote || stream.State == StreamState.Closed) - semaphoreClose.Release (); - }; - - var sentEndOfStream = false; - - var allHeaders = new NameValueCollection (); - allHeaders.Add (":method", method.Method.ToUpperInvariant ()); - allHeaders.Add (":path", uri.PathAndQuery); - allHeaders.Add (":scheme", uri.Scheme); - allHeaders.Add (":authority", uri.Authority); - if (headers != null && headers.Count > 0) - allHeaders.Add (headers); - - var headerData = Util.PackHeaders (allHeaders, connection.Settings.HeaderTableSize); - - var numFrames = (int)Math.Ceiling ((double)headerData.Length / (double)connection.Settings.MaxFrameSize); - - for (int i = 0; i < numFrames; i++) { - // First item is headers frame, others are continuation - IFrameContainsHeaders frame = (i == 0) ? - (IFrameContainsHeaders)new HeadersFrame (stream.StreamIdentifer) - : (IFrameContainsHeaders)new ContinuationFrame (stream.StreamIdentifer); - - // Set end flag if this is the last item - if (i == numFrames - 1) - frame.EndHeaders = true; - - var maxFrameSize = connection.Settings.MaxFrameSize; - - var amt = maxFrameSize; - if ( i * maxFrameSize + amt > headerData.Length) - amt = (uint)headerData.Length - (uint)(i * maxFrameSize); - frame.HeaderBlockFragment = new byte[amt]; - Array.Copy (headerData, i * maxFrameSize, frame.HeaderBlockFragment, 0, amt); - - // If we won't s end - if (data == null && frame is HeadersFrame) { - sentEndOfStream = true; - (frame as HeadersFrame).EndStream = true; - } - - await connection.QueueFrame (frame).ConfigureAwait (false); - } - - if (data != null) { - var supportsPosLength = true; // Keep track of if we threw exceptions trying pos/len of stream - - // Break stream up into data frames within allowed size - var dataFrameBuffer = new byte[connection.Settings.MaxFrameSize]; - while (true) { - - var rd = await data.ReadAsync (dataFrameBuffer, 0, dataFrameBuffer.Length).ConfigureAwait (false); - - if (rd <= 0) - break; - - // Make a new data frame with a buffer the size we read - var dataFrame = new DataFrame (stream.StreamIdentifer); - dataFrame.Data = new byte[rd]; - // Copy over the data we read - Array.Copy(dataFrameBuffer, 0, dataFrame.Data, 0, rd); - - try { - // See if the stream supports Length / Position to try and detect EOS - // we also want to see if we previously had an exception trying this - // and not try again if we did, since throwing exceptions every single - // read operation is wasteful - if (supportsPosLength && data.Position >= data.Length) { - dataFrame.EndStream = true; - sentEndOfStream = true; - } - } catch { - supportsPosLength = false; - sentEndOfStream = false; - } - - await connection.QueueFrame (dataFrame).ConfigureAwait (false); - } - } - - // Send an empty frame with end of stream flag - if (!sentEndOfStream) - await connection.QueueFrame(new DataFrame(stream.StreamIdentifer) { EndStream = true }).ConfigureAwait(false); - - if (!await semaphoreClose.WaitAsync (ConnectionSettings.ConnectionTimeout, cancelToken).ConfigureAwait (false)) - throw new TimeoutException (); - - var responseData = new List (); - var rxHeaderData = new List (); - - foreach (var f in stream.ReceivedFrames) { - if (f.Type == FrameType.Headers || f.Type == FrameType.Continuation) { - // Get the header data and add it to our buffer - var fch = (IFrameContainsHeaders)f; - if (fch.HeaderBlockFragment != null && fch.HeaderBlockFragment.Length > 0) - rxHeaderData.AddRange (fch.HeaderBlockFragment); - } else if (f.Type == FrameType.PushPromise) { - // TODO: In the future we need to implement PushPromise beyond grabbing header data - var fch = (IFrameContainsHeaders)f; - if (fch.HeaderBlockFragment != null && fch.HeaderBlockFragment.Length > 0) - rxHeaderData.AddRange (fch.HeaderBlockFragment); - } else if (f.Type == FrameType.Data) { - responseData.AddRange ((f as DataFrame).Data); - } else if (f.Type == FrameType.GoAway) { - var fga = f as GoAwayFrame; - if (fga != null && fga.AdditionalDebugData != null && fga.AdditionalDebugData.Length > 0) - responseData.AddRange (fga.AdditionalDebugData); - } - } - - var responseHeaders = Util.UnpackHeaders (rxHeaderData.ToArray (), - connection.Settings.MaxHeaderListSize.HasValue ? (int)connection.Settings.MaxHeaderListSize.Value : 8192, - (int)connection.Settings.HeaderTableSize); - - var strStatus = "500"; - if (responseHeaders [":status"] != null) - strStatus = responseHeaders [":status"]; - - var statusCode = HttpStatusCode.OK; - Enum.TryParse (strStatus, out statusCode); - - // Remove the stream from being tracked since we're done with it - await streamManager.Cleanup (stream.StreamIdentifer).ConfigureAwait (false); - - // Send a WINDOW_UPDATE frame to release our stream's data count - // TODO: Eventually need to do this on the stream itself too (if it's open) - await connection.FreeUpWindowSpace ().ConfigureAwait (false); - - return new Http2Response { - Status = statusCode, - Stream = stream, - Headers = responseHeaders, - Body = responseData.ToArray () - }; - } - - public async Task Ping (byte[] opaqueData, CancellationToken cancelToken) - { - if (opaqueData == null || opaqueData.Length <= 0) - throw new ArgumentNullException ("opaqueData"); - - await connection.Connect ().ConfigureAwait (false); - - var semaphoreWait = new SemaphoreSlim (0); - var opaqueDataMatch = false; - - var connectionStream = await streamManager.Get (0).ConfigureAwait (false); - - Http2Stream.FrameReceivedDelegate frameRxAction; - frameRxAction = new Http2Stream.FrameReceivedDelegate (frame => { - var pf = frame as PingFrame; - if (pf != null) { - opaqueDataMatch = pf.Ack && pf.OpaqueData != null && pf.OpaqueData.SequenceEqual (opaqueData); - semaphoreWait.Release (); - } - }); - - // Wire up the event to listen for ping response - connectionStream.OnFrameReceived += frameRxAction; - - // Construct ping request - var pingFrame = new PingFrame (); - pingFrame.OpaqueData = new byte[opaqueData.Length]; - opaqueData.CopyTo (pingFrame.OpaqueData, 0); - - // Send ping - await connection.QueueFrame (pingFrame).ConfigureAwait (false); - - // Wait for either a ping response or timeout - await semaphoreWait.WaitAsync (cancelToken).ConfigureAwait (false); - - // Cleanup the event - connectionStream.OnFrameReceived -= frameRxAction; - - return opaqueDataMatch; - } - - public async Task Disconnect () - { - return await Disconnect (Timeout.InfiniteTimeSpan).ConfigureAwait (false); - } - - public async Task Disconnect (TimeSpan timeout) - { - var connectionStream = await streamManager.Get (0).ConfigureAwait (false); - - var semaphoreWait = new SemaphoreSlim (0); - var cancelTokenSource = new CancellationTokenSource (); - var sentGoAway = false; - - var sentDelegate = new Http2Stream.FrameSentDelegate (frame => { - if (frame.Type == FrameType.GoAway) { - sentGoAway = true; - semaphoreWait.Release (); - } - }); - - connectionStream.OnFrameSent += sentDelegate; - - await connection.QueueFrame (new GoAwayFrame ()).ConfigureAwait (false); - - if (timeout != Timeout.InfiniteTimeSpan) - cancelTokenSource.CancelAfter (timeout); - - await semaphoreWait.WaitAsync (cancelTokenSource.Token).ConfigureAwait (false); - - connectionStream.OnFrameSent -= sentDelegate; - - connection.Disconnect (); - - return sentGoAway; - } - - public class Http2Response - { - public HttpStatusCode Status { get; set; } - public Http2Stream Stream { get; set; } - public NameValueCollection Headers { get;set; } - public byte[] Body { get;set; } - } - } -} +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using HttpTwo.Internal; + +namespace HttpTwo +{ + public class Http2Client + { + Http2Connection connection; + readonly IStreamManager streamManager; + IFlowControlManager flowControlManager; + + public Http2Client (Uri uri, X509CertificateCollection certificates = null, IStreamManager streamManager = null, IFlowControlManager flowControlManager = null) + : this (new Http2ConnectionSettings (uri, certificates), streamManager, flowControlManager) + { + } + + public Http2Client (string url, X509CertificateCollection certificates = null, IStreamManager streamManager = null, IFlowControlManager flowControlManager = null) + : this (new Http2ConnectionSettings (url, certificates), streamManager, flowControlManager) + { + } + + public Http2Client (Http2ConnectionSettings connectionSettings, IStreamManager streamManager = null, IFlowControlManager flowControlManager = null) + { + this.flowControlManager = flowControlManager ?? new FlowControlManager (); + this.streamManager = streamManager ?? new StreamManager (this.flowControlManager); + this.ConnectionSettings = connectionSettings; + } + + public Http2ConnectionSettings ConnectionSettings { get; private set; } + + public IStreamManager StreamManager { get { return streamManager; } } + public IFlowControlManager FlowControlManager { get { return flowControlManager; } } + + public async Task Connect() + { + if (connection != null && !connection.IsConnected()) + { + connection.Disconnect(); + connection = null; + } + + if (connection == null) + { + streamManager.Reset(); + connection = new Http2Connection(ConnectionSettings, this.streamManager, this.flowControlManager); + await connection.Connect().ConfigureAwait(false); + } + + } + + public async Task Post (Uri uri, NameValueCollection headers = null, byte[] data = null) + { + return await Send (uri, HttpMethod.Post, headers, data).ConfigureAwait (false); + } + + public async Task Post (Uri uri, NameValueCollection headers = null, Stream data = null) + { + return await Send (uri, HttpMethod.Post, headers, data).ConfigureAwait (false); + } + + public async Task Send (Uri uri, HttpMethod method, NameValueCollection headers = null, byte[] data = null) + { + MemoryStream ms = null; + + if (data != null) + ms = new MemoryStream (data); + + return await Send (new CancellationToken (), uri, method, headers, ms).ConfigureAwait (false); + } + + public async Task Send (Uri uri, HttpMethod method, NameValueCollection headers = null, Stream data = null) + { + return await Send (new CancellationToken (), uri, method, headers, data).ConfigureAwait (false); + } + + public async Task Send (CancellationToken cancelToken, Uri uri, HttpMethod method, NameValueCollection headers = null, Stream data = null) + { + var semaphoreClose = new SemaphoreSlim(0); + + await Connect(); + + List