Version Description
- New Feature: Compatibility with Cloudflare APO
- Improvement: Better resilience to network related issues
- Improvement: Faster cache purge
- Improvement: Overall stability improvements
- Deprecation: Removed the Invalidate All Cache option. The invalidate action is much better suited for single page invalidations.
Download this release
Release Info
Developer | nitropack |
Plugin | NitroPack |
Version | 1.5.0 |
Comparing to | |
See all releases |
Code changes from version 1.4.1 to 1.5.0
- cf-helper.php +26 -0
- constants.php +3 -1
- diagnostics.php +20 -2
- functions.php +188 -56
- integrations.php +59 -4
- main.php +7 -3
- nitropack-sdk/NitroPack/SDK/Api.php +28 -8
- nitropack-sdk/NitroPack/SDK/Api/Base.php +73 -3
- nitropack-sdk/NitroPack/SDK/Api/Cache.php +24 -1
- nitropack-sdk/NitroPack/SDK/Api/RequestMaker.php +12 -0
- nitropack-sdk/NitroPack/SDK/Api/SecureRequestMaker.php +12 -0
- nitropack-sdk/NitroPack/SDK/Api/SignedBase.php +5 -0
- nitropack-sdk/NitroPack/SDK/Backlog.php +229 -0
- nitropack-sdk/NitroPack/SDK/FileHandle.php +18 -0
- nitropack-sdk/NitroPack/SDK/Filesystem.php +44 -0
- nitropack-sdk/NitroPack/SDK/HealthStatus.php +8 -0
- nitropack-sdk/NitroPack/SDK/NitroPack.php +95 -11
- nitropack-sdk/NitroPack/SDK/ServiceDownException.php +4 -0
- nitropack-sdk/NitroPack/SDK/StorageDriver/Disk.php +73 -0
- nitropack-sdk/NitroPack/SDK/StorageDriver/DiskFileHandle.php +6 -0
- nitropack-sdk/NitroPack/SDK/StorageDriver/Redis.php +223 -4
- nitropack-sdk/NitroPack/SDK/StorageDriver/RedisFileHandle.php +6 -0
- readme.txt +9 -2
- view/dashboard.php +4 -3
- view/diag.php +1 -1
cf-helper.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class NitroPack_CF_Helper extends \CF\WordPress\Hooks {
|
4 |
+
public function isApoEnabled() {
|
5 |
+
return $this->isAutomaticPlatformOptimizationEnabled();
|
6 |
+
}
|
7 |
+
|
8 |
+
public function purgeUrl($url) {
|
9 |
+
$wpDomainList = $this->integrationAPI->getDomainList();
|
10 |
+
if (!count($wpDomainList)) {
|
11 |
+
return;
|
12 |
+
}
|
13 |
+
$wpDomain = $wpDomainList[0];
|
14 |
+
$urls = [$url];
|
15 |
+
|
16 |
+
$zoneTag = $this->api->getZoneTag($wpDomain);
|
17 |
+
|
18 |
+
if (isset($zoneTag) && !empty($urls)) {
|
19 |
+
$chunks = array_chunk($urls, 30);
|
20 |
+
|
21 |
+
foreach ($chunks as $chunk) {
|
22 |
+
$this->api->zonePurgeFiles($zoneTag, $chunk);
|
23 |
+
}
|
24 |
+
}
|
25 |
+
}
|
26 |
+
}
|
constants.php
CHANGED
@@ -6,11 +6,13 @@ function nitropack_trailingslashit($string) {
|
|
6 |
return rtrim( $string, '/\\' ) . '/';
|
7 |
}
|
8 |
|
9 |
-
define( 'NITROPACK_VERSION', '1.
|
10 |
define( 'NITROPACK_OPTION_GROUP', 'nitropack' );
|
11 |
define( 'NITROPACK_DATA_DIR', nitropack_trailingslashit(WP_CONTENT_DIR) . 'nitropack' );
|
12 |
define( 'NITROPACK_CONFIG_FILE', nitropack_trailingslashit(NITROPACK_DATA_DIR) . 'config.json' );
|
13 |
define( 'NITROPACK_PLUGIN_DIR', nitropack_trailingslashit(dirname(__FILE__)));
|
|
|
|
|
14 |
|
15 |
if (!defined("NITROPACK_USE_REDIS")) define("NITROPACK_USE_REDIS", false); // Set this to true to enable storing cache in Redis
|
16 |
if (!defined("NITROPACK_REDIS_HOST")) define("NITROPACK_REDIS_HOST", "127.0.0.1"); // Set this to the IP of your Redis server
|
6 |
return rtrim( $string, '/\\' ) . '/';
|
7 |
}
|
8 |
|
9 |
+
define( 'NITROPACK_VERSION', '1.5.0' );
|
10 |
define( 'NITROPACK_OPTION_GROUP', 'nitropack' );
|
11 |
define( 'NITROPACK_DATA_DIR', nitropack_trailingslashit(WP_CONTENT_DIR) . 'nitropack' );
|
12 |
define( 'NITROPACK_CONFIG_FILE', nitropack_trailingslashit(NITROPACK_DATA_DIR) . 'config.json' );
|
13 |
define( 'NITROPACK_PLUGIN_DIR', nitropack_trailingslashit(dirname(__FILE__)));
|
14 |
+
define( 'NITROPACK_INTEGRATIONS_ACTION', "nitropack_integrations_ready");
|
15 |
+
define( 'NITROPACK_HEARTBEAT_INTERVAL', 60*5); // 5min
|
16 |
|
17 |
if (!defined("NITROPACK_USE_REDIS")) define("NITROPACK_USE_REDIS", false); // Set this to true to enable storing cache in Redis
|
18 |
if (!defined("NITROPACK_REDIS_HOST")) define("NITROPACK_REDIS_HOST", "127.0.0.1"); // Set this to the IP of your Redis server
|
diagnostics.php
CHANGED
@@ -20,6 +20,23 @@ function npdiag_helper_trailingslashit($string) {
|
|
20 |
return rtrim( $string, '/\\' ) . '/';
|
21 |
}
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
function npdiag_get_general_info() {
|
24 |
global $wp_version;
|
25 |
if (null !== $nitro = get_nitropack_sdk()) {
|
@@ -32,17 +49,18 @@ function npdiag_get_general_info() {
|
|
32 |
} else {
|
33 |
$probe_result = 'Error: Cannot get SDK instance';
|
34 |
}
|
35 |
-
|
36 |
$info = array(
|
37 |
'Nitro_WP_version' => !empty($wp_version) ? $wp_version : get_bloginfo('version'),
|
38 |
'Nitro_Version' => defined('NITROPACK_VERSION') ? NITROPACK_VERSION : 'Undefined',
|
39 |
'Nitro_API_Connection' => $probe_result,
|
40 |
'Nitro_SDK_Version' => defined('NitroPack\SDK\Nitropack::VERSION') ? NitroPack\SDK\Nitropack::VERSION : 'Undefined',
|
|
|
41 |
'Advanced_Cache_Version' => defined('NITROPACK_ADVANCED_CACHE_VERSION') ? NITROPACK_ADVANCED_CACHE_VERSION : 'Undefined',
|
42 |
'Nitro_Absolute_Path' => defined('ABSPATH') ? ABSPATH : 'Undefined',
|
43 |
'Nitro_Plugin_Direcotry' => defined('NITROPACK_PLUGIN_DIR') ? NITROPACK_PLUGIN_DIR : dirname(__FILE__),
|
44 |
'Nitro_Data_Directory' => defined('NITROPACK_DATA_DIR') ? NITROPACK_DATA_DIR : 'Undefined',
|
45 |
-
'Nitro_Config_File' => defined('NITROPACK_CONFIG_FILE') ? NITROPACK_CONFIG_FILE : 'Undefined'
|
|
|
46 |
);
|
47 |
|
48 |
if (defined("NITROPACK_VERSION") && defined("NITROPACK_ADVANCED_CACHE_VERSION") && NITROPACK_VERSION == NITROPACK_ADVANCED_CACHE_VERSION && nitropack_is_dropin_cache_allowed()) {
|
20 |
return rtrim( $string, '/\\' ) . '/';
|
21 |
}
|
22 |
|
23 |
+
function npdiag_helper_compare_webhooks($nitro_sdk) {
|
24 |
+
try {
|
25 |
+
$siteConfig = nitropack_get_site_config();
|
26 |
+
if (!empty($siteConfig['siteId'])) {
|
27 |
+
$WHToken = nitropack_generate_webhook_token($siteConfig['siteId']);
|
28 |
+
$constructedWH = new \NitroPack\Url(strtolower(get_home_url())) . '?nitroWebhook=config&token=' . $WHToken;
|
29 |
+
$storedWH = $nitro_sdk->getApi()->getWebhook("config");
|
30 |
+
$matchResult = ($constructedWH == $storedWH) ? 'OK' : 'Warning: Webhooks do not match this site';
|
31 |
+
} else {
|
32 |
+
$matchResult = 'An empty SiteID was returned from site config.';
|
33 |
+
}
|
34 |
+
return $matchResult;
|
35 |
+
} catch (\Exception $e) {
|
36 |
+
return $e->getMessage();
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
function npdiag_get_general_info() {
|
41 |
global $wp_version;
|
42 |
if (null !== $nitro = get_nitropack_sdk()) {
|
49 |
} else {
|
50 |
$probe_result = 'Error: Cannot get SDK instance';
|
51 |
}
|
|
|
52 |
$info = array(
|
53 |
'Nitro_WP_version' => !empty($wp_version) ? $wp_version : get_bloginfo('version'),
|
54 |
'Nitro_Version' => defined('NITROPACK_VERSION') ? NITROPACK_VERSION : 'Undefined',
|
55 |
'Nitro_API_Connection' => $probe_result,
|
56 |
'Nitro_SDK_Version' => defined('NitroPack\SDK\Nitropack::VERSION') ? NitroPack\SDK\Nitropack::VERSION : 'Undefined',
|
57 |
+
'Nitro_WP_Cache' => defined('WP_CACHE') ? (WP_CACHE ? 'OK for drop-in' : 'Turned off') : 'Undefined',
|
58 |
'Advanced_Cache_Version' => defined('NITROPACK_ADVANCED_CACHE_VERSION') ? NITROPACK_ADVANCED_CACHE_VERSION : 'Undefined',
|
59 |
'Nitro_Absolute_Path' => defined('ABSPATH') ? ABSPATH : 'Undefined',
|
60 |
'Nitro_Plugin_Direcotry' => defined('NITROPACK_PLUGIN_DIR') ? NITROPACK_PLUGIN_DIR : dirname(__FILE__),
|
61 |
'Nitro_Data_Directory' => defined('NITROPACK_DATA_DIR') ? NITROPACK_DATA_DIR : 'Undefined',
|
62 |
+
'Nitro_Config_File' => defined('NITROPACK_CONFIG_FILE') ? NITROPACK_CONFIG_FILE : 'Undefined',
|
63 |
+
'Nitro_Webhooks' => $nitro ? npdiag_helper_compare_webhooks($nitro) : 'Error: Cannot get SDK instance'
|
64 |
);
|
65 |
|
66 |
if (defined("NITROPACK_VERSION") && defined("NITROPACK_ADVANCED_CACHE_VERSION") && NITROPACK_VERSION == NITROPACK_ADVANCED_CACHE_VERSION && nitropack_is_dropin_cache_allowed()) {
|
functions.php
CHANGED
@@ -804,7 +804,7 @@ function nitropack_is_advanced_cache_allowed() {
|
|
804 |
|
805 |
function nitropack_admin_notices() {
|
806 |
if (!empty($_COOKIE["nitropack_after_activate_notice"])) {
|
807 |
-
nitropack_print_notice("info", "<script>document.cookie = 'nitropack_after_activate_notice=1; expires=Thu, 01 Jan 1970 00:00:01 GMT;';</script>NitroPack has been successfully activated, but it is not connected yet. Please go to <a href='" . admin_url( 'options-general.php?page=nitropack' ) . "'>its settings</a> page to connect it in order to start
|
808 |
}
|
809 |
|
810 |
nitropack_print_hosting_notice();
|
@@ -821,7 +821,7 @@ function nitropack_print_hosting_notice() {
|
|
821 |
$documentedHostingSetups = array(
|
822 |
"flywheel" => array(
|
823 |
"name" => "Flywheel",
|
824 |
-
"helpUrl" => "https://help.nitropack.io/en/articles/
|
825 |
),
|
826 |
"wpengine" => array(
|
827 |
"name" => "WP Engine",
|
@@ -1008,7 +1008,7 @@ function nitropack_sdk_invalidate($url = NULL, $tag = NULL, $reason = NULL) {
|
|
1008 |
do_action('nitropack_integration_purge_all');
|
1009 |
}
|
1010 |
} catch (\Exception $e) {
|
1011 |
-
// Exception while signaling
|
1012 |
}
|
1013 |
} catch (\Exception $e) {
|
1014 |
return false;
|
@@ -1020,6 +1020,124 @@ function nitropack_sdk_invalidate($url = NULL, $tag = NULL, $reason = NULL) {
|
|
1020 |
return false;
|
1021 |
}
|
1022 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1023 |
function nitropack_sdk_purge($url = NULL, $tag = NULL, $reason = NULL, $type = \NitroPack\SDK\PurgeType::COMPLETE) {
|
1024 |
if (null !== $nitro = get_nitropack_sdk()) {
|
1025 |
try {
|
@@ -1034,6 +1152,7 @@ function nitropack_sdk_purge($url = NULL, $tag = NULL, $reason = NULL, $type = \
|
|
1034 |
}
|
1035 |
}
|
1036 |
|
|
|
1037 |
$nitro->purgeCache($url, $tag, $type, $reason);
|
1038 |
|
1039 |
try {
|
@@ -1047,7 +1166,7 @@ function nitropack_sdk_purge($url = NULL, $tag = NULL, $reason = NULL, $type = \
|
|
1047 |
do_action('nitropack_integration_purge_all');
|
1048 |
}
|
1049 |
} catch (\Exception $e) {
|
1050 |
-
// Exception while signaling
|
1051 |
}
|
1052 |
} catch (\Exception $e) {
|
1053 |
return false;
|
@@ -1151,6 +1270,7 @@ function nitropack_log_invalidate($url = NULL, $tag = NULL, $reason = NULL) {
|
|
1151 |
foreach ($tag as $tagSingle) {
|
1152 |
nitropack_log_invalidate($url, $tagSingle, $reason);
|
1153 |
}
|
|
|
1154 |
}
|
1155 |
|
1156 |
$keyBase = "";
|
@@ -1946,6 +2066,10 @@ function nitropack_warmup_stats() {
|
|
1946 |
try {
|
1947 |
$stats = $nitro->getApi()->getWarmupStats();
|
1948 |
} catch (\Exception $e) {
|
|
|
|
|
|
|
|
|
1949 |
}
|
1950 |
|
1951 |
nitropack_json_and_exit(array(
|
@@ -2042,7 +2166,7 @@ function nitropack_update_current_blog_config($siteId, $siteSecret, $blogId, $en
|
|
2042 |
"hosting" => $hosting,
|
2043 |
"alwaysBuffer" => $alwaysBuffer,
|
2044 |
"isEzoicActive" => nitropack_is_ezoic_active(),
|
2045 |
-
"
|
2046 |
"isDlmActive" => nitropack_is_dlm_active(),
|
2047 |
"dlm_downloading_url" => nitropack_is_dlm_active() ? nitropack_dlm_downloading_url() : NULL,
|
2048 |
"dlm_download_endpoint" => nitropack_is_dlm_active() ? nitropack_dlm_download_endpoint() : NULL,
|
@@ -2173,69 +2297,73 @@ function nitropack_handle_request($servedFrom = "unknown") {
|
|
2173 |
$siteConfig = nitropack_get_site_config();
|
2174 |
if ( $siteConfig && null !== $nitro = get_nitropack_sdk($siteConfig["siteId"], $siteConfig["siteSecret"]) ) {
|
2175 |
if (is_valid_nitropack_webhook()) {
|
2176 |
-
if (did_action(
|
2177 |
nitropack_handle_webhook();
|
2178 |
} else {
|
2179 |
-
add_action(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2180 |
}
|
2181 |
} else {
|
2182 |
-
|
2183 |
-
|
2184 |
-
|
2185 |
-
|
2186 |
-
|
|
|
|
|
|
|
2187 |
}
|
2188 |
-
|
2189 |
-
|
2190 |
-
|
2191 |
-
|
2192 |
-
// If this is an AJAX request, check whether the referer is cachable - this is needed for cases when NitroPack's "Enabled URLs" option is being used to whitelist certain URLs.
|
2193 |
-
// If we are not checking the referer, the AJAX requests on these pages can fail.
|
2194 |
-
$urlToCheck = nitropack_is_ajax() && !empty($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : $nitro->getUrl();
|
2195 |
-
if ($nitro->isAllowedUrl($urlToCheck)) {
|
2196 |
-
add_filter( 'nonce_life', 'nitropack_extend_nonce_life' );
|
2197 |
}
|
2198 |
|
2199 |
-
if ($nitro->
|
2200 |
-
|
2201 |
-
|
|
|
|
|
|
|
|
|
2202 |
}
|
2203 |
|
2204 |
-
if ($
|
2205 |
-
|
2206 |
-
|
2207 |
-
$cacheControlOverride = "public,max-age=30";
|
2208 |
-
if ($siteConfig["hosting"] == "siteground") {
|
2209 |
-
header('X-Cache-Enabled: True');
|
2210 |
-
}
|
2211 |
-
}
|
2212 |
|
2213 |
-
|
2214 |
-
|
2215 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2216 |
|
2217 |
-
|
|
|
|
|
2218 |
header('X-Nitro-Cache-From: ' . $servedFrom);
|
2219 |
$nitro->pageCache->readfile();
|
2220 |
exit;
|
2221 |
} else {
|
2222 |
-
|
2223 |
-
if (nitropack_is_warmup_request()) {
|
2224 |
-
$nitro->hasRemoteCache("default"); // Only ping the API letting our service know that this page must be cached.
|
2225 |
-
exit; // No need to continue handling this request. The response is not important.
|
2226 |
-
} else if (nitropack_is_lighthouse_request() || nitropack_is_gtmetrix_request() || nitropack_is_pingdom_request()) {
|
2227 |
-
$nitro->hasRemoteCache("default"); // Ping the API letting our service know that this page must be cached.
|
2228 |
-
}
|
2229 |
-
|
2230 |
-
$nitro->pageCache->useInvalidated(true);
|
2231 |
-
if ($nitro->hasLocalCache()) {
|
2232 |
-
header('X-Nitro-Cache: STALE');
|
2233 |
-
header('X-Nitro-Cache-From: ' . $servedFrom);
|
2234 |
-
$nitro->pageCache->readfile();
|
2235 |
-
exit;
|
2236 |
-
} else {
|
2237 |
-
$nitro->pageCache->useInvalidated(false);
|
2238 |
-
}
|
2239 |
}
|
2240 |
}
|
2241 |
}
|
@@ -2255,7 +2383,7 @@ function nitropack_is_dropin_cache_allowed() {
|
|
2255 |
|
2256 |
function nitropack_get_integration_setup_event() {
|
2257 |
$siteConfig = nitropack_get_site_config();
|
2258 |
-
if ($siteConfig && !empty($siteConfig["
|
2259 |
return "plugins_loaded";
|
2260 |
}
|
2261 |
|
@@ -2356,7 +2484,7 @@ function nitropack_admin_bar_script($hook) {
|
|
2356 |
wp_localize_script( 'nitropack_admin_bar_menu_script', 'frontendajax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' )));
|
2357 |
}
|
2358 |
|
2359 |
-
function
|
2360 |
wp_enqueue_style( 'load-fa', plugin_dir_url(__FILE__) . 'view/stylesheet/fontawesome/font-awesome.min.css?np_v=' . NITROPACK_VERSION);
|
2361 |
}
|
2362 |
|
@@ -2466,7 +2594,7 @@ function nitropack_plugin_notices() {
|
|
2466 |
} else {
|
2467 |
if (
|
2468 |
(!array_key_exists("isEzoicActive", $siteConfig) || $siteConfig["isEzoicActive"] !== nitropack_is_ezoic_active()) ||
|
2469 |
-
(!array_key_exists("
|
2470 |
(!array_key_exists("isDlmActive", $siteConfig) || $siteConfig["isDlmActive"] !== nitropack_is_dlm_active())
|
2471 |
) {
|
2472 |
if (!nitropack_update_current_blog_config($siteId, $siteSecret, $blogId)) {
|
@@ -2511,6 +2639,10 @@ function nitropack_plugin_notices() {
|
|
2511 |
return $npPluginNotices;
|
2512 |
}
|
2513 |
|
|
|
|
|
|
|
|
|
2514 |
function nitropack_display_admin_notices() {
|
2515 |
$noticesArray = nitropack_plugin_notices();
|
2516 |
foreach($noticesArray as $type => $notices){
|
804 |
|
805 |
function nitropack_admin_notices() {
|
806 |
if (!empty($_COOKIE["nitropack_after_activate_notice"])) {
|
807 |
+
nitropack_print_notice("info", "<script>document.cookie = 'nitropack_after_activate_notice=1; expires=Thu, 01 Jan 1970 00:00:01 GMT;';</script>NitroPack has been successfully activated, but it is not connected yet. Please go to <a href='" . admin_url( 'options-general.php?page=nitropack' ) . "'>its settings</a> page to connect it in order to start optimizing your site!");
|
808 |
}
|
809 |
|
810 |
nitropack_print_hosting_notice();
|
821 |
$documentedHostingSetups = array(
|
822 |
"flywheel" => array(
|
823 |
"name" => "Flywheel",
|
824 |
+
"helpUrl" => "https://help.nitropack.io/en/articles/4280090-delayed-content-updates-only-for-flywheel-hosting-users"
|
825 |
),
|
826 |
"wpengine" => array(
|
827 |
"name" => "WP Engine",
|
1008 |
do_action('nitropack_integration_purge_all');
|
1009 |
}
|
1010 |
} catch (\Exception $e) {
|
1011 |
+
// Exception while signaling 3rd party integration addons to purge their cache
|
1012 |
}
|
1013 |
} catch (\Exception $e) {
|
1014 |
return false;
|
1020 |
return false;
|
1021 |
}
|
1022 |
|
1023 |
+
/* Start Heartbeat Related Functions */
|
1024 |
+
function nitropack_print_heartbeat_script() {
|
1025 |
+
if (!nitropack_is_optimizer_request() && !nitropack_is_heartbeat_running() && time() - nitropack_last_heartbeat() > NITROPACK_HEARTBEAT_INTERVAL) {
|
1026 |
+
if (defined("NITROPACK_HEARTBEAT_PRINTED")) return;
|
1027 |
+
define("NITROPACK_HEARTBEAT_PRINTED", true);
|
1028 |
+
echo nitropack_get_heartbeat_script();
|
1029 |
+
}
|
1030 |
+
}
|
1031 |
+
|
1032 |
+
function nitropack_get_heartbeat_script() {
|
1033 |
+
$siteConfig = nitropack_get_site_config();
|
1034 |
+
if ($siteConfig && !empty($siteConfig["siteId"]) && !empty($siteConfig["siteSecret"])) {
|
1035 |
+
if (null !== $nitro = get_nitropack_sdk($siteConfig["siteId"], $siteConfig["siteSecret"]) ) {
|
1036 |
+
if (is_admin()) {
|
1037 |
+
$credentials = "same-origin";
|
1038 |
+
} else {
|
1039 |
+
$credentials = "omit";
|
1040 |
+
}
|
1041 |
+
|
1042 |
+
return "
|
1043 |
+
<script nitro-exclude>
|
1044 |
+
var heartbeatData = new FormData(); heartbeatData.append('nitroHeartbeat', '1');
|
1045 |
+
fetch(location.href, {method: 'POST', body: heartbeatData, credentials: '$credentials'});
|
1046 |
+
</script>";
|
1047 |
+
}
|
1048 |
+
}
|
1049 |
+
}
|
1050 |
+
|
1051 |
+
function is_valid_nitropack_heartbeat() {
|
1052 |
+
return !empty($_POST['nitroHeartbeat']);
|
1053 |
+
}
|
1054 |
+
|
1055 |
+
function nitropack_get_heartbeat_file() {
|
1056 |
+
if (null !== $nitro = get_nitropack_sdk()) {
|
1057 |
+
return nitropack_trailingslashit($nitro->getCacheDir()) . "heartbeat";
|
1058 |
+
} else {
|
1059 |
+
return nitropack_trailingslashit(NITROPACK_DATA_DIR) . "heartbeat";
|
1060 |
+
}
|
1061 |
+
}
|
1062 |
+
|
1063 |
+
function nitropack_last_heartbeat() {
|
1064 |
+
if (null !== $nitro = get_nitropack_sdk()) {
|
1065 |
+
try {
|
1066 |
+
return \NitroPack\SDK\Filesystem::fileMTime(nitropack_get_heartbeat_file());
|
1067 |
+
} catch (\Exception $e) {
|
1068 |
+
return 0;
|
1069 |
+
}
|
1070 |
+
}
|
1071 |
+
}
|
1072 |
+
|
1073 |
+
function nitropack_is_heartbeat_running() {
|
1074 |
+
if (null !== $nitro = get_nitropack_sdk()) {
|
1075 |
+
try {
|
1076 |
+
$heartbeatContent = \NitroPack\SDK\Filesystem::fileGetContents(nitropack_get_heartbeat_file());
|
1077 |
+
if ($heartbeatContent == "1") {
|
1078 |
+
return time() - nitropack_last_heartbeat() < NITROPACK_HEARTBEAT_INTERVAL;
|
1079 |
+
}
|
1080 |
+
} catch (\Exception $e) {
|
1081 |
+
return false;
|
1082 |
+
}
|
1083 |
+
}
|
1084 |
+
}
|
1085 |
+
|
1086 |
+
function nitropack_handle_heartbeat() {
|
1087 |
+
session_write_close();
|
1088 |
+
if (null !== $nitro = get_nitropack_sdk()) {
|
1089 |
+
try {
|
1090 |
+
\NitroPack\SDK\Filesystem::filePutContents(nitropack_get_heartbeat_file(), 1);
|
1091 |
+
if (nitropack_healthcheck()) {
|
1092 |
+
nitropack_flush_backlog();
|
1093 |
+
}
|
1094 |
+
nitropack_cache_cleanup();
|
1095 |
+
\NitroPack\SDK\Filesystem::filePutContents(nitropack_get_heartbeat_file(), 0);
|
1096 |
+
} catch (\Exception $e) {
|
1097 |
+
return false;
|
1098 |
+
}
|
1099 |
+
}
|
1100 |
+
exit;
|
1101 |
+
}
|
1102 |
+
|
1103 |
+
function nitropack_healthcheck() {
|
1104 |
+
if (null !== $nitro = get_nitropack_sdk()) {
|
1105 |
+
return $nitro->getHealthStatus() == \NitroPack\SDK\HealthStatus::HEALTHY || $nitro->checkHealthStatus() == \NitroPack\SDK\HealthStatus::HEALTHY;
|
1106 |
+
}
|
1107 |
+
return true;
|
1108 |
+
}
|
1109 |
+
|
1110 |
+
function nitropack_flush_backlog() {
|
1111 |
+
if (null !== $nitro = get_nitropack_sdk()) {
|
1112 |
+
try {
|
1113 |
+
if ($nitro->backlog->exists()) {
|
1114 |
+
$nitro->backlog->replay(30);
|
1115 |
+
}
|
1116 |
+
} catch (\Exception $e) {
|
1117 |
+
return false;
|
1118 |
+
}
|
1119 |
+
}
|
1120 |
+
return true;
|
1121 |
+
}
|
1122 |
+
|
1123 |
+
function nitropack_cache_cleanup() {
|
1124 |
+
if (null !== $nitro = get_nitropack_sdk()) {
|
1125 |
+
$cacheDirParent = dirname($nitro->getCacheDir());
|
1126 |
+
$entries = scandir($cacheDirParent);
|
1127 |
+
foreach ($entries as $entry) {
|
1128 |
+
if (strpos($entry, ".stale.") !== false) {
|
1129 |
+
$cacheDir = nitropack_trailingslashit($cacheDirParent) . $entry;
|
1130 |
+
try {
|
1131 |
+
\NitroPack\SDK\Filesystem::deleteDir($cacheDir);
|
1132 |
+
} catch (\Exception $e) {
|
1133 |
+
// TODO: Log this
|
1134 |
+
}
|
1135 |
+
}
|
1136 |
+
}
|
1137 |
+
}
|
1138 |
+
}
|
1139 |
+
/* End Heartbeat Related Functions */
|
1140 |
+
|
1141 |
function nitropack_sdk_purge($url = NULL, $tag = NULL, $reason = NULL, $type = \NitroPack\SDK\PurgeType::COMPLETE) {
|
1142 |
if (null !== $nitro = get_nitropack_sdk()) {
|
1143 |
try {
|
1152 |
}
|
1153 |
}
|
1154 |
|
1155 |
+
$nitro->purgeLocalCache(true);
|
1156 |
$nitro->purgeCache($url, $tag, $type, $reason);
|
1157 |
|
1158 |
try {
|
1166 |
do_action('nitropack_integration_purge_all');
|
1167 |
}
|
1168 |
} catch (\Exception $e) {
|
1169 |
+
// Exception while signaling 3rd party integration addons to purge their cache
|
1170 |
}
|
1171 |
} catch (\Exception $e) {
|
1172 |
return false;
|
1270 |
foreach ($tag as $tagSingle) {
|
1271 |
nitropack_log_invalidate($url, $tagSingle, $reason);
|
1272 |
}
|
1273 |
+
return;
|
1274 |
}
|
1275 |
|
1276 |
$keyBase = "";
|
2066 |
try {
|
2067 |
$stats = $nitro->getApi()->getWarmupStats();
|
2068 |
} catch (\Exception $e) {
|
2069 |
+
nitropack_json_and_exit(array(
|
2070 |
+
"type" => "error",
|
2071 |
+
"message" => "Error! There was an error while fetching warmup stats!"
|
2072 |
+
));
|
2073 |
}
|
2074 |
|
2075 |
nitropack_json_and_exit(array(
|
2166 |
"hosting" => $hosting,
|
2167 |
"alwaysBuffer" => $alwaysBuffer,
|
2168 |
"isEzoicActive" => nitropack_is_ezoic_active(),
|
2169 |
+
"isLateIntegrationInitRequired" => nitropack_is_late_integration_init_required(),
|
2170 |
"isDlmActive" => nitropack_is_dlm_active(),
|
2171 |
"dlm_downloading_url" => nitropack_is_dlm_active() ? nitropack_dlm_downloading_url() : NULL,
|
2172 |
"dlm_download_endpoint" => nitropack_is_dlm_active() ? nitropack_dlm_download_endpoint() : NULL,
|
2297 |
$siteConfig = nitropack_get_site_config();
|
2298 |
if ( $siteConfig && null !== $nitro = get_nitropack_sdk($siteConfig["siteId"], $siteConfig["siteSecret"]) ) {
|
2299 |
if (is_valid_nitropack_webhook()) {
|
2300 |
+
if (did_action(NITROPACK_INTEGRATIONS_ACTION)) {
|
2301 |
nitropack_handle_webhook();
|
2302 |
} else {
|
2303 |
+
add_action(NITROPACK_INTEGRATIONS_ACTION, 'nitropack_handle_webhook');
|
2304 |
+
}
|
2305 |
+
} else if (is_valid_nitropack_beacon()) {
|
2306 |
+
if (did_action(NITROPACK_INTEGRATIONS_ACTION)) {
|
2307 |
+
nitropack_handle_beacon();
|
2308 |
+
} else {
|
2309 |
+
add_action(NITROPACK_INTEGRATIONS_ACTION, 'nitropack_handle_beacon');
|
2310 |
+
}
|
2311 |
+
} else if (is_valid_nitropack_heartbeat()) {
|
2312 |
+
if (did_action(NITROPACK_INTEGRATIONS_ACTION)) {
|
2313 |
+
nitropack_handle_heartbeat();
|
2314 |
+
} else {
|
2315 |
+
add_action(NITROPACK_INTEGRATIONS_ACTION, 'nitropack_handle_heartbeat');
|
2316 |
}
|
2317 |
} else {
|
2318 |
+
$GLOBALS["NitroPack.instance"] = $nitro;
|
2319 |
+
if (nitropack_passes_cookie_requirements()) {
|
2320 |
+
// Check whether the current URL is cacheable
|
2321 |
+
// If this is an AJAX request, check whether the referer is cachable - this is needed for cases when NitroPack's "Enabled URLs" option is being used to whitelist certain URLs.
|
2322 |
+
// If we are not checking the referer, the AJAX requests on these pages can fail.
|
2323 |
+
$urlToCheck = nitropack_is_ajax() && !empty($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : $nitro->getUrl();
|
2324 |
+
if ($nitro->isAllowedUrl($urlToCheck)) {
|
2325 |
+
add_filter( 'nonce_life', 'nitropack_extend_nonce_life' );
|
2326 |
}
|
2327 |
+
|
2328 |
+
if ($nitro->isCacheAllowed()) {
|
2329 |
+
if (!empty($siteConfig["compression"])) {
|
2330 |
+
$nitro->enableCompression();
|
|
|
|
|
|
|
|
|
|
|
2331 |
}
|
2332 |
|
2333 |
+
if ($nitro->hasLocalCache()) {
|
2334 |
+
$cacheControlOverride = defined("NITROPACK_CACHE_CONTROL_OVERRIDE") ? NITROPACK_CACHE_CONTROL_OVERRIDE : NULL;
|
2335 |
+
if (!$cacheControlOverride && !empty($siteConfig["hosting"]) && in_array($siteConfig["hosting"], array("pagely", "siteground")) ) {
|
2336 |
+
$cacheControlOverride = "public,max-age=30";
|
2337 |
+
if ($siteConfig["hosting"] == "siteground") {
|
2338 |
+
header('X-Cache-Enabled: True');
|
2339 |
+
}
|
2340 |
}
|
2341 |
|
2342 |
+
if ($cacheControlOverride) {
|
2343 |
+
header('Cache-Control: ' . $cacheControlOverride);
|
2344 |
+
}
|
|
|
|
|
|
|
|
|
|
|
2345 |
|
2346 |
+
header('X-Nitro-Cache: HIT');
|
2347 |
+
header('X-Nitro-Cache-From: ' . $servedFrom);
|
2348 |
+
$nitro->pageCache->readfile();
|
2349 |
+
exit;
|
2350 |
+
} else {
|
2351 |
+
// We need the following if..else block to handle bot requests which will not be firing our beacon
|
2352 |
+
if (nitropack_is_warmup_request()) {
|
2353 |
+
$nitro->hasRemoteCache("default"); // Only ping the API letting our service know that this page must be cached.
|
2354 |
+
exit; // No need to continue handling this request. The response is not important.
|
2355 |
+
} else if (nitropack_is_lighthouse_request() || nitropack_is_gtmetrix_request() || nitropack_is_pingdom_request()) {
|
2356 |
+
$nitro->hasRemoteCache("default"); // Ping the API letting our service know that this page must be cached.
|
2357 |
+
}
|
2358 |
|
2359 |
+
$nitro->pageCache->useInvalidated(true);
|
2360 |
+
if ($nitro->hasLocalCache()) {
|
2361 |
+
header('X-Nitro-Cache: STALE');
|
2362 |
header('X-Nitro-Cache-From: ' . $servedFrom);
|
2363 |
$nitro->pageCache->readfile();
|
2364 |
exit;
|
2365 |
} else {
|
2366 |
+
$nitro->pageCache->useInvalidated(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2367 |
}
|
2368 |
}
|
2369 |
}
|
2383 |
|
2384 |
function nitropack_get_integration_setup_event() {
|
2385 |
$siteConfig = nitropack_get_site_config();
|
2386 |
+
if ($siteConfig && !empty($siteConfig["isLateIntegrationInitRequired"])) {
|
2387 |
return "plugins_loaded";
|
2388 |
}
|
2389 |
|
2484 |
wp_localize_script( 'nitropack_admin_bar_menu_script', 'frontendajax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' )));
|
2485 |
}
|
2486 |
|
2487 |
+
function nitropack_enqueue_load_fa() {
|
2488 |
wp_enqueue_style( 'load-fa', plugin_dir_url(__FILE__) . 'view/stylesheet/fontawesome/font-awesome.min.css?np_v=' . NITROPACK_VERSION);
|
2489 |
}
|
2490 |
|
2594 |
} else {
|
2595 |
if (
|
2596 |
(!array_key_exists("isEzoicActive", $siteConfig) || $siteConfig["isEzoicActive"] !== nitropack_is_ezoic_active()) ||
|
2597 |
+
(!array_key_exists("isLateIntegrationInitRequired", $siteConfig) || $siteConfig["isLateIntegrationInitRequired"] !== nitropack_is_late_integration_init_required()) ||
|
2598 |
(!array_key_exists("isDlmActive", $siteConfig) || $siteConfig["isDlmActive"] !== nitropack_is_dlm_active())
|
2599 |
) {
|
2600 |
if (!nitropack_update_current_blog_config($siteId, $siteSecret, $blogId)) {
|
2639 |
return $npPluginNotices;
|
2640 |
}
|
2641 |
|
2642 |
+
function nitropack_is_late_integration_init_required() {
|
2643 |
+
return nitropack_is_nginx_helper_active() || nitropack_is_apo_active();
|
2644 |
+
}
|
2645 |
+
|
2646 |
function nitropack_display_admin_notices() {
|
2647 |
$noticesArray = nitropack_plugin_notices();
|
2648 |
foreach($noticesArray as $type => $notices){
|
integrations.php
CHANGED
@@ -46,15 +46,34 @@ function nitropack_check_and_init_integrations() {
|
|
46 |
break;
|
47 |
}
|
48 |
|
49 |
-
if (
|
50 |
-
|
51 |
-
add_action('nitropack_integration_purge_all', 'nitropack_nginx_helper_purge_all');
|
52 |
}
|
53 |
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
55 |
}
|
56 |
|
57 |
function nitropack_init_late_integrations() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
if (defined('SHORTPIXEL_AI_VERSION')) { // ShortPixel
|
59 |
if (nitropack_is_ajax()) {
|
60 |
if (version_compare(SHORTPIXEL_AI_VERSION, "2", ">=")) { // ShortPixel AI 2.x
|
@@ -73,6 +92,11 @@ function nitropack_init_late_integrations() {
|
|
73 |
if (class_exists("WC_Cache_Helper")) {
|
74 |
remove_action('template_redirect', array('WC_Cache_Helper', 'geolocation_ajax_redirect'));
|
75 |
}
|
|
|
|
|
|
|
|
|
|
|
76 |
}
|
77 |
|
78 |
/** WP Engine **/
|
@@ -273,6 +297,37 @@ function nitropack_nginx_helper_purge_all() {
|
|
273 |
return true;
|
274 |
}
|
275 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
/** Pagely **/
|
277 |
function nitropack_pagely_purge_url($url) {
|
278 |
try {
|
46 |
break;
|
47 |
}
|
48 |
|
49 |
+
if ($siteConfig && empty($siteConfig["isLateIntegrationInitRequired"])) {
|
50 |
+
do_action(NITROPACK_INTEGRATIONS_ACTION);
|
|
|
51 |
}
|
52 |
|
53 |
+
// This is needed in order to load non-cache-related integrations like the one with ShortPixel and WooCommerce Geo Location.
|
54 |
+
if (did_action('plugins_loaded')) {
|
55 |
+
nitropack_init_late_integrations();
|
56 |
+
} else {
|
57 |
+
add_action('plugins_loaded', 'nitropack_init_late_integrations');
|
58 |
+
}
|
59 |
}
|
60 |
|
61 |
function nitropack_init_late_integrations() {
|
62 |
+
if (defined("NITROPACK_LATE_INTEGRATIONS")) return;
|
63 |
+
define("NITROPACK_LATE_INTEGRATIONS", true);
|
64 |
+
|
65 |
+
// Cache related integrations
|
66 |
+
if (nitropack_is_nginx_helper_active()) {
|
67 |
+
add_action('nitropack_integration_purge_url', 'nitropack_nginx_helper_purge_url');
|
68 |
+
add_action('nitropack_integration_purge_all', 'nitropack_nginx_helper_purge_all');
|
69 |
+
}
|
70 |
+
|
71 |
+
if (nitropack_is_apo_active()) {
|
72 |
+
add_action('nitropack_integration_purge_url', 'nitropack_apo_purge_url');
|
73 |
+
add_action('nitropack_integration_purge_all', 'nitropack_apo_purge_all');
|
74 |
+
}
|
75 |
+
|
76 |
+
// Non cache related integrations
|
77 |
if (defined('SHORTPIXEL_AI_VERSION')) { // ShortPixel
|
78 |
if (nitropack_is_ajax()) {
|
79 |
if (version_compare(SHORTPIXEL_AI_VERSION, "2", ">=")) { // ShortPixel AI 2.x
|
92 |
if (class_exists("WC_Cache_Helper")) {
|
93 |
remove_action('template_redirect', array('WC_Cache_Helper', 'geolocation_ajax_redirect'));
|
94 |
}
|
95 |
+
|
96 |
+
$siteConfig = nitropack_get_site_config();
|
97 |
+
if ($siteConfig && !empty($siteConfig["isLateIntegrationInitRequired"])) {
|
98 |
+
do_action(NITROPACK_INTEGRATIONS_ACTION);
|
99 |
+
}
|
100 |
}
|
101 |
|
102 |
/** WP Engine **/
|
297 |
return true;
|
298 |
}
|
299 |
|
300 |
+
// Cloudflare APO integration
|
301 |
+
function nitropack_is_apo_active() {
|
302 |
+
if (defined('CLOUDFLARE_PLUGIN_DIR')) {
|
303 |
+
require_once NITROPACK_PLUGIN_DIR . 'cf-helper.php';
|
304 |
+
$cfHelper = new NitroPack_CF_Helper();
|
305 |
+
return $cfHelper->isApoEnabled();
|
306 |
+
} else {
|
307 |
+
return false;
|
308 |
+
}
|
309 |
+
}
|
310 |
+
|
311 |
+
function nitropack_apo_purge_url($url) {
|
312 |
+
if (defined('CLOUDFLARE_PLUGIN_DIR')) {
|
313 |
+
require_once NITROPACK_PLUGIN_DIR . 'cf-helper.php';
|
314 |
+
$cfHelper = new NitroPack_CF_Helper();
|
315 |
+
return $cfHelper->purgeUrl($url);
|
316 |
+
} else {
|
317 |
+
return false;
|
318 |
+
}
|
319 |
+
}
|
320 |
+
|
321 |
+
function nitropack_apo_purge_all() {
|
322 |
+
if (defined('CLOUDFLARE_PLUGIN_DIR')) {
|
323 |
+
require_once NITROPACK_PLUGIN_DIR . 'cf-helper.php';
|
324 |
+
$cfHelper = new NitroPack_CF_Helper();
|
325 |
+
return $cfHelper->purgeCacheEverything();
|
326 |
+
} else {
|
327 |
+
return false;
|
328 |
+
}
|
329 |
+
}
|
330 |
+
|
331 |
/** Pagely **/
|
332 |
function nitropack_pagely_purge_url($url) {
|
333 |
try {
|
main.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
Plugin Name: NitroPack
|
4 |
Plugin URI: https://nitropack.io/platform/wordpress
|
5 |
Description: Everything you need for a fast website. Simple set up, easy to use, awesome support. Caching, Lazy Loading, Minification, Defer CSS/JS, CDN and more!
|
6 |
-
Version: 1.
|
7 |
Author: NitroPack LLC
|
8 |
Author URI: https://nitropack.io/
|
9 |
License: GPL2
|
@@ -66,6 +66,10 @@ if (nitropack_has_advanced_cache()) {
|
|
66 |
}
|
67 |
}
|
68 |
|
|
|
|
|
|
|
|
|
69 |
if ( is_admin() ) {
|
70 |
add_action( 'admin_menu', 'nitropack_menu' );
|
71 |
add_action( 'admin_init', 'register_nitropack_settings' );
|
@@ -119,8 +123,8 @@ function nitropack_action_links ( $links ) {
|
|
119 |
add_action( 'init', function() {
|
120 |
if (current_user_can( 'manage_options' )) {
|
121 |
// Enqueue font awesome
|
122 |
-
add_action( 'wp_enqueue_scripts', '
|
123 |
-
add_action( 'admin_enqueue_scripts', '
|
124 |
|
125 |
// Enqueue admin bar menu custom stylesheet
|
126 |
add_action( 'wp_enqueue_scripts', 'enqueue_nitropack_admin_bar_menu_stylesheet');
|
3 |
Plugin Name: NitroPack
|
4 |
Plugin URI: https://nitropack.io/platform/wordpress
|
5 |
Description: Everything you need for a fast website. Simple set up, easy to use, awesome support. Caching, Lazy Loading, Minification, Defer CSS/JS, CDN and more!
|
6 |
+
Version: 1.5.0
|
7 |
Author: NitroPack LLC
|
8 |
Author URI: https://nitropack.io/
|
9 |
License: GPL2
|
66 |
}
|
67 |
}
|
68 |
|
69 |
+
add_action('wp_footer', 'nitropack_print_heartbeat_script');
|
70 |
+
add_action('admin_footer', 'nitropack_print_heartbeat_script');
|
71 |
+
add_action('get_footer', 'nitropack_print_heartbeat_script');
|
72 |
+
|
73 |
if ( is_admin() ) {
|
74 |
add_action( 'admin_menu', 'nitropack_menu' );
|
75 |
add_action( 'admin_init', 'register_nitropack_settings' );
|
123 |
add_action( 'init', function() {
|
124 |
if (current_user_can( 'manage_options' )) {
|
125 |
// Enqueue font awesome
|
126 |
+
add_action( 'wp_enqueue_scripts', 'nitropack_enqueue_load_fa');
|
127 |
+
add_action( 'admin_enqueue_scripts', 'nitropack_enqueue_load_fa');
|
128 |
|
129 |
// Enqueue admin bar menu custom stylesheet
|
130 |
add_action( 'wp_enqueue_scripts', 'enqueue_nitropack_admin_bar_menu_stylesheet');
|
nitropack-sdk/NitroPack/SDK/Api.php
CHANGED
@@ -8,16 +8,36 @@ class Api {
|
|
8 |
private $tagger;
|
9 |
private $url;
|
10 |
private $allowedWebhooks = array('config', 'cache_clear', 'cache_ready', 'sitemap');
|
|
|
11 |
|
12 |
public function __construct($siteId, $siteSecret) {
|
13 |
-
$this->
|
14 |
-
$this->
|
15 |
-
$this->
|
16 |
-
$this->
|
17 |
-
$this->
|
18 |
-
$this->
|
19 |
-
$this->
|
20 |
-
$this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
}
|
22 |
|
23 |
public function getCache($url, $userAgent, $cookies, $isAjax, $layout) {
|
8 |
private $tagger;
|
9 |
private $url;
|
10 |
private $allowedWebhooks = array('config', 'cache_clear', 'cache_ready', 'sitemap');
|
11 |
+
private $children;
|
12 |
|
13 |
public function __construct($siteId, $siteSecret) {
|
14 |
+
$this->children = [];
|
15 |
+
$this->children["cache"] = new Api\Cache($siteId, $siteSecret);
|
16 |
+
$this->children["tagger"] = new Api\Tagger($siteId, $siteSecret);
|
17 |
+
$this->children["url"] = new Api\Url($siteId, $siteSecret);
|
18 |
+
$this->children["stats"] = new Api\Stats($siteId, $siteSecret);
|
19 |
+
$this->children["webhook"] = new Api\Webhook($siteId, $siteSecret);
|
20 |
+
$this->children["warmup"] = new Api\Warmup($siteId, $siteSecret);
|
21 |
+
$this->children["integration"] = new Api\Integration($siteId, $siteSecret);
|
22 |
+
$this->children["variation_cookie"] = new Api\VariationCookie($siteId, $siteSecret);
|
23 |
+
$this->children["request_maker"] = new Api\RequestMaker($siteId);
|
24 |
+
$this->children["secure_request_maker"] = new Api\SecureRequestMaker($siteId, $siteSecret);
|
25 |
+
|
26 |
+
foreach ($this->children as $name=>$child) {
|
27 |
+
$this->{$name} = $child;
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
public function setBacklog($backlog) {
|
32 |
+
foreach ($this->children as $child) {
|
33 |
+
$child->setBacklog($backlog);
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
public function setNitroPack($nitropack) {
|
38 |
+
foreach ($this->children as $child) {
|
39 |
+
$child->setNitroPack($nitropack);
|
40 |
+
}
|
41 |
}
|
42 |
|
43 |
public function getCache($url, $userAgent, $cookies, $isAjax, $layout) {
|
nitropack-sdk/NitroPack/SDK/Api/Base.php
CHANGED
@@ -1,20 +1,60 @@
|
|
1 |
<?php
|
2 |
namespace NitroPack\SDK\Api;
|
3 |
use \NitroPack\HttpClient;
|
|
|
|
|
|
|
4 |
|
5 |
class Base {
|
6 |
protected $baseUrl = 'https://api.getnitropack.com/';
|
7 |
protected $siteId;
|
|
|
|
|
|
|
8 |
|
9 |
public function __construct($siteId) {
|
10 |
$this->siteId = $siteId;
|
|
|
|
|
|
|
11 |
|
12 |
if (defined('NITROPACK_API_BASE_URL')) {
|
13 |
$this->baseUrl = NITROPACK_API_BASE_URL;
|
14 |
}
|
15 |
}
|
16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
protected function makeRequest($path, $headers = array(), $cookies = array(), $type = 'GET', $bodyData=array(), $async = false, $verifySSL = false) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
$http = new HttpClient($this->baseUrl . $path); // HttpClient keeps a cache of the opened connections, so creating a new instance every time is not an issue
|
19 |
$http->connect_timeout = 3; // in seconds
|
20 |
$http->ssl_timeout = 3; // in seconds
|
@@ -34,22 +74,45 @@ class Base {
|
|
34 |
|
35 |
$http->setVerifySSL($verifySSL);
|
36 |
|
|
|
|
|
|
|
|
|
37 |
if ($async) {
|
38 |
$http->fetch(true, $type, $async);
|
39 |
} else {
|
40 |
$retries = 1;
|
|
|
41 |
while ($retries--) {
|
42 |
try {
|
43 |
$http->fetch(true, $type, $async);
|
44 |
-
if ($http->getStatusCode() < 500)
|
|
|
|
|
|
|
45 |
} catch (\Exception $e) {
|
46 |
-
if ($retries == 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
}
|
48 |
|
49 |
if ($retries > 0) {
|
50 |
usleep(500000);
|
51 |
}
|
52 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
}
|
54 |
|
55 |
return $http;
|
@@ -67,6 +130,8 @@ class Base {
|
|
67 |
$errorMessage = 'Unknown';
|
68 |
}
|
69 |
|
|
|
|
|
70 |
if ($errorMessage == 'Unknown') { // Fallback to known HTTP errors
|
71 |
$statusCode = $httpResponse->getStatusCode();
|
72 |
switch ($statusCode) {
|
@@ -83,6 +148,7 @@ class Base {
|
|
83 |
$errorMessage = "Runtime Error";
|
84 |
break;
|
85 |
case ResponseStatus::SERVICE_UNAVAILABLE:
|
|
|
86 |
$errorMessage = "Service Unavailable";
|
87 |
break;
|
88 |
default:
|
@@ -91,6 +157,10 @@ class Base {
|
|
91 |
}
|
92 |
}
|
93 |
|
94 |
-
|
|
|
|
|
|
|
|
|
95 |
}
|
96 |
}
|
1 |
<?php
|
2 |
namespace NitroPack\SDK\Api;
|
3 |
use \NitroPack\HttpClient;
|
4 |
+
use \NitroPack\SDK\NitroPack;
|
5 |
+
use \NitroPack\SDK\HealthStatus;
|
6 |
+
use \NitroPack\SDK\ServiceDownException;
|
7 |
|
8 |
class Base {
|
9 |
protected $baseUrl = 'https://api.getnitropack.com/';
|
10 |
protected $siteId;
|
11 |
+
protected $isBacklogEnabled;
|
12 |
+
protected $backlog;
|
13 |
+
protected $nitropack;
|
14 |
|
15 |
public function __construct($siteId) {
|
16 |
$this->siteId = $siteId;
|
17 |
+
$this->isBacklogEnabled = false;
|
18 |
+
$this->backlog = NULL;
|
19 |
+
$this->nitropack = NULL;
|
20 |
|
21 |
if (defined('NITROPACK_API_BASE_URL')) {
|
22 |
$this->baseUrl = NITROPACK_API_BASE_URL;
|
23 |
}
|
24 |
}
|
25 |
|
26 |
+
public function setBacklog($backlog) {
|
27 |
+
$this->backlog = $backlog;
|
28 |
+
}
|
29 |
+
|
30 |
+
public function setNitroPack($nitropack) {
|
31 |
+
$this->nitropack = $nitropack;
|
32 |
+
}
|
33 |
+
|
34 |
+
protected function addToBacklog($entry) {
|
35 |
+
$this->backlog->append($entry);
|
36 |
+
}
|
37 |
+
|
38 |
protected function makeRequest($path, $headers = array(), $cookies = array(), $type = 'GET', $bodyData=array(), $async = false, $verifySSL = false) {
|
39 |
+
$backlogEntry = array(
|
40 |
+
"path" => $path,
|
41 |
+
"headers" => $headers,
|
42 |
+
"cookies" => $cookies,
|
43 |
+
"type" => $type,
|
44 |
+
"bodyData" => $bodyData,
|
45 |
+
"async" => $async,
|
46 |
+
"verifySSL" => $verifySSL
|
47 |
+
);
|
48 |
+
|
49 |
+
if ($this->nitropack && $this->nitropack->getHealthStatus() !== HealthStatus::HEALTHY) {
|
50 |
+
$unhealthyMsg = "Connection to NitroPack is not reliable at the moment.";
|
51 |
+
if ($this->isBacklogEnabled) {
|
52 |
+
$this->addToBacklog($backlogEntry);
|
53 |
+
$unhealthyMsg .= " Request has been added to the backlog for delayed processing.";
|
54 |
+
}
|
55 |
+
throw new ServiceDownException($unhealthyMsg);
|
56 |
+
}
|
57 |
+
|
58 |
$http = new HttpClient($this->baseUrl . $path); // HttpClient keeps a cache of the opened connections, so creating a new instance every time is not an issue
|
59 |
$http->connect_timeout = 3; // in seconds
|
60 |
$http->ssl_timeout = 3; // in seconds
|
74 |
|
75 |
$http->setVerifySSL($verifySSL);
|
76 |
|
77 |
+
if ($this->isBacklogEnabled) {
|
78 |
+
$http->backlogEntry = $backlogEntry;
|
79 |
+
}
|
80 |
+
|
81 |
if ($async) {
|
82 |
$http->fetch(true, $type, $async);
|
83 |
} else {
|
84 |
$retries = 1;
|
85 |
+
$isRequestProcessed = false;
|
86 |
while ($retries--) {
|
87 |
try {
|
88 |
$http->fetch(true, $type, $async);
|
89 |
+
if ($http->getStatusCode() < 500) {
|
90 |
+
$isRequestProcessed = true;
|
91 |
+
break;
|
92 |
+
}
|
93 |
} catch (\Exception $e) {
|
94 |
+
if ($retries == 0) {
|
95 |
+
if (!$isRequestProcessed) {
|
96 |
+
$this->nitropack && $this->nitropack->setHealthStatus(HealthStatus::SICK);
|
97 |
+
if ($this->isBacklogEnabled) {
|
98 |
+
$this->addToBacklog($backlogEntry);
|
99 |
+
}
|
100 |
+
}
|
101 |
+
throw $e;
|
102 |
+
}
|
103 |
}
|
104 |
|
105 |
if ($retries > 0) {
|
106 |
usleep(500000);
|
107 |
}
|
108 |
}
|
109 |
+
|
110 |
+
if (!$isRequestProcessed) { // In case all response codes were 500+
|
111 |
+
$this->nitropack && $this->nitropack->setHealthStatus(HealthStatus::UNDER_THE_WEATHER);
|
112 |
+
if ($this->isBacklogEnabled) {
|
113 |
+
$this->addToBacklog($backlogEntry);
|
114 |
+
}
|
115 |
+
}
|
116 |
}
|
117 |
|
118 |
return $http;
|
130 |
$errorMessage = 'Unknown';
|
131 |
}
|
132 |
|
133 |
+
$isServiceUnavailable = false;
|
134 |
+
|
135 |
if ($errorMessage == 'Unknown') { // Fallback to known HTTP errors
|
136 |
$statusCode = $httpResponse->getStatusCode();
|
137 |
switch ($statusCode) {
|
148 |
$errorMessage = "Runtime Error";
|
149 |
break;
|
150 |
case ResponseStatus::SERVICE_UNAVAILABLE:
|
151 |
+
$isServiceUnavailable = true;
|
152 |
$errorMessage = "Service Unavailable";
|
153 |
break;
|
154 |
default:
|
157 |
}
|
158 |
}
|
159 |
|
160 |
+
if ($isServiceUnavailable) {
|
161 |
+
throw new ServiceDownException(sprintf($template, $errorMessage));
|
162 |
+
} else {
|
163 |
+
throw new \RuntimeException(sprintf($template, $errorMessage));
|
164 |
+
}
|
165 |
}
|
166 |
}
|
nitropack-sdk/NitroPack/SDK/Api/Cache.php
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
namespace NitroPack\SDK\Api;
|
3 |
|
4 |
use \NitroPack\SDK\NitroPack;
|
|
|
5 |
|
6 |
class Cache extends SignedBase {
|
7 |
protected $secret;
|
@@ -12,6 +13,7 @@ class Cache extends SignedBase {
|
|
12 |
}
|
13 |
|
14 |
public function get($url, $userAgent, $cookies, $isAjax, $layout = 'default', $remoteAddr = NULL) {
|
|
|
15 |
$path = 'cache/get/' . $this->siteId . '/' . $layout;
|
16 |
$remoteAddr = $remoteAddr ? $remoteAddr : NitroPack::getRemoteAddr();
|
17 |
$headers = array(
|
@@ -41,6 +43,7 @@ class Cache extends SignedBase {
|
|
41 |
}
|
42 |
|
43 |
public function getLastPurge() {
|
|
|
44 |
$path = 'cache/getlastpurge/' . $this->siteId;
|
45 |
|
46 |
$httpResponse = $this->makeRequest($path);
|
@@ -54,6 +57,7 @@ class Cache extends SignedBase {
|
|
54 |
}
|
55 |
|
56 |
public function purge($url = NULL, $pagecacheOnly = false, $reason = NULL) {
|
|
|
57 |
$path = 'cache/purge/' . $this->siteId;
|
58 |
|
59 |
if (is_array($url)) {
|
@@ -64,6 +68,11 @@ class Cache extends SignedBase {
|
|
64 |
$httpMulti = new \NitroPack\HttpClientMulti();
|
65 |
|
66 |
$httpMulti->onSuccess(function($client) use ($path, &$url, &$requests, $httpMulti, $cache, $reason) {
|
|
|
|
|
|
|
|
|
|
|
67 |
if ($url) {
|
68 |
$_url = array_shift($url);
|
69 |
$params = array();
|
@@ -81,6 +90,10 @@ class Cache extends SignedBase {
|
|
81 |
|
82 |
$httpMulti->onError(function($client, $exception) use ($httpMulti, $retries) {
|
83 |
if ($exception instanceof \NitroPack\SocketReadTimedOutException) {
|
|
|
|
|
|
|
|
|
84 |
return;
|
85 |
}
|
86 |
|
@@ -94,6 +107,10 @@ class Cache extends SignedBase {
|
|
94 |
$retries->offsetSet($client, $clientRetries + 1);
|
95 |
$client->replay();
|
96 |
$httpMulti->push($client);
|
|
|
|
|
|
|
|
|
97 |
}
|
98 |
});
|
99 |
|
@@ -106,7 +123,12 @@ class Cache extends SignedBase {
|
|
106 |
$params["reason"] = $reason;
|
107 |
}
|
108 |
|
109 |
-
|
|
|
|
|
|
|
|
|
|
|
110 |
$httpMulti->push($httpClient);
|
111 |
$requests[] = $httpClient;
|
112 |
}
|
@@ -164,6 +186,7 @@ class Cache extends SignedBase {
|
|
164 |
}
|
165 |
|
166 |
public function purgeByTag($tag, $reason = NULL) {
|
|
|
167 |
$path = 'cache/purge/' . $this->siteId;
|
168 |
|
169 |
$params = array();
|
2 |
namespace NitroPack\SDK\Api;
|
3 |
|
4 |
use \NitroPack\SDK\NitroPack;
|
5 |
+
use \NitroPack\SDK\ServiceDownException;
|
6 |
|
7 |
class Cache extends SignedBase {
|
8 |
protected $secret;
|
13 |
}
|
14 |
|
15 |
public function get($url, $userAgent, $cookies, $isAjax, $layout = 'default', $remoteAddr = NULL) {
|
16 |
+
$this->isBacklogEnabled = false;
|
17 |
$path = 'cache/get/' . $this->siteId . '/' . $layout;
|
18 |
$remoteAddr = $remoteAddr ? $remoteAddr : NitroPack::getRemoteAddr();
|
19 |
$headers = array(
|
43 |
}
|
44 |
|
45 |
public function getLastPurge() {
|
46 |
+
$this->isBacklogEnabled = false;
|
47 |
$path = 'cache/getlastpurge/' . $this->siteId;
|
48 |
|
49 |
$httpResponse = $this->makeRequest($path);
|
57 |
}
|
58 |
|
59 |
public function purge($url = NULL, $pagecacheOnly = false, $reason = NULL) {
|
60 |
+
$this->isBacklogEnabled = true;
|
61 |
$path = 'cache/purge/' . $this->siteId;
|
62 |
|
63 |
if (is_array($url)) {
|
68 |
$httpMulti = new \NitroPack\HttpClientMulti();
|
69 |
|
70 |
$httpMulti->onSuccess(function($client) use ($path, &$url, &$requests, $httpMulti, $cache, $reason) {
|
71 |
+
if ($client->getStatusCode() >= 500 && !empty($client->backlogEntry)) {
|
72 |
+
$this->addToBacklog($client->backlogEntry);
|
73 |
+
$this->nitropack && $this->nitropack->setHealthStatus(HealthStatus::UNDER_THE_WEATHER);
|
74 |
+
}
|
75 |
+
|
76 |
if ($url) {
|
77 |
$_url = array_shift($url);
|
78 |
$params = array();
|
90 |
|
91 |
$httpMulti->onError(function($client, $exception) use ($httpMulti, $retries) {
|
92 |
if ($exception instanceof \NitroPack\SocketReadTimedOutException) {
|
93 |
+
if (!empty($client->backlogEntry)) {
|
94 |
+
$this->addToBacklog($client->backlogEntry);
|
95 |
+
$this->nitropack && $this->nitropack->setHealthStatus(HealthStatus::SICK);
|
96 |
+
}
|
97 |
return;
|
98 |
}
|
99 |
|
107 |
$retries->offsetSet($client, $clientRetries + 1);
|
108 |
$client->replay();
|
109 |
$httpMulti->push($client);
|
110 |
+
} else {
|
111 |
+
if (!empty($client->backlogEntry)) {
|
112 |
+
$this->addToBacklog($client->backlogEntry);
|
113 |
+
}
|
114 |
}
|
115 |
});
|
116 |
|
123 |
$params["reason"] = $reason;
|
124 |
}
|
125 |
|
126 |
+
try {
|
127 |
+
$httpClient = $this->makeRequestAsync($path, array(), array(), 'POST', $params);
|
128 |
+
} catch (ServiceDownException $e) {
|
129 |
+
continue;
|
130 |
+
}
|
131 |
+
|
132 |
$httpMulti->push($httpClient);
|
133 |
$requests[] = $httpClient;
|
134 |
}
|
186 |
}
|
187 |
|
188 |
public function purgeByTag($tag, $reason = NULL) {
|
189 |
+
$this->isBacklogEnabled = true;
|
190 |
$path = 'cache/purge/' . $this->siteId;
|
191 |
|
192 |
$params = array();
|
nitropack-sdk/NitroPack/SDK/Api/RequestMaker.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace NitroPack\SDK\Api;
|
3 |
+
|
4 |
+
class RequestMaker extends Base {
|
5 |
+
public function __construct($siteId) {
|
6 |
+
parent::__construct($siteId);
|
7 |
+
}
|
8 |
+
|
9 |
+
public function makeRequest($path, $headers = array(), $cookies = array(), $type = 'GET', $bodyData=array(), $async = false, $verifySSL = false) {
|
10 |
+
return parent::makeRequest($path, $headers, $cookies, $type, $bodyData, $async, $verifySSL);
|
11 |
+
}
|
12 |
+
}
|
nitropack-sdk/NitroPack/SDK/Api/SecureRequestMaker.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace NitroPack\SDK\Api;
|
3 |
+
|
4 |
+
class SecureRequestMaker extends SignedBase {
|
5 |
+
public function __construct($siteId, $siteSecret) {
|
6 |
+
parent::__construct($siteId, $siteSecret);
|
7 |
+
}
|
8 |
+
|
9 |
+
public function makeRequest($path, $headers = array(), $cookies = array(), $type = 'GET', $bodyData=array(), $async = false, $verifySSL = false) {
|
10 |
+
return parent::makeRequest($path, $headers, $cookies, $type, $bodyData, $async, $verifySSL);
|
11 |
+
}
|
12 |
+
}
|
nitropack-sdk/NitroPack/SDK/Api/SignedBase.php
CHANGED
@@ -28,6 +28,11 @@ class SignedBase extends Base {
|
|
28 |
$this->secret = $siteSecret;
|
29 |
}
|
30 |
|
|
|
|
|
|
|
|
|
|
|
31 |
protected function makeRequest($path, $headers = array(), $cookies = array(), $type = 'GET', $bodyData=array(), $async = false, $verifySSL = false) {
|
32 |
// calculate request signature
|
33 |
$url = new Url($this->baseUrl . $path);
|
28 |
$this->secret = $siteSecret;
|
29 |
}
|
30 |
|
31 |
+
protected function addToBacklog($entry) {
|
32 |
+
$entry["siteSecret"] = $this->secret;
|
33 |
+
parent::addToBacklog($entry);
|
34 |
+
}
|
35 |
+
|
36 |
protected function makeRequest($path, $headers = array(), $cookies = array(), $type = 'GET', $bodyData=array(), $async = false, $verifySSL = false) {
|
37 |
// calculate request signature
|
38 |
$url = new Url($this->baseUrl . $path);
|
nitropack-sdk/NitroPack/SDK/Backlog.php
ADDED
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace NitroPack\SDK;
|
4 |
+
use NitroPack\SDK\Api\ResponseStatus;
|
5 |
+
|
6 |
+
class Backlog {
|
7 |
+
const TTL = 86400; // 1 day in seconds
|
8 |
+
|
9 |
+
private $dataDir;
|
10 |
+
private $nitropack;
|
11 |
+
private $communicators;
|
12 |
+
private $queue;
|
13 |
+
private $queuePath;
|
14 |
+
private $isAcquired;
|
15 |
+
private $fileHandle;
|
16 |
+
private $header;
|
17 |
+
private $backlogFile = array('data', 'backlog.queue');
|
18 |
+
|
19 |
+
public function __construct($dataDir, $nitropack) {
|
20 |
+
$this->dataDir = $dataDir;
|
21 |
+
$this->nitropack = $nitropack;
|
22 |
+
$this->communicators = array();
|
23 |
+
$this->queue = array();
|
24 |
+
$this->queuePath = $this->getQueuePath();
|
25 |
+
$this->isAcquired = false;
|
26 |
+
$this->fileHandle = NULL;
|
27 |
+
$this->header = new \stdClass();
|
28 |
+
$this->header->offset = 0;
|
29 |
+
$this->header->firstProcessingTimestamp = 0;
|
30 |
+
$this->header->lastProcessingTimestamp = 0;
|
31 |
+
}
|
32 |
+
|
33 |
+
public function __destruct() {
|
34 |
+
$this->closeHandle();
|
35 |
+
}
|
36 |
+
|
37 |
+
public function append($entry) {
|
38 |
+
$fh = $this->getHandle();
|
39 |
+
Filesystem::flock($fh, LOCK_EX);
|
40 |
+
Filesystem::fseek($fh, 0, SEEK_END);
|
41 |
+
$this->writeEntry($fh, $this->encodeEntry($entry));
|
42 |
+
Filesystem::flock($fh, LOCK_UN);
|
43 |
+
}
|
44 |
+
|
45 |
+
public function replay($timeLimit = 10) {
|
46 |
+
$fh = $this->getHandle();
|
47 |
+
Filesystem::flock($fh, LOCK_EX);
|
48 |
+
$lastProcessingTimestamp = $this->header->lastProcessingTimestamp;
|
49 |
+
if (time() - $lastProcessingTimestamp <= $timeLimit) {
|
50 |
+
return;
|
51 |
+
}
|
52 |
+
$this->acquireBacklog($fh);
|
53 |
+
Filesystem::flock($fh, LOCK_UN);
|
54 |
+
|
55 |
+
$initialProcesssTime = $this->header->firstProcessingTimestamp;
|
56 |
+
if (time() - $initialProcesssTime > self::TTL) {
|
57 |
+
// In case there have been previous attempts at clearing the backlog and these attempts started more than the specified TTL seconds ago
|
58 |
+
// Perform a full purge and clear the backlog
|
59 |
+
}
|
60 |
+
|
61 |
+
if ($this->header->offset > 0) {
|
62 |
+
Filesystem::fseek($fh, $this->header->offset, SEEK_SET);
|
63 |
+
}
|
64 |
+
$start = microtime(true);
|
65 |
+
while (!$this->isEndOfQueue($fh) && NULL != ($entry = $this->getentry($fh)) && microtime(true) - $start < $timeLimit) {
|
66 |
+
$elapsedTime = microtime(true) - $start;
|
67 |
+
try {
|
68 |
+
$this->replayEntry($entry, $timeLimit - $elapsedTime);
|
69 |
+
} catch (\Exception $e) {
|
70 |
+
break;
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
if ($this->isEndOfQueue($fh)) {
|
75 |
+
$this->closeHandle();
|
76 |
+
Filesystem::deleteFile($this->queuePath);
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
private function isEndOfQueue($fh) {
|
81 |
+
return Filesystem::feof($fh);
|
82 |
+
}
|
83 |
+
|
84 |
+
public function dumpEntries() {
|
85 |
+
$fh = $this->getHandle();
|
86 |
+
while (!$this->isEndOfQueue($fh) && NULL != ($entry = $this->getentry($fh))) {
|
87 |
+
try {
|
88 |
+
var_dump($entry);
|
89 |
+
} catch (\Exception $e) {
|
90 |
+
break;
|
91 |
+
}
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
public function dumpHeader() {
|
96 |
+
$this->getHandle();
|
97 |
+
var_dump($this->header);
|
98 |
+
}
|
99 |
+
|
100 |
+
public function resetOffset() {
|
101 |
+
$fh = $this->getHandle();
|
102 |
+
$this->header->offset = 0;
|
103 |
+
$this->writeHeader($fh);
|
104 |
+
}
|
105 |
+
|
106 |
+
public function exists() {
|
107 |
+
return Filesystem::fileExists($this->queuePath);
|
108 |
+
}
|
109 |
+
|
110 |
+
private function replayEntry($entry, $timeLimit) {
|
111 |
+
if (array_key_exists("siteSecret", $entry)) {
|
112 |
+
$requestMaker = $this->nitropack->getApi()->secure_request_maker;
|
113 |
+
} else {
|
114 |
+
$requestMaker = $this->nitropack->getApi()->request_maker;
|
115 |
+
}
|
116 |
+
$httpResponse = $requestMaker->makeRequest($entry["path"], $entry["headers"], $entry["cookies"], $entry["type"], $entry["bodyData"], false, $entry["verifySSL"]);
|
117 |
+
$status = ResponseStatus::getStatus($httpResponse->getStatusCode());
|
118 |
+
$headers = $httpResponse->getHeaders();
|
119 |
+
|
120 |
+
$start = microtime(true);
|
121 |
+
while ($status == ResponseStatus::OK && !empty($headers["x-nitro-repeat"]) && microtime(true) - $start < $timeLimit) {
|
122 |
+
$httpResponse->replay(); // In reality $httpResponse is an instance of HttpClient which has the replay method
|
123 |
+
$status = ResponseStatus::getStatus($httpResponse->getStatusCode());
|
124 |
+
$headers = $httpResponse->getHeaders();
|
125 |
+
if ($status != ResponseStatus::OK) {
|
126 |
+
throw \RuntimeException("Unable to replay backlogged entry");
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
if ($status == ResponseStatus::OK && empty($headers["x-nitro-repeat"])) {
|
131 |
+
$fh = $this->getHandle();
|
132 |
+
$this->header->offset = Filesystem::ftell($fh);
|
133 |
+
$this->writeHeader($fh);
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
private function getQueuePath() {
|
138 |
+
$backlogFile = $this->backlogFile;
|
139 |
+
array_unshift($backlogFile, $this->dataDir);
|
140 |
+
return Filesystem::getOsPath($backlogFile);
|
141 |
+
}
|
142 |
+
|
143 |
+
private function getEntry($fh = NULL) {
|
144 |
+
$closeFile = empty($fh);
|
145 |
+
$fh = !empty($fh) ? $fh : $this->getHandle();
|
146 |
+
$entry = @Filesystem::fgets($fh);
|
147 |
+
if ($closeFile) {
|
148 |
+
Filesystem::fclose($fh);
|
149 |
+
}
|
150 |
+
return $this->decodeEntry($entry);
|
151 |
+
}
|
152 |
+
|
153 |
+
private function writeEntry($fh, $entry) {
|
154 |
+
Filesystem::fwrite($fh, $entry . "\n");
|
155 |
+
Filesystem::fflush($fh);
|
156 |
+
}
|
157 |
+
|
158 |
+
private function encodeEntry($entry) {
|
159 |
+
return base64_encode(json_encode($entry));
|
160 |
+
}
|
161 |
+
|
162 |
+
private function decodeEntry($entry) {
|
163 |
+
return json_decode(base64_decode($entry), true);
|
164 |
+
}
|
165 |
+
|
166 |
+
private function acquireBacklog($fh) {
|
167 |
+
$this->header->lastProcessingTimestamp = time();
|
168 |
+
if (!$this->header->firstProcessingTimestamp) {
|
169 |
+
$this->header->firstProcessingTimestamp = $this->header->lastProcessingTimestamp;
|
170 |
+
}
|
171 |
+
$this->writeHeader($fh);
|
172 |
+
$this->isAcquired = true;
|
173 |
+
}
|
174 |
+
|
175 |
+
private function releaseBacklog($fh) {
|
176 |
+
$this->header->lastProcessingTimestamp = 0;
|
177 |
+
$this->writeHeader($fh);
|
178 |
+
$this->isAcquired = false;
|
179 |
+
}
|
180 |
+
|
181 |
+
private function readHeader($fh) {
|
182 |
+
$offsetBackup = Filesystem::ftell($fh);
|
183 |
+
Filesystem::fseek($fh, 0, SEEK_SET);
|
184 |
+
$header = Filesystem::fread($fh, 12);
|
185 |
+
$parts = unpack("Loffset/LfirstProcessingTimestamp/LlastProcessingTimestamp", $header);
|
186 |
+
$this->header->offset = $parts["offset"];
|
187 |
+
$this->header->firstProcessingTimestamp = $parts["firstProcessingTimestamp"];
|
188 |
+
$this->header->lastProcessingTimestamp = $parts["lastProcessingTimestamp"];
|
189 |
+
Filesystem::fseek($fh, $offsetBackup, SEEK_SET);
|
190 |
+
}
|
191 |
+
|
192 |
+
private function writeHeader($fh) {
|
193 |
+
$offsetBackup = Filesystem::ftell($fh);
|
194 |
+
Filesystem::fseek($fh, 0, SEEK_SET);
|
195 |
+
Filesystem::fwrite($fh, pack("L*", $this->header->offset, $this->header->firstProcessingTimestamp, $this->header->lastProcessingTimestamp), 12);
|
196 |
+
Filesystem::fflush($fh);
|
197 |
+
Filesystem::fseek($fh, $offsetBackup, SEEK_SET);
|
198 |
+
}
|
199 |
+
|
200 |
+
private function getHandle() {
|
201 |
+
if ($this->fileHandle) return $this->fileHandle;
|
202 |
+
|
203 |
+
$fh = Filesystem::fopen($this->queuePath, "c+");
|
204 |
+
Filesystem::flock($fh, LOCK_EX);
|
205 |
+
Filesystem::fseek($fh, 0, SEEK_END);
|
206 |
+
$pos = Filesystem::ftell($fh);
|
207 |
+
if ($pos > 11) { // The first 12 bytes are reserved for the header. If the last pos is further than the 12th byte then the log is considered initialized
|
208 |
+
$this->readHeader($fh);
|
209 |
+
} else {
|
210 |
+
$this->writeHeader($fh);
|
211 |
+
}
|
212 |
+
Filesystem::fseek($fh, 12, SEEK_SET);
|
213 |
+
Filesystem::flock($fh, LOCK_UN);
|
214 |
+
|
215 |
+
$this->fileHandle = $fh;
|
216 |
+
|
217 |
+
return $this->fileHandle;
|
218 |
+
}
|
219 |
+
|
220 |
+
private function closeHandle() {
|
221 |
+
if ($this->fileHandle) {
|
222 |
+
if ($this->isAcquired) {
|
223 |
+
$this->releaseBacklog($this->fileHandle);
|
224 |
+
}
|
225 |
+
Filesystem::fclose($this->fileHandle);
|
226 |
+
$this->fileHandle = NULL;
|
227 |
+
}
|
228 |
+
}
|
229 |
+
}
|
nitropack-sdk/NitroPack/SDK/FileHandle.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace NitroPack\SDK;
|
3 |
+
|
4 |
+
class FileHandle {
|
5 |
+
protected $handle;
|
6 |
+
|
7 |
+
public function __construct($handle) {
|
8 |
+
$this->handle = $handle;
|
9 |
+
}
|
10 |
+
|
11 |
+
public function getHandle() {
|
12 |
+
return $this->handle;
|
13 |
+
}
|
14 |
+
|
15 |
+
public function setHandle($handle) {
|
16 |
+
$this->handle = $handle;
|
17 |
+
}
|
18 |
+
}
|
nitropack-sdk/NitroPack/SDK/Filesystem.php
CHANGED
@@ -118,6 +118,50 @@ class Filesystem {
|
|
118 |
return false;
|
119 |
}
|
120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
private static function explodeByHeaders($content) {
|
122 |
$headers = [];
|
123 |
$pos = strpos($content, "\r\n\r\n");
|
118 |
return false;
|
119 |
}
|
120 |
|
121 |
+
public static function fopen($file, $mode) {
|
122 |
+
return Filesystem::getStorageDriver()->fopen($file, $mode);
|
123 |
+
}
|
124 |
+
|
125 |
+
public static function fclose($fh) {
|
126 |
+
return Filesystem::getStorageDriver()->fclose($fh);
|
127 |
+
}
|
128 |
+
|
129 |
+
public static function fflush($fh) {
|
130 |
+
return Filesystem::getStorageDriver()->fflush($fh);
|
131 |
+
}
|
132 |
+
|
133 |
+
public static function fseek($fh, $offset, $whence = SEEK_SET) {
|
134 |
+
return Filesystem::getStorageDriver()->fseek($fh, $offset, $whence);
|
135 |
+
}
|
136 |
+
|
137 |
+
public static function ftell($fh) {
|
138 |
+
return Filesystem::getStorageDriver()->ftell($fh);
|
139 |
+
}
|
140 |
+
|
141 |
+
public static function fwrite($fh, $string, $length = NULL) {
|
142 |
+
return Filesystem::getStorageDriver()->fwrite($fh, $string, $length);
|
143 |
+
}
|
144 |
+
|
145 |
+
public static function fread($fh, $length) {
|
146 |
+
return Filesystem::getStorageDriver()->fread($fh, $length);
|
147 |
+
}
|
148 |
+
|
149 |
+
public static function fgetc($fh) {
|
150 |
+
return Filesystem::getStorageDriver()->fgetc($fh);
|
151 |
+
}
|
152 |
+
|
153 |
+
public static function fgets($fh, $length = NULL) {
|
154 |
+
return Filesystem::getStorageDriver()->fgets($fh, $length);
|
155 |
+
}
|
156 |
+
|
157 |
+
public static function flock($fh, $operation, $wouldblock = NULL) {
|
158 |
+
return Filesystem::getStorageDriver()->flock($fh, $operation, $wouldblock);
|
159 |
+
}
|
160 |
+
|
161 |
+
public static function feof($fh) {
|
162 |
+
return Filesystem::getStorageDriver()->feof($fh);
|
163 |
+
}
|
164 |
+
|
165 |
private static function explodeByHeaders($content) {
|
166 |
$headers = [];
|
167 |
$pos = strpos($content, "\r\n\r\n");
|
nitropack-sdk/NitroPack/SDK/HealthStatus.php
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace NitroPack\SDK;
|
3 |
+
|
4 |
+
class HealthStatus {
|
5 |
+
const UNDER_THE_WEATHER = "UNDER THE WEATHER";
|
6 |
+
const HEALTHY = "HEALTHY";
|
7 |
+
const SICK = "SICK";
|
8 |
+
}
|
nitropack-sdk/NitroPack/SDK/NitroPack.php
CHANGED
@@ -2,11 +2,12 @@
|
|
2 |
namespace NitroPack\SDK;
|
3 |
|
4 |
class NitroPack {
|
5 |
-
const VERSION = '0.
|
6 |
const PAGECACHE_LOCK_EXPIRATION_TIME = 300; // in seconds
|
7 |
private $dataDir;
|
8 |
private $cachePath = array('data', 'pagecache');
|
9 |
private $configFile = array('data', 'config.json');
|
|
|
10 |
private $pageCacheLockFile = array('data', 'get_cache.lock');
|
11 |
private $cachePathSuffix = NULL;
|
12 |
private $configTTL; // In seconds
|
@@ -18,9 +19,12 @@ class NitroPack {
|
|
18 |
private $url;
|
19 |
private $config;
|
20 |
private $device;
|
21 |
-
public $pageCache; // TODO: consider better ways of protecting/providing this outside the class
|
22 |
private $api;
|
23 |
|
|
|
|
|
|
|
|
|
24 |
private static $cachePrefixes = array();
|
25 |
private static $cookieFilters = array();
|
26 |
|
@@ -84,6 +88,10 @@ class NitroPack {
|
|
84 |
$this->siteId = $siteId;
|
85 |
$this->siteSecret = $siteSecret;
|
86 |
$this->dataDir = $dataDir;
|
|
|
|
|
|
|
|
|
87 |
if (empty($userAgent)) {
|
88 |
$this->userAgent = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36';
|
89 |
} else {
|
@@ -131,6 +139,8 @@ class NitroPack {
|
|
131 |
}
|
132 |
|
133 |
$this->api = new Api($this->siteId, $siteSecret);
|
|
|
|
|
134 |
|
135 |
$this->pageCache->setDataDir($this->getCacheDir());
|
136 |
|
@@ -195,6 +205,40 @@ class NitroPack {
|
|
195 |
return Filesystem::getOsPath($cachePath);
|
196 |
}
|
197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
public function hasCache($layout = 'default') {
|
199 |
if ($this->hasLocalCache()) {
|
200 |
return true;
|
@@ -204,8 +248,12 @@ class NitroPack {
|
|
204 |
}
|
205 |
|
206 |
public function hasLocalCache($checkIfRequestIsAllowed = true) {
|
|
|
207 |
if (!$this->isAllowedUrl($this->url) || ($checkIfRequestIsAllowed && !$this->isAllowedRequest())) return false;
|
208 |
$cacheRevision = !empty($this->config->RevisionHash) ? $this->config->RevisionHash : NULL;
|
|
|
|
|
|
|
209 |
|
210 |
if (!$this->pageCache->getUseInvalidated()) {
|
211 |
$ttl = $this->config->PageCache->ExpireTime;
|
@@ -217,6 +265,7 @@ class NitroPack {
|
|
217 |
}
|
218 |
|
219 |
public function hasRemoteCache($layout, $checkIfRequestIsAllowed = true) {
|
|
|
220 |
if (!$this->isAllowedUrl($this->url) || ($checkIfRequestIsAllowed && !$this->isAllowedRequest()) || $this->isPageCacheLocked()) return false;
|
221 |
$resp = $this->api->getCache($this->url, $this->userAgent, $this->supportedCookiesFilter(self::getCookies()), $this->isAJAXRequest(), $layout);
|
222 |
|
@@ -278,21 +327,28 @@ class NitroPack {
|
|
278 |
$apiResult = true;
|
279 |
if ($url) {
|
280 |
if (is_array($url)) {
|
281 |
-
foreach ($url as
|
|
|
282 |
if ($invalidate) {
|
283 |
$localResult &= $this->invalidateLocalUrlCache($urlLink);
|
284 |
} else {
|
285 |
$localResult &= $this->purgeLocalUrlCache($urlLink);
|
286 |
}
|
287 |
}
|
288 |
-
$apiResult &= $this->api->purgeCache($url, false, $reason);
|
289 |
} else {
|
|
|
290 |
if ($invalidate) {
|
291 |
$localResult &= $this->invalidateLocalUrlCache($url);
|
292 |
} else {
|
293 |
$localResult &= $this->purgeLocalUrlCache($url);
|
294 |
}
|
|
|
|
|
|
|
295 |
$apiResult &= $this->api->purgeCache($url, false, $reason);
|
|
|
|
|
|
|
296 |
}
|
297 |
}
|
298 |
|
@@ -312,12 +368,17 @@ class NitroPack {
|
|
312 |
$localResult &= $this->purgeLocalUrlCache($url);
|
313 |
}
|
314 |
}
|
|
|
|
|
|
|
|
|
|
|
315 |
} catch (\Exception $e) {
|
316 |
$hadError = true;
|
317 |
$attemptsLeft--;
|
318 |
sleep(3);
|
319 |
}
|
320 |
-
} while (
|
321 |
}
|
322 |
} else {
|
323 |
if ($invalidate) {
|
@@ -370,9 +431,22 @@ class NitroPack {
|
|
370 |
return $staleCacheDir;
|
371 |
}
|
372 |
|
373 |
-
public function fetchConfig() {
|
|
|
374 |
$fetcher = new Api\RemoteConfigFetcher($this->siteId, $this->siteSecret);
|
375 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
376 |
$config = json_decode($configContents);
|
377 |
if ($config) {
|
378 |
$config->SDKVersion = NitroPack::VERSION;
|
@@ -402,6 +476,7 @@ class NitroPack {
|
|
402 |
if (!empty($this->config->CacheIntegrations)) {
|
403 |
if (!empty($this->config->CacheIntegrations->Varnish)) {
|
404 |
if ($url) {
|
|
|
405 |
$varnish = new Integrations\Varnish($this->config->CacheIntegrations->Varnish->Servers, $this->config->CacheIntegrations->Varnish->PurgeSingleMethod);
|
406 |
$varnish->purge($url);
|
407 |
} else {
|
@@ -548,6 +623,7 @@ class NitroPack {
|
|
548 |
}
|
549 |
|
550 |
public function purgeLocalUrlCache($url) {
|
|
|
551 |
$this->purgeProxyCache($url);
|
552 |
$localResult = true;
|
553 |
$cacheDir = $this->getCacheDir();
|
@@ -562,6 +638,7 @@ class NitroPack {
|
|
562 |
}
|
563 |
|
564 |
public function invalidateLocalUrlCache($url) {
|
|
|
565 |
$this->purgeProxyCache($url);
|
566 |
$localResult = true;
|
567 |
$cacheDir = $this->getCacheDir();
|
@@ -625,10 +702,12 @@ class NitroPack {
|
|
625 |
if (Filesystem::fileExists($file) || $this->fetchConfig()) {
|
626 |
$config = json_decode(Filesystem::fileGetContents($file));
|
627 |
if (empty($config->SDKVersion) || $config->SDKVersion !== NitroPack::VERSION || empty($config->LastFetch) || time() - $config->LastFetch >= $this->configTTL) {
|
628 |
-
if ($this->
|
629 |
-
|
630 |
-
|
631 |
-
|
|
|
|
|
632 |
}
|
633 |
}
|
634 |
$this->config = $config;
|
@@ -704,6 +783,11 @@ class NitroPack {
|
|
704 |
array_unshift($pageCacheLockFile, $this->dataDir);
|
705 |
return Filesystem::getOsPath($pageCacheLockFile);
|
706 |
}
|
|
|
|
|
|
|
|
|
|
|
707 |
}
|
708 |
|
709 |
class NoConfigException extends \Exception {}
|
2 |
namespace NitroPack\SDK;
|
3 |
|
4 |
class NitroPack {
|
5 |
+
const VERSION = '0.20.0';
|
6 |
const PAGECACHE_LOCK_EXPIRATION_TIME = 300; // in seconds
|
7 |
private $dataDir;
|
8 |
private $cachePath = array('data', 'pagecache');
|
9 |
private $configFile = array('data', 'config.json');
|
10 |
+
private $healthStatusFile = array('data', 'service-health');
|
11 |
private $pageCacheLockFile = array('data', 'get_cache.lock');
|
12 |
private $cachePathSuffix = NULL;
|
13 |
private $configTTL; // In seconds
|
19 |
private $url;
|
20 |
private $config;
|
21 |
private $device;
|
|
|
22 |
private $api;
|
23 |
|
24 |
+
public $backlog;
|
25 |
+
public $healthStatus;
|
26 |
+
public $pageCache; // TODO: consider better ways of protecting/providing this outside the class
|
27 |
+
|
28 |
private static $cachePrefixes = array();
|
29 |
private static $cookieFilters = array();
|
30 |
|
88 |
$this->siteId = $siteId;
|
89 |
$this->siteSecret = $siteSecret;
|
90 |
$this->dataDir = $dataDir;
|
91 |
+
$this->backlog = new Backlog($dataDir, $this);
|
92 |
+
$this->healthStatus = HealthStatus::HEALTHY;
|
93 |
+
$this->loadHealthStatus();
|
94 |
+
|
95 |
if (empty($userAgent)) {
|
96 |
$this->userAgent = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36';
|
97 |
} else {
|
139 |
}
|
140 |
|
141 |
$this->api = new Api($this->siteId, $siteSecret);
|
142 |
+
$this->api->setBacklog($this->backlog);
|
143 |
+
$this->api->setNitroPack($this);
|
144 |
|
145 |
$this->pageCache->setDataDir($this->getCacheDir());
|
146 |
|
205 |
return Filesystem::getOsPath($cachePath);
|
206 |
}
|
207 |
|
208 |
+
public function getHealthStatus() {
|
209 |
+
return $this->healthStatus;
|
210 |
+
}
|
211 |
+
|
212 |
+
public function getHealthStatusFile() {
|
213 |
+
$healthStatusFile = $this->healthStatusFile;
|
214 |
+
array_unshift($healthStatusFile, $this->dataDir);
|
215 |
+
return Filesystem::getOsPath($healthStatusFile);
|
216 |
+
}
|
217 |
+
|
218 |
+
public function setHealthStatus($status) {
|
219 |
+
$this->healthStatus = $status;
|
220 |
+
Filesystem::filePutContents($this->getHealthStatusFile(), $status);
|
221 |
+
}
|
222 |
+
|
223 |
+
public function loadHealthStatus() {
|
224 |
+
if (Filesystem::fileExists($this->getHealthStatusFile())) {
|
225 |
+
$this->healthStatus = Filesystem::fileGetContents($this->getHealthStatusFile());
|
226 |
+
} else {
|
227 |
+
$this->healthStatus = HealthStatus::HEALTHY;
|
228 |
+
}
|
229 |
+
}
|
230 |
+
|
231 |
+
public function checkHealthStatus() {
|
232 |
+
try {
|
233 |
+
// TODO: Potentially replace this with a dedicated method in the API
|
234 |
+
$this->fetchConfig(true);
|
235 |
+
$this->setHealthStatus(HealthStatus::HEALTHY);
|
236 |
+
return HealthStatus::HEALTHY;
|
237 |
+
} catch (\Exception $e) {
|
238 |
+
return $this->getHealthStatus();
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
public function hasCache($layout = 'default') {
|
243 |
if ($this->hasLocalCache()) {
|
244 |
return true;
|
248 |
}
|
249 |
|
250 |
public function hasLocalCache($checkIfRequestIsAllowed = true) {
|
251 |
+
if ($this->backlog->exists()) return false;
|
252 |
if (!$this->isAllowedUrl($this->url) || ($checkIfRequestIsAllowed && !$this->isAllowedRequest())) return false;
|
253 |
$cacheRevision = !empty($this->config->RevisionHash) ? $this->config->RevisionHash : NULL;
|
254 |
+
if ($this->getHealthStatus() !== HealthStatus::HEALTHY) {
|
255 |
+
return false;
|
256 |
+
}
|
257 |
|
258 |
if (!$this->pageCache->getUseInvalidated()) {
|
259 |
$ttl = $this->config->PageCache->ExpireTime;
|
265 |
}
|
266 |
|
267 |
public function hasRemoteCache($layout, $checkIfRequestIsAllowed = true) {
|
268 |
+
if ($this->backlog->exists()) return false;
|
269 |
if (!$this->isAllowedUrl($this->url) || ($checkIfRequestIsAllowed && !$this->isAllowedRequest()) || $this->isPageCacheLocked()) return false;
|
270 |
$resp = $this->api->getCache($this->url, $this->userAgent, $this->supportedCookiesFilter(self::getCookies()), $this->isAJAXRequest(), $layout);
|
271 |
|
327 |
$apiResult = true;
|
328 |
if ($url) {
|
329 |
if (is_array($url)) {
|
330 |
+
foreach ($url as &$urlLink) {
|
331 |
+
$urlLink = $this->normalizeUrl($urlLink);
|
332 |
if ($invalidate) {
|
333 |
$localResult &= $this->invalidateLocalUrlCache($urlLink);
|
334 |
} else {
|
335 |
$localResult &= $this->purgeLocalUrlCache($urlLink);
|
336 |
}
|
337 |
}
|
|
|
338 |
} else {
|
339 |
+
$url = $this->normalizeUrl($url);
|
340 |
if ($invalidate) {
|
341 |
$localResult &= $this->invalidateLocalUrlCache($url);
|
342 |
} else {
|
343 |
$localResult &= $this->purgeLocalUrlCache($url);
|
344 |
}
|
345 |
+
}
|
346 |
+
|
347 |
+
try {
|
348 |
$apiResult &= $this->api->purgeCache($url, false, $reason);
|
349 |
+
} catch (ServiceDownException $e) {
|
350 |
+
$apiResult = false;
|
351 |
+
// TODO: Potentially log this
|
352 |
}
|
353 |
}
|
354 |
|
368 |
$localResult &= $this->purgeLocalUrlCache($url);
|
369 |
}
|
370 |
}
|
371 |
+
} catch (ServiceDownException $e) {
|
372 |
+
$this->purgeLocalCache(true); // TODO: This will leave stale cache files. Think of a way to delete them on systems that do not have a heartbeat (i.e custom integrations).
|
373 |
+
$apiResult = false;
|
374 |
+
// TODO: Log this
|
375 |
+
break;
|
376 |
} catch (\Exception $e) {
|
377 |
$hadError = true;
|
378 |
$attemptsLeft--;
|
379 |
sleep(3);
|
380 |
}
|
381 |
+
} while ($attemptsLeft > 0 && count($purgedUrls) > 0);
|
382 |
}
|
383 |
} else {
|
384 |
if ($invalidate) {
|
431 |
return $staleCacheDir;
|
432 |
}
|
433 |
|
434 |
+
public function fetchConfig($ignoreHealthStatus = false) {
|
435 |
+
// TODO: Record failures and repeat with a delay
|
436 |
$fetcher = new Api\RemoteConfigFetcher($this->siteId, $this->siteSecret);
|
437 |
+
|
438 |
+
if (!$ignoreHealthStatus) {
|
439 |
+
$fetcher->setBacklog($this->backlog);
|
440 |
+
$fetcher->setNitroPack($this);
|
441 |
+
}
|
442 |
+
|
443 |
+
try {
|
444 |
+
$configContents = $fetcher->get(); // this can throw in case of http errors or validation failures
|
445 |
+
} catch (\Exception $e) {
|
446 |
+
// TODO: Record this failure, possibly in the backlog
|
447 |
+
throw $e;
|
448 |
+
}
|
449 |
+
|
450 |
$config = json_decode($configContents);
|
451 |
if ($config) {
|
452 |
$config->SDKVersion = NitroPack::VERSION;
|
476 |
if (!empty($this->config->CacheIntegrations)) {
|
477 |
if (!empty($this->config->CacheIntegrations->Varnish)) {
|
478 |
if ($url) {
|
479 |
+
$url = $this->normalizeUrl($url);
|
480 |
$varnish = new Integrations\Varnish($this->config->CacheIntegrations->Varnish->Servers, $this->config->CacheIntegrations->Varnish->PurgeSingleMethod);
|
481 |
$varnish->purge($url);
|
482 |
} else {
|
623 |
}
|
624 |
|
625 |
public function purgeLocalUrlCache($url) {
|
626 |
+
$url = $this->normalizeUrl($url);
|
627 |
$this->purgeProxyCache($url);
|
628 |
$localResult = true;
|
629 |
$cacheDir = $this->getCacheDir();
|
638 |
}
|
639 |
|
640 |
public function invalidateLocalUrlCache($url) {
|
641 |
+
$url = $this->normalizeUrl($url);
|
642 |
$this->purgeProxyCache($url);
|
643 |
$localResult = true;
|
644 |
$cacheDir = $this->getCacheDir();
|
702 |
if (Filesystem::fileExists($file) || $this->fetchConfig()) {
|
703 |
$config = json_decode(Filesystem::fileGetContents($file));
|
704 |
if (empty($config->SDKVersion) || $config->SDKVersion !== NitroPack::VERSION || empty($config->LastFetch) || time() - $config->LastFetch >= $this->configTTL) {
|
705 |
+
if ($this->getHealthStatus() === HealthStatus::HEALTHY) {
|
706 |
+
if ($this->fetchConfig()) {
|
707 |
+
$config = json_decode(Filesystem::fileGetContents($file));
|
708 |
+
} else {
|
709 |
+
throw new NoConfigException("Can't load config file");
|
710 |
+
}
|
711 |
}
|
712 |
}
|
713 |
$this->config = $config;
|
783 |
array_unshift($pageCacheLockFile, $this->dataDir);
|
784 |
return Filesystem::getOsPath($pageCacheLockFile);
|
785 |
}
|
786 |
+
|
787 |
+
private function normalizeUrl($url) {
|
788 |
+
$urlObj = new \NitroPack\Url($url);
|
789 |
+
return $urlObj->getNormalized();
|
790 |
+
}
|
791 |
}
|
792 |
|
793 |
class NoConfigException extends \Exception {}
|
nitropack-sdk/NitroPack/SDK/ServiceDownException.php
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace NitroPack\SDK;
|
3 |
+
|
4 |
+
class ServiceDownException extends \RuntimeException {}
|
nitropack-sdk/NitroPack/SDK/StorageDriver/Disk.php
CHANGED
@@ -1,6 +1,8 @@
|
|
1 |
<?php
|
2 |
namespace NitroPack\SDK\StorageDriver;
|
3 |
|
|
|
|
|
4 |
class Disk {
|
5 |
public function getOsPath($parts) {
|
6 |
return implode(DIRECTORY_SEPARATOR, $parts);
|
@@ -100,4 +102,75 @@ class Disk {
|
|
100 |
public function rename($oldName, $newName) {
|
101 |
return @rename($oldName, $newName);
|
102 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
}
|
1 |
<?php
|
2 |
namespace NitroPack\SDK\StorageDriver;
|
3 |
|
4 |
+
use \NitroPack\SDK\FileHandle;
|
5 |
+
|
6 |
class Disk {
|
7 |
public function getOsPath($parts) {
|
8 |
return implode(DIRECTORY_SEPARATOR, $parts);
|
102 |
public function rename($oldName, $newName) {
|
103 |
return @rename($oldName, $newName);
|
104 |
}
|
105 |
+
|
106 |
+
public function fopen($file, $mode) {
|
107 |
+
$fh = @fopen($file, $mode);
|
108 |
+
if ($fh) {
|
109 |
+
return new DiskFileHandle($fh);
|
110 |
+
} else {
|
111 |
+
return false;
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
public function fclose($fh) {
|
116 |
+
if (!($fh instanceof FileHandle)) return false;
|
117 |
+
return @fclose($fh->getHandle());
|
118 |
+
}
|
119 |
+
|
120 |
+
public function fflush($fh) {
|
121 |
+
if (!($fh instanceof FileHandle)) return false;
|
122 |
+
return @fflush($fh->getHandle());
|
123 |
+
}
|
124 |
+
|
125 |
+
public function fseek($fh, $offset, $whence = SEEK_SET) {
|
126 |
+
if (!($fh instanceof FileHandle)) return false;
|
127 |
+
return @fseek($fh->getHandle(), $offset, $whence);
|
128 |
+
}
|
129 |
+
|
130 |
+
public function ftell($fh) {
|
131 |
+
if (!($fh instanceof FileHandle)) return false;
|
132 |
+
return @ftell($fh->getHandle());
|
133 |
+
}
|
134 |
+
|
135 |
+
public function fwrite($fh, $string, $length = NULL) {
|
136 |
+
if (!($fh instanceof FileHandle)) return false;
|
137 |
+
if ($length !== NULL) {
|
138 |
+
return @fwrite($fh->getHandle(), $string, $length);
|
139 |
+
} else {
|
140 |
+
return @fwrite($fh->getHandle(), $string);
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
public function fread($fh, $length) {
|
145 |
+
if (!($fh instanceof FileHandle)) return false;
|
146 |
+
return @fread($fh->getHandle(), $length);
|
147 |
+
}
|
148 |
+
|
149 |
+
public function fgetc($fh) {
|
150 |
+
if (!($fh instanceof FileHandle)) return false;
|
151 |
+
return @fgetc($fh->getHandle());
|
152 |
+
}
|
153 |
+
|
154 |
+
public function fgets($fh, $length = NULL) {
|
155 |
+
if (!($fh instanceof FileHandle)) return false;
|
156 |
+
if ($length !== NULL) {
|
157 |
+
return @fgets($fh->getHandle(), $length);
|
158 |
+
} else {
|
159 |
+
return @fgets($fh->getHandle());
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
public function flock($fh, $operation, $wouldblock = NULL) {
|
164 |
+
if (!($fh instanceof FileHandle)) return false;
|
165 |
+
if ($wouldblock !== NULL) {
|
166 |
+
return @flock($fh->getHandle(), $operation, $wouldblock);
|
167 |
+
} else {
|
168 |
+
return @flock($fh->getHandle(), $operation);
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
public function feof($fh) {
|
173 |
+
if (!($fh instanceof FileHandle)) return false;
|
174 |
+
return @feof($fh->getHandle());
|
175 |
+
}
|
176 |
}
|
nitropack-sdk/NitroPack/SDK/StorageDriver/DiskFileHandle.php
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace NitroPack\SDK\StorageDriver;
|
3 |
+
|
4 |
+
use \NitroPack\SDK\FileHandle;
|
5 |
+
|
6 |
+
class DiskFileHandle extends FileHandle {}
|
nitropack-sdk/NitroPack/SDK/StorageDriver/Redis.php
CHANGED
@@ -3,7 +3,11 @@
|
|
3 |
// This is also a bad design for emulating a file system performance wise. Only use this driver when you need shared storage between multiple servers. On a single server with an SSD using the Disk diver is a better idea.
|
4 |
namespace NitroPack\SDK\StorageDriver;
|
5 |
|
|
|
|
|
6 |
class Redis {
|
|
|
|
|
7 |
private $redis;
|
8 |
|
9 |
private function preparePathInput($path) {
|
@@ -120,6 +124,13 @@ class Redis {
|
|
120 |
public function trunkDir($dir) {
|
121 |
$dir = $this->preparePathInput($dir);
|
122 |
if (!$this->isDir($dir)) return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
$success = false;
|
124 |
try {
|
125 |
$this->redis->eval('
|
@@ -132,7 +143,7 @@ repeat
|
|
132 |
redis.call("UNLINK", list[i]);
|
133 |
end;
|
134 |
until cursor == "0";
|
135 |
-
', array($
|
136 |
$success = true;
|
137 |
} catch (\Exception $e) {
|
138 |
// TODO: Log an error
|
@@ -153,6 +164,7 @@ until cursor == "0";
|
|
153 |
public function dirForeach($dir, $callback) {
|
154 |
$dir = $this->preparePathInput($dir);
|
155 |
if (!$this->isDir($dir)) return false;
|
|
|
156 |
$it = NULL;
|
157 |
$prevScanMode = $this->redis->getOption(\Redis::OPT_SCAN);
|
158 |
$this->redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY);
|
@@ -166,11 +178,11 @@ until cursor == "0";
|
|
166 |
}
|
167 |
} catch (\Exception $e) {
|
168 |
// TODO: Log an error
|
169 |
-
|
170 |
} finally {
|
171 |
$this->redis->setOption(\Redis::OPT_SCAN, $prevScanMode);
|
172 |
}
|
173 |
-
return
|
174 |
}
|
175 |
|
176 |
public function mtime($path) {
|
@@ -227,7 +239,7 @@ repeat
|
|
227 |
redis.call("RENAME", s, changed);
|
228 |
end;
|
229 |
until cursor == "0";
|
230 |
-
', array($this->getOsPath(array($oldKey, "*")), $oldKey . DIRECTORY_SEPARATOR, $newKey . DIRECTORY_SEPARATOR), 0);
|
231 |
}
|
232 |
|
233 |
$success = true;
|
@@ -236,4 +248,211 @@ until cursor == "0";
|
|
236 |
}
|
237 |
return $success;
|
238 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
}
|
3 |
// This is also a bad design for emulating a file system performance wise. Only use this driver when you need shared storage between multiple servers. On a single server with an SSD using the Disk diver is a better idea.
|
4 |
namespace NitroPack\SDK\StorageDriver;
|
5 |
|
6 |
+
use \NitroPack\SDK\FileHandle;
|
7 |
+
|
8 |
class Redis {
|
9 |
+
const LOCK_TTL = 30000;
|
10 |
+
const LOCK_TIMEOUT = 30;
|
11 |
private $redis;
|
12 |
|
13 |
private function preparePathInput($path) {
|
124 |
public function trunkDir($dir) {
|
125 |
$dir = $this->preparePathInput($dir);
|
126 |
if (!$this->isDir($dir)) return false;
|
127 |
+
|
128 |
+
if ($dir == DIRECTORY_SEPARATOR) {
|
129 |
+
$osPath = DIRECTORY_SEPARATOR . "*";
|
130 |
+
} else {
|
131 |
+
$osPath = $this->getOsPath(array($dir, "*"));
|
132 |
+
}
|
133 |
+
|
134 |
$success = false;
|
135 |
try {
|
136 |
$this->redis->eval('
|
143 |
redis.call("UNLINK", list[i]);
|
144 |
end;
|
145 |
until cursor == "0";
|
146 |
+
', array($osPath), 0);
|
147 |
$success = true;
|
148 |
} catch (\Exception $e) {
|
149 |
// TODO: Log an error
|
164 |
public function dirForeach($dir, $callback) {
|
165 |
$dir = $this->preparePathInput($dir);
|
166 |
if (!$this->isDir($dir)) return false;
|
167 |
+
$result = true;
|
168 |
$it = NULL;
|
169 |
$prevScanMode = $this->redis->getOption(\Redis::OPT_SCAN);
|
170 |
$this->redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY);
|
178 |
}
|
179 |
} catch (\Exception $e) {
|
180 |
// TODO: Log an error
|
181 |
+
$result = false;
|
182 |
} finally {
|
183 |
$this->redis->setOption(\Redis::OPT_SCAN, $prevScanMode);
|
184 |
}
|
185 |
+
return $result;
|
186 |
}
|
187 |
|
188 |
public function mtime($path) {
|
239 |
redis.call("RENAME", s, changed);
|
240 |
end;
|
241 |
until cursor == "0";
|
242 |
+
', array($this->getOsPath(array($oldKey, "*")), $this->prepareForLuaPattern($oldKey . DIRECTORY_SEPARATOR), $newKey . DIRECTORY_SEPARATOR), 0);
|
243 |
}
|
244 |
|
245 |
$success = true;
|
248 |
}
|
249 |
return $success;
|
250 |
}
|
251 |
+
|
252 |
+
private function prepareForLuaPattern($pattern) {
|
253 |
+
$specialPatternChars = array("%", "(", ")", ".", "+", "-", "*", "?", "[", "^", "$");
|
254 |
+
$regex = "/(" . implode("|", array_map("preg_quote", $specialPatternChars)) . ")/";
|
255 |
+
return preg_replace($regex, "%$1", $pattern);
|
256 |
+
//return str_replace(array("%", "(", ")", ".", "+", "-", "*", "?", "[", "^", "$"), array("%%", "%(", "%)", "%.", "%+", "%-", "%*", "%?", "%[", "%^", "%$"), $pattern);
|
257 |
+
}
|
258 |
+
|
259 |
+
public function fopen($file, $mode) {
|
260 |
+
$fh = new \stdClass();
|
261 |
+
$fh->pos = 0;
|
262 |
+
$fh->content = "";
|
263 |
+
$fh->canRead = in_array($mode, array("r", "r+", "w+", "a+", "x+", "c+"));
|
264 |
+
$fh->canWrite = in_array($mode, array("r+", "w", "w+", "a", "a+", "x", "x+", "c", "c+"));
|
265 |
+
$fh->mode = $mode;
|
266 |
+
$fh->writeOccurred = false;
|
267 |
+
$fh->isOpen = true;
|
268 |
+
$fh->path = $file;
|
269 |
+
|
270 |
+
switch ($mode) {
|
271 |
+
case "r":
|
272 |
+
case "r+":
|
273 |
+
if (!$this->exists($file)) return false;
|
274 |
+
$fh->content = $this->getContent($file);
|
275 |
+
break;
|
276 |
+
case "w":
|
277 |
+
case "w+":
|
278 |
+
// Do nothing
|
279 |
+
break;
|
280 |
+
case "a":
|
281 |
+
case "a+":
|
282 |
+
if (!$this->exists($file)) return false;
|
283 |
+
$fh->content = $this->getContent($file);
|
284 |
+
break;
|
285 |
+
case "x":
|
286 |
+
case "x+":
|
287 |
+
if ($this->exists($file)) return false;
|
288 |
+
break;
|
289 |
+
case "c":
|
290 |
+
case "c+":
|
291 |
+
if ($this->exists($file)) {
|
292 |
+
$fh->content = $this->getContent($file);
|
293 |
+
}
|
294 |
+
break;
|
295 |
+
}
|
296 |
+
|
297 |
+
return new RedisFileHandle($fh);
|
298 |
+
}
|
299 |
+
|
300 |
+
public function fclose($handle) {
|
301 |
+
if (!($handle instanceof FileHandle)) return false;
|
302 |
+
$fh = $handle->getHandle();
|
303 |
+
if (!$fh->isOpen) return true;
|
304 |
+
|
305 |
+
if ($fh->canWrite && $fh->writeOccurred) {
|
306 |
+
$status = $this->fflush($handle);
|
307 |
+
if ($status) {
|
308 |
+
$fh->isOpen = false;
|
309 |
+
$fh->canRead = false;
|
310 |
+
$fh->canWrite = false;
|
311 |
+
}
|
312 |
+
return $status;
|
313 |
+
}
|
314 |
+
|
315 |
+
return true;
|
316 |
+
}
|
317 |
+
|
318 |
+
public function fflush($fh) {
|
319 |
+
if (!($fh instanceof FileHandle)) return false;
|
320 |
+
$fh = $fh->getHandle();
|
321 |
+
if ($fh->canWrite && $fh->writeOccurred && $fh->isOpen) {
|
322 |
+
if ($this->setContent($fh->path, $fh->content)) {
|
323 |
+
$fh->writeOccurred = false; // Reset the counter
|
324 |
+
return true;
|
325 |
+
} else {
|
326 |
+
return false;
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
return false;
|
331 |
+
}
|
332 |
+
|
333 |
+
public function fseek($fh, $offset, $whence = SEEK_SET) {
|
334 |
+
if (!($fh instanceof FileHandle)) return false;
|
335 |
+
$fh = $fh->getHandle();
|
336 |
+
switch ($whence) {
|
337 |
+
case SEEK_CUR:
|
338 |
+
$fh->pos += $offset;
|
339 |
+
break;
|
340 |
+
case SEEK_END:
|
341 |
+
$fh->pos = strlen($fh->content) + $offset;
|
342 |
+
break;
|
343 |
+
default:
|
344 |
+
$fh->pos = $offset;
|
345 |
+
break;
|
346 |
+
}
|
347 |
+
|
348 |
+
return 0;
|
349 |
+
}
|
350 |
+
|
351 |
+
public function ftell($fh) {
|
352 |
+
if (!($fh instanceof FileHandle)) return false;
|
353 |
+
$fh = $fh->getHandle();
|
354 |
+
return $fh->pos;
|
355 |
+
}
|
356 |
+
|
357 |
+
public function fwrite($fh, $string, $length = NULL) {
|
358 |
+
if (!($fh instanceof FileHandle)) return false;
|
359 |
+
$fh = $fh->getHandle();
|
360 |
+
if (!$fh->canWrite) return false;
|
361 |
+
|
362 |
+
if ($length === NULL || $length > strlen($string)) {
|
363 |
+
$length = strlen($string);
|
364 |
+
}
|
365 |
+
|
366 |
+
if ($fh->mode[0] == "a" || $fh->pos > strlen($fh->content)) {
|
367 |
+
$fh->content .= substr($string, 0, $length);
|
368 |
+
$fh->pos = strlen($fh->content);
|
369 |
+
} else {
|
370 |
+
$head = substr($fh->content, 0, $fh->pos);
|
371 |
+
$tail = substr($fh->content, $fh->pos + $length);
|
372 |
+
$fh->content = $head . substr($string, 0, $length) . $tail;
|
373 |
+
$fh->pos = strlen($head) + $length;
|
374 |
+
}
|
375 |
+
|
376 |
+
$fh->writeOccurred = true;
|
377 |
+
|
378 |
+
return $length;
|
379 |
+
}
|
380 |
+
|
381 |
+
public function fread($fh, $length) {
|
382 |
+
if (!($fh instanceof FileHandle)) return false;
|
383 |
+
$fh = $fh->getHandle();
|
384 |
+
if (!$fh->canRead) return false;
|
385 |
+
|
386 |
+
$result = substr($fh->content, $fh->pos, $length);
|
387 |
+
$fh->pos += strlen($result);//$length;
|
388 |
+
|
389 |
+
return $result;
|
390 |
+
}
|
391 |
+
|
392 |
+
public function fgetc($fh) {
|
393 |
+
if (!($fh instanceof FileHandle)) return false;
|
394 |
+
$fh = $fh->getHandle();
|
395 |
+
if (!$fh->canRead) return false;
|
396 |
+
|
397 |
+
return $fh->content[$fh->pos++];
|
398 |
+
}
|
399 |
+
|
400 |
+
public function fgets($fh, $length = NULL) {
|
401 |
+
if (!($fh instanceof FileHandle)) return false;
|
402 |
+
$fh = $fh->getHandle();
|
403 |
+
if (!$fh->canRead) return false;
|
404 |
+
|
405 |
+
if ($length === NULL) {
|
406 |
+
$length = strlen($fh->content);
|
407 |
+
}
|
408 |
+
|
409 |
+
$pos = strpos($fh->content, "\n", $fh->pos);
|
410 |
+
if ($pos === false) {
|
411 |
+
if ($fh->pos >= strlen($fh->content)) return false;
|
412 |
+
$result = substr($fh->content, $fh->pos, $length);
|
413 |
+
} else {
|
414 |
+
$result = substr($fh->content, $fh->pos, $pos - $fh->pos+1);
|
415 |
+
}
|
416 |
+
$fh->pos += strlen($result);
|
417 |
+
return $result;
|
418 |
+
}
|
419 |
+
|
420 |
+
public function flock($fh, $operation, $wouldblock = NULL) {
|
421 |
+
if (!($fh instanceof FileHandle)) return false;
|
422 |
+
$fh = $fh->getHandle();
|
423 |
+
if (!$fh->isOpen) return false;
|
424 |
+
switch ($operation) {
|
425 |
+
case LOCK_SH:
|
426 |
+
// In this case we acquire a lock in order to wait for any other process that has currently locked the file
|
427 |
+
// And then release the lock immediately.
|
428 |
+
// The sole purpose of this is to block until the writer process is complete.
|
429 |
+
return $this->acquireLock($fh->path, self::LOCK_TTL) && $this->releaseLock($fh->path);
|
430 |
+
case LOCK_EX:
|
431 |
+
return $this->acquireLock($fh->path, self::LOCK_TTL);
|
432 |
+
case LOCK_UN:
|
433 |
+
return $this->releaseLock($fh->path);
|
434 |
+
default:
|
435 |
+
return true;
|
436 |
+
}
|
437 |
+
return true;
|
438 |
+
}
|
439 |
+
|
440 |
+
public function feof($fh) {
|
441 |
+
if (!($fh instanceof FileHandle)) return false;
|
442 |
+
$fh = $fh->getHandle();
|
443 |
+
if (!$fh->isOpen) return false;
|
444 |
+
return $fh->pos >= strlen($fh->content);
|
445 |
+
}
|
446 |
+
|
447 |
+
private function acquireLock($path, $ttl) {
|
448 |
+
$startTime = microtime(true);
|
449 |
+
while (false === ($result = $this->redis->set('lock:' . $path, time(), ['nx', 'px' => self::LOCK_TTL])) && microtime(true) - $startTime < self::LOCK_TIMEOUT) {
|
450 |
+
usleep(50000);
|
451 |
+
}
|
452 |
+
return $result;
|
453 |
+
}
|
454 |
+
|
455 |
+
private function releaseLock($path) {
|
456 |
+
return $this->redis->del('lock:' . $path);
|
457 |
+
}
|
458 |
}
|
nitropack-sdk/NitroPack/SDK/StorageDriver/RedisFileHandle.php
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace NitroPack\SDK\StorageDriver;
|
3 |
+
|
4 |
+
use \NitroPack\SDK\FileHandle;
|
5 |
+
|
6 |
+
class RedisFileHandle extends FileHandle {}
|
readme.txt
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
Contributors: nitropack
|
3 |
Tags: cache,perfomance,optimize,pagespeed,lazy load,cdn,critical css,compression,defer css javascript,minify css,minify,webp
|
4 |
Requires at least: 4.7
|
5 |
-
Tested up to: 5.
|
6 |
Requires PHP: 5.3
|
7 |
-
Stable tag: 1.
|
8 |
License: GNU General Public License, version 2
|
9 |
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
@@ -169,6 +169,13 @@ No. We’ve designed NitroPack to be a very lightweight solution that adds no CP
|
|
169 |
|
170 |
== Changelog ==
|
171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
= 1.4.1 =
|
173 |
* Improvement: Performance improvements in content updates
|
174 |
* Improvement: Better compatibility with Download Monitor
|
2 |
Contributors: nitropack
|
3 |
Tags: cache,perfomance,optimize,pagespeed,lazy load,cdn,critical css,compression,defer css javascript,minify css,minify,webp
|
4 |
Requires at least: 4.7
|
5 |
+
Tested up to: 5.6
|
6 |
Requires PHP: 5.3
|
7 |
+
Stable tag: 1.5.0
|
8 |
License: GNU General Public License, version 2
|
9 |
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
169 |
|
170 |
== Changelog ==
|
171 |
|
172 |
+
= 1.5.0 =
|
173 |
+
* New Feature: Compatibility with Cloudflare APO
|
174 |
+
* Improvement: Better resilience to network related issues
|
175 |
+
* Improvement: Faster cache purge
|
176 |
+
* Improvement: Overall stability improvements
|
177 |
+
* Deprecation: Removed the Invalidate All Cache option. The invalidate action is much better suited for single page invalidations.
|
178 |
+
|
179 |
= 1.4.1 =
|
180 |
* Improvement: Performance improvements in content updates
|
181 |
* Improvement: Better compatibility with Download Monitor
|
view/dashboard.php
CHANGED
@@ -21,7 +21,6 @@
|
|
21 |
<div class="optimizations-subcount"><span data-optimized-pages-desktop>0</span> desktop pages</div>
|
22 |
</div>
|
23 |
<div class="row mt-5 justify-content-center">
|
24 |
-
<button id="optimizations-invalidate-cache" class="btn btn-light btn-outline-secondary btn-widget-optimizations">Invalidate Cache</button>
|
25 |
<button id="optimizations-purge-cache" class="btn btn-light btn-outline-secondary btn-widget-optimizations">Purge Cache</button>
|
26 |
</div>
|
27 |
</div>
|
@@ -97,7 +96,9 @@
|
|
97 |
<h5 class="card-title">Settings</h5>
|
98 |
<ul class="list-group list-group-flush">
|
99 |
<li class="list-group-item px-0 d-flex justify-content-between align-items-center">
|
100 |
-
<span>Cache Warmup</
|
|
|
|
|
101 |
<span id="loading-warmup-status">
|
102 |
Loading cache warmup status <i class="fa fa-refresh fa-spin" style="color: var(--blue);"></i>
|
103 |
</span>
|
@@ -385,7 +386,7 @@
|
|
385 |
},
|
386 |
complete: function() {
|
387 |
if (!getOptimizationsTimeout) {
|
388 |
-
getOptimizationsTimeout = setTimeout(function() {getOptimizationsTimeout = null; getOptimizations();},
|
389 |
}
|
390 |
}
|
391 |
})
|
21 |
<div class="optimizations-subcount"><span data-optimized-pages-desktop>0</span> desktop pages</div>
|
22 |
</div>
|
23 |
<div class="row mt-5 justify-content-center">
|
|
|
24 |
<button id="optimizations-purge-cache" class="btn btn-light btn-outline-secondary btn-widget-optimizations">Purge Cache</button>
|
25 |
</div>
|
26 |
</div>
|
96 |
<h5 class="card-title">Settings</h5>
|
97 |
<ul class="list-group list-group-flush">
|
98 |
<li class="list-group-item px-0 d-flex justify-content-between align-items-center">
|
99 |
+
<span>Cache Warmup</br>
|
100 |
+
<small>Learn more about this feature <a href="https://help.nitropack.io/en/articles/3534265-cache-warmup" target="_blank" rel="noreferrer noopener">here</a></small>
|
101 |
+
</span>
|
102 |
<span id="loading-warmup-status">
|
103 |
Loading cache warmup status <i class="fa fa-refresh fa-spin" style="color: var(--blue);"></i>
|
104 |
</span>
|
386 |
},
|
387 |
complete: function() {
|
388 |
if (!getOptimizationsTimeout) {
|
389 |
+
getOptimizationsTimeout = setTimeout(function() {getOptimizationsTimeout = null; getOptimizations();}, 60000);
|
390 |
}
|
391 |
}
|
392 |
})
|
view/diag.php
CHANGED
@@ -19,7 +19,7 @@
|
|
19 |
</li>
|
20 |
<li class="list-group-item px-0 d-flex justify-content-between align-items-center">
|
21 |
<span id="loading-plugins-status">
|
22 |
-
Include
|
23 |
</span>
|
24 |
<span id="active-plugins-toggle">
|
25 |
<label id="active-plugins-slider" class="switch">
|
19 |
</li>
|
20 |
<li class="list-group-item px-0 d-flex justify-content-between align-items-center">
|
21 |
<span id="loading-plugins-status">
|
22 |
+
Include active plugins list
|
23 |
</span>
|
24 |
<span id="active-plugins-toggle">
|
25 |
<label id="active-plugins-slider" class="switch">
|