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 a shim to override wp.apiRequest, allowing HTTP/1.0 emulation #5741

Merged
merged 7 commits into from
Mar 27, 2018

Conversation

pento
Copy link
Member

@pento pento commented Mar 22, 2018

Description

#4396 switched API requests to using HTTP/1.0, using Backbone's emulateHTTP setting. #5253 changed to using wp.apiRequests() for API requests, which uses jQuery. Unfortunately, jQuery doesn't have a setting similar to emulateHTTP, so we need to manually do it, instead.

It seems that a lot of web application firewalls have stricter rules for PUT requests, so the merge of #5253 manifests itself as posts sporadically failing to save. For example: #5675, #5632, #5660.

I've also opened a core ticket to address this behaviour.

How Has This Been Tested?

Using your browser's dev tools, watch the network requests, and confirm that the save request for a post is sent as a POST, with the X-HTTP-Method-Override: PUT header. The post should save correctly.

If you have a WAF that currently blocks saving posts in Gutenberg 2.4, this PR should fix that behaviour.

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code has proper inline documentation.

@pento pento added [Type] Bug An existing feature does not function as intended Core REST API Task Task for Core REST API efforts labels Mar 22, 2018
@pento pento self-assigned this Mar 22, 2018
@pento pento requested review from youknowriad and aduth March 22, 2018 05:26
@pento pento requested a review from a team March 23, 2018 01:24
@pento
Copy link
Member Author

pento commented Mar 23, 2018

This is ready to merge, with review. It's been tested against a previously failing WAF.

lib/compat.php Outdated
var oldApiRequest = wp.apiRequest;
wp.apiRequest = function ( options ) {
if ( options.method ) {
if( [ 'GET', 'PUT', 'DELETE' ].indexOf( options.method.toUpperCase() ) >= 0 ) {
Copy link
Member

Choose a reason for hiding this comment

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

Space after if is missing :)

Copy link
Member Author

Choose a reason for hiding this comment

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

🙃


$scripts->add_inline_script( 'wp-api-request', $api_request_fix, 'after' );
}
add_action( 'wp_default_scripts', 'gutenberg_shim_api_request_emulate_http' );
Copy link
Member

Choose a reason for hiding this comment

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

The previous shim was called inside gutenberg_editor_scripts_and_styles. This code uses wp_default_scripts action. Not sure what is the difference as I can't find what this action does.

Copy link
Member Author

Choose a reason for hiding this comment

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

No practical difference, I just copy/pasta-ed from the gutenberg_shim_fix_api_request_plain_permalinks shim above it.

lib/compat.php Outdated
var oldApiRequest = wp.apiRequest;
wp.apiRequest = function ( options ) {
if ( options.method ) {
if ( [ 'GET', 'PUT', 'DELETE' ].indexOf( options.method.toUpperCase() ) >= 0 ) {
Copy link
Member

@gziolo gziolo Mar 23, 2018

Choose a reason for hiding this comment

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

One thing I noticed which differs from Backbone:

(type === 'PUT' || type === 'DELETE' || type === 'PATCH')

https://github.com/jashkenas/backbone/blob/master/backbone.js#L1587

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops!

Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

I haven't tested but I think it looks good. It would be the best idea to ask someone who has issues with the regular HTTP methods to confirm.

lib/compat.php Outdated
if ( options.method ) {
if ( [ 'PATCH', 'PUT', 'DELETE' ].indexOf( options.method.toUpperCase() ) >= 0 ) {
if ( ! options.headers ) {
options.headers = [];
Copy link
Member

Choose a reason for hiding this comment

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

This should be assigned as an object, not array.

http://api.jquery.com/jquery.ajax/ (settings.headers)

Copy link
Member

@gziolo gziolo Mar 23, 2018

Choose a reason for hiding this comment

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

Missed that, but array is also an object in JS... so might work, but obviously should be updated.

Copy link
Member Author

Choose a reason for hiding this comment

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

👍🏻

var oldApiRequest = wp.apiRequest;
wp.apiRequest = function ( options ) {
if ( options.method ) {
if ( [ 'PATCH', 'PUT', 'DELETE' ].indexOf( options.method.toUpperCase() ) >= 0 ) {
Copy link
Member

Choose a reason for hiding this comment

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

Alternatively, we could inverse the check to better reflect that we support only GET and POST:

if ( [ 'GET', 'POST' ].indexOf( options.method.toUpperCase() ) === -1 ) {

Aside: Curiosity got me wondering: https://jsperf.com/indexof-array-vs-regexp (tl;dr: This is good)

Copy link
Member Author

Choose a reason for hiding this comment

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

OPTIONS shouldn't be converted, either. (Neither should HEAD, CONNECT, or TRACE, even though we don't specifically use them anywhere.) I'm inclined to go with the same conversion as Backbone, as that's the one that is most likely to be supported.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm. I'm not clear what exactly we're doing in this pull request then. Are we targeting HTTP/1.0 ? There are only 3 defined verbs (previously overlooked HEAD):

https://www.w3.org/Protocols/HTTP/1.0/spec.html#Methods

It makes me a bit nervous that we target Backbone, as it's not obvious why we target Backbone (aside from anecdotal evidence that it appears to be well-supported).

var oldApiRequest = wp.apiRequest;
wp.apiRequest = function ( options ) {
if ( options.method ) {
if ( [ 'PATCH', 'PUT', 'DELETE' ].indexOf( options.method.toUpperCase() ) >= 0 ) {
Copy link
Member

Choose a reason for hiding this comment

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

Hmm. I'm not clear what exactly we're doing in this pull request then. Are we targeting HTTP/1.0 ? There are only 3 defined verbs (previously overlooked HEAD):

https://www.w3.org/Protocols/HTTP/1.0/spec.html#Methods

It makes me a bit nervous that we target Backbone, as it's not obvious why we target Backbone (aside from anecdotal evidence that it appears to be well-supported).

@pento
Copy link
Member Author

pento commented Mar 27, 2018

Hmm. I'm not clear what exactly we're doing in this pull request then. Are we targeting HTTP/1.0?

Not exactly. More like "simulate HTTP/1.0 for the HTTP/1.1 verbs that have problematic support".

It makes me a bit nervous that we target Backbone, as it's not obvious why we target Backbone (aside from anecdotal evidence that it appears to be well-supported).

Backbone just happens to be the previous implementation of the same thing, via the Backbone.emulateHTTP setting. There have been various proposals over the years for a standard way to tunnel HTTP/1.1 verbs through a POST request, X-HTTP-Method-Override is the one that is usually used in any new REST API implementations.

@pento pento merged commit b99137d into master Mar 27, 2018
@pento pento deleted the fix/http-10-emulation branch March 27, 2018 00:59
@jutta-livingmiracles
Copy link

Hi, this issue is still occurring for me. I'm not a developer in any way, so if someone can help me troubleshoot, that would be really lovely. I have a SiteGround-hosted WordPress site; WP 5.0.2 and cannot update pages. I get the "Updating failed" error in the editor, and the following console errors in the Chrome Inspector:

POST /wp-json/wp/v2/pages/1242?_locale=user 403
POST /wp-json/wp/v2/pages/1242/autosaves?_locale=user 403

@nerrad
Copy link
Contributor

nerrad commented Dec 31, 2018

@jutta-livingmiracles I'm not sure this pull addresses the same issue you were experiencing because this old pull references wp.apiRequest which is no longer used by Gutenberg (now uses wp.apiFetch).

I would recommend creating a new issue documenting your problems and it can be addressed there. Please make sure you can reproduce this without any themes/plugins active on your site as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Core REST API Task Task for Core REST API efforts [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants