public void ParseHeaderWithParameters2() { HeaderField field = HeaderField.Parse("Content-Disposition: attachment; filename=test.pdf"); Assert.AreEqual("Content-Disposition", field.FieldName); Assert.AreEqual("attachment", field.FieldValue); Assert.AreEqual(1, field.Parameters.Count); Assert.AreEqual("test.pdf", field.Parameters["filename"]); }
public void ParseHeaderWithoutParameters() { HeaderField field = HeaderField.Parse("Content-type : multipart/mixed"); Assert.AreEqual("Content-type", field.FieldName); Assert.AreEqual(MultipartMessage.MediaTypeMultipartMixed, field.FieldValue); Assert.AreEqual(0, field.Parameters.Count); Assert.AreEqual("Content-type: multipart/mixed", field.ToString()); }
public async Task ClientsShouldBeAbleToReceiveInformationalHeaders() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); // Send and receive first set of informational headers var readInfoHeaders1Task = res.stream.ReadHeadersAsync(); Assert.False(readInfoHeaders1Task.IsCompleted); var infoHeaders1 = new HeaderField[] { new HeaderField { Name = ":status", Value = "100" }, new HeaderField { Name = "extension-field", Value = "bar" }, }; await inPipe.WriteHeaders(res.hEncoder, 1u, false, infoHeaders1); var recvdInfoHeaders1 = await readInfoHeaders1Task; Assert.True(infoHeaders1.SequenceEqual(recvdInfoHeaders1)); // Send and receive second set of informational headers var readInfoHeaders2Task = res.stream.ReadHeadersAsync(); Assert.False(readInfoHeaders2Task.IsCompleted); var infoHeaders2 = new HeaderField[] { new HeaderField { Name = ":status", Value = "108" }, new HeaderField { Name = "extension-field-b", Value = "bar2" }, }; await inPipe.WriteHeaders(res.hEncoder, 1u, false, infoHeaders2); var recvdInfoHeaders2 = await readInfoHeaders2Task; Assert.True(infoHeaders2.SequenceEqual(recvdInfoHeaders2)); // Send and receive final headers var recvHeadersTask = res.stream.ReadHeadersAsync(); Assert.False(recvHeadersTask.IsCompleted); await inPipe.WriteHeaders(res.hEncoder, 1u, true, DefaultStatusHeaders); var recvdHeaders = await recvHeadersTask; Assert.True(DefaultStatusHeaders.SequenceEqual(recvdHeaders)); }
public async Task IfSettingsDecreaseHeaderTableNextOutgoingHeadersShouldContainAnUpdate() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => { Task.Run(() => { var res = new HeaderField[] { new HeaderField { Name = ":status", Value = "200" }, }; s.WriteHeadersAsync(res, false); }); return(true); }; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); // Change remote settings 2 times var settings = Settings.Default; settings.HeaderTableSize = 8; await inPipe.WriteSettings(settings); await outPipe.AssertSettingsAck(); settings.HeaderTableSize = 30; await inPipe.WriteSettings(settings); await outPipe.AssertSettingsAck(); // Establish a stream // When we send a response through it we should observe the size udpate var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, DefaultGetHeaders); // Wait for the incoming status headers with header update var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal((byte)(HeadersFrameFlags.EndOfHeaders), fh.Flags); Assert.Equal(1u, fh.StreamId); Assert.Equal(3, fh.Length); var data = new byte[fh.Length]; await outPipe.ReadAllWithTimeout(new ArraySegment <byte>(data)); Assert.Equal(0x28, data[0]); // Size Update to 8 Assert.Equal(0x3e, data[1]); // Size Update to 30 Assert.Equal(0x88, data[2]); // :status 200 }
internal static HeaderField <sbyte[]>[] HeaderFields(string[] keys) { //JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes: //ORIGINAL LINE: @SuppressWarnings("unchecked") HeaderField<byte[]>[] fields = new HeaderField[keys.length]; HeaderField <sbyte[]>[] fields = new HeaderField[keys.Length]; for (int i = 0; i < keys.Length; i++) { fields[i] = HeaderField(keys[i]); } return(fields); }
public void SetHeader(string key, string value) { var headerField = new HeaderField() { Name = key, Value = value, Sensitive = false }; SetHeader(headerField); }
public static @string String(this HeaderField hf) { @string suffix = default; if (hf.Sensitive) { suffix = " (sensitive)"; } return(fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix)); }
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes: //ORIGINAL LINE: @SuppressWarnings("unchecked") @Override <Value> Value initialHeader(HeaderField<Value> field) internal override Value initialHeader <Value>(HeaderField <Value> field) { if (field == TX_ID) { return(( Value )( object )1L); } else { return(base.initialHeader(field)); } }
public void ParseHeaderWithParameters() { HeaderField field = HeaderField.Parse("Content-type: multipart/mixed; boundary=\"23232\"; test=2"); Assert.AreEqual("Content-type", field.FieldName); Assert.AreEqual(MultipartMessage.MediaTypeMultipartMixed, field.FieldValue); Assert.AreEqual(2, field.Parameters.Count); Assert.AreEqual("\"23232\"", field.Parameters["boundary"]); Assert.AreEqual("2", field.Parameters["test"]); Assert.AreEqual("Content-type: multipart/mixed; boundary=\"23232\"; test=2", field.ToString()); }
private void EnsureAvailable(int available) { while (_count > 0 && _maxSize - _size < available) { ref HeaderField field = ref _buffer[_removeIndex]; _size -= field.Length; field = default; _count--; _removeIndex = (_removeIndex + 1) % _buffer.Length; }
public object Parse(string value, object argument) { var headers = new List <HeaderField>(); using (var sr = new StringReader(value)) { using (var sw = new StringWriter()) { while (true) { var line = sr.ReadLine(); // end of block reached, no more headers if (string.IsNullOrEmpty(line)) { break; } sw.Write(line); while (true) { var next = sr.Peek(); var isContinuation = next == Characters.Space || next == Characters.Tabulator; if (isContinuation) { var continuation = sr.ReadLine(); if (string.IsNullOrEmpty(continuation)) { break; } sw.Write(continuation.Trim()); } else { break; } } var literals = sw.ToString(); if (!string.IsNullOrEmpty(literals)) { var header = new HeaderField(); sw.Clear(); header.Deserialize(literals); headers.Add(header); } } } } return(headers); }
static async void HandleIncomingStream(IStream stream) { try { // Read the headers var headers = await stream.ReadHeadersAsync(); var method = headers.First(h => h.Name == ":method").Value; var path = headers.First(h => h.Name == ":path").Value; // Print the request method and path Console.WriteLine("Method: {0}, Path: {1}", method, path); // Read the request body and write it to console var buf = new byte[2048]; while (true) { var readResult = await stream.ReadAsync(new ArraySegment <byte>(buf)); if (readResult.EndOfStream) { break; } // Print the received bytes Console.WriteLine(Encoding.ASCII.GetString(buf, 0, readResult.BytesRead)); } // Send a response which consists of headers and a payload var responseHeaders = new HeaderField[] { new HeaderField { Name = ":status", Value = "200" }, new HeaderField { Name = "content-type", Value = "text/html" }, }; await stream.WriteHeadersAsync(responseHeaders, false); await stream.WriteAsync(new ArraySegment <byte>( responseBody), true); // Request is fully handled here } catch (Exception e) { Console.WriteLine("Error during handling request: {0}", e.Message); stream.Cancel(); } }
public void IfSizeIsChangedMultipleTimesAllNecessaryUpdatesShouldBeEncoded( int[] sizeChanges, string expectedTableUpdateHexBytes) { var encoder = new Encoder(new Encoder.Options { HuffmanStrategy = HuffmanStrategy.Never, }); foreach (var newSize in sizeChanges) { encoder.DynamicTableSize = newSize; } var fields = new HeaderField[] { new HeaderField { Name = "ab", Value = "cd", Sensitive = true } }; var result = new Buffer(); result.AddHexString(expectedTableUpdateHexBytes); result.AddHexString("1002"); result.WriteString("ab"); result.AddHexString("02"); result.WriteString("cd"); var res = EncodeToTempBuf(encoder, fields, MaxFrameSize); Assert.Equal(result.Bytes, res.Bytes); Assert.Equal(1, res.FieldCount); Assert.Equal(0, encoder.DynamicTableUsedSize); Assert.Equal(0, encoder.DynamicTableLength); Assert.Equal(sizeChanges[sizeChanges.Length - 1], encoder.DynamicTableSize); // Encode a further header block // This may not contain any tableupdate data result = new Buffer(); result.AddHexString("1002"); result.WriteString("ab"); result.AddHexString("02"); result.WriteString("cd"); res = EncodeToTempBuf(encoder, fields, MaxFrameSize); Assert.Equal(result.Bytes, res.Bytes); Assert.Equal(1, res.FieldCount); Assert.Equal(0, encoder.DynamicTableUsedSize); Assert.Equal(0, encoder.DynamicTableLength); Assert.Equal(sizeChanges[sizeChanges.Length - 1], encoder.DynamicTableSize); }
public void WrapsAroundBuffer() { var header3 = new HeaderField(Encoding.ASCII.GetBytes("header-3"), Encoding.ASCII.GetBytes("value3")); var header4 = new HeaderField(Encoding.ASCII.GetBytes("header-4"), Encoding.ASCII.GetBytes("value4")); // Make the table small enough that the circular buffer kicks in. var dynamicTable = new DynamicTable(HeaderField.RfcOverhead * 3); dynamicTable.Insert(header4.Name, header4.Value); dynamicTable.Insert(header3.Name, header3.Value); dynamicTable.Insert(_header2.Name, _header2.Value); dynamicTable.Insert(_header1.Name, _header1.Value); VerifyTableEntries(dynamicTable, _header1, _header2); }
private void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e) { if (HeaderField != null && HeaderField.Contains(e.PropertyName)) { BeginInvokeInUiThread(SyncHeaderAutomatic); } if (StatusField != null && StatusField.Contains(e.PropertyName)) { BeginInvokeInUiThread(SyncStatusImageAutomatic); } if (ColorField != null && ColorField.Contains(e.PropertyName)) { BeginInvokeInUiThread(SyncColorAutomatic); } }
public void SetHeader(HeaderField headerField) { if (string.IsNullOrEmpty(headerField.Name)) { return; } var hf = Heads.Where(b => b.Name == headerField.Name).FirstOrDefault(); if (!string.IsNullOrEmpty(hf.Name)) { Heads.Remove(hf); } Heads.Add(headerField); }
private static bool Encode(Http3HeadersEnumerator headersEnumerator, Span <byte> buffer, bool throwIfNoneEncoded, ref int totalHeaderSize, out int length) { length = 0; do { // Match the current header to the QPACK static table. Possible outcomes: // 1. Known header and value. Write index. // 2. Known header with custom value. Write name index and full value. // 3. Unknown header. Write full name and value. var(staticTableId, matchedValue) = headersEnumerator.GetQPackStaticTableId(); var name = headersEnumerator.Current.Key; var value = headersEnumerator.Current.Value; int headerLength; if (matchedValue) { if (!QPackEncoder.EncodeStaticIndexedHeaderField(staticTableId, buffer.Slice(length), out headerLength)) { if (length == 0 && throwIfNoneEncoded) { throw new QPackEncodingException("TODO sync with corefx" /* CoreStrings.HPackErrorNotEnoughBuffer */); } return(false); } } else { var valueEncoding = ReferenceEquals(headersEnumerator.EncodingSelector, KestrelServerOptions.DefaultHeaderEncodingSelector) ? null : headersEnumerator.EncodingSelector(name); if (!EncodeHeader(buffer.Slice(length), staticTableId, name, value, valueEncoding, out headerLength)) { if (length == 0 && throwIfNoneEncoded) { throw new QPackEncodingException("TODO sync with corefx" /* CoreStrings.HPackErrorNotEnoughBuffer */); } return(false); } } // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.3 totalHeaderSize += HeaderField.GetLength(name.Length, value.Length); length += headerLength; } while (headersEnumerator.MoveNext()); return(true); }
private void VerifyTableEntries(DynamicTable dynamicTable, params HeaderField[] entries) { Assert.Equal(entries.Length, dynamicTable.Count); Assert.Equal(entries.Sum(e => e.Length), dynamicTable.Size); for (int i = 0; i < entries.Length; i++) { HeaderField headerField = dynamicTable[i]; Assert.NotSame(entries[i].Name, headerField.Name); Assert.Equal(entries[i].Name, headerField.Name); Assert.NotSame(entries[i].Value, headerField.Value); Assert.Equal(entries[i].Value, headerField.Value); } }
/// <summary> /// 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. /// </summary> /// <param name="header">Header.</param> public void Add(HeaderField header) { var headerSize = header.Size; if (headerSize > this.capacity) { this.Clear(); return; } while(this.size + headerSize > this.capacity) { this.Remove(); } this.headerFields[this.head++] = header; this.size += header.Size; if (this.head == this.headerFields.Length) { this.head = 0; } }
// Size returns the size of an entry per RFC 7541 section 4.1. public static uint Size(this HeaderField hf) { // http://http2.github.io/http2-spec/compression.html#rfc.section.4.1 // "The size of the dynamic table is the sum of the size of // its entries. The size of an entry is the sum of its name's // length in octets (as defined in Section 5.2), its value's // length in octets (see Section 5.2), plus 32. The size of // an entry is calculated using the length of the name and // value without any Huffman encoding applied." // This can overflow if somebody makes a large HeaderField // Name and/or Value by hand, but we don't care, because that // won't happen on the wire because the encoding doesn't allow // it. return(uint32(len(hf.Name) + len(hf.Value) + 32L)); }
public HeaderField this[String index] { get { HeaderField lResult = BaseGet(index) as HeaderField; if (lResult == null) { lResult = new HeaderField(""); BaseSet(index, lResult); } return(lResult); } set { BaseSet(index, value); } }
private void HandleDecodeIndexed() { var idx = this._tasks[0].IntData; this.Reset(); var tableHeader = this._headerTable.GetAt(idx); AllowTableSizeUpdates = false; Done = true; HeaderField = new HeaderField { Name = tableHeader.Name, Value = tableHeader.Value, Sensitive = false }; HeaderSize = 32 + tableHeader.NameLen + tableHeader.ValueLen; }
public async Task <Http2Result> Post(byte[] data) { var result = new Http2Result(); var headers = new HeaderField[] { new HeaderField { Name = ":method", Value = "POST" }, new HeaderField { Name = ":scheme", Value = "http" }, new HeaderField { Name = ":path", Value = _path }, new HeaderField { Name = ":authority", Value = _authority }, }; var stream = await _conn.CreateStreamAsync(headers, true); result.Heads = (await stream.ReadHeadersAsync()).ToList(); List <byte> list = new List <byte>(); while (true) { var buf = new byte[8192]; var res = await stream.ReadAsync(new ArraySegment <byte>(buf)); if (res.EndOfStream) { break; } list.AddRange(buf.AsSpan().Slice(0, res.BytesRead).ToArray()); } if (list.Any()) { result.Body = list.ToArray(); } return(result); }
public async Task RespondingInvalidHeadersShouldTriggerAnException() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var r = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); var headers = new HeaderField[] { new HeaderField { Name = "status", Value = "200" }, }; var ex = await Assert.ThrowsAsync <Exception>(async() => await r.stream.WriteHeadersAsync(headers, false)); Assert.Equal("ErrorInvalidPseudoHeader", ex.Message); }
public async Task ItShouldBePossibleToSendInformationalHeaders() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var r = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); var infoHeaders = new HeaderField[] { new HeaderField { Name = ":status", Value = "100" }, new HeaderField { Name = "extension-field", Value = "bar" }, }; await r.stream.WriteHeadersAsync(infoHeaders, false); await r.stream.WriteHeadersAsync(DefaultStatusHeaders, false); await r.stream.WriteAsync(new ArraySegment<byte>(new byte[0]), true); await outPipe.ReadAndDiscardHeaders(1, false); await outPipe.ReadAndDiscardHeaders(1, false); await outPipe.ReadAndDiscardData(1, true, 0); }
public object Parse(string value, object argument) { var headers = new List<HeaderField>(); using (var sr = new StringReader(value)) { using (var sw = new StringWriter()) { while (true) { var line = sr.ReadLine(); // end of block reached, no more headers if (string.IsNullOrEmpty(line)) { break; } sw.Write(line); while (true) { var next = sr.Peek(); var isContinuation = next == Characters.Space || next == Characters.Tabulator; if (isContinuation) { var continuation = sr.ReadLine(); if (string.IsNullOrEmpty(continuation)) { break; } sw.Write(continuation.Trim()); } else { break; } } var literals = sw.ToString(); if (!string.IsNullOrEmpty(literals)) { var header = new HeaderField(); sw.Clear(); header.Deserialize(literals); headers.Add(header); } } } } return headers; }
public async Task ReceivingTrailersShouldUnblockDataReceptionAndPresentThem( bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( isServer, loggerProvider, inPipe, outPipe); var readDataTask = res.stream.ReadAllToArrayWithTimeout(); var fh = new FrameHeader { Type = FrameType.Data, Length = 4, StreamId = 1, Flags = (byte)0, }; await inPipe.WriteFrameHeader(fh); await inPipe.WriteAsync( new ArraySegment <byte>( System.Text.Encoding.ASCII.GetBytes("ABCD"))); var trailers = new HeaderField[] { new HeaderField { Name = "trai", Value = "ler" }, }; await inPipe.WriteHeaders(res.hEncoder, 1, true, trailers); var bytes = await readDataTask; Assert.Equal(4, bytes.Length); Assert.Equal("ABCD", System.Text.Encoding.ASCII.GetString(bytes)); Assert.Equal(StreamState.HalfClosedRemote, res.stream.State); var rcvdTrailers = await res.stream.ReadTrailersAsync(); Assert.Equal(trailers, rcvdTrailers); }
public void DynamicTable_WrapsRingBuffer_Success(int targetInsertIndex) { FieldInfo insertIndexField = typeof(DynamicTable).GetField("_insertIndex", BindingFlags.NonPublic | BindingFlags.Instance); DynamicTable table = new DynamicTable(maxSize: 256); Stack <byte[]> insertedHeaders = new Stack <byte[]>(); // Insert into dynamic table until its insert index into its ring buffer loops back to 0. do { InsertOne(); }while ((int)insertIndexField.GetValue(table) != 0); // Finally loop until the insert index reaches the target. while ((int)insertIndexField.GetValue(table) != targetInsertIndex) { InsertOne(); } void InsertOne() { byte[] data = Encoding.ASCII.GetBytes($"header-{insertedHeaders.Count}"); insertedHeaders.Push(data); table.Insert(data, data); } // Now check to see that we can retrieve the remaining headers. // Some headers will have been evacuated from the table during this process, so we don't exhaust the entire insertedHeaders stack. Assert.True(table.Count > 0); Assert.True(table.Count < insertedHeaders.Count); for (int i = 0; i < table.Count; ++i) { HeaderField dynamicField = table[i]; byte[] expectedData = insertedHeaders.Pop(); Assert.True(expectedData.AsSpan().SequenceEqual(dynamicField.Name)); Assert.True(expectedData.AsSpan().SequenceEqual(dynamicField.Value)); } }
public void Insert(ReadOnlySpan <byte> name, ReadOnlySpan <byte> value) { int entryLength = HeaderField.GetLength(name.Length, value.Length); EnsureAvailable(entryLength); if (entryLength > _maxSize) { // http://httpwg.org/specs/rfc7541.html#rfc.section.4.4 // It is not an error to attempt to add an entry that is larger than the maximum size; // an attempt to add an entry larger than the maximum size causes the table to be emptied // of all existing entries and results in an empty table. return; } var entry = new HeaderField(name, value); _buffer[_insertIndex] = entry; _insertIndex = (_insertIndex + 1) % _buffer.Length; _size += entry.Length; _count++; }
public void ShouldHandleExampleC2_3OfTheSpecificationCorrectly() { var encoder = new Encoder(new Encoder.Options { HuffmanStrategy = HuffmanStrategy.Never, }); var fields = new HeaderField[] { new HeaderField { Name = "password", Value = "secret", Sensitive = true } }; var result = new Buffer(); result.AddHexString("100870617373776f726406736563726574"); var res = EncodeToTempBuf(encoder, fields, MaxFrameSize); Assert.Equal(result.Bytes, res.Bytes); Assert.Equal(1, res.FieldCount); Assert.Equal(0, encoder.DynamicTableUsedSize); Assert.Equal(0, encoder.DynamicTableLength); }
public void ShouldHandleExampleC2_4OfTheSpecificationCorrectly() { var encoder = new Encoder(new Encoder.Options { HuffmanStrategy = HuffmanStrategy.Never, }); var fields = new HeaderField[] { new HeaderField { Name = ":method", Value = "GET", Sensitive = false } }; var result = new Buffer(); result.AddHexString("82"); var res = EncodeToTempBuf(encoder, fields, MaxFrameSize); Assert.Equal(result.Bytes, res.Bytes); Assert.Equal(1, res.FieldCount); Assert.Equal(0, encoder.DynamicTableUsedSize); Assert.Equal(0, encoder.DynamicTableLength); }
public void Resize(int maxSize) { // TODO: What would cause us to need to grow the table size? The connection-level limit should prevent this, right? // Understand this better. If we do need to resize, we may want a better resize strategy. if (maxSize > _maxSize) { var newBuffer = new HeaderField[maxSize / HeaderField.RfcOverhead]; for (var i = 0; i < Count; i++) { newBuffer[i] = _buffer[i]; } _buffer = newBuffer; _maxSize = maxSize; } else { _maxSize = maxSize; EnsureAvailable(0); } }
/// <summary> /// 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. /// </summary> /// <param name="header">Header.</param> 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; } }
/// <summary> /// 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. /// </summary> /// <param name="capacity">Capacity.</param> 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(this.size > capacity) { this.Remove(); } } var 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 (this.headerFields != null && this.headerFields.Length == maxEntries) { return; } var tmp = new HeaderField[maxEntries]; // initially length will be 0 so there will be no copy var len = this.Length(); var cursor = this.tail; for(var i = 0; i < len; i++) { var entry = this.headerFields[cursor++]; tmp[i] = entry; if (cursor == this.headerFields.Length) { cursor = 0; } } this.tail = 0; this.head = this.tail + len; this.headerFields = tmp; }