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

Fix silencing errors in extension handling. #12586

Merged
merged 9 commits into from
Oct 3, 2018
Merged

Fix silencing errors in extension handling. #12586

merged 9 commits into from
Oct 3, 2018

Conversation

dereuromark
Copy link
Member

@dereuromark dereuromark commented Sep 23, 2018

This fixes most of the issues from #12585

It does however not fix the silencing of non configured extensions.
So if you didnt whitelist ical and then just accessed /controller/action.ical it would just silently serve the content of /controller/action without any indication of problems. One would except a 404 here, though.
The reason for this last issue seems to be that in such a case routing just ignores the ext, and sets it to null for going into the controller. That's bad.

@dereuromark dereuromark added this to the 3.6.12 milestone Sep 23, 2018
*
* This is needed for RequestHandlerComponent and recognition of types.
*
* @param string $type
Copy link

Choose a reason for hiding this comment

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

Missing parameter comment

* This is needed for RequestHandlerComponent and recognition of types.
*
* @param string $type
* @param string|array $definition
Copy link

Choose a reason for hiding this comment

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

Missing parameter comment

* @param string|array $definition Definition of the content type.
* @return void
*/
public function setType($type, $definition)
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need setType() when there is withType() ?

Copy link
Member Author

Choose a reason for hiding this comment

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

See the ticket referenced. The type() deprecation lost a way to set new type definitions.
As such RequestHandler would just not be able to serve those anymore without calling

$this->response->setType('ical', 'my/ical');

etc
This has nothing to do with withType() response modification. This is only for setting/adding/replacing a (new) type into the map.

Copy link
Member

@ADmad ADmad Sep 23, 2018

Choose a reason for hiding this comment

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

Okay, in that case addType() would be a more intuitive name since it's adds/maps a new type, doesn't actually set type for the response.

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought about it. add() kind of implies that you could also add a type into existing mappings.
But that is not the case, this replaces existing ones completely. As such I thought set() would be more intuitive and correct.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think the fact that addType() can overwrite existing mapping will cause confusion to anymore. Someone requiring to overwrite existing type mapping is unlikely anyway. mapType() would have been the ideal name but method with that name already exists for another purpose 🙁.

Copy link
Member

Choose a reason for hiding this comment

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

How about the name setTypeMap() ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Or setTypeMapDefinition().
Modified, also added tests for it.

* This is needed for RequestHandlerComponent and recognition of types.
*
* @param string $type Content type.
* @param string|array $definition Definition of the content type.
Copy link
Member

@ADmad ADmad Sep 23, 2018

Choose a reason for hiding this comment

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

$mimeType/$contentType would be a more intuitive variable name.

@markstory
Copy link
Member

markstory commented Sep 24, 2018

Always raising a 404 on unknown 'extensions' also means that an application can never have . in the last path segment as any value after the . would be treated as an extension. I suspect this is the reason for the current behavior.

@dereuromark
Copy link
Member Author

dereuromark commented Sep 24, 2018

OK. Then this current change is still valid, as I didnt touch the non-configured ones.

But IMO there shouldn't be any such . in there, as it changes the meaning - and currently that is ignored so it is not consistent.
For me, accessing /action.foo and /action should trigger two different things unless explictly configured otherwise, right now it is the same.

@ADmad
Copy link
Member

ADmad commented Sep 24, 2018

Tests should be added to give a better idea of how exactly this change impacts the handing of URLs with dot in last segment.

@dereuromark
Copy link
Member Author

The existing tests also fail, how would we adjust those here?
I think they operate on a false assumption, as reality makes those behave differently.

@@ -594,6 +598,9 @@ public function renderAs(Controller $controller, $type, array $options = [])
$viewClass = null;
if ($builder->getClassName() === null) {
$viewClass = App::className($view, 'View', 'View');
if ($viewClass === false) {
throw new RuntimeException('Configured view class can not be found: ' . $view);
}
Copy link
Member

Choose a reason for hiding this comment

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

Won't this break use cases where extensions are using sub-directories to render their views and not a custom view class?

Copy link
Member

Choose a reason for hiding this comment

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

The tests failures will go away if this particular change is reverted.

Copy link
Member Author

Choose a reason for hiding this comment

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

Should we only fail if you actually configure to a specific className? I feel like silently doing nothing here prevents this developer mistake from being visible.

Copy link
Member

@ADmad ADmad Sep 25, 2018

Choose a reason for hiding this comment

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

Not sure how will you decide if it's a dev error. As @markstory pointed out one can just use View class to render content of any string type by relying on templates in sub directories and just set appropriate content/mime type for response instance.

@markstory
Copy link
Member

For me, accessing /action.foo and /action should trigger two different things unless explictly configured otherwise, right now it is the same.

Is your statement for when an extension is enabled in routing? If foo is not a declared extension then I would expect .foo to be part of the last parameter. If that last parameter is the action then an error should be raised. That is how a clean application skeleton is working for me locally, and I don't think that should change.

@ADmad
Copy link
Member

ADmad commented Sep 25, 2018

If foo is not a declared extension then I would expect .foo to be part of the last parameter. If that last parameter is the action then an error should be raised.

RequestHandler::$ext would be set to foo only if extension parsing has been enabled for foo isn't it? So I don't think the check and exception throwing added at line 336-338 should cause any problem.

@dereuromark
Copy link
Member Author

Is it adjusted as per your feedback now?

@ADmad
Copy link
Member

ADmad commented Sep 27, 2018

Shouldn't new test be added for change in RequestHandler?

@dereuromark
Copy link
Member Author

Added a test case and also removed the deprecated initialize() call (as it didnt do anything anyway).

public function testUnrecognizedExtensionFailure()
{
Router::extensions(['json', 'foo'], false);
$this->Controller->request = $this->Controller->request->withParam('_ext', 'foo');
Copy link
Member

Choose a reason for hiding this comment

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

Using setRequest()/getRequest() instead of the $request property will save use effort when doing 3.next/4.x merges :)

Copy link
Member Author

Choose a reason for hiding this comment

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

You want me to touch the code again? :)

