Exemplo n.º 1
0
 private void TryToCleanup()
 {
     if (Interlocked.Increment(ref this.closedThreads) == 2)
     {
         ProtocolEventHelper.EnqueueProtocolEvent(new ProtocolEventInfo(this));
         (newFrameSignal as IDisposable).Dispose();
         newFrameSignal = null;
     }
 }
Exemplo n.º 2
0
        public static void AbortAll()
        {
            HTTPManager.Logger.Information("HTTPManager", "AbortAll called!");

            // This is an immediate shutdown request!

            RequestEventHelper.Clear();
            ConnectionEventHelper.Clear();
            PluginEventHelper.Clear();
            ProtocolEventHelper.Clear();

            HostManager.Shutdown();

            ProtocolEventHelper.CancelActiveProtocols();
        }
Exemplo n.º 3
0
        /// <summary>
        /// Update function that should be called regularly from a Unity event(Update, LateUpdate). Callbacks are dispatched from this function.
        /// </summary>
        public static void OnUpdate()
        {
            RequestEventHelper.ProcessQueue();
            ConnectionEventHelper.ProcessQueue();
            ProtocolEventHelper.ProcessQueue();
            PluginEventHelper.ProcessQueue();

            BestHTTP.Extensions.Timer.Process();

            if (heartbeats != null)
            {
                heartbeats.Update();
            }

            BufferPool.Maintain();
        }
Exemplo n.º 4
0
        private void CloseWithError(HTTPRequestStates state, string message)
        {
            if (!string.IsNullOrEmpty(message))
            {
                this.baseRequest.Exception = new Exception(message);
            }
            this.baseRequest.State = state;

            this.closed = true;

            HTTPManager.Heartbeats.Unsubscribe(this);
            HTTPUpdateDelegator.OnApplicationForegroundStateChanged -= OnApplicationForegroundStateChanged;

            CloseStream();
            ProtocolEventHelper.EnqueueProtocolEvent(new ProtocolEventInfo(this));
        }
Exemplo n.º 5
0
        private bool OnData(HTTPRequest request, HTTPResponse response, byte[] dataFragment, int dataFragmentLength)
        {
            if (this.State == States.Connecting)
            {
                string contentType = response.GetFirstHeaderValue("content-type");
                bool   IsUpgraded  = response.StatusCode == 200 &&
                                     !string.IsNullOrEmpty(contentType) &&
                                     contentType.ToLower().StartsWith("text/event-stream");

                if (IsUpgraded)
                {
                    ProtocolEventHelper.AddProtocol(this);

                    if (this.OnOpen != null)
                    {
                        try
                        {
                            this.OnOpen(this);
                        }
                        catch (Exception ex)
                        {
                            HTTPManager.Logger.Exception("EventSource", "OnOpen", ex, request.Context);
                        }
                    }

                    this.RetryCount = 0;
                    this.State      = States.Open;
                }
                else
                {
                    this.State = States.Closing;
                    request.Abort();
                }
            }

            if (this.State == States.Closing)
            {
                return(true);
            }

            if (FeedData(dataFragment, dataFragmentLength))
            {
                ProtocolEventHelper.EnqueueProtocolEvent(new ProtocolEventInfo(this));
            }

            return(true);
        }
