Example #1
0
        void OnResponse(int httpStatus, byte[] buffer, int bufferLength)
        {
            try
            {
                using (var ms = new BufferPoolMemoryStream())
                {
                    Stream = ms;

                    XHR_GetStatusLine(NativeId, OnBufferCallback);
                    XHR_GetResponseHeaders(NativeId, OnBufferCallback);

                    if (buffer != null && bufferLength > 0)
                    {
                        ms.Write(buffer, 0, bufferLength);
                    }

                    ms.Seek(0L, SeekOrigin.Begin);

                    var    internalBuffer = ms.GetBuffer();
                    string tmp            = System.Text.Encoding.UTF8.GetString(internalBuffer);
                    HTTPManager.Logger.Information(this.NativeId + " OnResponse - full response ", tmp);

                    SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler;
                    CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false);

                    CurrentRequest.Response.Receive(buffer != null && bufferLength > 0 ? (int)bufferLength : -1, true);

#if !BESTHTTP_DISABLE_COOKIES
                    if (CurrentRequest.IsCookiesEnabled)
                    {
                        BestHTTP.Cookies.CookieJar.Set(CurrentRequest.Response);
                    }
#endif
                }
            }
            catch (Exception e)
            {
                HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e);

                if (CurrentRequest != null)
                {
                    // Something gone bad, Response must be null!
                    CurrentRequest.Response = null;

                    switch (State)
                    {
                    case HTTPConnectionStates.AbortRequested:
                        CurrentRequest.State = HTTPRequestStates.Aborted;
                        break;

                    case HTTPConnectionStates.TimedOut:
                        CurrentRequest.State = HTTPRequestStates.TimedOut;
                        break;

                    default:
                        CurrentRequest.Exception = e;
                        CurrentRequest.State     = HTTPRequestStates.Error;
                        break;
                    }
                }
            }
            finally
            {
                Connections.Remove(NativeId);

                Stream = null;

                if (CurrentRequest != null)
                {
                    lock (HTTPManager.Locker)
                    {
                        State = HTTPConnectionStates.Closed;
                        if (CurrentRequest.State == HTTPRequestStates.Processing)
                        {
                            if (CurrentRequest.Response != null)
                            {
                                CurrentRequest.State = HTTPRequestStates.Finished;
                            }
                            else
                            {
                                CurrentRequest.State = HTTPRequestStates.Error;
                            }
                        }
                    }
                }

                LastProcessTime = DateTime.UtcNow;

                if (OnConnectionRecycled != null)
                {
                    RecycleNow();
                }

                XHR_Release(NativeId);
            }
        }
        public BufferSegment EncodeMessage(Message message)
        {
            var memBuffer = BufferPool.Get(256, true);
            var stream    = new BufferPoolMemoryStream(memBuffer, 0, memBuffer.Length, true, true, false, true);

            // Write 5 bytes for placeholder for length prefix
            stream.WriteByte(0);
            stream.WriteByte(0);
            stream.WriteByte(0);
            stream.WriteByte(0);
            stream.WriteByte(0);

            var bufferWriter = new BufferPoolBufferWriter(stream);
            var writer       = new MessagePackWriter(bufferWriter);

            switch (message.type)
            {
            case MessageTypes.StreamItem:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streamitem-message-encoding-1
                // [2, Headers, InvocationId, Item]

                writer.WriteArrayHeader(4);

                writer.Write(2);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);
                WriteValue(ref writer, bufferWriter, message.item);

                break;

            case MessageTypes.Completion:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#completion-message-encoding-1
                // [3, Headers, InvocationId, ResultKind, Result?]

                byte resultKind = (byte)(!string.IsNullOrEmpty(message.error) ? /*error*/ 1 : message.result != null ? /*non-void*/ 3 : /*void*/ 2);

                writer.WriteArrayHeader(resultKind == 2 ? 4 : 5);

                writer.Write(3);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);
                writer.Write(resultKind);

                if (resultKind == 1)     // error
                {
                    WriteString(ref writer, message.error);
                }
                else if (resultKind == 3)     // non-void
                {
                    WriteValue(ref writer, bufferWriter, message.result);
                }

                break;

            case MessageTypes.Invocation:
            // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#invocation-message-encoding-1
            // [1, Headers, InvocationId, NonBlocking, Target, [Arguments], [StreamIds]]

            case MessageTypes.StreamInvocation:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#streaminvocation-message-encoding-1
                // [4, Headers, InvocationId, Target, [Arguments], [StreamIds]]

                writer.WriteArrayHeader(message.streamIds != null ? 6 : 5);

                writer.Write((int)message.type);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);
                WriteString(ref writer, message.target);
                writer.WriteArrayHeader(message.arguments != null ? message.arguments.Length : 0);
                if (message.arguments != null)
                {
                    for (int i = 0; i < message.arguments.Length; ++i)
                    {
                        WriteValue(ref writer, bufferWriter, message.arguments[i]);
                    }
                }

                if (message.streamIds != null)
                {
                    writer.WriteArrayHeader(message.streamIds.Length);

                    for (int i = 0; i < message.streamIds.Length; ++i)
                    {
                        WriteValue(ref writer, bufferWriter, message.streamIds[i]);
                    }
                }

                break;

            case MessageTypes.CancelInvocation:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#cancelinvocation-message-encoding-1
                // [5, Headers, InvocationId]

                writer.WriteArrayHeader(3);

                writer.Write(5);
                WriteHeaders(ref writer);
                WriteString(ref writer, message.invocationId);

                break;

            case MessageTypes.Ping:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#ping-message-encoding-1
                // [6]

                writer.WriteArrayHeader(1);
                writer.Write(6);

                break;

            case MessageTypes.Close:
                // https://github.com/aspnet/AspNetCore/blob/master/src/SignalR/docs/specs/HubProtocol.md#close-message-encoding-1
                // [7, Error, AllowReconnect?]

                writer.WriteArrayHeader(string.IsNullOrEmpty(message.error) ? 1 : 2);

                writer.Write(7);
                if (!string.IsNullOrEmpty(message.error))
                {
                    WriteString(ref writer, message.error);
                }

                break;
            }

            writer.Flush();

            // get how much bytes got written to the buffer. This includes the 5 placeholder bytes too.
            int length = (int)stream.Position;

            // this is the length without the 5 placeholder bytes
            int contentLength = length - 5;

            // get the stream's internal buffer. We set the releaseBuffer flag to false, so we can use it safely.
            var buffer = stream.GetBuffer();

            // add varint length prefix
            byte prefixBytes = GetRequiredBytesForLengthPrefix(contentLength);

            WriteLengthAsVarInt(buffer, 5 - prefixBytes, contentLength);

            // return with the final segment
            return(new BufferSegment(buffer, 5 - prefixBytes, contentLength + prefixBytes));
        }
        void OnResponse(int httpStatus, byte[] buffer, int bufferLength)
        {
            HTTPConnectionStates proposedConnectionState = HTTPConnectionStates.Processing;
            bool resendRequest = false;

            try
            {
                if (this.CurrentRequest.IsCancellationRequested)
                {
                    return;
                }

                using (var ms = new BufferPoolMemoryStream())
                {
                    Stream = ms;

                    XHR_GetStatusLine(NativeId, OnBufferCallback);
                    XHR_GetResponseHeaders(NativeId, OnBufferCallback);

                    if (buffer != null && bufferLength > 0)
                    {
                        ms.Write(buffer, 0, bufferLength);
                    }

                    ms.Seek(0L, SeekOrigin.Begin);

                    var    internalBuffer = ms.GetBuffer();
                    string tmp            = System.Text.Encoding.UTF8.GetString(internalBuffer);
                    HTTPManager.Logger.Information(this.NativeId + " OnResponse - full response ", tmp, this.Context);

                    SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler;
                    CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false);

                    CurrentRequest.Response.Receive(buffer != null && bufferLength > 0 ? (int)bufferLength : -1, true);

                    KeepAliveHeader keepAlive = null;
                    ConnectionHelper.HandleResponse(this.ToString(), this.CurrentRequest, out resendRequest, out proposedConnectionState, ref keepAlive);
                }
            }
            catch (Exception e)
            {
                HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e, this.Context);

                if (this.ShutdownType == ShutdownTypes.Immediate)
                {
                    return;
                }