@dereuromark
Copy link
Member Author

All done.

* @param string|array $mimeType Definition of the mime type.
* @return void
*/
public function setTypeMap($type, $mimeType)
Copy link
Member

Choose a reason for hiding this comment

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

Is this method setting a map or just a single type? The name and implementation don't appear to match.

Copy link
Member Author

Choose a reason for hiding this comment

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

A single one. Is setTypeToMap() or setTypeIntoMap() better?
I didnt want to addType() as it would also overwrite the existing one.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm. On second thought I don't have a better name that isn't long.

Copy link
Member

Choose a reason for hiding this comment

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

setTypeMap() suggests that it sets whole map of types, not single type.

Main keyword here is map. After look at dictionary, I think it could be mapType(), associateType(), linkType().

Copy link
Member Author

Choose a reason for hiding this comment

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

I like mapType() but that one is already taken! To not confuse people I would stick to the method.

Copy link
Member

Choose a reason for hiding this comment

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

I also don't think setTypeMap() for setting one tpye within the map is a good name.
The best would really be mapType(), unfortunately.

How about setTypeMapped() or setMappedType()?
Or updateType()?

associateType(), isn't bad either.

@markstory markstory self-assigned this Sep 30, 2018
/**
* Sets a content type definition into the map.
*
* E.g.: setType('xhtml' => ['application/xhtml+xml', 'application/xhtml'])
Copy link
Member

Choose a reason for hiding this comment

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

Invalid syntax. Uses wrong function name.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@dereuromark
Copy link
Member Author

Are you ok with this as is? Or do I have to use a different method name like updateTypeMap() etc?

@markstory markstory modified the milestones: 3.6.12, 3.6.13 Oct 2, 2018
@ravage84 ravage84 added the http label Oct 2, 2018
@ravage84
Copy link
Member

ravage84 commented Oct 3, 2018

Out of curiosity I setup a vanilla CakePHP and and tested some cases.

CakePHP Core 3.6.12
CakePHP App 3.6.5

Implemented TestController with event() and anotherIcal() , added the templates /Test/event.ctp & /Test/another_ical.ctp and added a route for /test/another.ical to TestController::anotherIcal() but used the fallback routes for everyhting else.

URL RequestHandler active Extensions active Expected Result
/test/event Yes - TestController::event() As expected
/test/event.ical Yes - Not sure The action event.ical is not defined in TestController *2
/test/another.ical Yes - TestController::anotherIcal() As expected
/test/event Yes ical TestController::event() As expected
/test/event.ical Yes ical TestController::event() As expected
/test/another.ical Yes ical TestController::anotherIcal() The action another is not defined in TestController *1
/test/event No - TestController::event() As expected
/test/event.ical No - Not sure The action event.ical is not defined in TestController *2
/test/another.ical No - TestController::anotherIcal() As expected
/test/event No ical TestController::event() As expected
/test/event.ical No ical TestController::event() As expected
/test/another.ical No ical TestController::anotherIcal() The action another is not defined in TestController *1
  1. Routing should have precedence over extension handling
  2. Action names with a dot, like event.ical, are invalid anyway, we could raise a not found exception with a more helpful errro message like The extension ".ical" is not handled nor is there a route configured..

@dereuromark dereuromark merged commit c7c772b into master Oct 3, 2018
@dereuromark
Copy link
Member Author

Can u open that up as a separate ticket. This should not get pickybacked on this one.

@ravage84
Copy link
Member

ravage84 commented Oct 3, 2018

@dereuromark thought it was relevant. Anyway, done so in #12624

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

Successfully merging this pull request may close these issues.

None yet

5 participants