Exemplo n.º 6
0
        private void ReceiveThreadFunc()
        {
            try
            {
                while (!closed)
                {
                    try
                    {
                        WebSocketFrameReader frame = new WebSocketFrameReader();
                        frame.Read(this.Stream);

                        if (HTTPManager.Logger.Level == Logger.Loglevels.All)
                        {
                            HTTPManager.Logger.Information("WebSocketResponse", "Frame received: " + frame.Type, this.Context);
                        }

                        lastMessage = DateTime.UtcNow;

                        // A server MUST NOT mask any frames that it sends to the client.  A client MUST close a connection if it detects a masked frame.
                        // In this case, it MAY use the status code 1002 (protocol error)
                        // (These rules might be relaxed in a future specification.)
                        if (frame.HasMask)
                        {
                            HTTPManager.Logger.Warning("WebSocketResponse", "Protocol Error: masked frame received from server!", this.Context);
                            Close(1002, "Protocol Error: masked frame received from server!");
                            continue;
                        }

                        if (!frame.IsFinal)
                        {
                            if (OnIncompleteFrame == null)
                            {
                                IncompleteFrames.Add(frame);
                            }
                            else
                            {
                                CompletedFrames.Enqueue(frame);
                            }
                            continue;
                        }

                        switch (frame.Type)
                        {
                        // For a complete documentation and rules on fragmentation see http://tools.ietf.org/html/rfc6455#section-5.4
                        // A fragmented Frame's last fragment's opcode is 0 (Continuation) and the FIN bit is set to 1.
                        case WebSocketFrameTypes.Continuation:
                            // Do an assemble pass only if OnFragment is not set. Otherwise put it in the CompletedFrames, we will handle it in the HandleEvent phase.
                            if (OnIncompleteFrame == null)
                            {
                                frame.Assemble(IncompleteFrames);

                                // Remove all incomplete frames
                                IncompleteFrames.Clear();

                                // Control frames themselves MUST NOT be fragmented. So, its a normal text or binary frame. Go, handle it as usual.
                                goto case WebSocketFrameTypes.Binary;
                            }
                            else
                            {
                                CompletedFrames.Enqueue(frame);
                                ProtocolEventHelper.EnqueueProtocolEvent(new ProtocolEventInfo(this));
                            }
                            break;

                        case WebSocketFrameTypes.Text:
                        case WebSocketFrameTypes.Binary:
                            frame.DecodeWithExtensions(WebSocket);
                            CompletedFrames.Enqueue(frame);
                            ProtocolEventHelper.EnqueueProtocolEvent(new ProtocolEventInfo(this));
                            break;

                        // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in response, unless it already received a Close frame.
                        case WebSocketFrameTypes.Ping:
                            if (!closeSent && !closed)
                            {
                                Send(new WebSocketFrame(this.WebSocket, WebSocketFrameTypes.Pong, frame.Data));
                            }
                            break;

                        case WebSocketFrameTypes.Pong:
                            waitingForPong = false;

                            try
                            {
                                // Get the ticks from the frame's payload
                                long ticksSent = BitConverter.ToInt64(frame.Data, 0);

                                // the difference between the current time and the time when the ping message is sent
                                TimeSpan diff = TimeSpan.FromTicks(lastMessage.Ticks - ticksSent);

                                // add it to the buffer
                                this.rtts.Add((int)diff.TotalMilliseconds);

                                // and calculate the new latency
                                this.Latency = CalculateLatency();
                            }
                            catch
                            {
                                // https://tools.ietf.org/html/rfc6455#section-5.5
                                // A Pong frame MAY be sent unsolicited.  This serves as a
                                // unidirectional heartbeat.  A response to an unsolicited Pong frame is
                                // not expected.
                            }

                            break;

                        // If an endpoint receives a Close frame and did not previously send a Close frame, the endpoint MUST send a Close frame in response.
                        case WebSocketFrameTypes.ConnectionClose:
                            HTTPManager.Logger.Information("WebSocketResponse", "ConnectionClose packet received!", this.Context);

                            CloseFrame = frame;
                            if (!closeSent)
                            {
                                Send(new WebSocketFrame(this.WebSocket, WebSocketFrameTypes.ConnectionClose, null));
                            }
                            closed = true;
                            break;
                        }
                    }
                    catch (Exception e)
                    {
                        if (HTTPUpdateDelegator.IsCreated)
                        {
                            this.baseRequest.Exception = e;
                            this.baseRequest.State     = HTTPRequestStates.Error;
                        }
                        else
                        {
                            this.baseRequest.State = HTTPRequestStates.Aborted;
                        }

                        closed = true;
                        newFrameSignal.Set();
                    }
                }

                HTTPManager.Logger.Information("WebSocketResponse", "Ending Read thread! closed: " + closed, this.Context);
            }
            finally
            {
                HTTPManager.Heartbeats.Unsubscribe(this);
                HTTPUpdateDelegator.OnApplicationForegroundStateChanged -= OnApplicationForegroundStateChanged;

                HTTPManager.Logger.Information("WebSocketResponse", "ReceiveThread - Closed!", this.Context);

                TryToCleanup();
            }
        }
