Problem/Motivation

There is a reference to #2364011: [meta] External caches mix up response formats on URLs where content negotiation is in use issue in Drupal\Core\EventSubscriber\AcceptNegotiation406 as @todo. However the mentioned issue has been fixed.

Proposed resolution

If I'm not wrong, the initial approach of #2364011: [meta] External caches mix up response formats on URLs where content negotiation is in use was to use same URL but change the format of response with request headers (instead of current query string "_format"). Since that approach is not any more in action, we can simply remove this @todo as the check in this subscriber is still relevant.

Remaining tasks

- Confirm the proposed solution
- Patch
- Review
- Test

User interface changes

N/A

API changes

N/A

Data model changes

N/A

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

vijaycs85 created an issue. See original summary.

vijaycs85’s picture

Version: 8.3.x-dev » 8.2.x-dev
Status: Active » Needs review
Issue tags: +Novice
FileSize
553 bytes

Initial patch...

Andy_D’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +mssprintjan17

All good here.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review

So the question is - what needed fixing?

dawehner’s picture

To be honest I would first try to actually throw away this class and see whether it is still needed.

Wim Leers’s picture

Status: Needs review » Needs work

NW for #5.

vijaycs85’s picture

Title: Remove/React @todo on Drupal\Core\EventSubscriber\AcceptNegotiation406 class » Remove Drupal\Core\EventSubscriber\AcceptNegotiation406 class
Status: Needs work » Needs review
FileSize
3.34 KB

thanks all for the reviews. Just updating the title. will update summary once testbot is happy with this patch...

Status: Needs review » Needs work

The last submitted patch, 7: 2835626-7.patch, failed testing.

naveenvalecha’s picture

EntityResourceTestBase and its child classes still testing the Accept header. Does rest still provide support for Accept Header or we only provide support of _format query parameter?

//Naveen

Wim Leers’s picture

#10: no, it's not testing that. That test coverage is sending the Accept request header in some cases only because of edge cases in Drupal's/Symfony's PHP exception handling. We're fixing those edge cases, which may allow us to remove those.

See #2842465: Behavior of fatal PHP errors/uncaught exceptions results is mostly undefined: \Drupal\Core\EventSubscriber\DefaultExceptionSubscriber::onJson() is the de facto catch-all.

Wim Leers’s picture

Assigned: Unassigned » Wim Leers

Looking into this myself now.

Wim Leers’s picture

Title: Remove Drupal\Core\EventSubscriber\AcceptNegotiation406 class » Rename & document Drupal\Core\EventSubscriber\AcceptNegotiation406 KernelEvents::VIEW event subscriber
Version: 8.2.x-dev » 8.3.x-dev
Component: rest.module » request processing system
Issue tags: -Novice +Needs tests, +API-First Initiative