#if !BESTHTTP_DISABLE_CACHING
                if (this.CurrentRequest.UseStreaming)
                {
                    HTTPCacheService.DeleteEntity(this.CurrentRequest.CurrentUri);
                }
#endif

                // Something gone bad, Response must be null!
                this.CurrentRequest.Response = null;

                if (!this.CurrentRequest.IsCancellationRequested)
                {
                    this.CurrentRequest.Exception = e;
                    this.CurrentRequest.State     = HTTPRequestStates.Error;
                }

                proposedConnectionState = HTTPConnectionStates.Closed;
            }
            finally
            {
                // Exit ASAP
                if (this.ShutdownType != ShutdownTypes.Immediate)
                {
                    if (this.CurrentRequest.IsCancellationRequested)
                    {
                        // we don't know what stage the request is cancelled, we can't safely reuse the tcp channel.
                        proposedConnectionState = HTTPConnectionStates.Closed;

                        this.CurrentRequest.Response = null;

                        this.CurrentRequest.State = this.CurrentRequest.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted;
                    }
                    else if (resendRequest)
                    {
                        RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.CurrentRequest, RequestEvents.Resend));
                    }
                    else if (this.CurrentRequest.Response != null && this.CurrentRequest.Response.IsUpgraded)
                    {
                        proposedConnectionState = HTTPConnectionStates.WaitForProtocolShutdown;
                    }
                    else if (this.CurrentRequest.State == HTTPRequestStates.Processing)
                    {
                        if (this.CurrentRequest.Response != null)
                        {
                            this.CurrentRequest.State = HTTPRequestStates.Finished;
                        }
                        else
                        {
                            this.CurrentRequest.Exception = new Exception(string.Format("[{0}] Remote server closed the connection before sending response header! Previous request state: {1}. Connection state: {2}",
                                                                                        this.ToString(),
                                                                                        this.CurrentRequest.State.ToString(),
                                                                                        this.State.ToString()));
                            this.CurrentRequest.State = HTTPRequestStates.Error;

                            proposedConnectionState = HTTPConnectionStates.Closed;
                        }
                    }

                    this.CurrentRequest = null;

                    if (proposedConnectionState == HTTPConnectionStates.Processing)
                    {
                        proposedConnectionState = HTTPConnectionStates.Closed;
                    }

                    ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, proposedConnectionState));
                }
            }
        }