Exemplo n.º 7
0
        void ParseLine(byte[] buffer, int count)
        {
            // If the line is empty (a blank line) => Dispatch the event
            if (count == 0)
            {
                if (CurrentMessage != null)
                {
                    CompletedMessages.Add(CurrentMessage);
                    ProtocolEventHelper.EnqueueProtocolEvent(new ProtocolEventInfo(this));
                    CurrentMessage = null;
                }

                return;
            }

            // If the line starts with a U+003A COLON character (:) => Ignore the line.
            if (buffer[0] == 0x3A)
            {
                return;
            }

            //If the line contains a U+003A COLON character (:)
            int colonIdx = -1;

            for (int i = 0; i < count && colonIdx == -1; ++i)
            {
                if (buffer[i] == 0x3A)
                {
                    colonIdx = i;
                }
            }

            string field;
            string value;

            if (colonIdx != -1)
            {
                // Collect the characters on the line before the first U+003A COLON character (:), and let field be that string.
                field = Encoding.UTF8.GetString(buffer, 0, colonIdx);

                //Collect the characters on the line after the first U+003A COLON character (:), and let value be that string. If value starts with a U+0020 SPACE character, remove it from value.
                if (colonIdx + 1 < count && buffer[colonIdx + 1] == 0x20)
                {
                    colonIdx++;
                }

                colonIdx++;

                // discarded because it is not followed by a blank line
                if (colonIdx >= count)
                {
                    return;
                }

                value = Encoding.UTF8.GetString(buffer, colonIdx, count - colonIdx);
            }
            else
            {
                // Otherwise, the string is not empty but does not contain a U+003A COLON character (:) =>
                //      Process the field using the whole line as the field name, and the empty string as the field value.
                field = Encoding.UTF8.GetString(buffer, 0, count);
                value = string.Empty;
            }

            if (CurrentMessage == null)
            {
                CurrentMessage = new BestHTTP.ServerSentEvents.Message();
            }

            switch (field)
            {
            // If the field name is "id" => Set the last event ID buffer to the field value.
            case "id":
                CurrentMessage.Id = value;
                break;

            // If the field name is "event" => Set the event type buffer to field value.
            case "event":
                CurrentMessage.Event = value;
                break;

            // If the field name is "data" => Append the field value to the data buffer, then append a single U+000A LINE FEED (LF) character to the data buffer.
            case "data":
                // Append a new line if we already have some data. This way we can skip step 3.) in the EventSource's OnMessageReceived.
                // We do only null check, because empty string can be valid payload
                if (CurrentMessage.Data != null)
                {
                    CurrentMessage.Data += Environment.NewLine;
                }

                CurrentMessage.Data += value;
                break;

            // If the field name is "retry" => If the field value consists of only ASCII digits, then interpret the field value as an integer in base ten,
            //  and set the event stream's reconnection time to that integer. Otherwise, ignore the field.
            case "retry":
                int result;
                if (int.TryParse(value, out result))
                {
                    CurrentMessage.Retry = TimeSpan.FromMilliseconds(result);
                }
                break;

            // Otherwise: The field is ignored.
            default:
                break;
            }
        }
Exemplo n.º 8
0
        private void SendThreadFunc()
        {
            try
            {
                using (WriteOnlyBufferedStream bufferedStream = new WriteOnlyBufferedStream(this.Stream, 16 * 1024))
                {
                    while (!closed && !closeSent)
                    {
                        //if (HTTPManager.Logger.Level <= Logger.Loglevels.All)
                        //    HTTPManager.Logger.Information("WebSocketResponse", "SendThread - Waiting...");
                        newFrameSignal.WaitOne();

                        try
                        {
                            //if (HTTPManager.Logger.Level <= Logger.Loglevels.All)
                            //    HTTPManager.Logger.Information("WebSocketResponse", "SendThread - Wait is over, about " + this.unsentFrames.Count.ToString() + " new frames!");

                            WebSocketFrame frame;
                            while (this.unsentFrames.TryDequeue(out frame))
                            {
                                if (!closeSent)
                                {
                                    using (var rawData = frame.Get())
                                        bufferedStream.Write(rawData.Data, 0, rawData.Length);

                                    BufferPool.Release(frame.Data);

                                    if (frame.Type == WebSocketFrameTypes.ConnectionClose)
                                    {
                                        closeSent = true;
                                    }
                                }

                                Interlocked.Add(ref this._bufferedAmount, -frame.DataLength);
                            }

                            bufferedStream.Flush();
                        }
                        catch (Exception ex)
                        {
                            if (HTTPUpdateDelegator.IsCreated)
                            {
                                this.baseRequest.Exception = ex;
                                this.baseRequest.State     = HTTPRequestStates.Error;
                            }
                            else
                            {
                                this.baseRequest.State = HTTPRequestStates.Aborted;
                            }

                            closed = true;
                        }
                    }
                }
            }
            finally
            {
                Interlocked.Exchange(ref sendThreadCreated, 0);

                HTTPManager.Logger.Information("WebSocketResponse", "SendThread - Closed!");

                if (Interlocked.Increment(ref this.closedThreads) == 2)
                {
                    ProtocolEventHelper.EnqueueProtocolEvent(new ProtocolEventInfo(this));
                }
            }
        }