Changeset 3141611
- Timestamp:
- 08/26/2024 10:59:14 AM (16 months ago)
- Location:
- http-requests-manager
- Files:
-
- 7 edited
- 4 copied
-
tags/1.3.4 (copied) (copied from http-requests-manager/trunk)
-
tags/1.3.4/assets/css/admin.css (modified) (1 diff)
-
tags/1.3.4/assets/js/admin.js (copied) (copied from http-requests-manager/trunk/assets/js/admin.js) (7 diffs)
-
tags/1.3.4/http-requests-manager.php (copied) (copied from http-requests-manager/trunk/http-requests-manager.php) (24 diffs)
-
tags/1.3.4/readme.txt (copied) (copied from http-requests-manager/trunk/readme.txt) (2 diffs)
-
tags/1.3.4/templates/page-settings.php (modified) (4 diffs)
-
trunk/assets/css/admin.css (modified) (1 diff)
-
trunk/assets/js/admin.js (modified) (7 diffs)
-
trunk/http-requests-manager.php (modified) (24 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/templates/page-settings.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
http-requests-manager/tags/1.3.4/assets/css/admin.css
r3002923 r3141611 96 96 } 97 97 98 .media-frame-title,99 .media-frame-content {98 #vphrm-modal.vphrm-modal .media-frame-title, 99 #vphrm-modal.vphrm-modal .media-frame-content { 100 100 left: 0; 101 101 } -
http-requests-manager/tags/1.3.4/assets/js/admin.js
r3110053 r3141611 134 134 /*var element = document.getElementById("vphrm-content"); 135 135 element.scrollIntoView();*/ 136 137 136 137 138 138 139 139 // save view 140 140 VPHRM.view_save(); 141 142 141 142 143 143 $('select.vphrm-group-view').attr('disabled', 'disabled'); 144 setTimeout(function(){$('select.vphrm-group-view').removeAttr('disabled');},100); 144 setTimeout(function () 145 { 146 $('select.vphrm-group-view').removeAttr('disabled'); 147 }, 100); 145 148 }; 146 149 VPHRM.view = function () … … 404 407 page_style: '' 405 408 }; 406 409 407 410 context.page_num = context.page_num || 0; 408 411 … … 426 429 // group same page by color 427 430 if (context.last_page_id !== row.page_id) 428 { 431 { 429 432 context.last_page_id = row.page_id; 430 433 context.page_num++; 431 context.page_style = ' style="background-color:' + page_colors[ context.page_num % page_colors.length ] + '"'; 434 context.page_style = ' style="background-color:' + page_colors[ context.page_num % page_colors.length ] + '"'; 432 435 } 433 436 … … 642 645 } 643 646 } 647 648 // get more details about core 649 if (row.request_source === 'core') 650 { 651 var more_info = VPHRM.row_core_more_info(row,request_args); 652 if(more_info) 653 { 654 row.request_source += ': '+more_info; 655 } 656 } 657 644 658 // count req_types and req_sources 645 659 stats.req_types[row.request_group] = (stats.req_types[row.request_group] || 0) + 1; … … 738 752 }; 739 753 754 // get more infor about core function for grouping 755 VPHRM.row_core_more_info = function (row, request_args) 756 { 757 758 // pingback, enclosure, oembed, siteahelth, browse happy, serve happy, 759 backtrace = request_args._info.backtrace || []; 760 for (x in backtrace) 761 { 762 763 if (backtrace[x] === 'do_enclose') 764 { 765 return 'enclosure'; 766 } 767 if (backtrace[x] === 'pingback') 768 { 769 return 'pingback'; 770 } 771 if (backtrace[x] === 'WP_oEmbed->fetch') 772 { 773 return 'oEmbed'; 774 } 775 if (backtrace[x] === '_wp_cron') 776 { 777 return 'cron'; 778 } 779 if (backtrace[x] === 'WP_Site_Health->perform_test') 780 { 781 return 'health'; 782 } 783 } 784 785 786 // check url 787 var base_url = row['url'].split('?')[0].replace('http://', 'https://'); 788 789 if (base_url.search('https://api.wordpress.org/plugins/update-check/') != -1) 790 { 791 return 'update'; 792 } 793 if (base_url.search('https://api.wordpress.org/themes/update-check/') != -1) 794 { 795 return 'update'; 796 } 797 if (base_url.search('https://api.wordpress.org/core/checksums/') != -1) 798 { 799 return 'update'; 800 } 801 if (base_url.search('https://api.wordpress.org/core/version-check/') != -1) 802 { 803 return 'version-check'; 804 } 805 if (base_url.search('https://api.wordpress.org/core/serve-happy/') != -1) 806 { 807 return 'serve-happy'; 808 } 809 if (base_url.search('https://api.wordpress.org/core/browse-happy/') != -1) 810 { 811 return 'browse-happy'; 812 } 813 if (base_url.search('https://api.wordpress.org/plugins/info/') != -1) 814 { 815 return 'plugins-info'; 816 } 817 if (base_url.search('https://api.wordpress.org/translations/') != -1) 818 { 819 return 'translations'; 820 } 821 822 return ''; 823 } 824 740 825 741 826 // Clear … … 789 874 { 790 875 page.badges = []; 876 791 877 page.badges.push('<i class="vphrm-badge"><b>page_type:</b> ' + page.page_type + '</i>'); 792 878 page.badges.push('<i class="vphrm-badge"><b>is_user_logged_in:</b> ' + (page.info.is_user_logged_in ? 'true' : 'false') + '</i>'); … … 796 882 page.badges.push('<i class="vphrm-badge"><b>ajax_action:</b> ' + page.info.ajax_action + '</i>'); 797 883 } 884 page.badges.push('<i class="vphrm-badge"><b>requests:</b> ' + page.info.req_num + '</i>'); 798 885 } 799 886 -
http-requests-manager/tags/1.3.4/http-requests-manager.php
r3110053 r3141611 5 5 Plugin URI: https://veppa.com/http-requests-manager/ 6 6 Description: Limit, Debug, Optimize WP_HTTP requests. Limit by request count, page load time, reduce timeout for each request. Speed up login and admin pages. 7 Version: 1.3. 37 Version: 1.3.4 8 8 Author: veppa 9 9 Author URI: https://veppa.com/ … … 32 32 * TODO: 33 33 34 - youtube video embed url when saved shows as empty url. why? it is reported as not secure on localhost. 35 - when block all set and user navigates to plugins page show notification that operation mode prevents external requests. 36 34 + Modal left padding 200px fixed on some sites. 35 + Do not add cp hooks if logging disabled. waste of resources. 36 + Group view prevented requests: pingback, enclosure, browse happy, serve happy, update, translation, health, oEmbed etc. 37 + Show total number of requests in details view for given page. 38 + Force blocking rules: use double prevention in block all external and allow only wp requests by defining constants. 39 + Make "Only log HTTP requests" default operation mode. 40 41 - marketing: remove all other affiliate links. 42 - - incorporate video tutorials to interface with popup/direct link. 43 44 45 - safe-mode: show instruction about safe mode and operation mode on beginning. after dismissed move note to bottom. 46 - [maybe] group prevent enclosure checks and pingbacks. write 1 log instead of 50+ 47 - reduce ajax data. sometimes report loads slow. 230kb compressed = 1.3mb uncompressed 48 - truncate long response, 49 - remove duplicate info about requests in page array. 50 - optimization: define hooks only for selected mode and logging combination. define hooks granularly. 51 52 ------------------------------------------- 53 * conflict test. 54 * 1) always monitor and log passively: 55 - add debug log: 56 - on shutdown check if pre_http hook and capturing hook removed. add to debug log. 57 - check if block_external constants predefined. 58 - keep last 20 records 59 - use passive logging. clear log after 5 days. if issue happens and log is cleared then record. 60 if issue happens and already in log then do not log until log cleared. 1 day, 3 day or 5 day period is good. 61 - do this because conflict may happen on any page any time. it is not once and for all conflict. 62 * 2) pretest before switching operation mode. try 5 pages admin and frontend then show report passed and failed. 63 * - can be separate button to test before switching. 64 * 3) add safemode URL parameter to disable logging so user can switch to log only mode in case plugin conflict shows white screen of death. (otherwise user have to manually change plugin folder in order to disable blocking.) 65 -------------------------------------------- 66 67 - [maybe] show noticifation if constants (WP_HTTP_BLOCK_EXTERNAL, WP_ACCESSIBLE_HOSTS) defined in config (not me) and conflicting with current operation mode. 68 69 - when ajax called by plugin then do not block wp_HTTP by that plugin in smart block mode. example, cludflare plugin calls API 5+ times inside ajax call. updraft takes long to complete update via ajax call. 70 - youtube video embed url when saved shows as empty url. why? it is reported as not secure on localhost. 71 - when block all set and user navigates to plugins page show notification that operation mode prevents external requests. 72 37 73 - show 47% requests blocked in dashboard at a glance. with option to remove from there. 38 74 - delay and bulk write logs to speed up. … … 69 105 { 70 106 71 const VERSION = '1.3. 3';107 const VERSION = '1.3.4'; 72 108 const ID = 'http-requests-manager'; 73 109 const TIMEOUT = 2; … … 107 143 { 108 144 self::timer_float_start(); 109 self::cp_init(); 145 146 // cp ony if logging is not disabled 147 if(!self::get_option('disable_logging')) 148 { 149 self::cp_init(); 150 add_action('shutdown', [$this, 'db_update_page']); 151 } 110 152 111 153 // setup variables … … 115 157 116 158 add_action('init', [$this, 'init']); 117 add_filter('http_request_args', [$this, 'log_start_timer'], 10, 2); 159 add_filter('http_request_args', [$this, 'log_start_timer'], 10, 2); 118 160 add_filter('pre_http_request', [$this, 'log_pre_http_request'], PHP_INT_MAX, 3); 119 161 add_action('http_api_debug', [$this, 'db_capture_request'], 10, 5); 120 162 add_action('vphrm_cleanup_cron', [$this, 'db_cleanup']); 121 163 add_action('pre_get_ready_cron_jobs', [$this, 'cron_prevent_in_my_ajax']); 122 add_action('shutdown', [$this, 'db_update_page']);123 164 124 165 // admin page actions only. for optimisation purpose these are used only on admin pages … … 1055 1096 } 1056 1097 1057 function log_start_timer($parsed_args = array(), $url='')1098 function log_start_timer($parsed_args = array(), $url = '') 1058 1099 { 1059 1100 self::cp('[start] request'); … … 1066 1107 self::$timer_before = self::timer_float(); 1067 1108 } 1068 1109 1069 1110 // pre-populate request args [_info] for recording original url before any modification. 1070 1111 // this will show url for denyed empty requests. … … 1073 1114 return $parsed_args; 1074 1115 } 1075 1076 1116 1077 1117 /** 1078 1118 * force logging when pre populated by oyher plugin. (from cache or error) … … 1084 1124 */ 1085 1125 function log_pre_http_request($pre, $parsed_args, $url) 1086 { 1087 if(false !== $pre){ 1126 { 1127 if(false !== $pre) 1128 { 1088 1129 // request handled by other plugin (cache or error response). 1089 1130 // request will not be sent. reponse provided by other plugin. log this to debug window. … … 1196 1237 } 1197 1238 } 1198 1239 1199 1240 return self::$requests; 1200 1241 } … … 1211 1252 { 1212 1253 case 'mode': 1213 $mode_default = ' block_smart';1254 $mode_default = 'log'; 1214 1255 $modes = self::modes(); 1215 1256 // check if mode exists … … 1347 1388 return self::$modes; 1348 1389 } 1349 1350 static public function db_truncate_field($value, $max_length)1351 { 1352 return strlen($value) >$max_length ? substr($value,0,$max_length) : $value;1353 } 1390 1391 static public function db_truncate_field($value, $max_length) 1392 { 1393 return strlen($value) > $max_length ? substr($value, 0, $max_length) : $value; 1394 } 1354 1395 1355 1396 function db_capture_page() … … 1372 1413 $log_data = apply_filters('vphrm_log_page_data', [ 1373 1414 'url' => self::page_url(), 1374 'page_type' => self::db_truncate_field( self::current_page_type(), 20),1415 'page_type' => self::db_truncate_field(self::current_page_type(), 20), 1375 1416 'runtime' => self::timer_float(), 1376 1417 'info' => json_encode($info), … … 1405 1446 // capture request to apply request limits even if not logging. 1406 1447 $this->request_log($url, $args['stream']); 1407 1408 1448 1409 1449 // show nonempty url for checkpoint. if url empty use original url. 1410 $url_cp = empty($url)?'[empty] '.(!empty($args['_info']['request_url_original'])?$args['_info']['request_url_original']:''):$url; 1411 1450 $url_cp = empty($url) ? '[empty] ' . (!empty($args['_info']['request_url_original']) ? $args['_info']['request_url_original'] : '') : $url; 1451 1452 // remove request_url_original if it matches $url 1453 if(!empty($args['_info']['request_url_original']) && $args['_info']['request_url_original'] === $url) 1454 { 1455 unset($args['_info']['request_url_original']); 1456 } 1457 1458 1412 1459 self::cp('request: ' . $url_cp); 1413 1460 … … 1448 1495 'runtime' => ( microtime(true) - $this->start_time ), 1449 1496 'date_added' => current_time('mysql'), 1450 'page_id' => self::$page_id, 1451 'request_status' => self::db_truncate_field( self::current_request_status($response), 20),1452 'request_group' => self::db_truncate_field( self::current_request_group($args), 20),1453 'request_source' => self::db_truncate_field( self::current_request_source($args), 255),1497 'page_id' => self::$page_id, 1498 'request_status' => self::db_truncate_field(self::current_request_status($response), 20), 1499 'request_group' => self::db_truncate_field(self::current_request_group($args), 20), 1500 'request_source' => self::db_truncate_field(self::current_request_source($args), 255), 1454 1501 ]); 1455 1456 1502 1457 1503 if(false !== $log_data) 1458 1504 { 1459 $wpdb->insert(self::db_table_log(), $log_data); 1505 $wpdb->insert(self::db_table_log(), $log_data); 1460 1506 1461 1507 // store last request id … … 1595 1641 static public function cp($title = '') 1596 1642 { 1597 1598 $cp_count = count(self::$cp_arr); 1599 1600 if(!strlen($title)) 1601 { 1602 $title = 'CP ' . $cp_count; 1603 } 1604 1605 $last = end(self::$cp_arr); 1606 1607 $return = array('t' => self::timer_float(), 'm' => self::cp_memory()); 1608 1609 $return['name'] = $title; 1610 1611 self::$cp_arr[] = $return; 1643 if(!self::get_option('disable_logging')) 1644 { 1645 $cp_count = count(self::$cp_arr); 1646 1647 if(!strlen($title)) 1648 { 1649 $title = 'CP ' . $cp_count; 1650 } 1651 1652 // $last = end(self::$cp_arr); 1653 1654 $return = array('t' => self::timer_float(), 'm' => self::cp_memory()); 1655 1656 $return['name'] = $title; 1657 1658 self::$cp_arr[] = $return; 1659 } 1612 1660 } 1613 1661 … … 2062 2110 */ 2063 2111 function manage_pre_http_request($pre, $parsed_args, $url) 2064 { 2112 { 2065 2113 // request not handled (cache or error) by other plugins 2066 2114 if(false === $pre) 2067 { 2115 { 2068 2116 // return error if block 2069 2117 if(self::$request_action === 'block') … … 2077 2125 return $pre; 2078 2126 } 2079 2080 2081 function manage_do_action_http_api_debug($pre, $parsed_args, $url){2127 2128 function manage_do_action_http_api_debug($pre, $parsed_args, $url) 2129 { 2082 2130 /** This action is documented in wp-includes/class-wp-http.php */ 2083 2131 do_action('http_api_debug', $pre, 'response', 'WpOrg\Requests\Requests', $parsed_args, $url); 2084 2132 } 2085 2086 2133 2087 2134 /** … … 2117 2164 function manage_perform_request_blocking_by_url($url, $info = '') 2118 2165 { 2119 $this->log_start_timer(array(), $url);2166 $this->log_start_timer(array(), $url); 2120 2167 2121 2168 self::$request_action = 'block'; … … 2587 2634 add_filter('site_transient_update_themes', [$this, 'disable_maybe_update_filter'], 10, 2); 2588 2635 add_filter('site_transient_update_core', [$this, 'disable_maybe_update_filter'], 10, 2); 2636 2637 // double implementation for block all modes. in case some plugins remove all pre_http_request actions. 2638 $this->manage_block_using_constants(); 2589 2639 } 2590 2640 … … 2605 2655 2606 2656 // default no skip 2657 return false; 2658 } 2659 2660 function manage_block_using_constants() 2661 { 2662 // current operation mode 2663 $mode = self::get_mode(); 2664 2665 $block_defined = defined('WP_HTTP_BLOCK_EXTERNAL'); 2666 $block = $block_defined ? WP_HTTP_BLOCK_EXTERNAL : false; 2667 2668 $host_defined = defined('WP_ACCESSIBLE_HOSTS'); 2669 $host = $host_defined ? WP_ACCESSIBLE_HOSTS : ''; 2670 2671 //block_external 2672 if($mode === 'block_external') 2673 { 2674 // block all external 2675 if(!$block_defined && $host == '') 2676 { 2677 // can define our own constant here 2678 define('WP_HTTP_BLOCK_EXTERNAL', true); 2679 return true; 2680 } 2681 } 2682 2683 //block_external_no_wp 2684 if($mode === 'block_external_no_wp') 2685 { 2686 // block all external 2687 if(!$block_defined && (!$host_defined || $host == '*.wordpress.org')) 2688 { 2689 // can define our own constant here 2690 define('WP_HTTP_BLOCK_EXTERNAL', true); 2691 2692 if(!$host_defined) 2693 { 2694 define('WP_ACCESSIBLE_HOSTS', '*.wordpress.org'); 2695 } 2696 return true; 2697 } 2698 } 2699 2607 2700 return false; 2608 2701 } … … 3291 3384 { 3292 3385 global $wpdb; 3293 3386 3294 3387 $version = HTTP_Requests_Manager::VERSION; 3295 3388 $db_version = HTTP_Requests_Manager::get_option('version', 0); … … 3298 3391 { 3299 3392 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); 3300 3393 3301 3394 // increase request_source varchar length to 255 3302 3395 // ALTER TABLE `wp_vphrm_log` CHANGE `request_source` `request_source` varchar(255); 3303 $wpdb->query("ALTER TABLE " .self::db_table_log()." CHANGE `request_source` `request_source` varchar(255)");3396 $wpdb->query("ALTER TABLE " . self::db_table_log() . " CHANGE `request_source` `request_source` varchar(255)"); 3304 3397 } 3305 3398 } … … 3393 3486 VPHRM(); 3394 3487 3395 3396 3397 3398 -
http-requests-manager/tags/1.3.4/readme.txt
r3110053 r3141611 4 4 Tags: wp_http, log, debug, optimization, limit 5 5 Requires at least: 4.7 6 Tested up to: 6. 57 Stable tag: 1.3. 36 Tested up to: 6.6 7 Stable tag: 1.3.4 8 8 License: GPLv2 9 9 … … 192 192 == Changelog == 193 193 194 = 1.3.4 - 26 August 2024 = 195 196 * Added: Separate group view requests by core: pingback, enclosure, browse happy, serve happy, update, translation, health, oEmbed etc. 197 * Added: Show total number of requests in detail view for given page. 198 * Added: When possible force blocking rules by defining constants to (block all external) and (allow only wp requests). 199 * Fixed: Do not add cp (checkpoint) hooks if logging disabled. Prevent waste of memory. 200 * Update: Made "Only log HTTP requests" default operation mode. 201 194 202 = 1.3.3 - 30 June 2024 = 195 203 196 * Added: URL becomes empty when it is not validated by WordPress. Empty request URLs now shows clickable text [empty]. Original URL w hill be shows inside Checkpoint.204 * Added: URL becomes empty when it is not validated by WordPress. Empty request URLs now shows clickable text [empty]. Original URL will be shows inside Checkpoint. 197 205 * Added: When request responded (from cache or error) by other plugin without sending to remote server it will be labeled as 'other' and not blocked. 198 206 * Fixed: color coding requests from same page on logs page. 199 * Fixed: Requests with longer plugin name were not recorded to database because of bug. Now datab se field length increased and longer strings will be truncated to fit when needed.207 * Fixed: Requests with longer plugin name were not recorded to database because of bug. Now database field length increased and longer strings will be truncated to fit when needed. 200 208 201 209 = 1.3.2 - 26 June 2024 = -
http-requests-manager/tags/1.3.4/templates/page-settings.php
r3081974 r3141611 182 182 <li><?php _e('Maximum 10 custom rules allowed. Please contact if you need more.', 'http-requests-manager'); ?></li> 183 183 </ol> 184 185 <p><b><a href="https://veppa.com/http-requests-manager/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Learn more', 'http-requests-manager') ?> →</a></b></p> 184 185 <p><b><a href="https://veppa.com/allow-wp_http-request/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Allow Request Tutorial', 'http-requests-manager') ?> →</a></b></p> 186 <p><b><a href="https://veppa.com/block-wp_http-request/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Block Request Tutorial', 'http-requests-manager') ?> →</a></b></p> 186 187 187 188 </div> … … 225 226 <!-- more --> 226 227 <p> 227 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/share-button/ " target="_blank">228 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/share-button/?utm_source=wp&utm_medium=plugin&utm_campaign=options" target="_blank"> 228 229 <span class="vphrm-card-val">⦿</span> 229 230 <span class="vphrm-card-name"><b class="vphrm-card-h3">Share button without plugin</b> 230 231 Fast load times, tiny code, no negative effect on Page Speed score. Free.</span> 231 232 </a> 232 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/#rankmath" target="_blank"> 233 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/speed/?utm_source=wp&utm_medium=plugin&utm_campaign=options" target="_blank"> 234 <span class="vphrm-card-val">⪫</span> 235 <span class="vphrm-card-name"><b class="vphrm-card-h3">90+ PageSpeed</b> 236 Download PDF checklist that I use to get 90+ score on WordPress websites.</span> 237 </a> 238 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#rankmath" target="_blank"> 233 239 <span class="vphrm-card-val">◢</span> 234 240 <span class="vphrm-card-name"><b class="vphrm-card-h3">Rank Math SEO plugin</b> 235 241 Optimize posts for target keyword. Content SEO checklist with best practices. Track SEO performance for each post using Google Search Console data.</span> 236 242 </a> 237 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/ #a2hosting" target="_blank">243 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#a2hosting" target="_blank"> 238 244 <span class="vphrm-card-val">a2</span> 239 245 <span class="vphrm-card-name"><b class="vphrm-card-h3">A2hosting</b> 240 246 WordPress hosting for any website. Good for beginners and has managed hosting for big sites as well.</span> 241 247 </a> 242 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/ #kwfinder" target="_blank">248 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#kwfinder" target="_blank"> 243 249 <span class="vphrm-card-val">𝕂</span> 244 250 <span class="vphrm-card-name"><b class="vphrm-card-h3">KWfinder</b> … … 264 270 <span class="vphrm-card-name"><?php _e('Support forum', 'http-requests-manager'); ?></span> 265 271 </a> 272 <a class="vphrm-card vphrm-card-wide" href="https://youtube.com/playlist?list=PLvn-qBzU0II7b5D4OYDnKpNpuvxiM0f4b" target="_blank"> 273 <span class="vphrm-card-val">►</span> 274 <span class="vphrm-card-name"><?php _e('Video tutorials', 'http-requests-manager'); ?></span> 275 </a> 266 276 </p> 267 277 … … 283 293 </a> 284 294 </p> 295 296 <p><b><?php _e('Thank you!', 'http-requests-manager') ?></b></p> 285 297 286 298 <p><a href="#top"><?php _e('Back to top', 'http-requests-manager') ?> ↑</a></p> -
http-requests-manager/trunk/assets/css/admin.css
r3002923 r3141611 96 96 } 97 97 98 .media-frame-title,99 .media-frame-content {98 #vphrm-modal.vphrm-modal .media-frame-title, 99 #vphrm-modal.vphrm-modal .media-frame-content { 100 100 left: 0; 101 101 } -
http-requests-manager/trunk/assets/js/admin.js
r3110053 r3141611 134 134 /*var element = document.getElementById("vphrm-content"); 135 135 element.scrollIntoView();*/ 136 137 136 137 138 138 139 139 // save view 140 140 VPHRM.view_save(); 141 142 141 142 143 143 $('select.vphrm-group-view').attr('disabled', 'disabled'); 144 setTimeout(function(){$('select.vphrm-group-view').removeAttr('disabled');},100); 144 setTimeout(function () 145 { 146 $('select.vphrm-group-view').removeAttr('disabled'); 147 }, 100); 145 148 }; 146 149 VPHRM.view = function () … … 404 407 page_style: '' 405 408 }; 406 409 407 410 context.page_num = context.page_num || 0; 408 411 … … 426 429 // group same page by color 427 430 if (context.last_page_id !== row.page_id) 428 { 431 { 429 432 context.last_page_id = row.page_id; 430 433 context.page_num++; 431 context.page_style = ' style="background-color:' + page_colors[ context.page_num % page_colors.length ] + '"'; 434 context.page_style = ' style="background-color:' + page_colors[ context.page_num % page_colors.length ] + '"'; 432 435 } 433 436 … … 642 645 } 643 646 } 647 648 // get more details about core 649 if (row.request_source === 'core') 650 { 651 var more_info = VPHRM.row_core_more_info(row,request_args); 652 if(more_info) 653 { 654 row.request_source += ': '+more_info; 655 } 656 } 657 644 658 // count req_types and req_sources 645 659 stats.req_types[row.request_group] = (stats.req_types[row.request_group] || 0) + 1; … … 738 752 }; 739 753 754 // get more infor about core function for grouping 755 VPHRM.row_core_more_info = function (row, request_args) 756 { 757 758 // pingback, enclosure, oembed, siteahelth, browse happy, serve happy, 759 backtrace = request_args._info.backtrace || []; 760 for (x in backtrace) 761 { 762 763 if (backtrace[x] === 'do_enclose') 764 { 765 return 'enclosure'; 766 } 767 if (backtrace[x] === 'pingback') 768 { 769 return 'pingback'; 770 } 771 if (backtrace[x] === 'WP_oEmbed->fetch') 772 { 773 return 'oEmbed'; 774 } 775 if (backtrace[x] === '_wp_cron') 776 { 777 return 'cron'; 778 } 779 if (backtrace[x] === 'WP_Site_Health->perform_test') 780 { 781 return 'health'; 782 } 783 } 784 785 786 // check url 787 var base_url = row['url'].split('?')[0].replace('http://', 'https://'); 788 789 if (base_url.search('https://api.wordpress.org/plugins/update-check/') != -1) 790 { 791 return 'update'; 792 } 793 if (base_url.search('https://api.wordpress.org/themes/update-check/') != -1) 794 { 795 return 'update'; 796 } 797 if (base_url.search('https://api.wordpress.org/core/checksums/') != -1) 798 { 799 return 'update'; 800 } 801 if (base_url.search('https://api.wordpress.org/core/version-check/') != -1) 802 { 803 return 'version-check'; 804 } 805 if (base_url.search('https://api.wordpress.org/core/serve-happy/') != -1) 806 { 807 return 'serve-happy'; 808 } 809 if (base_url.search('https://api.wordpress.org/core/browse-happy/') != -1) 810 { 811 return 'browse-happy'; 812 } 813 if (base_url.search('https://api.wordpress.org/plugins/info/') != -1) 814 { 815 return 'plugins-info'; 816 } 817 if (base_url.search('https://api.wordpress.org/translations/') != -1) 818 { 819 return 'translations'; 820 } 821 822 return ''; 823 } 824 740 825 741 826 // Clear … … 789 874 { 790 875 page.badges = []; 876 791 877 page.badges.push('<i class="vphrm-badge"><b>page_type:</b> ' + page.page_type + '</i>'); 792 878 page.badges.push('<i class="vphrm-badge"><b>is_user_logged_in:</b> ' + (page.info.is_user_logged_in ? 'true' : 'false') + '</i>'); … … 796 882 page.badges.push('<i class="vphrm-badge"><b>ajax_action:</b> ' + page.info.ajax_action + '</i>'); 797 883 } 884 page.badges.push('<i class="vphrm-badge"><b>requests:</b> ' + page.info.req_num + '</i>'); 798 885 } 799 886 -
http-requests-manager/trunk/http-requests-manager.php
r3110053 r3141611 5 5 Plugin URI: https://veppa.com/http-requests-manager/ 6 6 Description: Limit, Debug, Optimize WP_HTTP requests. Limit by request count, page load time, reduce timeout for each request. Speed up login and admin pages. 7 Version: 1.3. 37 Version: 1.3.4 8 8 Author: veppa 9 9 Author URI: https://veppa.com/ … … 32 32 * TODO: 33 33 34 - youtube video embed url when saved shows as empty url. why? it is reported as not secure on localhost. 35 - when block all set and user navigates to plugins page show notification that operation mode prevents external requests. 36 34 + Modal left padding 200px fixed on some sites. 35 + Do not add cp hooks if logging disabled. waste of resources. 36 + Group view prevented requests: pingback, enclosure, browse happy, serve happy, update, translation, health, oEmbed etc. 37 + Show total number of requests in details view for given page. 38 + Force blocking rules: use double prevention in block all external and allow only wp requests by defining constants. 39 + Make "Only log HTTP requests" default operation mode. 40 41 - marketing: remove all other affiliate links. 42 - - incorporate video tutorials to interface with popup/direct link. 43 44 45 - safe-mode: show instruction about safe mode and operation mode on beginning. after dismissed move note to bottom. 46 - [maybe] group prevent enclosure checks and pingbacks. write 1 log instead of 50+ 47 - reduce ajax data. sometimes report loads slow. 230kb compressed = 1.3mb uncompressed 48 - truncate long response, 49 - remove duplicate info about requests in page array. 50 - optimization: define hooks only for selected mode and logging combination. define hooks granularly. 51 52 ------------------------------------------- 53 * conflict test. 54 * 1) always monitor and log passively: 55 - add debug log: 56 - on shutdown check if pre_http hook and capturing hook removed. add to debug log. 57 - check if block_external constants predefined. 58 - keep last 20 records 59 - use passive logging. clear log after 5 days. if issue happens and log is cleared then record. 60 if issue happens and already in log then do not log until log cleared. 1 day, 3 day or 5 day period is good. 61 - do this because conflict may happen on any page any time. it is not once and for all conflict. 62 * 2) pretest before switching operation mode. try 5 pages admin and frontend then show report passed and failed. 63 * - can be separate button to test before switching. 64 * 3) add safemode URL parameter to disable logging so user can switch to log only mode in case plugin conflict shows white screen of death. (otherwise user have to manually change plugin folder in order to disable blocking.) 65 -------------------------------------------- 66 67 - [maybe] show noticifation if constants (WP_HTTP_BLOCK_EXTERNAL, WP_ACCESSIBLE_HOSTS) defined in config (not me) and conflicting with current operation mode. 68 69 - when ajax called by plugin then do not block wp_HTTP by that plugin in smart block mode. example, cludflare plugin calls API 5+ times inside ajax call. updraft takes long to complete update via ajax call. 70 - youtube video embed url when saved shows as empty url. why? it is reported as not secure on localhost. 71 - when block all set and user navigates to plugins page show notification that operation mode prevents external requests. 72 37 73 - show 47% requests blocked in dashboard at a glance. with option to remove from there. 38 74 - delay and bulk write logs to speed up. … … 69 105 { 70 106 71 const VERSION = '1.3. 3';107 const VERSION = '1.3.4'; 72 108 const ID = 'http-requests-manager'; 73 109 const TIMEOUT = 2; … … 107 143 { 108 144 self::timer_float_start(); 109 self::cp_init(); 145 146 // cp ony if logging is not disabled 147 if(!self::get_option('disable_logging')) 148 { 149 self::cp_init(); 150 add_action('shutdown', [$this, 'db_update_page']); 151 } 110 152 111 153 // setup variables … … 115 157 116 158 add_action('init', [$this, 'init']); 117 add_filter('http_request_args', [$this, 'log_start_timer'], 10, 2); 159 add_filter('http_request_args', [$this, 'log_start_timer'], 10, 2); 118 160 add_filter('pre_http_request', [$this, 'log_pre_http_request'], PHP_INT_MAX, 3); 119 161 add_action('http_api_debug', [$this, 'db_capture_request'], 10, 5); 120 162 add_action('vphrm_cleanup_cron', [$this, 'db_cleanup']); 121 163 add_action('pre_get_ready_cron_jobs', [$this, 'cron_prevent_in_my_ajax']); 122 add_action('shutdown', [$this, 'db_update_page']);123 164 124 165 // admin page actions only. for optimisation purpose these are used only on admin pages … … 1055 1096 } 1056 1097 1057 function log_start_timer($parsed_args = array(), $url='')1098 function log_start_timer($parsed_args = array(), $url = '') 1058 1099 { 1059 1100 self::cp('[start] request'); … … 1066 1107 self::$timer_before = self::timer_float(); 1067 1108 } 1068 1109 1069 1110 // pre-populate request args [_info] for recording original url before any modification. 1070 1111 // this will show url for denyed empty requests. … … 1073 1114 return $parsed_args; 1074 1115 } 1075 1076 1116 1077 1117 /** 1078 1118 * force logging when pre populated by oyher plugin. (from cache or error) … … 1084 1124 */ 1085 1125 function log_pre_http_request($pre, $parsed_args, $url) 1086 { 1087 if(false !== $pre){ 1126 { 1127 if(false !== $pre) 1128 { 1088 1129 // request handled by other plugin (cache or error response). 1089 1130 // request will not be sent. reponse provided by other plugin. log this to debug window. … … 1196 1237 } 1197 1238 } 1198 1239 1199 1240 return self::$requests; 1200 1241 } … … 1211 1252 { 1212 1253 case 'mode': 1213 $mode_default = ' block_smart';1254 $mode_default = 'log'; 1214 1255 $modes = self::modes(); 1215 1256 // check if mode exists … … 1347 1388 return self::$modes; 1348 1389 } 1349 1350 static public function db_truncate_field($value, $max_length)1351 { 1352 return strlen($value) >$max_length ? substr($value,0,$max_length) : $value;1353 } 1390 1391 static public function db_truncate_field($value, $max_length) 1392 { 1393 return strlen($value) > $max_length ? substr($value, 0, $max_length) : $value; 1394 } 1354 1395 1355 1396 function db_capture_page() … … 1372 1413 $log_data = apply_filters('vphrm_log_page_data', [ 1373 1414 'url' => self::page_url(), 1374 'page_type' => self::db_truncate_field( self::current_page_type(), 20),1415 'page_type' => self::db_truncate_field(self::current_page_type(), 20), 1375 1416 'runtime' => self::timer_float(), 1376 1417 'info' => json_encode($info), … … 1405 1446 // capture request to apply request limits even if not logging. 1406 1447 $this->request_log($url, $args['stream']); 1407 1408 1448 1409 1449 // show nonempty url for checkpoint. if url empty use original url. 1410 $url_cp = empty($url)?'[empty] '.(!empty($args['_info']['request_url_original'])?$args['_info']['request_url_original']:''):$url; 1411 1450 $url_cp = empty($url) ? '[empty] ' . (!empty($args['_info']['request_url_original']) ? $args['_info']['request_url_original'] : '') : $url; 1451 1452 // remove request_url_original if it matches $url 1453 if(!empty($args['_info']['request_url_original']) && $args['_info']['request_url_original'] === $url) 1454 { 1455 unset($args['_info']['request_url_original']); 1456 } 1457 1458 1412 1459 self::cp('request: ' . $url_cp); 1413 1460 … … 1448 1495 'runtime' => ( microtime(true) - $this->start_time ), 1449 1496 'date_added' => current_time('mysql'), 1450 'page_id' => self::$page_id, 1451 'request_status' => self::db_truncate_field( self::current_request_status($response), 20),1452 'request_group' => self::db_truncate_field( self::current_request_group($args), 20),1453 'request_source' => self::db_truncate_field( self::current_request_source($args), 255),1497 'page_id' => self::$page_id, 1498 'request_status' => self::db_truncate_field(self::current_request_status($response), 20), 1499 'request_group' => self::db_truncate_field(self::current_request_group($args), 20), 1500 'request_source' => self::db_truncate_field(self::current_request_source($args), 255), 1454 1501 ]); 1455 1456 1502 1457 1503 if(false !== $log_data) 1458 1504 { 1459 $wpdb->insert(self::db_table_log(), $log_data); 1505 $wpdb->insert(self::db_table_log(), $log_data); 1460 1506 1461 1507 // store last request id … … 1595 1641 static public function cp($title = '') 1596 1642 { 1597 1598 $cp_count = count(self::$cp_arr); 1599 1600 if(!strlen($title)) 1601 { 1602 $title = 'CP ' . $cp_count; 1603 } 1604 1605 $last = end(self::$cp_arr); 1606 1607 $return = array('t' => self::timer_float(), 'm' => self::cp_memory()); 1608 1609 $return['name'] = $title; 1610 1611 self::$cp_arr[] = $return; 1643 if(!self::get_option('disable_logging')) 1644 { 1645 $cp_count = count(self::$cp_arr); 1646 1647 if(!strlen($title)) 1648 { 1649 $title = 'CP ' . $cp_count; 1650 } 1651 1652 // $last = end(self::$cp_arr); 1653 1654 $return = array('t' => self::timer_float(), 'm' => self::cp_memory()); 1655 1656 $return['name'] = $title; 1657 1658 self::$cp_arr[] = $return; 1659 } 1612 1660 } 1613 1661 … … 2062 2110 */ 2063 2111 function manage_pre_http_request($pre, $parsed_args, $url) 2064 { 2112 { 2065 2113 // request not handled (cache or error) by other plugins 2066 2114 if(false === $pre) 2067 { 2115 { 2068 2116 // return error if block 2069 2117 if(self::$request_action === 'block') … … 2077 2125 return $pre; 2078 2126 } 2079 2080 2081 function manage_do_action_http_api_debug($pre, $parsed_args, $url){2127 2128 function manage_do_action_http_api_debug($pre, $parsed_args, $url) 2129 { 2082 2130 /** This action is documented in wp-includes/class-wp-http.php */ 2083 2131 do_action('http_api_debug', $pre, 'response', 'WpOrg\Requests\Requests', $parsed_args, $url); 2084 2132 } 2085 2086 2133 2087 2134 /** … … 2117 2164 function manage_perform_request_blocking_by_url($url, $info = '') 2118 2165 { 2119 $this->log_start_timer(array(), $url);2166 $this->log_start_timer(array(), $url); 2120 2167 2121 2168 self::$request_action = 'block'; … … 2587 2634 add_filter('site_transient_update_themes', [$this, 'disable_maybe_update_filter'], 10, 2); 2588 2635 add_filter('site_transient_update_core', [$this, 'disable_maybe_update_filter'], 10, 2); 2636 2637 // double implementation for block all modes. in case some plugins remove all pre_http_request actions. 2638 $this->manage_block_using_constants(); 2589 2639 } 2590 2640 … … 2605 2655 2606 2656 // default no skip 2657 return false; 2658 } 2659 2660 function manage_block_using_constants() 2661 { 2662 // current operation mode 2663 $mode = self::get_mode(); 2664 2665 $block_defined = defined('WP_HTTP_BLOCK_EXTERNAL'); 2666 $block = $block_defined ? WP_HTTP_BLOCK_EXTERNAL : false; 2667 2668 $host_defined = defined('WP_ACCESSIBLE_HOSTS'); 2669 $host = $host_defined ? WP_ACCESSIBLE_HOSTS : ''; 2670 2671 //block_external 2672 if($mode === 'block_external') 2673 { 2674 // block all external 2675 if(!$block_defined && $host == '') 2676 { 2677 // can define our own constant here 2678 define('WP_HTTP_BLOCK_EXTERNAL', true); 2679 return true; 2680 } 2681 } 2682 2683 //block_external_no_wp 2684 if($mode === 'block_external_no_wp') 2685 { 2686 // block all external 2687 if(!$block_defined && (!$host_defined || $host == '*.wordpress.org')) 2688 { 2689 // can define our own constant here 2690 define('WP_HTTP_BLOCK_EXTERNAL', true); 2691 2692 if(!$host_defined) 2693 { 2694 define('WP_ACCESSIBLE_HOSTS', '*.wordpress.org'); 2695 } 2696 return true; 2697 } 2698 } 2699 2607 2700 return false; 2608 2701 } … … 3291 3384 { 3292 3385 global $wpdb; 3293 3386 3294 3387 $version = HTTP_Requests_Manager::VERSION; 3295 3388 $db_version = HTTP_Requests_Manager::get_option('version', 0); … … 3298 3391 { 3299 3392 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); 3300 3393 3301 3394 // increase request_source varchar length to 255 3302 3395 // ALTER TABLE `wp_vphrm_log` CHANGE `request_source` `request_source` varchar(255); 3303 $wpdb->query("ALTER TABLE " .self::db_table_log()." CHANGE `request_source` `request_source` varchar(255)");3396 $wpdb->query("ALTER TABLE " . self::db_table_log() . " CHANGE `request_source` `request_source` varchar(255)"); 3304 3397 } 3305 3398 } … … 3393 3486 VPHRM(); 3394 3487 3395 3396 3397 3398 -
http-requests-manager/trunk/readme.txt
r3110053 r3141611 4 4 Tags: wp_http, log, debug, optimization, limit 5 5 Requires at least: 4.7 6 Tested up to: 6. 57 Stable tag: 1.3. 36 Tested up to: 6.6 7 Stable tag: 1.3.4 8 8 License: GPLv2 9 9 … … 192 192 == Changelog == 193 193 194 = 1.3.4 - 26 August 2024 = 195 196 * Added: Separate group view requests by core: pingback, enclosure, browse happy, serve happy, update, translation, health, oEmbed etc. 197 * Added: Show total number of requests in detail view for given page. 198 * Added: When possible force blocking rules by defining constants to (block all external) and (allow only wp requests). 199 * Fixed: Do not add cp (checkpoint) hooks if logging disabled. Prevent waste of memory. 200 * Update: Made "Only log HTTP requests" default operation mode. 201 194 202 = 1.3.3 - 30 June 2024 = 195 203 196 * Added: URL becomes empty when it is not validated by WordPress. Empty request URLs now shows clickable text [empty]. Original URL w hill be shows inside Checkpoint.204 * Added: URL becomes empty when it is not validated by WordPress. Empty request URLs now shows clickable text [empty]. Original URL will be shows inside Checkpoint. 197 205 * Added: When request responded (from cache or error) by other plugin without sending to remote server it will be labeled as 'other' and not blocked. 198 206 * Fixed: color coding requests from same page on logs page. 199 * Fixed: Requests with longer plugin name were not recorded to database because of bug. Now datab se field length increased and longer strings will be truncated to fit when needed.207 * Fixed: Requests with longer plugin name were not recorded to database because of bug. Now database field length increased and longer strings will be truncated to fit when needed. 200 208 201 209 = 1.3.2 - 26 June 2024 = -
http-requests-manager/trunk/templates/page-settings.php
r3081974 r3141611 182 182 <li><?php _e('Maximum 10 custom rules allowed. Please contact if you need more.', 'http-requests-manager'); ?></li> 183 183 </ol> 184 185 <p><b><a href="https://veppa.com/http-requests-manager/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Learn more', 'http-requests-manager') ?> →</a></b></p> 184 185 <p><b><a href="https://veppa.com/allow-wp_http-request/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Allow Request Tutorial', 'http-requests-manager') ?> →</a></b></p> 186 <p><b><a href="https://veppa.com/block-wp_http-request/?utm_source=wp&utm_medium=plugin&utm_campaign=options#doc" target="_blank"><?php _e('Block Request Tutorial', 'http-requests-manager') ?> →</a></b></p> 186 187 187 188 </div> … … 225 226 <!-- more --> 226 227 <p> 227 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/share-button/ " target="_blank">228 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/share-button/?utm_source=wp&utm_medium=plugin&utm_campaign=options" target="_blank"> 228 229 <span class="vphrm-card-val">⦿</span> 229 230 <span class="vphrm-card-name"><b class="vphrm-card-h3">Share button without plugin</b> 230 231 Fast load times, tiny code, no negative effect on Page Speed score. Free.</span> 231 232 </a> 232 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/#rankmath" target="_blank"> 233 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/speed/?utm_source=wp&utm_medium=plugin&utm_campaign=options" target="_blank"> 234 <span class="vphrm-card-val">⪫</span> 235 <span class="vphrm-card-name"><b class="vphrm-card-h3">90+ PageSpeed</b> 236 Download PDF checklist that I use to get 90+ score on WordPress websites.</span> 237 </a> 238 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#rankmath" target="_blank"> 233 239 <span class="vphrm-card-val">◢</span> 234 240 <span class="vphrm-card-name"><b class="vphrm-card-h3">Rank Math SEO plugin</b> 235 241 Optimize posts for target keyword. Content SEO checklist with best practices. Track SEO performance for each post using Google Search Console data.</span> 236 242 </a> 237 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/ #a2hosting" target="_blank">243 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#a2hosting" target="_blank"> 238 244 <span class="vphrm-card-val">a2</span> 239 245 <span class="vphrm-card-name"><b class="vphrm-card-h3">A2hosting</b> 240 246 WordPress hosting for any website. Good for beginners and has managed hosting for big sites as well.</span> 241 247 </a> 242 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/ #kwfinder" target="_blank">248 <a class="vphrm-card vphrm-card-wide" href="https://veppa.com/tools/?utm_source=wp&utm_medium=plugin&utm_campaign=options#kwfinder" target="_blank"> 243 249 <span class="vphrm-card-val">𝕂</span> 244 250 <span class="vphrm-card-name"><b class="vphrm-card-h3">KWfinder</b> … … 264 270 <span class="vphrm-card-name"><?php _e('Support forum', 'http-requests-manager'); ?></span> 265 271 </a> 272 <a class="vphrm-card vphrm-card-wide" href="https://youtube.com/playlist?list=PLvn-qBzU0II7b5D4OYDnKpNpuvxiM0f4b" target="_blank"> 273 <span class="vphrm-card-val">►</span> 274 <span class="vphrm-card-name"><?php _e('Video tutorials', 'http-requests-manager'); ?></span> 275 </a> 266 276 </p> 267 277 … … 283 293 </a> 284 294 </p> 295 296 <p><b><?php _e('Thank you!', 'http-requests-manager') ?></b></p> 285 297 286 298 <p><a href="#top"><?php _e('Back to top', 'http-requests-manager') ?> ↑</a></p>
Note: See TracChangeset
for help on using the changeset viewer.