Example #4
0
        protected override void ThreadFunc()
        {
            // XmlHttpRequest setup

            this.NativeId = XHR_Create(HTTPRequest.MethodNames[(byte)CurrentRequest.MethodType],
                                       CurrentRequest.CurrentUri.OriginalString,
                                       CurrentRequest.Credentials != null ? CurrentRequest.Credentials.UserName : null,
                                       CurrentRequest.Credentials != null ? CurrentRequest.Credentials.Password : null,
                                       CurrentRequest.WithCredentials ? 1 : 0);
            Connections.Add(NativeId, this);

            CurrentRequest.EnumerateHeaders((header, values) =>
            {
                if (!header.Equals("Content-Length"))
                {
                    for (int i = 0; i < values.Count; ++i)
                    {
                        XHR_SetRequestHeader(NativeId, header, values[i]);
                    }
                }
            }, /*callBeforeSendCallback:*/ true);

            XHR_SetResponseHandler(NativeId, WebGLConnection.OnResponse, WebGLConnection.OnError, WebGLConnection.OnTimeout, WebGLConnection.OnAborted);
            // Setting OnUploadProgress result in an addEventListener("progress", ...) call making the request non-simple.
            // https://forum.unity.com/threads/best-http-released.200006/page-49#post-3696220
            XHR_SetProgressHandler(NativeId,
                                   CurrentRequest.OnDownloadProgress == null ? (OnWebGLProgressDelegate)null : WebGLConnection.OnDownloadProgress,
                                   CurrentRequest.OnUploadProgress == null ? (OnWebGLProgressDelegate)null : WebGLConnection.OnUploadProgress);

            XHR_SetTimeout(NativeId, (uint)(CurrentRequest.ConnectTimeout.TotalMilliseconds + CurrentRequest.Timeout.TotalMilliseconds));

            byte[] body              = CurrentRequest.GetEntityBody();
            int    length            = 0;
            bool   releaseBodyBuffer = false;

            if (body == null)
            {
                var upStreamInfo = CurrentRequest.GetUpStream();
                if (upStreamInfo.Stream != null)
                {
                    var internalBuffer = BufferPool.Get(upStreamInfo.Length > 0 ? upStreamInfo.Length : HTTPRequest.UploadChunkSize, true);
                    using (BufferPoolMemoryStream ms = new BufferPoolMemoryStream(internalBuffer, 0, internalBuffer.Length, true, true, false, true))
                    {
                        var buffer    = BufferPool.Get(HTTPRequest.UploadChunkSize, true);
                        int readCount = -1;
                        while ((readCount = upStreamInfo.Stream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            ms.Write(buffer, 0, readCount);
                        }

                        BufferPool.Release(buffer);

                        length = (int)ms.Position;
                        body   = ms.GetBuffer();

                        releaseBodyBuffer = true;
                    }
                }
            }
            else
            {
                length = body.Length;

                XHR_Send(NativeId, body, length);
            }

            XHR_Send(NativeId, body, length);

            if (releaseBodyBuffer)
            {
                BufferPool.Release(body);
            }
        }