PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0
Elementor Website Builder – more than just a page builder v4.3.0
4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 All 454 releases
elementor / core / common / modules / events-manager / rest-api / events-proxy-rest-api.php

events-proxy-rest-api.php in Elementor Website Builder – more than just a page builder 4.3.0, at core/common/modules/events-manager/rest-api/events-proxy-rest-api.php

190 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Core\Common\Modules\EventsManager\RestApi;
4
5 use Elementor\Core\Common\Modules\EventsManager\Module;
6 use Elementor\Utils;
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit; // Exit if accessed directly.
10 }
11
12 class Events_Proxy_REST_API {
13 const API_NAMESPACE = 'elementor/v1';
14 const API_BASE = 'events';
15
16 const REQUEST_TIMEOUT = 3;
17 const MAX_BODY_BYTES = 10 * MB_IN_BYTES;
18
19 const FORWARDED_REQUEST_HEADERS = [ 'content-type', 'authorization', 'content-encoding' ];
20
21 const RAW_RESPONSE_HEADER = 'X-Elementor-Raw-Proxy-Response';
22
23 const ASYNC_DISPATCH_PATHS = [ 'track', 'engage', 'groups', 'record' ];
24 const ASYNC_DISPATCH_SUCCESS_BODY = '1';
25
26 public function register_hooks() {
27 add_action( 'rest_api_init', fn() => $this->register_routes() );
28 add_filter( 'rest_authentication_errors', [ $this, 'bypass_nonce_check_for_own_routes' ], 0 );
29 add_filter( 'rest_pre_serve_request', [ $this, 'maybe_serve_raw_response' ], 10, 2 );
30 }
31
32 public function bypass_nonce_check_for_own_routes( $result ) {
33 if ( $this->is_own_route_request() ) {
34 return true;
35 }
36
37 return $result;
38 }
39
40 public function maybe_serve_raw_response( $served, $result ) {
41 if ( ! ( $result instanceof \WP_REST_Response ) ) {
42 return $served;
43 }
44
45 $headers = $result->get_headers();
46
47 if ( empty( $headers[ self::RAW_RESPONSE_HEADER ] ) ) {
48 return $served;
49 }
50
51 if ( ! headers_sent() ) {
52 status_header( $result->get_status() );
53
54 foreach ( $headers as $name => $value ) {
55 if ( self::RAW_RESPONSE_HEADER === $name ) {
56 continue;
57 }
58
59 header( "{$name}: {$value}" );
60 }
61 }
62
63 Utils::print_unescaped_internal_string( $result->get_data() );
64
65 return true;
66 }
67
68 private function is_own_route_request(): bool {
69 $request_uri = Utils::get_super_global_value( $_SERVER, 'REQUEST_URI' ) ?? '';
70
71 return false !== strpos( $request_uri, self::API_NAMESPACE . '/' . self::API_BASE . '/' );
72 }
73
74 private function register_routes() {
75 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/api/(?P<path>.+)', [
76 'methods' => [ 'GET', 'POST' ],
77 'callback' => fn( \WP_REST_Request $request ) => $this->proxy_api_request( $request ),
78 'permission_callback' => fn() => current_user_can( 'edit_posts' ),
79 ] );
80
81 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/libs/(?P<file>[\w\-.]+)', [
82 'methods' => 'GET',
83 'callback' => fn( \WP_REST_Request $request ) => $this->proxy_libs_request( $request ),
84 'permission_callback' => fn() => current_user_can( 'edit_posts' ),
85 ] );
86 }
87
88 private function proxy_api_request( \WP_REST_Request $request ) {
89 $path = ltrim( (string) $request->get_param( 'path' ), '/' );
90 $query = $request->get_query_params();
91
92 unset( $query['rest_route'] );
93
94 $url = Module::get_mixpanel_api_host() . '/' . $path;
95
96 if ( ! empty( $query ) ) {
97 $url = add_query_arg( $query, $url );
98 }
99
100 return $this->forward_request( $url, $request, $this->is_fire_and_forget_path( $path ) );
101 }
102
103 private function is_fire_and_forget_path( string $path ): bool {
104 return in_array( strtok( $path, '/' ), self::ASYNC_DISPATCH_PATHS, true );
105 }
106
107 private function proxy_libs_request( \WP_REST_Request $request ) {
108 $file = (string) $request->get_param( 'file' );
109 $url = Module::get_mixpanel_lib_host() . '/' . $file;
110
111 return $this->forward_request( $url, $request, false );
112 }
113
114 private function forward_request( string $url, \WP_REST_Request $request, bool $async = true ): \WP_REST_Response {
115 $body = $request->get_body();
116
117 if ( strlen( $body ) > self::MAX_BODY_BYTES ) {
118 return $this->build_raw_response( '', 413 );
119 }
120
121 $args = [
122 'method' => $request->get_method(),
123 'timeout' => self::REQUEST_TIMEOUT,
124 'redirection' => 2,
125 'headers' => $this->build_forwarded_headers( $request ),
126 ];
127
128 if ( ! empty( $body ) ) {
129 $args['body'] = $body;
130 }
131
132 if ( $async ) {
133 $args['blocking'] = false;
134
135 wp_safe_remote_request( $url, $args );
136
137 return $this->build_raw_response( self::ASYNC_DISPATCH_SUCCESS_BODY, 200, 'text/plain' );
138 }
139
140 $response = wp_safe_remote_request( $url, $args );
141
142 if ( is_wp_error( $response ) ) {
143 return $this->build_raw_response( '', 502 );
144 }
145
146 return $this->build_raw_response(
147 wp_remote_retrieve_body( $response ),
148 (int) wp_remote_retrieve_response_code( $response ),
149 wp_remote_retrieve_header( $response, 'content-type' )
150 );
151 }
152
153 private function build_forwarded_headers( \WP_REST_Request $request ): array {
154 $headers = [];
155
156 foreach ( self::FORWARDED_REQUEST_HEADERS as $header_name ) {
157 $value = $request->get_header( $header_name );
158
159 if ( null !== $value && '' !== $value ) {
160 $headers[ $header_name ] = $value;
161 }
162 }
163
164 $remote_address = Utils::get_super_global_value( $_SERVER, 'REMOTE_ADDR' );
165 $host = Utils::get_super_global_value( $_SERVER, 'HTTP_HOST' );
166
167 if ( $remote_address ) {
168 $headers['x-forwarded-for'] = $remote_address;
169 }
170
171 if ( $host ) {
172 $headers['x-forwarded-host'] = $host;
173 }
174
175 return $headers;
176 }
177
178 private function build_raw_response( string $body, int $status, string $content_type = '' ) {
179 $response = new \WP_REST_Response( $body, $status );
180
181 if ( $content_type ) {
182 $response->header( 'Content-Type', $content_type );
183 }
184
185 $response->header( self::RAW_RESPONSE_HEADER, '1' );
186
187 return $response;
188 }
189 }
190