Beispiel #1
0
    public Task <Stream> UpgradeAsync()
    {
        if (!IsUpgradableRequest)
        {
            if (Request.ProtocolVersion != System.Net.HttpVersion.Version11)
            {
                throw new InvalidOperationException("Upgrade requires HTTP/1.1.");
            }
            throw new InvalidOperationException("This request cannot be upgraded because it has a body.");
        }
        if (Response.HasStarted)
        {
            throw new InvalidOperationException("This request cannot be upgraded, the response has already started.");
        }

        // Set the status code and reason phrase
        Response.StatusCode   = StatusCodes.Status101SwitchingProtocols;
        Response.ReasonPhrase = HttpReasonPhrase.Get(StatusCodes.Status101SwitchingProtocols);

        Response.SendOpaqueUpgrade(); // TODO: Async
        Request.SwitchToOpaqueMode();
        Response.SwitchToOpaqueMode();
        var opaqueStream = new OpaqueStream(Request.Body, Response.Body);

        return(Task.FromResult <Stream>(opaqueStream));
    }
Beispiel #2
0
        private string BuildResponseBody(int httpStatusCode)
        {
            var reasonPhrase = HttpReasonPhrase.Get(httpStatusCode);
            var separator    = string.IsNullOrEmpty(reasonPhrase) ? "" : "; ";

            return(string.Format($"Status Code: {httpStatusCode}{separator}{reasonPhrase}"));
        }
Beispiel #3
0
    private string GetReasonPhrase(int statusCode)
    {
        string?reasonPhrase = ReasonPhrase;

        if (string.IsNullOrWhiteSpace(reasonPhrase))
        {
            // If the user hasn't set this then it is generated on the fly if possible.
            reasonPhrase = HttpReasonPhrase.Get(statusCode) ?? string.Empty;
        }
        return(reasonPhrase);
    }
Beispiel #4
0
    public Task <Stream> UpgradeAsync()
    {
        if (!IsUpgradableRequest)
        {
            throw new InvalidOperationException("This request cannot be upgraded, it is incompatible.");
        }
        if (Response.HasStarted)
        {
            throw new InvalidOperationException("This request cannot be upgraded, the response has already started.");
        }

        // Set the status code and reason phrase
        Response.StatusCode   = StatusCodes.Status101SwitchingProtocols;
        Response.ReasonPhrase = HttpReasonPhrase.Get(StatusCodes.Status101SwitchingProtocols);

        Response.SendOpaqueUpgrade(); // TODO: Async
        Request.SwitchToOpaqueMode();
        Response.SwitchToOpaqueMode();
        var opaqueStream = new OpaqueStream(Request.Body, Response.Body);

        return(Task.FromResult <Stream>(opaqueStream));
    }
