public static void ToHttp2Headers(HttpHeaders inHeaders, IHttp2Headers output) { // Choose 8 as a default size because it is unlikely we will see more than 4 Connection headers values, but // still allowing for "enough" space in the map to reduce the chance of hash code collision. var connectionBlacklist = ToLowercaseMap(inHeaders.GetAll(HttpHeaderNames.Connection), 8); foreach (var entry in inHeaders) { AsciiString aName = entry.Key.ToLowerCase(); if (!HttpToHttp2HeaderBlacklist.Contains(aName) && !connectionBlacklist.Contains(aName)) { // https://tools.ietf.org/html/rfc7540#section-8.1.2.2 makes a special exception for TE if (aName.ContentEqualsIgnoreCase(HttpHeaderNames.Te)) { ToHttp2HeadersFilterTE(entry, output); } else if (aName.ContentEqualsIgnoreCase(HttpHeaderNames.Cookie)) { AsciiString value = AsciiString.Of(entry.Value); uint uValueCount = (uint)value.Count; // split up cookies to allow for better compression // https://tools.ietf.org/html/rfc7540#section-8.1.2.5 try { int index = value.ForEachByte(ByteProcessor.FindSemicolon); if (uValueCount > (uint)index) // != -1 { int start = 0; do { _ = output.Add(HttpHeaderNames.Cookie, value.SubSequence(start, index, false)); // skip 2 characters "; " (see https://tools.ietf.org/html/rfc6265#section-4.2.1) start = index + 2; } while ((uint)start < uValueCount && uValueCount > (uint)(index = value.ForEachByte(start, value.Count - start, ByteProcessor.FindSemicolon))); // != -1 if ((uint)start >= uValueCount) { ThrowHelper.ThrowArgumentException_CookieValueIsOfUnexpectedFormat(value); } _ = output.Add(HttpHeaderNames.Cookie, value.SubSequence(start, value.Count, false)); } else { _ = output.Add(HttpHeaderNames.Cookie, value); } } catch (Exception) { // This is not expect to happen because FIND_SEMI_COLON never throws but must be caught // because of the ByteProcessor interface. ThrowHelper.ThrowInvalidOperationException(); } } else { _ = output.Add(aName, entry.Value); } } } }
private static CharSequenceMap <AsciiString> ToLowercaseMap(IEnumerable <ICharSequence> values, int arraySizeHint) { var valueConverter = UnsupportedValueConverter <AsciiString> .Instance; var result = new CharSequenceMap <AsciiString>(true, valueConverter, arraySizeHint); foreach (var item in values) { AsciiString lowerCased = AsciiString.Of(item).ToLowerCase(); try { int index = lowerCased.ForEachByte(ByteProcessor.FindComma); if (index != -1) { int start = 0; do { _ = result.Add(lowerCased.SubSequence(start, index, false).Trim(), AsciiString.Empty); start = index + 1; } while (start < lowerCased.Count && (index = lowerCased.ForEachByte(start, lowerCased.Count - start, ByteProcessor.FindComma)) != -1); _ = result.Add(lowerCased.SubSequence(start, lowerCased.Count, false).Trim(), AsciiString.Empty); } else { _ = result.Add(lowerCased.Trim(), AsciiString.Empty); } } catch (Exception) { // This is not expect to happen because FIND_COMMA never throws but must be caught // because of the ByteProcessor interface. ThrowHelper.ThrowInvalidOperationException(); } } return(result); }