-
Notifications
You must be signed in to change notification settings - Fork 53
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
Path prefix must be present #113
Conversation
Make sure the path prefix is actually present even if the request has been altered before.
Open for ideas how this can be proved by tests in this library. |
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 guess if you send the exact same request multiple times, a fix is needed. while that certainly is an edge case, i would expect the client to handle it correctly.
we started using this code rather than comparing the paths because you could happen to have a path that starts with the same characters as the prefix you want to add. in that case, the prefix still needs to be added once. with the change as is, this fix is reverted.
could we instead remove the identifier of the $first request when we get a response back? or would that bring back the original problem of adding the prefix repeatedly on retries?
@@ -48,7 +48,7 @@ public function handleRequest(RequestInterface $request, callable $next, callabl | |||
{ | |||
$identifier = spl_object_hash((object) $first); | |||
|
|||
if (!array_key_exists($identifier, $this->alteredRequests)) { | |||
if (!array_key_exists($identifier, $this->alteredRequests) || 0 !== strpos($request->getUri()->getPath(), $this->uri->getPath())) { |
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.
should we assign $request->getUri()->getPath() and $this->uri->getPath() to variables to not execute the code twice, in the expected case of having to add the prefix?
I agree with @dbu, removing the request identifier when a response is resolved should be a better way of doing this (if it's work of course). |
Highlights: * Block installation of the latest vimeo/psalm. vimeo/psalm#1016 * Introducing new API and HTTP clients for testing. * Updated Management API tests to use the new test API- and HTTP clients. * Explode entity CRUD operation trait and interface to smaller components per operation. * Added missing organization property to Company entity. * Added more details about the actual behavior of the Company membership API. * Introducing new base class and interface for developer- and company entity. * Refactored basic and Management API tests. * Bump minimum version of php-http/client-common to >=1.8.1. 1.8.0 contains a regression bug: php-http/client-common#112 * Add new required patch to php-http/client-common. php-http/client-common#113 * Disable process timeout to prevent failing test runs caused by PHPUnit executed via a Composer command. * Removed useless PHP nightly builds from testing matrix. * Added test to getDeveloperByApp. * Introducing new base exception interface for our client. * Introducing API client specific RuntimeException class. * Throw more meaningful exceptions when an entity property has an incorrect value. * Be more type-strict by replacing array arguments on data object's setters with type strict variable-length arguments. * Fix implementation of getProperties(). (BC breaking change.)
We're running in to an issue which we think might be caused by #103, and should probably be mitigated by this. Really, the root of the problem is that From the manual:
One workaround would be to store a reference to the object, which of course would cause it not to be deallocated, but the downside is of course increased memory usage. |
One fix to the problem (^) might be to save the altered request uris instead and check: It could possibly create some kind of issue I haven't thought about. -- edit -- -- edit -- |
Due to PSR7 immutability and the fact that Client request does not have attributes (like the server one), the only viable solution here is to use the headers to store some id generated on the first run of this plugin. However it would send this information to the server. Maybe we can add a store header managed directly by the PluginClient (named X-HTTPlug-PluginMetadata), which is deleted when passed to the underlying client ? So plugins can safely use this header when in the plugin chain to store some metadata ? |
i like that header idea joel. does it mean the plugins potentially need to share that header? alternatively, we could define that the plugin client will scan the headers and remove any headers starting with |
Sounds good @joelwurtz, but do we really need an id for this? If that's too much work to check there's I think @dbu's solution will suffice but with -- edit -- |
yes it would be fine @nicholasruunu, certainly it does not need a id @dbu i think we can start with a single header, as a header can have multiple value it should be fine ATM. We can always change that later (i would make this internal first, external plugin should not use it : we dont provide bc contract) |
X-HTTPlug-PluginMetadata: addPath-altered=1
That would be a convention. Maybe that is good enough. Can plugins add such
values with psr-7 methods or does it need string parsing?
|
@dbu Yeah, you can do it with Not sure if I'd add -- edit -- |
@nicholasruunu tested this a bit with guzzle psr-7 and indeed that works fine. lets do this then! i'd go with just the marker without if a plugin ever needs to remove its flags, it can unset its value from the array and then do you want to start a new pull request for this? |
@dbu I'd because my library installs this PR as a patch and I would like test the described solution first in IRL to be sure that it actually solves this problem. |
Using a single (non-unique) flag will mean that there can't be multiple AddPathPlugins. I'm not sure when you'd ever have that, but it is technically possible, so I'd argue that either the flag name should be unique (e.g. addPath-altered-34dfd3, where the last part has been generated randomly in the plugin constructor), or that it should have a value unique to the plugin instance. |
oh, good point. the spl_object_hash should be good for this one, right? plugins won't be added and removed from the chain on the fly, so no spl hash collision risk i think. we could use |
@dbu I think that |
|
then i vote for |
mt_rand is not unique, so you can have the same problem as without it. |
Yeah, I agree that I think for the original issue (#103) of being able to resend an altered request I think this is the wrong way to go though. What you want is to resend the original request, not let all the plugins figure out if it's unaltered or not. |
good point to ask the fundamental sanity question... when does the plugin see the "same" request multiple times? why would that not start with the original request but with the one that has already been altered? |
@nicholasruunu i went looking up the code and the reason this happens like this is that the plugin method is passed $first which is the first plugin, for when they want to "restart" the request. the plugins only have the request at its current state, not the original unmodified request. so when they want to restart for whatever reason, all plugins are applied a second time to the request. so i think we do need the discussed solution. @mxr576 do you have time for a pull request to implement the solution with the static increment? |
@dbu Not sure I get it, but if you want to restart a request, shouldn't you create a $first plugin to handle that? Take this first request and reuse that for a retry? |
this is how the plugin chain is created: client-common/src/PluginClient.php Line 145 in e35c5f3
the plugin does not have access to the original request, and the "first" plugin is not hardwired with the unchanged request. therefore if a plugin restarts the chain, it only has access to the request as it currently looks. changing this would be quite a big change for how the plugin system works. |
just found that the redirect plugin is one place to restart a request: client-common/src/Plugin/RedirectPlugin.php Line 142 in 5496bf2
that would be an example where its very important to not add the prefix if we already handled the request, regardless of whether the prefix is in the path or not - once the server told us the url we don't want it to change anymore. |
@dbu Yeah I see what you mean... this package is breaking my brain. 😩 |
another thought: adding a path only makes sense for known hosts. we could change the add path plugin to be configured with a regex for the domains for which to add a prefix. the only place we ever call $first is in the RedirectPlugin. retry for example does not restart from the beginning, but from the current location. therefore i think instead of trying to track the chain, i'd make AddPath look at the domain and assume that no plugin restarts the same request with the same path. (if authentication needs a separate request, that should not be done with a plugin that restarts the request to do an authentication request, but by implementing Authentication and injecting a client to the auth implementation) |
I added an alternative solution here: #141 |
There are no good solution. Just solutions that redefine what edge cases we will support. In 2.x will have a simple approach. See #141 Im closing this as "wont fix" |
Make sure the path prefix is actually present even if the request has been altered before.
What's in this PR?
Follow up on #103.
Everything worked fine until I refactored my tests and started to use a singleton API client for offline testing. If I call the same API endpoints in the same test with the client then the first API request gets prefixed properly but the second one does not, because the generated
$identifier
is already exist in the$this->alteredRequests
.