Beispiel #5
0
    internal unsafe void SendError(ulong requestId, int httpStatusCode, IList <string>?authChallenges = null)
    {
        HttpApiTypes.HTTP_RESPONSE_V2 httpResponse = new HttpApiTypes.HTTP_RESPONSE_V2();
        httpResponse.Response_V1.Version = new HttpApiTypes.HTTP_VERSION();
        httpResponse.Response_V1.Version.MajorVersion = (ushort)1;
        httpResponse.Response_V1.Version.MinorVersion = (ushort)1;

        List <GCHandle>?pinnedHeaders = null;
        GCHandle        gcHandle;

        try
        {
            // Copied from the multi-value headers section of SerializeHeaders
            if (authChallenges != null && authChallenges.Count > 0)
            {
                pinnedHeaders = new List <GCHandle>(authChallenges.Count + 3);

                HttpApiTypes.HTTP_RESPONSE_INFO[] knownHeaderInfo = new HttpApiTypes.HTTP_RESPONSE_INFO[1];
                gcHandle = GCHandle.Alloc(knownHeaderInfo, GCHandleType.Pinned);
                pinnedHeaders.Add(gcHandle);
                httpResponse.pResponseInfo = (HttpApiTypes.HTTP_RESPONSE_INFO *)gcHandle.AddrOfPinnedObject();

                knownHeaderInfo[httpResponse.ResponseInfoCount].Type   = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders;
                knownHeaderInfo[httpResponse.ResponseInfoCount].Length =
                    (uint)Marshal.SizeOf <HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS>();

                HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS header = new HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS();

                header.HeaderId = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderWwwAuthenticate;
                header.Flags    = HttpApiTypes.HTTP_RESPONSE_INFO_FLAGS.PreserveOrder; // The docs say this is for www-auth only.

                HttpApiTypes.HTTP_KNOWN_HEADER[] nativeHeaderValues = new HttpApiTypes.HTTP_KNOWN_HEADER[authChallenges.Count];
                gcHandle = GCHandle.Alloc(nativeHeaderValues, GCHandleType.Pinned);
                pinnedHeaders.Add(gcHandle);
                header.KnownHeaders = (HttpApiTypes.HTTP_KNOWN_HEADER *)gcHandle.AddrOfPinnedObject();

                for (int headerValueIndex = 0; headerValueIndex < authChallenges.Count; headerValueIndex++)
                {
                    // Add Value
                    string headerValue = authChallenges[headerValueIndex];
                    byte[] bytes       = HeaderEncoding.GetBytes(headerValue);
                    nativeHeaderValues[header.KnownHeaderCount].RawValueLength = (ushort)bytes.Length;
                    gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
                    pinnedHeaders.Add(gcHandle);
                    nativeHeaderValues[header.KnownHeaderCount].pRawValue = (byte *)gcHandle.AddrOfPinnedObject();
                    header.KnownHeaderCount++;
                }

                // This type is a struct, not an object, so pinning it causes a boxed copy to be created. We can't do that until after all the fields are set.
                gcHandle = GCHandle.Alloc(header, GCHandleType.Pinned);
                pinnedHeaders.Add(gcHandle);
                knownHeaderInfo[0].pInfo = (HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS *)gcHandle.AddrOfPinnedObject();

                httpResponse.ResponseInfoCount = 1;
            }

            httpResponse.Response_V1.StatusCode = (ushort)httpStatusCode;
            string?statusDescription = HttpReasonPhrase.Get(httpStatusCode);
            uint   dataWritten       = 0;
            uint   statusCode;
            byte[] byteReason = statusDescription != null?HeaderEncoding.GetBytes(statusDescription) : Array.Empty <byte>();
            fixed(byte *pReason = byteReason)
            {
                httpResponse.Response_V1.pReason      = (byte *)pReason;
                httpResponse.Response_V1.ReasonLength = (ushort)byteReason.Length;

                byte[] byteContentLength = new byte[] { (byte)'0' };
                fixed(byte *pContentLength = byteContentLength)
                {
                    (&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].pRawValue      = (byte *)pContentLength;
                    (&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].RawValueLength = (ushort)byteContentLength.Length;
                    httpResponse.Response_V1.Headers.UnknownHeaderCount = 0;

                    statusCode =
                        HttpApi.HttpSendHttpResponse(
                            _requestQueue.Handle,
                            requestId,
                            0,
                            &httpResponse,
                            null,
                            &dataWritten,
                            IntPtr.Zero,
                            0,
                            SafeNativeOverlapped.Zero,
                            IntPtr.Zero);
                }
            }
            if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
            {
                // if we fail to send a 401 something's seriously wrong, abort the request
                HttpApi.HttpCancelHttpRequest(_requestQueue.Handle, requestId, IntPtr.Zero);
            }
        }
        finally
        {
            if (pinnedHeaders != null)
            {
                foreach (GCHandle handle in pinnedHeaders)
                {
                    if (handle.IsAllocated)
                    {
                        handle.Free();
                    }
                }
            }
        }
    }