← All changes
|
jetpack_vendor/automattic/jetpack-connection/src/class-client.php
+46
-6
13.6.2
→
16.3-beta
View file →
| @@ -38,10 +38,36 @@ | ||
| 38 | 38 | */ |
| 39 | 39 | $args['url'] = apply_filters( 'jetpack_remote_request_url', $args['url'] ); |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | + // Feed failures into the outgoing-request error flow of Error_Handler: any known | |
| 43 | + // connection error is stored and surfaced to the user. See the Error_Handler | |
| 44 | + // class docblock for the full picture of both error-handling flows. | |
| 45 | + // Outgoing XML-RPC calls (Jetpack_IXR_Client) are funneled through this method too; | |
| 46 | + // tell the transports apart by the endpoint the request targets. | |
| 47 | + // The literals match Error_Handler::ERROR_TYPE_XMLRPC / ERROR_TYPE_REST. The constants | |
| 48 | + // themselves must not be referenced from this class: during a plugin update, a stale | |
| 49 | + // Error_Handler that predates them can already be loaded, and resolving them against | |
| 50 | + // it would fatal the request. | |
| 51 | + $request_path = (string) wp_parse_url( empty( $args['url'] ) ? '' : $args['url'], PHP_URL_PATH ); | |
| 52 | + $error_type = '/xmlrpc.php' === substr( $request_path, -strlen( '/xmlrpc.php' ) ) ? 'xmlrpc' : 'rest'; | |
| 53 | + | |
| 42 | 54 | $result = self::build_signed_request( $args, $body ); |
| 43 | 55 | if ( is_wp_error( $result ) ) { |
| 56 | + // The request was never made, so it has no response to check. Report the signing | |
| 57 | + // failure; attribution comes from the error itself — see | |
| 58 | + // `Error_Handler::check_signed_request_for_errors()`. | |
| 59 | + // Reporting is best-effort and must never fatal a request running mid-plugin-update: | |
| 60 | + // skip it when the already-loaded Error_Handler is a stale version predating this method. | |
| 61 | + if ( method_exists( Error_Handler::class, 'check_signed_request_for_errors' ) ) { | |
| 62 | + Error_Handler::get_instance()->check_signed_request_for_errors( | |
| 63 | + $result, | |
| 64 | + empty( $args['url'] ) ? '' : $args['url'], | |
| 65 | + empty( $args['method'] ) ? 'POST' : $args['method'], | |
| 66 | + $error_type | |
| 67 | + ); | |
| 68 | + } | |
| 69 | + | |
| 44 | 70 | return $result; |
| 45 | 71 | } |
| 46 | 72 | |
| 47 | 73 | $response = self::_wp_remote_request( $result['url'], $result['request'] ); |
| @@ -50,9 +76,9 @@ | ||
| 50 | 76 | $response, |
| 51 | 77 | $result['auth'], |
| 52 | 78 | empty( $args['url'] ) ? '' : $args['url'], |
| 53 | 79 | empty( $args['method'] ) ? 'POST' : $args['method'], |
| 54 | - 'rest' | |
| 80 | + $error_type | |
| 55 | 81 | ); |
| 56 | 82 | |
| 57 | 83 | /** |
| 58 | 84 | * Fired when the remote request response has been received. |
| @@ -109,11 +135,22 @@ | ||
| 109 | 135 | if ( 'header' !== $args['auth_location'] ) { |
| 110 | 136 | $args['auth_location'] = 'query_string'; |
| 111 | 137 | } |
| 112 | 138 | |
| 113 | - $token = ( new Tokens() )->get_access_token( $args['user_id'] ); | |
| 139 | + // Return the specific reason the token could not be loaded instead of a bare `false`. | |
| 140 | + // Note the returned `WP_Error` is truthy, so this must not be tested with `! $token`. | |
| 141 | + $token = ( new Tokens() )->get_access_token( | |
| 142 | + $args['user_id'], | |
| 143 | + false, // token_key | |
| 144 | + false // suppress_errors | |
| 145 | + ); | |
| 146 | + if ( is_wp_error( $token ) ) { | |
| 147 | + return $token; | |
| 148 | + } | |
| 114 | 149 | if ( ! $token ) { |
| 115 | - return new WP_Error( 'missing_token' ); | |
| 150 | + // `get_access_token()` explains itself for every case but one: it returns a bare | |
| 151 | + // `false` when the tokens are locked. That lock is one-shot and self-healing. | |
| 152 | + return new WP_Error( 'tokens_locked' ); | |
| 116 | 153 | } |
| 117 | 154 | |
| 118 | 155 | $method = strtoupper( $args['method'] ); |
| 119 | 156 | |
| @@ -163,9 +200,12 @@ | ||
| 163 | 200 | if ( is_array( $body_to_hash ) ) { |
| 164 | 201 | // We cast this to a new variable, because the array form of $body needs to be |
| 165 | 202 | // maintained so it can be passed into the request later on in the code. |
| 166 | 203 | if ( array() !== $body_to_hash ) { |
| 167 | - $body_to_hash = wp_json_encode( self::_stringify_data( $body_to_hash ) ); | |
| 204 | + $body_to_hash = wp_json_encode( | |
| 205 | + self::_stringify_data( $body_to_hash ), | |
| 206 | + 0 // phpcs:ignore Jetpack.Functions.JsonEncodeFlags.ZeroFound -- No `json_encode()` flags because this needs to match whatever is calculating the hash on the other end. | |
| 207 | + ); | |
| 168 | 208 | } else { |
| 169 | 209 | $body_to_hash = ''; |
| 170 | 210 | } |
| 171 | 211 | } |
| @@ -416,9 +456,9 @@ | ||
| 416 | 456 | * |
| 417 | 457 | * @param string $path REST API path. |
| 418 | 458 | * @param string $version REST API version. Default is `2`. |
| 419 | 459 | * @param array $args Arguments to {@see WP_Http}. Default is `array()`. |
| 420 | - * @param null|string|array $body Body passed to {@see WP_Http}. Default is `null`. | |
| 460 | + * @param null|string|array $body Body passed to {@see WP_Http}. Default is `null`. | |
| 421 | 461 | * @param string $base_api_path REST API root. Default is `wpcom`. |
| 422 | 462 | * |
| 423 | 463 | * @return array|WP_Error $response Response data, else {@see WP_Error} on failure. |
| 424 | 464 | * @phan-return _WP_Remote_Response_Array|WP_Error |
| @@ -437,9 +477,9 @@ | ||
| 437 | 477 | $args['headers'] = array( 'Content-Type' => 'application/json' ); |
| 438 | 478 | } |
| 439 | 479 | |
| 440 | 480 | if ( isset( $body ) && ! is_string( $body ) ) { |
| 441 | - $body = wp_json_encode( $body ); | |
| 481 | + $body = wp_json_encode( $body, JSON_UNESCAPED_SLASHES ); | |
| 442 | 482 | } |
| 443 | 483 | |
| 444 | 484 | return self::remote_request( $args, $body ); |
| 445 | 485 | } |