So, this is still necessary for the case of:

  1. controller returns render array
  2. ?_format= is set to something other than html
  3. hence there is no KernelEvents::VIEW event subscriber (see https://www.drupal.org/docs/8/api/render-api/the-drupal-8-render-pipeline for more about such event subscribers) to transform the render array to the requested format, because render arrays can only be transformed to the HTML format
  4. this \Drupal\Core\EventSubscriber\AcceptNegotiation406 VIEW event subscriber detects that, and throws a 406 response, because the requested format is not acceptable

Conclusion: the problems are:

  1. we still need this class
  2. test coverage is missing if the only failing tests are REST tests — there must be render system functional test that ensures this response is what you get when you request a controller that returns a render array in a non-HTML format
  3. this event subscriber is poorly named, RenderArrayNonHtmlSubscriber would be a better name
  4. the class docblock is also misleading
  5. the overall documentation is misleading
Wim Leers’s picture

Assigned: Wim Leers » Unassigned
naveenvalecha’s picture

Status: Needs work » Needs review
FileSize
3.32 KB

thanks for the insight. Here's the initial patch without tests. We need new tests to ensure this functionality.Will write tests once the bot will be happy with these changes.

// Naveen

vijaycs85’s picture

thanks @Wim Leers and @naveenvalecha.

  1. +++ b/core/lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php
    @@ -8,19 +8,19 @@
    + * Provides a subscriber to return a 406 response when a request call a
    

    Something like this:

    ... return 406 when requested for non-HTML format and controller returns an array.

  2. +++ b/core/lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php
    @@ -8,19 +8,19 @@
    - * @todo fix or replace this in https://www.drupal.org/node/2364011
    

    Nice, finally it's going away :)

I believe we still need to address conlusion #13.2 and probably #13.5?

Wim Leers’s picture

#15: that's looking great already! Thanks :)

+++ b/core/lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php
@@ -8,19 +8,19 @@
+   * Throws an HTTP 406 error if a controller request in non-HTMl format.

s/if …/when a render array controller result must be converted to a format other than HTML/

Wim Leers’s picture

Ignore #17, I like #16.1 better :)

dawehner’s picture

  1. +++ b/core/core.services.yml
    @@ -1039,8 +1039,8 @@ services:
    -  accept_negotiation_406:
    -    class: Drupal\Core\EventSubscriber\AcceptNegotiation406
    +  renderer_non_html:
    +    class: Drupal\Core\EventSubscriber\RenderArrayNonHtmlSubscriber
         tags:
           - { name: event_subscriber }
       main_content_renderer.html:
    

    If we rename a service, we need a container rebuilding, aka. an empty update method.

  2. +++ b/core/lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php
    @@ -8,19 +8,19 @@
    + * Provides a subscriber to return a 406 response when a request call a
    + * controller that returns a render array in a non-HTML format.
    

    ... And nothing converted it in the meantime. Maybe we should document it.

  3. +++ b/core/lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php
    @@ -8,19 +8,19 @@
    +   * Throws an HTTP 406 error if a controller request in non-HTMl format.
    

    Let's better use HTML instead of HTMl :)

20th’s picture

In addition to #16 and #19,

  1.  /**
    - * View subscriber rendering a 406 if we could not route or render a request.
    + * Provides a subscriber to return a 406 response when a request call a
    + * controller that returns a render array in a non-HTML format.
      *
    - * @todo fix or replace this in https://www.drupal.org/node/2364011
      */
    -class AcceptNegotiation406 implements EventSubscriberInterface {
    +class RenderArrayNonHtmlSubscriber implements EventSubscriberInterface {
    

    Class summary should fit on a single line.

  2. -        // @see \Drupal\Core\EventSubscriber\AcceptNegotiation406::onViewDetect406()
    +        // @see \Drupal\Core\EventSubscriber\RenderArrayNonHtmlSubscriber::onViewDetect406()
    

    That method onViewDetect406() is renamed in this patch.

Wim Leers’s picture

Status: Needs review » Needs work

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

20th’s picture

FileSize
5.7 KB
3.21 KB

Rewriting documentation to hopefully make it more clear and adding empty update hook. Still needs tests.

Wim Leers’s picture

Status: Needs work » Needs review

Thanks! Setting to "needs review" so testbot can start the testing :)

dawehner’s picture

+++ b/core/lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php
@@ -0,0 +1,44 @@
+ * View subscriber throwing a 406 when response is not in requested format.

Well, this documentation should talk about HTML, not about some generic case.

Wim Leers’s picture

Status: Needs review » Needs work

#25: agreed, and I provided a suggestion in #17.

Finally, it'd be good to create this patch with git diff -M5%, to record it as a rename/move, instead of a file deletion + creation.

20th’s picture

Status: Needs work » Needs review
FileSize
4.5 KB

Uploading a patch made with git diff -M5% and with updated class summary string.

Both variants

/**
 * View subscriber throwing a 406 when a render array controller result must be
 * converted to a format other than HTML.
 */
class RenderArrayNonHtmlSubscriber implements EventSubscriberInterface {

and

/**
 * Throws an HTTP 406 error when a render array controller result must be
 * converted to a format other than HTML.
 */
class RenderArrayNonHtmlSubscriber implements EventSubscriberInterface {

are too long and do not fit on a single line as the coding standards require. Can this requirement be overlooked this time?

Wim Leers’s picture

Status: Needs review » Needs work

Thanks, that's much better!

  1. Regarding the class docblock:
    Throws 406 if requesting non-HTML format and controller returns render array.
    

    That fits, and again takes a cue from @vijaycs85's excellent suggestion in #16.1.

  2. +++ b/core/lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php
    @@ -8,26 +8,27 @@
    -    // not HTML though we can also assume that the requested format is invalid
    -    // so we provide a 406 response.
    +    // not HTML though, we can also assume the following:
    +    //   - HTML resulting from a render array is invalid for requested format;
    +    //   - result was not converted by anything else;
    +    //   - it is better to return 406 error.
    

    I think it's better to revert this change; the original comment was clearer.

  3. +++ b/core/modules/system/system.install
    @@ -1811,3 +1811,19 @@ function system_update_8301() {
    + * Add detailed cron logging configuration.
    

    The 'accept_negotiation_406' service was renamed.

  4. +++ b/core/modules/system/system.install
    @@ -1811,3 +1811,19 @@ function system_update_8301() {
    +  // Empty update to cause a cache rebuild.
    

    s/cache/container/

20th’s picture

Status: Needs work » Needs review
FileSize
4.31 KB
1.95 KB

Addressing feedback from #28, thanks @Wim Leers! I should stay away from code comments :)

Still needs tests.

Wim Leers’s picture

Status: Needs review » Needs work

This looks perfect now other than it needs tests :)

The missing test coverage:

[…] there must be render system functional test that ensures this response is what you get when you request a controller that returns a render array in a non-HTML format

If you want, I can give additional pointers.

20th’s picture

Status: Needs work » Needs review
FileSize
9.02 KB
4.71 KB
4.71 KB

I wasn't sure where is the best place to put these tests, I think in system module is good.

20th’s picture

Status: Needs review » Needs work

Test only patch should not have passed...

20th’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests

No, they should have passed after all. The tested code already exists in core and we simple rename it here. And I'm adding a missing test coverage here that should be passing.

Wim Leers’s picture

Status: Needs review » Needs work

#33: exactly! :)

However, you can prove that your tests are testing the right thing by deleting (or commenting) this line:

$events[KernelEvents::VIEW][] = ['onRespond', -10];

That will cause the event subscriber to not run. And should then result in failing tests. If the tests do not fail, then the test are not testing what they should be testing :)


  1. +++ b/core/modules/system/tests/modules/render_array_non_html_subscriber_test/src/RenderArrayNonHtmlSubscriberTestController.php
    @@ -0,0 +1,28 @@
    +  public function rawStrign() {
    

    s/Strign/String/

  2. +++ b/core/modules/system/tests/src/Functional/Render/RenderArrayNonHtmlSubscriberTest.php
    @@ -0,0 +1,61 @@
    +  /**
    +   * Tests that event subscriber does not interfere with normal requests.
    +   */
    +  public function testRenderedResponse() {
    +    $url = Url::fromRoute('render_array_non_html_subscriber_test.render_array');
    +
    +    $this->drupalGet($url);
    +    $this->assertSession()->statusCodeEquals(200);
    +    $this->assertRaw(t('Controller response successfully rendered.'));
    +  }
    +
    +  /**
    +   * Tests that correct response code is returned for any non-HTML format.
    +   */
    +  public function testNonHtmlRequestFormat() {
    +    foreach (['json', 'hal+json', 'xml', 'foo'] as $format) {
    +      $url = Url::fromRoute('render_array_non_html_subscriber_test.render_array', [
    +        '_format' => $format,
    +      ]);
    +
    +      $this->drupalGet($url);
    +      $this->assertSession()->statusCodeEquals(406);
    +      $this->assertNoRaw(t('Controller response successfully rendered.'));
    +    }
    +  }
    +
    +  /**
    +   * Tests that event subscriber does not interfere with raw string responses.
    +   */
    +  public function testRawStringResponse() {
    +    $url = Url::fromRoute('render_array_non_html_subscriber_test.raw_string', [
    +      '_format' => 'foo',
    +    ]);
    +
    +    $this->drupalGet($url);
    +    $this->assertSession()->statusCodeEquals(200);
    +    $this->assertRaw(t('Raw controller response.'));
    +  }
    

    We can merge these into a single testBlah() method. No need to install Drupal three times.

Primarily NW for creating such a failing patch!

Wim Leers’s picture

Oh, and after that, this is RTBC :)

20th’s picture

Right, there are not unit tests, so they have some setup overhead. Thanks for the reviews, @Wim Leers!

Now it should be failing as expected.

Status: Needs review » Needs work

The last submitted patch, 36: 2835626-36-TESTSONLY.patch, failed testing.

20th’s picture

Status: Needs work » Needs review
Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

Perfect! :)

P.S.: tip for the future: upload the failing patch first. Then the issue won't be marked NW. :)

20th’s picture

Got it. Thanks!

xjm’s picture

+++ b/core/modules/system/system.install
@@ -1834,3 +1834,19 @@ function system_update_8301() {
+function system_update_8400() {
+  // Empty update to cause a container rebuild.
+}
+

Don't we do post updates or something for this now?

xjm’s picture

+++ b/core/lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php
@@ -8,25 +8,23 @@
-class AcceptNegotiation406 implements EventSubscriberInterface {
+class RenderArrayNonHtmlSubscriber implements EventSubscriberInterface {
...
-  public function onViewDetect406(GetResponseForControllerResultEvent $event) {
+  public function onRespond(GetResponseForControllerResultEvent $event) {

Just a note: These changes are considered backwards-compatible because event subscribers, services, etc. are considered internal: https://www.drupal.org/core/d8-bc-policy#paramconverters

However, since it's an internal API change, this issue is correctly filed as an 8.4.x-only (minor version) issue.

Just basically confirming all is well here since I had to double-check.

xjm’s picture

Issue tags: -Documentation

Mostly not a docs patch.

dawehner’s picture

Yeah I don't really see an advantage of adding this to 8.3.x, at least not for before 8.3.0

Wim Leers’s picture

+1, this is pure clean-up.

xjm’s picture

Status: Reviewed & tested by the community » Needs review

See question in #41 though. Thanks!

xjm’s picture

Wim Leers’s picture

IDK the answer to #41.

18:49:41 <WimLeers> dawehner: catch Do we nowadays use hook_update or hook_post_update if a change simply requires a container rebuild? xjm is asking at https://www.drupal.org/node/2835626#comment-11930278, but IDK the answer. https://www.drupal.org/node/2563673 definitely doesn't mention this
dawehner’s picture

I would use a hook_update_N(), as the system is not in a working/consistent state, unless the container is rebuild. hook_post_update_X assumes to have a fully working environment.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

Given #49

alexpott’s picture

+++ b/core/modules/system/system.install
@@ -1834,3 +1834,19 @@ function system_update_8301() {
+/**
+ *  The 'accept_negotiation_406' service was renamed.
+ */
+function system_update_8400() {
+  // Empty update to cause a container rebuild.
+}

This is a bit of an odd one - I'm not sure this is necessary. A container rebuild is guaranteed when you update from 8.3.0 (or whatever release you are on) to 8.4.0 since the container cache key changes. I'll ping @dawehner about this given it was due to #19 we added this. I'm unsure.

dawehner’s picture

I guess we don't care about oeople running dev versions? I'm talking about people using git, as they might pull a specific commit in using composer.

catch’s picture

Status: Reviewed & tested by the community » Needs work

If you're running a dev version, you can use $settings['deployment_identifier']. Part of why we added that stuff in the first place was to stop sites having fatal errors before updates had even been run.

Also 8.4.x already has an update committed to it.

xjm’s picture

So from #53 @catch is saying to remove the update hook implementation. Thanks!

himanshu-dixit’s picture

Status: Needs work » Needs review
FileSize
9.69 KB
559 bytes

Here is the new patch,

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

Even better :)

dawehner’s picture

If you're running a dev version, you can use $settings['deployment_identifier'].

To be honest though, nobody really knows about that.

himanshu-dixit’s picture

@dawehner Yeah! I just came to know about this from this issue. Maybe this is because i am new to Drupal :)

The last submitted patch, 36: 2835626-36.patch, failed testing.

The last submitted patch, 36: 2835626-36-TESTSONLY.patch, failed testing.

xjm’s picture

If you're running a dev version, you can use $settings['deployment_identifier'].

To be honest though, nobody really knows about that.

Maybe we should make a followup to fix that in the docs?

xjm’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record

So while this BC break is allowed as an internal change, the language in our policy that allows it is definitely in the fine print. So it occurs to me now that we should add a change record for this. Sorry I didn't think of that sooner!

Wim Leers’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs change record
catch’s picture

Status: Reviewed & tested by the community » Needs work

Needs a re-roll.

jofitz’s picture

Status: Needs work » Needs review
FileSize
8.46 KB

Re-rolled.

catch’s picture

Status: Needs review » Reviewed & tested by the community

Thanks!

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 65: 2835626-65.patch, failed testing.

jofitz’s picture

Status: Needs work » Reviewed & tested by the community

Now passing fine, suspect a testbot glitch. Returning to RTBC.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 65: 2835626-65.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Reviewed & tested by the community

DrupalCI infra fail.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 65: 2835626-65.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Reviewed & tested by the community

Random fail in Drupal\Tests\views\FunctionalJavascript\Plugin\views\Handler\ContextualFilterTest.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 65: 2835626-65.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Reviewed & tested by the community

Sigh.

  • catch committed 823aae4 on 8.4.x
    Issue #2835626 by 20th, vijaycs85, himanshu-dixit, naveenvalecha, Jo...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Fixed this on commit, and committed/pushed to 8.4.x, thanks!


FILE: ...lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
 37 | ERROR | [x] Visibility must be declared on method
    |       |     "getSubscribedEvents"
----------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------
Wim Leers’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.