private void ValidateNonOriginHostHeader(string hostText) { if (_requestTargetForm == ProtoRequestTarget.AuthorityForm) { if (hostText != RawTarget) { BadProtoRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText); } } else if (_requestTargetForm == ProtoRequestTarget.AbsoluteForm) { // If the target URI includes an authority component, then a // client MUST send a field - value for Host that is identical to that // authority component, excluding any userinfo subcomponent and its "@" // delimiter. // System.Uri doesn't not tell us if the port was in the original string or not. // When IsDefaultPort = true, we will allow Host: with or without the default port if (hostText != _absoluteRequestTarget.Authority) { if (!_absoluteRequestTarget.IsDefaultPort || hostText != _absoluteRequestTarget.Authority + ":" + _absoluteRequestTarget.Port.ToString(CultureInfo.InvariantCulture)) { BadProtoRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText); } } } if (!ProtoUtilities.IsHostHeaderValid(hostText)) { BadProtoRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText); } }
internal void EnsureHostHeaderExists() { // https://tools.ietf.org/html/rfc7230#section-5.4 // A server MUST respond with a 400 (Bad Request) status code to any // HTTP/1.1 request message that lacks a Host header field and to any // request message that contains more than one Host header field or a // Host header field with an invalid field-value. var hostCount = ProtoRequestHeaders.HostCount; var hostText = ProtoRequestHeaders.HeaderHost.ToString(); if (hostCount <= 0) { if (_httpVersion == Proto.ProtoVersion.Proto10) { return; } BadProtoRequestException.Throw(RequestRejectionReason.MissingHostHeader); } else if (hostCount > 1) { BadProtoRequestException.Throw(RequestRejectionReason.MultipleHostHeaders); } else if (_requestTargetForm != ProtoRequestTarget.OriginForm) { // Tail call ValidateNonOriginHostHeader(hostText); } else if (!ProtoUtilities.IsHostHeaderValid(hostText)) { BadProtoRequestException.Throw(RequestRejectionReason.InvalidHostHeader, hostText); } }
private BadProtoRequestException(string message, int statusCode, RequestRejectionReason reason, ProtoMethod?requiredMethod) : base(message) { StatusCode = statusCode; Reason = reason; if (requiredMethod.HasValue) { AllowedHeader = ProtoUtilities.MethodToString(requiredMethod.Value); } }
private bool TryValidateAuthorityAndHost(out string hostText) { // :authority (optional) // Prefer this over Host var authority = RequestHeaders[HeaderNames.Authority]; var host = ProtoRequestHeaders.HeaderHost; if (!StringValues.IsNullOrEmpty(authority)) { // https://tools.ietf.org/html/rfc7540#section-8.1.2.3 // Clients that generate HTTP/2 requests directly SHOULD use the ":authority" // pseudo - header field instead of the Host header field. // An intermediary that converts an HTTP/2 request to HTTP/1.1 MUST // create a Host header field if one is not present in a request by // copying the value of the ":authority" pseudo - header field. // We take this one step further, we don't want mismatched :authority // and Host headers, replace Host if :authority is defined. The application // will operate on the Host header. ProtoRequestHeaders.HeaderHost = authority; host = authority; } // https://tools.ietf.org/html/rfc7230#section-5.4 // A server MUST respond with a 400 (Bad Request) status code to any // HTTP/1.1 request message that lacks a Host header field and to any // request message that contains more than one Host header field or a // Host header field with an invalid field-value. hostText = host.ToString(); if (host.Count > 1 || !ProtoUtilities.IsHostHeaderValid(hostText)) { // RST replaces 400 ResetAndAbort(new ConnectionAbortedException(CoreStrings.FormatBadRequest_InvalidHostHeader_Detail(hostText)), Proto2ErrorCode.PROTOCOL_ERROR); return(false); } return(true); }
private bool TryValidateMethod() { // :method _methodText = RequestHeaders[HeaderNames.Method].ToString(); Method = ProtoUtilities.GetKnownMethod(_methodText); if (Method == ProtoMethod.None) { ResetAndAbort(new ConnectionAbortedException(CoreStrings.FormatProto2ErrorMethodInvalid(_methodText)), Proto2ErrorCode.PROTOCOL_ERROR); return(false); } if (Method == ProtoMethod.Custom) { if (ProtoCharacters.IndexOfInvalidTokenChar(_methodText) >= 0) { ResetAndAbort(new ConnectionAbortedException(CoreStrings.FormatProto2ErrorMethodInvalid(_methodText)), Proto2ErrorCode.PROTOCOL_ERROR); return(false); } } return(true); }