-
-
Notifications
You must be signed in to change notification settings - Fork 88
Comments
RequestFactory: invalid byte sequences in $_SERVER['REQUEST_URI']#30
RequestFactory: invalid byte sequences in $_SERVER['REQUEST_URI'] #30JanTvrdik wants to merge 1 commit intonette:master from
Conversation
fprochazka
commented
Dec 16, 2014
Exception is fine with me. But the main problem here is how to present the error to the user. If I visit such URL I definitely shouldn't see error page generated by Tracy. It should fall to error presenter - I don't wanna have log full of bluescreens - I want to have one string line in access.log. But that is a problem, because you can access the request in bootstrap, custom scripts or just simply somewhere else, before the app hits Application::processRequest().
One thing that crosses my mind is to return instance of Http\InvalidRequest that would provide backwards compatibility, the control characters could be removed from the url in this request and when such Http\InvalidRequest hits Application, it could directly forward to ErrorPresenter, without even hiting the router.
JanTvrdik
commented
Dec 16, 2014
you can access the request in bootstrap, custom scripts or just simply somewhere else
Then it should be your job to handle it. But it is obviously a nasty BC break.
return instance of Http\InvalidRequest that would provide backwards compatibility
👎 That does not feel right.
Another think to consider – if you choose to change the API and throw exception, we may also reconsider how we handle control characters (note: that is something different than invalid encoding which is what the exception is primary for). Currently we ignore keys with control characters and discard values with control characters.
fprochazka
commented
Dec 16, 2014
The whole problem is that the usage of Http\Request in Nette\Application (the api) is really bad.
The api should look like this
$app = $container->getByType(Nette\Application\Application::class); $app->handle($container->getByType(Nette\Http\RequestFactory::class)->create());
Then, you have perfect control of when the request is created and what you do with it.
$app = $container->getByType(Nette\Application\Application::class); try { $app->handle($container->getByType(Nette\Http\RequestFactory::class)->create()); } catch (Nette\Http\InvalidRequestException $e) { $app->handleInvalidRequest($e); // this could for example open the error presenter }
In the end, the entire thing could be hidden in $app->run()
public function run() { try { $this->handle($this->httpFactory->create()); } catch (Nette\Http\InvalidRequestException $e) { $this->handleInvalidRequest($e); // this could for example open the error presenter } }
At this point, you're gaining the possibility to handle the exception created by invalid request in appropriate level of abstraction. It should either be rejected by the http server (nginx), or it should be handled by the front controller as bad request and error presenter should be shown.
Having Http\Request as a service is bad IMHO.
The service Http\Request should be replaced with Http\RequestStack, with BC compatible methods and it should always return the values of the last Http\Request. But the point is, you get a "singleton" container, just like the User is for Identity and the Http\Request can be changed how you please. This would also provide possibility of better handling requests in applications that are controlled by react.php
Symfony has much better front controller API ... We should investigate how Symfony and other frameworks handle this situation and then decide what is best for us.
JanTvrdik
commented
Dec 16, 2014
So Http\RequestStack would be (for most practical purposes) something like Nullable<Http\Request> in C#?
fprochazka
commented
Dec 16, 2014
I was thinking something like
class RequestStack implements IRequest { private $requests = []; private $lastRequest; public function append(IRequestFactory $factory) { $this->lastRequest = $factory->create(); $this->requests[] = $this->lastRequest; return $this->lastRequest; } public function getUrl() { return $this->lastRequest->getUrl(); } // ... }
How will the stack be handled is a detail, it could even be container only for the lastRequest. The point is, that we must have a container for the Request object, it should have never been a service (just like Identity is also not a service).
$_SERVER['REQUEST_URI'] (追記ここまで)
$_SERVER['REQUEST_URI'] (削除ここまで)acdaec6 to
563c85a
Compare
hrach
commented
Dec 17, 2014
+1 Only thing which bothers me is not logging this exception. Don't worry about making the bc break. Can't imagine how somehow would create theses malformed urls intentionaly.
JanTvrdik
commented
Dec 17, 2014
Related – I would also like to change how we handle invalid Host header. Currently it is simply ignored.
JanTvrdik
commented
Dec 19, 2014
@fprochazka I look at the RequestStack in Symfony. It was introduced in Symfony 2.4 (a year ago). Explanation of the motivation for that decision is available in a blogpost and documentation.
It seems to be the best solution to the problem. Thoughts? cc @dg
Note: Nette needs the RequestStack much less than Symfony because we already have Application\Request with Application\Request's stack managed by Application\Application.
Maybe – just maybe – we may 1) remove Http\Request from DIC 2) Move Application\Application::$requests to Application\RequestStack and put that in the DIC. 3) Add optional $httpRequest parameter to Application\Request. Is that stupid? I think that (1) and (2) are fine, not really sure about (3) though.
fprochazka
commented
Dec 20, 2014
@JanTvrdik well, beeing the one suggesting it, I hardly can disagree :)
dg
commented
Dec 20, 2014
RequestStack is not suitable for Nette, because it solves a different problem (as @JanTvrdik said, Nette has stack of Application\Request). When you need to create Http\Request inside Application, Nette has native solution: interface & generated factory.
fprochazka
commented
Dec 20, 2014
@dg I'm not sure you understand the problem. The problem is that Http\Request is a value object not a service. Having it as a service is wrong and it's causing problems. That's why new service that will contain the Request object should be introduced. Where is it created and how it's handled is a detail.
dg
commented
Dec 20, 2014
@fprochazka I understand.
(btw, value object can be service.)
fprochazka
commented
Dec 20, 2014
Well obviousely, but that doesn't make it right.
Identity is not a service.
dg
commented
Dec 20, 2014
"Identity is not a service → value object as a service is wrong" is fallacy of the converse.
fprochazka
commented
Dec 21, 2014
The Http\Request is an immutable value object (FYI it should probably clone the url it returns from getUrl, it might be changed otherwise). We have now here two ways to solve this problem
- The DIC in Nette handles scopes and can drop instances of services, that can change when next request occurs. This also means it knows the entire graph of services that has to be dropped when the
Http\Requestservice is changed - this concept is known to all advanced DIC containers in all advanced languages, like C# and If I'm not mistaken, even Symfony has now scopes in DIC - You introduce container object for the value object, that will then be service instead - which is obviousely much simpler solution
Because Nette has no scopes in DIC, having value objects as a service is wrong. Simple as that.
dg
commented
Dec 21, 2014
@fprochazka in czech. Prove that for each value object is wrong to have it as a service.
fprochazka
commented
Dec 21, 2014
I don't want to say "there isn't a case where having value object as a service is a good thing", but I can't think of any.
I also don't have to prove it (at least not about Http\Request), because the symfony community has already proven that having request VO as a service was a bad decision by practise and therefore I'm applying that findings on Nette. It's exactly the same case. You prove them (and every other high level framework in other languages that came to the same conclusion) wrong :)
In other case (there are two, the specific one with Http\Request and my general statement about VO as service beeing wrong, which is my opinion not a fact), you prove me wrong by counter example (or other weapon of your choise) - where is it a good thing to have a VO directly as a service in DIC? I've never stated "for every" but if you wanna generalize, let me correct myself:
Because Nette has no scopes mechanizm in DIC, having MOST value objects as services is wrong.
Also about the Identity example, that you like to repeat instead of continuing in the discussion, I haven't stated that there is an implication, it was just a very good example, because it's the most obviouse one.
hrach
commented
Dec 21, 2014
Basically, DIC should almost always hold only services, not the entities, crates, etc. This is the general rule, which also aplies here.
JanTvrdik
commented
Dec 21, 2014
Regarding Http\RequestStack – could anyone describe a real-world use-case where it would hold more than a single Http\Request instance? I was originally thinking about React.PHP but even there it makes no sense, because you would actually need a queue not a stack.
fprochazka
commented
Dec 21, 2014
Stack makes sense with subrequests in symfony. They don't have snippets and controls. They have subrequests (recursive calling of controllers and their output is then composed into a single template), which can be converted to proxy-level subrequests, which makes caching them a reall bliss.
I don't think that makes much sense with Nette, implementing this would be probably hard. Stack might make sense when you have forwarded requests. But I'm not sure.
dg
commented
Dec 21, 2014
@fprochazka You said that the value object cannot be a service, which is a strong & general statement, but you cannot prove it (the fact that some value objects cannot be used as services is really not an evidence, see fallacy of the converse).
I would prefer to end debate about VO.
fprochazka
commented
Dec 21, 2014
Would you preffer to end it at this issue (because it's a little bit off topic), or end it all together? Because I don't think the debate is over, because you haven't conviced me and neither have I convinced you.
dg
commented
Dec 21, 2014
Here. It is off topic.
55488bd to
2042d2e
Compare
4960652 to
5e67add
Compare
689f4ae to
33aae19
Compare
09923de to
02ae846
Compare
dc02250 to
80e8e2b
Compare
The goal is to not simply ignore invalid byte sequences in URL, but throw Exception or return NULL. Current Nette behavior is unwanted, e.g. these URLs should not be accepted at all:
How would you prefer to handle it? Exception or by returning NULL. Both break the RequestFactory API. I would prefer the exception, e. g.
Nette\Http\InvalidRequestException. Do you like the name?cc @fprochazka