This repository has been archived by the owner on Aug 30, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathhttp2-server-push.php
149 lines (122 loc) · 4.39 KB
/
http2-server-push.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<?php
/*
Plugin Name: HTTP/2 Server Push
Plugin URI: https://github.com/daveross/http2-server-push
Description: Enables HTTP/2 server push for local JavaScript and CSS resources enqueued in the page.
Version: 1.4
Author: David Michael Ross
Author URI: http://davidmichaelross.com
*/
// Global variables to keep track of resource URLs
$http2_script_srcs = array();
$http2_stylesheet_srcs = array();
/**
* Cloudflare gives an HTTP 520 error when more than 8k of headers are present. Limiting $this
* plugin's output to 4k should keep those errors away.
*/
define('HTTP2_MAX_HEADER_SIZE', 1024 * 4);
$http2_header_size_accumulator = 0;
/**
* Determine if the plugin should render its own resource hints, or defer to WordPress.
* WordPress natively supports resource hints since 4.6. Can be overridden with
* 'http2_render_resource_hints' filter.
* @return boolean true if the plugin should render resource hints.
*/
function http2_should_render_prefetch_headers() {
return apply_filters('http2_render_resource_hints', !function_exists( 'wp_resource_hints' ) );
}
/**
* Start an output buffer so this plugin can call header() later without errors.
* Need to use a function here instead of calling ob_start in the template_redirect
* action as WordPress will pass an empty string as the first (only?) parameter
* and PHP will try to use that as a function name.
*/
function http2_ob_start() {
ob_start();
}
add_action('init', 'http2_ob_start');
/**
* @param string $src URL
*
* @return void
*/
function http2_link_preload_header($src) {
global $http2_header_size_accumulator;
if (strpos($src, site_url()) !== false) {
$preload_src = apply_filters('http2_link_preload_src', $src);
if (!empty($preload_src)) {
$header = sprintf(
'Link: <%s>; rel=preload; as=%s',
esc_url( http2_link_url_to_relative_path( $preload_src ) ),
sanitize_html_class( http2_link_resource_hint_as( current_filter() ) )
);
// Make sure we haven't hit the header limit
if(($http2_header_size_accumulator + strlen($header)) < HTTP2_MAX_HEADER_SIZE) {
$http2_header_size_accumulator += strlen($header);
header( $header, false );
}
$GLOBALS['http2_' . http2_link_resource_hint_as( current_filter() ) . '_srcs'][] = http2_link_url_to_relative_path( $preload_src );
}
}
return $src;
}
if(!is_admin()) {
add_filter('script_loader_src', 'http2_link_preload_header', 99, 1);
add_filter('style_loader_src', 'http2_link_preload_header', 99, 1);
}
/**
* Render "resource hints" in the <head> section of the page. These encourage preload/prefetch behavior
* when HTTP/2 support is lacking.
*/
function http2_resource_hints() {
$resource_types = array('script', 'style');
array_walk( $resource_types, function( $resource_type ) {
$resources = http2_get_resources($GLOBALS, $resource_type);
array_walk( $resources, function( $src ) use ( $resource_type ) {
printf( '<link rel="preload" href="%s" as="%s">', esc_url($src), esc_html( $resource_type ) );
});
});
}
if(!is_admin() && http2_should_render_prefetch_headers()) {
add_action( 'wp_head', 'http2_resource_hints', 99, 1);
}
/**
* Get resources of a certain type that have been enqueued through the WordPress API.
* Needed because some plugins mangle these global values
* @param array $globals the $GLOBALS array
* @param string $resource_type resource type (script, style)
* @return array
*/
function http2_get_resources($globals = null, $resource_type) {
$globals = (null === $globals) ? $GLOBALS : $globals;
$resource_type_key = "http2_{$resource_type}_srcs";
if(!(is_array($globals) && isset($globals[$resource_type_key]))) {
return array();
}
else if(!is_array($globals[$resource_type_key])) {
return array($globals[$resource_type_key]);
}
else {
return $globals[$resource_type_key];
}
}
/**
* Convert an URL with authority to a relative path
*
* @param string $src URL
*
* @return string mixed relative path
*/
function http2_link_url_to_relative_path($src) {
return '//' === substr($src, 0, 2) ? preg_replace('/^\/\/([^\/]*)\//', '/', $src) : preg_replace('/^http(s)?:\/\/[^\/]*/', '', $src);
}
/**
* Maps a WordPress hook to an "as" parameter in a resource hint
*
* @param string $current_hook pass current_filter()
*
* @return string 'style' or 'script'
*/
function http2_link_resource_hint_as( $current_hook ) {
return 'style_loader_src' === $current_hook ? 'style' : 'script';
}