-
-
Notifications
You must be signed in to change notification settings - Fork 144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Expose DuplexStreamInterface for connections that requires Upgrades #382
Conversation
Still working on some failing tests, you can help with review while I fix the tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bosunski Thanks for looking into this, would love to get this feature in!
It looks like this is still a work in progress, but I like how this doesn't seem to affect a lot of code at the moment. I've added some minor remarks below.
Let us know if there's anything we can help with. Keep it up! 💪
src/Browser.php
Outdated
@@ -720,7 +721,7 @@ private function withOptions(array $options) | |||
* @param string $url | |||
* @param array $headers | |||
* @param string|ReadableStreamInterface $body | |||
* @return PromiseInterface<ResponseInterface,Exception> | |||
* @return PromiseInterface<ResponseInterface,Exception, ConnectionInterface> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what this is supposed to mean? The promise either fulfills with a ResponseInterface
or rejects with an Exception
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added it in the case, where an upgrade
did happen, the promise will resolve with a ConnectionInterface
rather than an Exception
or a ResponseInterface
. Still not sure if this is the best way to express that.
@@ -707,7 +708,7 @@ public function withResponseBuffer($maximumSize) | |||
* @see self::withFollowRedirects() | |||
* @see self::withRejectErrorResponse() | |||
*/ | |||
private function withOptions(array $options) | |||
public function withOptions(array $options) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I like exposing the withOptions()
method again. It has been deprecated with clue/reactphp-buzz#172 not too long ago.
Do we really need this method? It's my understanding we could just rely on the Connection: upgrade
and/or Upgrade: …
request header(s) being present?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@clue I didn't know the method was deprecated.
But my thought behind it was to let the consumer using Browser
to be able to choose to watch out for the upgrade event or not. As by default, Browser
will not. This means that, if an upgrade did happen, I need to configure browser to capture it like $browser->withOptions(['upgrade' => true])
(default is false
in Transaction.php
).
This, however, won't be necessary if the default mode should be to capture the upgrade. That was the reason I made the method public (also seeing it was public in clue/reactphp-buzz
)
src/Client/Request.php
Outdated
if ($this->responseIsAnUpgradeResponse($psrResponse)) { | ||
$this->stream->removeListener('data', array($this, 'handleData')); | ||
|
||
$this->emit('upgrade', array($this->stream, $psrResponse, $this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I like exposing the React\Http\Client\Request
which is currently marked as @internal
only. There's ongoing effort to remove all classes from this namespace as there's some duplication with the Io
namespace.
Do we really need to expose the response and request objects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is the right Request
object to expose, but it's kind of a good idea to expose a
request object because there could be cases where you want to verify some things about the request after upgrade and then close the connection if that verification fails. That's my thought about it. But sure, I might be wrong here.
I actually read a piece of Node docs to see how upgrade
is being handled by the consumer side. I followed that in adding the upgrade event also. Here is a sample piece of how node handles the upgrade.
Sure, If there's a better you think this could be or should be done, I'll be willing to make it work.
src/Client/Request.php
Outdated
if ($this->responseIsAnUpgradeResponse($psrResponse)) { | ||
$this->stream->removeListener('data', array($this, 'handleData')); | ||
|
||
$this->emit('upgrade', array($this->stream, $psrResponse, $this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about the whole upgrade
event. How does this interfere with the existing request/response semantics?
I like how this is exposed on the server side via https://github.com/reactphp/http#streaming-outgoing-response, perhaps we can find a similar solution here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually followed a Node piece I read here as I also mentioned above.
I will check out the sample you sent, I kind of like it, knowing that a ThroughStream
also implements DuplexStreamInterface
.
Hello @clue , are there anything that can be done to move this PR forward since this is still a desired feature? |
{ | ||
return $this->request; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: missing EOL
use Psr\Http\Message\ResponseInterface; | ||
use React\Stream\DuplexStreamInterface; | ||
|
||
class UpgradedResponse |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds a new class as part of our public API in the internal React\Http\Client
namespace.
@@ -770,7 +771,7 @@ private function withOptions(array $options) | |||
* @param string $url | |||
* @param array $headers | |||
* @param string|ReadableStreamInterface $body | |||
* @return PromiseInterface<ResponseInterface,\Exception> | |||
* @return PromiseInterface<ResponseInterface,\Exception,ConnectionInterface> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this supposed to do? The PromiseInterface
does not currently nor the foreseeable future have a third template type.
@bosunski Thank you for the friendly reminder! I agree this feature would be nice to have, but in all honestly I don't see the currently suggested API as a viable alternative. We're moving towards a more type-safe future (see also https://github.com/orgs/reactphp/discussions/472) and this PR currently seems to add APIs that can not be covered by the existing type system. I think we're on the same page that the underlying feature request is still interesting, but I wonder what a decent API could look like? |
Thanks for your response @clue This PR is already 2 years old 😅 and I also agree that this PR needs a reimplementation to be consistent with the changes that have happened within ReactPHP itself and also to align with the current goals. I'm still very interested in getting this out of the way. Maybe we can start by talking about how the upgrade API will be used from a developers' point of view. |
Based on the Idea of #376 by @clue
Emits an
upgrade
event when:Connection
header is present in the response headers and values hasupgrade
101
Event emitted exposes a
React\Stream\DuplexStreamInterface
, aPsr\Http\Message\ResponseInterface
andReact\Http\Client\Request
.Summary of changes
data
event parser.React\Http\Io\Sender
andReact\Http\Io\Transaction
to be configurable for Upgrade connectionsExample Usage:
Websocket Client Usage:
Example Usage in
clue/reactphp-docker