Skip to content
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

Add PSR-7 request adapter (#266) #270

Closed
wants to merge 3 commits into from
Closed

Add PSR-7 request adapter (#266) #270

wants to merge 3 commits into from

Conversation

remorhaz
Copy link
Contributor

This PR shows partial job, just to sync expectations. It only implements converting requests between Amp and PSR-7 and vice versa.

composer.json Outdated
@@ -34,7 +34,8 @@
"amphp/socket": "^1",
"amphp/sync": "^1.3",
"league/uri": "^6",
"psr/http-message": "^1"
"psr/http-message": "^1",
"laminas/laminas-diactoros": "^2.3"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this back to require-dev, it should remain an optional feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that's impossible because adapter uses Request object from Laminas library. I've tried to find League implementation but they use Laminas in their examples; so Laminas is not optional anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can avoid requiring concrete PSR-7 implementation, though, if we accept RequestInterface in corresponding method (and then call with* methods). But the problem is body - it's mutable in PSR-7.

Copy link
Contributor

@nicolas-grekas nicolas-grekas May 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the added classes are mandatory to use the http-client. If one doesn't use any of them (I don't), one shouldn't be forced to install a dead-code dependency. Moving it back to require-dev fixes the issue.

About diactoros, could it be possible to be compatible with nyholm/psr7? That's the lib we should recommend IMHO (I don't know about the policy of the amp project here, I'm just talking for Symfony-related apps.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, I'll try one idea...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got rid of requiring Laminas with injecting PSR-7 RequestFactoryInterface. Maybe it would be more convenient to inject it in constructor - but from* methods will never use it, so in this case we'll have to split adapter into independent from* and to* parts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, most use cases will require using either "fromPsrRequest/toPsrResponse" or "toPsrRequest/fromPsrResponse" pair of methods. One of them will require RequestFactoryInterface, so I'll better inject it in constructor.

@@ -34,7 +34,8 @@
"amphp/socket": "^1",
"amphp/sync": "^1.3",
"league/uri": "^6",
"psr/http-message": "^1"
"psr/http-message": "^1",
"psr/http-factory": "^1"
Copy link
Contributor

@nicolas-grekas nicolas-grekas May 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now you can also remove this one as it's not a dependency at all!
(it's only a dependency of the external part that instantiates the factory)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a dependency as much as psr/http-message is a dependency for UriInterface.

Copy link
Contributor

@nicolas-grekas nicolas-grekas May 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look at the code: UriInterface is part of the public signature of Request::getUri().

RequestFactoryInterface on the contrary is only part of the constructor of PsrAdapter. This means you can load the class definition of PsrAdapter without any issue. (And you can't for Request).

The thing that instantiates PsrAdapter must have psr/http-factory as a dependency to use the class, but the class and the package themself do not dependent on psr/http-factory.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that psr/http-factory is much lesser evil than Laminas - it's just interfaces. In case it's unacceptable - the only escape is to extract adapters to separate library.

Copy link
Contributor

@nicolas-grekas nicolas-grekas May 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it is not dependency of this package... This should be moved to require-dev as that's the place where it belongs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's say we remove this line. What did we lose in the process? Did we break anything? What does this line provide in the first place?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@remorhaz: "conflict" is very much an option, and probably the way to go.

As most users won't use the PSR converter, I'm leaning towards a separate package, as breaking changes seem more likely than for the rest of the code in this package.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's say we remove this line. What did we lose in the process? Did we break anything?

Of course. If someone uses factory v1 in his code and then upgrades to factory v2 that breaks BC (by renaming factory method, for example), he gets his service broken without any chance of being preserved from it by Composer. That's how the dependency works; just ignoring the dependency doesn't remove it.

"Conflicting" with factory v2+ is pretty the same as requiring v1 - it will not allow to install factory v2 with our library, even if user doesn't use PSR adapters. It's just using wrong mechanism instead of right one, I think; it solves no problems. I hope @kelunik will decide to create a separate package - that's the best way to keep original client's dependencies untouched.

Copy link
Contributor

@nicolas-grekas nicolas-grekas May 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Software design, package principles here, should prepare us for events that are possible. Here the v1/v2 example is unrealistic IMHO when talking about psr/http-factory. It's not a good enough reason to force it as a dependency to everyone "just in case". Even adding a "conflict" looks like useless precaution to me.

Anyway, we don't have to agree on this as a separate package might provide a path forward for both our povs.

Thanks for the talk.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas In the case of psr/http-factory the BC break is indeed unlikely, as PSRs don't currently evolve and probably never will, because releasing a v2 on the same package would be a huge pain.

"conflict" seems to be the right choice currently for we're compatible with this API, but don't want an installation dependency.

Indeed, a separate package solves the "problem" entirely. :)

@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use strict_types in any @amphp project, so we shouldn't use it here.


private function readFromStream(): string
{
$data = wait(timeout($this->getOpenStream()->read(), $this->timeout));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use wait, because it has issues with nesting, see amphp/amp#311 and amphp/amp#313. This means we need to buffer the entire body for these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that's really bad: the body can be very big in some cases. Is there any workaround that will allow as to read the body by parts? Maybe some non-promise generator?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only one would be writing huge responses to files, I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any chance of solving these issues in the future? In this case I can buffer the entire body for now, but keep the infrastructure to restore current behavior in future.

Is there any (relatively) simple way to reproduce the issues using my code? Maybe I'll think something out.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's very easy to reproduce, just call that code inside Loop::run() and it'll stop the outer Loop::run(), too.


final class PsrStreamBody implements RequestBody
{

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

@remorhaz
Copy link
Contributor Author

I've made some changes:

  • Added response adapters;
  • Adapter returns promises instead of "direct" objects;
  • Message bodies are buffered entirely before resolving the message promise (escaping wait() issues).
  • Minor codestyle fixes, tests added, etc.

@trowski
Copy link
Member

trowski commented May 12, 2020

I agree with @kelunik, this really should be a separate package.

See the PSR adaptor I've been working on for http-server: https://github.com/trowski/psr-http-bridge

@remorhaz
Copy link
Contributor Author

So, should I move my code to separate repository?

@kelunik
Copy link
Member

kelunik commented May 12, 2020

@remorhaz Do you want to maintain it yourself or should I create a new repository in the @amphp namespace? I'd be fine with both.

@remorhaz
Copy link
Contributor Author

I guess it's better to keep it in amphp space.

@remorhaz
Copy link
Contributor Author

@kelunik, so what about a new repository?

@kelunik
Copy link
Member

kelunik commented May 17, 2020

@remorhaz I've just created https://github.com/amphp/http-client-psr7, so you can PR against that. :-)

@enumag
Copy link
Contributor

enumag commented May 25, 2020

ping @remorhaz

@remorhaz
Copy link
Contributor Author

Yep, @enumag, I remember; just havin' some hot time on main job. I'll make a PR in a day or two.

kelunik added a commit to amphp/http-client-psr7 that referenced this pull request Jun 16, 2020
Co-authored-by: Niklas Keller <me@kelunik.com>
Co-authored-by: Jáchym Toušek <enumag@gmail.com>
@kelunik
Copy link
Member

kelunik commented Jun 16, 2020

Closing, because of amphp/http-client-psr7@487af25.

@kelunik kelunik closed this Jun 16, 2020
trowski pushed a commit to amphp/http-client-guzzle-adapter that referenced this pull request Aug 31, 2024
Co-authored-by: Niklas Keller <me@kelunik.com>
Co-authored-by: Jáchym Toušek <enumag@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants