Skip to content

Writing to an invalid stream creates an infinite loop of errors #2

Description

@onnoschutze

In the context of React v0.4.1 in the Ratchet Websocket library (0.3.1) I encountered the following situation. When the buffer code somehow tries to write to an invalid stream (Buffer.php L83) it will emit the error "tried to write to invalid stream".

Buffer.php

public function handleWrite()
{
   if (!is_resource($this->stream)) {
       $this->emit('error', array(new \RuntimeException('Tried to write to invalid stream.'), $this));
       return;
   }
   ...

The stream will receive this error & emits the error also. Somehow this triggers writing to the buffer again, which then emits the "tried to write to invalid stream" error again, thus creating an infinite loop.

Stream.php

public function __construct($stream, LoopInterface $loop)
{
   ...

   $this->buffer->on('error', function ($error) {
        $this->emit('error', array($error, $this));
        $this->close();
   });

   ...
}

Removing the " $this->emit('error', array($error, $this));" from the Stream __construct fixes the infinite loop, but it seems to be that not the real problem is fixed in this way. I have constructed a piece of code that enables me to reproduce this problem (via websockets). I will try to create a unit test that fails for this piece of code. But I hope this report helps as a start.

Activity

  1. added this to the v0.4.2 milestone on Jun 4, 2014
  2. cboden commented on Jun 8, 2014

    @cboden
    Member

    Part of this issue was on a write error Ratchet tried to send another message (lol) as pointed out in ratchetphp/Ratchet#137. That has been resolved in ratchetphp/Ratchet#200.

    I still have a concern that if a write error occurs (attempting to write to a closed stream) and the developer does not take the appropriate action the resources will not be cleaned up...If !is_resource() or feof() are detected that will not change, I wonder if in addition to the error emission React should clean up the resources at that point.

  3. steverhoades commented on Sep 3, 2014

    @steverhoades
    Contributor

    Would it make sense in this case for the stream to keep track of it's closed state? For instance:

    class Stream extends EventEmitter implements ReadableStreamInterface, WritableStreamInterface
    {
        public $bufferSize = 4096;
        public $stream;
        protected $readable = true;
        protected $writable = true;
        protected $closing = false;
        protected $loop;
        protected $buffer;
        protected $closed = false; //<--- NEW
    
        public function __construct($stream, LoopInterface $loop)
        {
            $this->buffer->on('error', function ($error) {
                if($this->closed) {
                    return;
                }
                $this->emit('error', array($error, $this));
                $this->close();
            });
        }
    
        public function handleClose()
        {
            if (is_resource($this->stream)) {
                fclose($this->stream);
            }
            $this->closed = true;
        }
    }
  4. removed this from the v0.4.2 milestone on Nov 11, 2014
  5. bohdanly commented on Jul 1, 2015

    @bohdanly
    Contributor

    Hi, take a look on #25. Same issue.

  6. modified the milestones: v0.4.3, v0.4.4 on Oct 1, 2015
  7. clue commented on Aug 14, 2016

    @clue
    Member

    We believe this has been fixed as part of #25 and #40 👍 A release will be tagged soon, please report back if this problem persists!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions