static string GetOrAddCachedValue([NotNull] ref string?cache, HeaderDescriptor descriptor, ReadOnlySpan <byte> value) { string?lastValue = cache; if (lastValue is null || !ByteArrayHelpers.EqualsOrdinalAscii(lastValue, value)) { cache = lastValue = descriptor.GetHeaderValue(value); } return(lastValue); }
public void OnHeader(ReadOnlySpan <byte> name, ReadOnlySpan <byte> value) { if (!HeaderDescriptor.TryGet(name, out HeaderDescriptor descriptor)) { throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_header_name, Encoding.ASCII.GetString(name))); } string headerValue = descriptor.GetHeaderValue(value); _headers.TryAddWithoutValidation(descriptor, headerValue.Split(',').Select(x => x.Trim())); }
internal static string GetSingleHeaderString(string name, IEnumerable <string> values) { string separator = HttpHeaderParser.DefaultSeparator; if (HeaderDescriptor.TryGet(name, out var descriptor) && (descriptor.Parser != null) && (descriptor.Parser.SupportsMultipleValues)) { separator = descriptor.Parser.Separator; } return(string.Join(separator, values)); }
public void OnResponseHeader(ReadOnlySpan <byte> name, ReadOnlySpan <byte> value) { // TODO: ISSUE 31309: Optimize HPACK static table decoding lock (SyncObject) { if (_state != StreamState.ExpectingHeaders) { throw new Http2ProtocolException(Http2ProtocolErrorCode.ProtocolError); } if (name.SequenceEqual(s_statusHeaderName)) { if (value.Length != 3) { throw new Exception("Invalid status code"); } // Copied from HttpConnection byte status1 = value[0], status2 = value[1], status3 = value[2]; if (!IsDigit(status1) || !IsDigit(status2) || !IsDigit(status3)) { throw new HttpRequestException(SR.net_http_invalid_response); } _response.SetStatusCodeWithoutValidation((HttpStatusCode)(100 * (status1 - '0') + 10 * (status2 - '0') + (status3 - '0'))); } else { if (!HeaderDescriptor.TryGet(name, out HeaderDescriptor descriptor)) { // Invalid header name throw new HttpRequestException(SR.net_http_invalid_response); } string headerValue = descriptor.GetHeaderValue(value); // Note we ignore the return value from TryAddWithoutValidation; // if the header can't be added, we silently drop it. if (descriptor.HeaderType == HttpHeaderType.Content) { _response.Content.Headers.TryAddWithoutValidation(descriptor, headerValue); } else { _response.Headers.TryAddWithoutValidation(descriptor, headerValue); } } } }
public void RoundTripsUtf8(string input) { byte[] encoded = Encoding.UTF8.GetBytes(input); Assert.True(HeaderDescriptor.TryGet("custom-header", out HeaderDescriptor descriptor)); Assert.Null(descriptor.KnownHeader); string roundtrip = descriptor.GetHeaderValue(encoded, Encoding.UTF8); Assert.Equal(input, roundtrip); Assert.True(HeaderDescriptor.TryGet("Cache-Control", out descriptor)); Assert.NotNull(descriptor.KnownHeader); roundtrip = descriptor.GetHeaderValue(encoded, Encoding.UTF8); Assert.Equal(input, roundtrip); }
// adapted from header deserialization code in Http2Connection.cs private static HttpHeaders HPackDecode(Memory <byte> memory) { var header = new HttpRequestHeaders(); var hpackDecoder = new HPackDecoder(maxDynamicTableSize: 0, maxResponseHeadersLength: HttpHandlerDefaults.DefaultMaxResponseHeadersLength * 1024); hpackDecoder.Decode(memory.Span, true, ((_, name, value) => HeaderHandler(name, value)), null); return(header); void HeaderHandler(ReadOnlySpan <byte> name, ReadOnlySpan <byte> value) { if (!HeaderDescriptor.TryGet(name, out HeaderDescriptor descriptor)) { throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_header_name, Encoding.ASCII.GetString(name))); } string headerValue = descriptor.GetHeaderValue(value); header.TryAddWithoutValidation(descriptor, headerValue.Split(',').Select(x => x.Trim())); } }
public DiagnosticsHandler(HttpMessageHandler innerHandler, DistributedContextPropagator propagator, bool autoRedirect = false) { Debug.Assert(IsGloballyEnabled()); Debug.Assert(innerHandler is not null && propagator is not null); _innerHandler = innerHandler; _propagator = propagator; // Prepare HeaderDescriptors for fields we need to clear when following redirects if (autoRedirect && _propagator.Fields is IReadOnlyCollection <string> fields && fields.Count > 0) { var fieldDescriptors = new List <HeaderDescriptor>(fields.Count); foreach (string field in fields) { if (field is not null && HeaderDescriptor.TryGet(field, out HeaderDescriptor descriptor)) { fieldDescriptors.Add(descriptor); } } _propagatorFields = fieldDescriptors.ToArray(); } }
public void OnResponseHeader(ReadOnlySpan <byte> name, ReadOnlySpan <byte> value) { // TODO: ISSUE 31309: Optimize HPACK static table decoding lock (SyncObject) { if (_state != StreamState.ExpectingHeaders && _state != StreamState.ExpectingTrailingHeaders) { throw new Http2ProtocolException(Http2ProtocolErrorCode.ProtocolError); } if (name.SequenceEqual(s_statusHeaderName)) { if (_state == StreamState.ExpectingTrailingHeaders) { // Pseudo-headers not allowed in trailers. if (NetEventSource.IsEnabled) { _connection.Trace("Pseudo-header in trailer headers."); } throw new HttpRequestException(SR.net_http_invalid_response_pseudo_header_in_trailer); } byte status1, status2, status3; if (value.Length != 3 || !IsDigit(status1 = value[0]) || !IsDigit(status2 = value[1]) || !IsDigit(status3 = value[2])) { throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_status_code, Encoding.ASCII.GetString(value))); } _response.SetStatusCodeWithoutValidation((HttpStatusCode)(100 * (status1 - '0') + 10 * (status2 - '0') + (status3 - '0'))); } else { if (!HeaderDescriptor.TryGet(name, out HeaderDescriptor descriptor)) { // Invalid header name throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_header_name, Encoding.ASCII.GetString(name))); } string headerValue = descriptor.GetHeaderValue(value); // Note we ignore the return value from TryAddWithoutValidation; // if the header can't be added, we silently drop it. if (_state == StreamState.ExpectingTrailingHeaders) { _response.TrailingHeaders.TryAddWithoutValidation(descriptor, headerValue); } else if (descriptor.HeaderType == HttpHeaderType.Content) { _response.Content.Headers.TryAddWithoutValidation(descriptor, headerValue); } else { _response.Headers.TryAddWithoutValidation(descriptor, headerValue); } } } }
/// <summary>Uses <see cref="HeaderDescriptor.GetHeaderValue"/>, but first special-cases several known headers for which we can use caching.</summary> public string GetResponseHeaderValueWithCaching(HeaderDescriptor descriptor, ReadOnlySpan <byte> value) { return (ReferenceEquals(descriptor.KnownHeader, KnownHeaders.Date) ? GetOrAddCachedValue(ref _lastDateHeaderValue, descriptor, value) : ReferenceEquals(descriptor.KnownHeader, KnownHeaders.Server) ? GetOrAddCachedValue(ref _lastServerHeaderValue, descriptor, value) : descriptor.GetHeaderValue(value));
public void OnResponseHeader(ReadOnlySpan <byte> name, ReadOnlySpan <byte> value) { Debug.Assert(name != null && name.Length > 0); // TODO: ISSUE 31309: Optimize HPACK static table decoding lock (SyncObject) { if (name[0] == (byte)':') { if (_state != StreamState.ExpectingHeaders && _state != StreamState.ExpectingStatus) { // Pseudo-headers are allowed only in header block if (NetEventSource.IsEnabled) { _connection.Trace($"Pseudo-header in {_state} state."); } throw new Http2ProtocolException(SR.net_http_invalid_response_pseudo_header_in_trailer); } if (name.SequenceEqual(s_statusHeaderName)) { if (_state != StreamState.ExpectingStatus) { if (NetEventSource.IsEnabled) { _connection.Trace("Received duplicate status headers."); } throw new Http2ProtocolException(SR.Format(SR.net_http_invalid_response_status_code, "duplicate status")); } byte status1, status2, status3; if (value.Length != 3 || !IsDigit(status1 = value[0]) || !IsDigit(status2 = value[1]) || !IsDigit(status3 = value[2])) { throw new Http2ProtocolException(SR.Format(SR.net_http_invalid_response_status_code, Encoding.ASCII.GetString(value))); } int statusValue = (100 * (status1 - '0') + 10 * (status2 - '0') + (status3 - '0')); _response = new HttpResponseMessage() { Version = HttpVersion.Version20, RequestMessage = _request, Content = new HttpConnectionResponseContent(), StatusCode = (HttpStatusCode)statusValue }; TaskCompletionSource <bool> shouldSendRequestBodyWaiter = _shouldSendRequestBodyWaiter; if (statusValue < 200) { if (_response.StatusCode == HttpStatusCode.Continue && shouldSendRequestBodyWaiter != null) { if (NetEventSource.IsEnabled) { _connection.Trace("Received 100Continue status."); } shouldSendRequestBodyWaiter.TrySetResult(true); _shouldSendRequestBodyWaiter = null; } // We do not process headers from 1xx responses. _state = StreamState.ExpectingIgnoredHeaders; } else { _state = StreamState.ExpectingHeaders; // If we tried 100-Continue and got rejected signal that we should not send request body. _shouldSendRequestBody = (int)Response.StatusCode < 300; shouldSendRequestBodyWaiter?.TrySetResult(_shouldSendRequestBody); } } else { if (NetEventSource.IsEnabled) { _connection.Trace("Invalid response pseudo-header '{System.Text.Encoding.ASCII.GetString(name)}'."); } throw new Http2ProtocolException(SR.net_http_invalid_response); } } else { if (_state == StreamState.ExpectingIgnoredHeaders) { // for 1xx response we ignore all headers. return; } if (_state != StreamState.ExpectingHeaders && _state != StreamState.ExpectingTrailingHeaders) { if (NetEventSource.IsEnabled) { _connection.Trace($"Received header before status."); } throw new Http2ProtocolException(SR.net_http_invalid_response); } if (!HeaderDescriptor.TryGet(name, out HeaderDescriptor descriptor)) { // Invalid header name throw new Http2ProtocolException(SR.Format(SR.net_http_invalid_response_header_name, Encoding.ASCII.GetString(name))); } string headerValue = descriptor.GetHeaderValue(value); // Note we ignore the return value from TryAddWithoutValidation; // if the header can't be added, we silently drop it. if (_state == StreamState.ExpectingTrailingHeaders) { _response.TrailingHeaders.TryAddWithoutValidation(descriptor.HeaderType == HttpHeaderType.Request ? descriptor.AsCustomHeader() : descriptor, headerValue); } else if (descriptor.HeaderType == HttpHeaderType.Content) { _response.Content.Headers.TryAddWithoutValidation(descriptor, headerValue); } else { _response.Headers.TryAddWithoutValidation(descriptor.HeaderType == HttpHeaderType.Request ? descriptor.AsCustomHeader() : descriptor, headerValue); } } } }
void HandleHasBytesAvailableEvent(object sender, CFStream.StreamEventArgs e) { var stream = (CFHTTPStream)sender; StreamBucket bucket; if (!streamBuckets.TryGetValue(stream.Handle, out bucket)) { return; } if (bucket.Response.Task.IsCompleted) { bucket.ContentStream.ReadStreamData(); return; } var header = stream.GetResponseHeader(); // Is this possible? if (!header.IsHeaderComplete) { throw new NotImplementedException(); } bucket.ContentStream = new CFContentStream(stream); var response_msg = new HttpResponseMessage(header.ResponseStatusCode); response_msg.RequestMessage = bucket.Request; response_msg.ReasonPhrase = header.ResponseStatusLine; response_msg.Content = bucket.ContentStream; var fields = header.GetAllHeaderFields(); if (fields != null) { foreach (var entry in fields) { if (entry.Key == null) { continue; } var key = entry.Key.ToString(); var value = entry.Value == null ? string.Empty : entry.Value.ToString(); HttpHeaders item_headers; if (HeaderDescriptor.TryGet(key, out var descriptor) && descriptor.HeaderType == HttpHeaderType.Content) { item_headers = response_msg.Content.Headers; } else { item_headers = response_msg.Headers; if (cookies != null && (key == "Set-Cookie" || key == "Set-Cookie2")) { AddCookie(value, bucket.Request.RequestUri, key); } } item_headers.TryAddWithoutValidation(key, value); } } // cancellation (CancellationTokenRegistration) can happen in parallel if (!bucket.Response.Task.IsCanceled) { bucket.Response.TrySetResult(response_msg); bucket.ContentStream.ReadStreamData(); } }
internal static bool IsContentHeader(string name) { return(HeaderDescriptor.TryGet(name, out var descriptor) && descriptor.HeaderType == HttpHeaderType.Content); }
public void OnResponseHeader(ReadOnlySpan <byte> name, ReadOnlySpan <byte> value) { Debug.Assert(name != null && name.Length > 0); // TODO: ISSUE 31309: Optimize HPACK static table decoding lock (SyncObject) { if (_state != StreamState.ExpectingHeaders && _state != StreamState.ExpectingTrailingHeaders) { throw new Http2ProtocolException(Http2ProtocolErrorCode.ProtocolError); } if (name[0] == (byte)':') { if (_state == StreamState.ExpectingTrailingHeaders) { // Pseudo-headers not allowed in trailers. if (NetEventSource.IsEnabled) { _connection.Trace("Pseudo-header in trailer headers."); } throw new HttpRequestException(SR.net_http_invalid_response_pseudo_header_in_trailer); } if (name.SequenceEqual(s_statusHeaderName)) { if (_response != null) { if (NetEventSource.IsEnabled) { _connection.Trace("Received duplicate status headers."); } throw new Http2ProtocolException(Http2ProtocolErrorCode.ProtocolError); } byte status1, status2, status3; if (value.Length != 3 || !IsDigit(status1 = value[0]) || !IsDigit(status2 = value[1]) || !IsDigit(status3 = value[2])) { throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_status_code, Encoding.ASCII.GetString(value))); } int statusValue = (100 * (status1 - '0') + 10 * (status2 - '0') + (status3 - '0')); _response = new HttpResponseMessage() { Version = HttpVersion.Version20, RequestMessage = _request, Content = new HttpConnectionResponseContent(), StatusCode = (HttpStatusCode)statusValue }; } else { if (NetEventSource.IsEnabled) { _connection.Trace("Invalid response pseudo-header '{System.Text.Encoding.ASCII.GetString(name)}'."); } throw new HttpRequestException(SR.net_http_invalid_response); } } else { if (_response == null) { if (NetEventSource.IsEnabled) { _connection.Trace($"Received header before status pseudo-header."); } throw new HttpRequestException(SR.net_http_invalid_response); } if (!HeaderDescriptor.TryGet(name, out HeaderDescriptor descriptor)) { // Invalid header name throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_header_name, Encoding.ASCII.GetString(name))); } string headerValue = descriptor.GetHeaderValue(value); // Note we ignore the return value from TryAddWithoutValidation; // if the header can't be added, we silently drop it. if (_state == StreamState.ExpectingTrailingHeaders) { _response.TrailingHeaders.TryAddWithoutValidation(descriptor, headerValue); } else if (descriptor.HeaderType == HttpHeaderType.Content) { _response.Content.Headers.TryAddWithoutValidation(descriptor, headerValue); } else { _response.Headers.TryAddWithoutValidation(descriptor, headerValue); } } } }
/// <summary>Uses <see cref="HeaderDescriptor.GetHeaderValue"/>, but first special-cases several known headers for which we can use caching.</summary> public string GetResponseHeaderValueWithCaching(HeaderDescriptor descriptor, ReadOnlySpan <byte> value, Encoding?valueEncoding) { return (descriptor.Equals(KnownHeaders.Date) ? GetOrAddCachedValue(ref _lastDateHeaderValue, descriptor, value, valueEncoding) : descriptor.Equals(KnownHeaders.Server) ? GetOrAddCachedValue(ref _lastServerHeaderValue, descriptor, value, valueEncoding) : descriptor.GetHeaderValue(value, valueEncoding));