4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
23 * Implements the Content-Rewrite feature which rewrites all image, css and script
24 * links to their proxied versions, which can be quite a latency improvement, and
25 * save the gadget dev's server from melting down
28 class ContentRewriter
extends DomRewriter
{
31 private $defaultRewrite = array('include-url' => array('*'), 'exclude-url' => array(), 'refresh' => '86400');
33 public function __construct(GadgetContext
$context, Gadget
&$gadget) {
34 parent
::__construct($context, $gadget);
35 // if no rewrite params are set in the gadget but rewrite_by_default is on, use our default rules (rewrite all)
36 if (! isset($gadget->gadgetSpec
->rewrite
) && Config
::get('rewrite_by_default')) {
37 $this->rewrite
= $this->defaultRewrite
;
39 $this->rewrite
= $gadget->gadgetSpec
->rewrite
;
41 // the base url of the gadget is used for relative paths
42 $this->baseUrl
= substr($this->context
->getUrl(), 0, strrpos($this->context
->getUrl(), '/') +
1);
46 * Register our dom node observers
48 * @param GadgetRewriter $gadgetRewriter
50 public function register(GadgetRewriter
&$gadgetRewriter) {
51 $gadgetRewriter->addObserver('img', $this, 'rewriteImage');
52 $gadgetRewriter->addObserver('style', $this, 'rewriteStyle');
53 $gadgetRewriter->addObserver('script', $this, 'rewriteScript');
54 $gadgetRewriter->addObserver('link', $this, 'rewriteStyleLink');
58 * Produces the proxied version of a URL if it falls within the content-rewrite params and
59 * will append a refresh param to the proxied url based on the expires param, and the gadget
60 * url so that the proxy server knows to rewrite it's content or not
64 private function getProxyUrl($url) {
65 if (strpos(strtolower($url), 'http://') === false && strpos(strtolower($url), 'https://') === false) {
66 $url = $this->baseUrl
. $url;
68 $url = Config
::get('web_prefix') . '/gadgets/proxy?url=' . urlencode($url);
69 $url .= '&refresh=' . (isset($this->rewrite
['expires']) && is_numeric($this->rewrite
['expires']) ?
$this->rewrite
['expires'] : '3600');
70 $url .= '&gadget=' . urlencode($this->context
->getUrl());
75 * Checks the URL against the include-url and exclude-url params
79 private function includedUrl($url) {
80 $included = $excluded = false;
81 if (isset($this->rewrite
['include-url'])) {
82 foreach ($this->rewrite
['include-url'] as $includeUrl) {
83 if ($includeUrl == '*' ||
strpos($url, $includeUrl) !== false) {
89 if (isset($this->rewrite
['exclude-url'])) {
90 foreach ($this->rewrite
['exclude-url'] as $excludeUrl) {
91 if ($excludeUrl == '*' ||
strpos($url, $excludeUrl) !== false) {
97 return ($included && ! $excluded);
101 * Rewrites the src attribute of an img tag
103 * @param DOMElement $node
105 public function rewriteImage(DOMElement
&$node) {
106 if (($src = $node->getAttribute('src')) != null && $this->includedUrl($src)) {
107 $node->setAttribute('src', $this->getProxyUrl($src));
112 * Uses rewriteCSS to find url(<url tag>) constructs and rewrite them to their
113 * proxied counterparts
115 * @param DOMElement $node
117 public function rewriteStyle(DOMElement
&$node) {
118 $node->nodeValue
= $this->rewriteCSS($node->nodeValue
);
122 * Does the actual CSS rewriting, this is a seperate function so it can be called
123 * from the proxy handler too
125 * @param string $content
128 public function rewriteCSS($content) {
130 // loop through the url elements in the content
131 while (($pos = strpos($content, 'url')) !== false) {
132 // output everything before this url tag
133 $newVal .= substr($content, 0, $pos +
3);
134 $content = substr($content, $pos +
3);
135 // low tech protection against miss-reading tags, if the open ( is to far away, this is probabbly a miss-read
136 if (($beginTag = strpos($content, '(')) < 4) {
137 $content = substr($content, $beginTag +
1);
138 $endTag = strpos($content, ')');
139 $tag = str_replace(array("'", "\""), '', trim(substr($content, 0, $endTag)));
140 // at this point $tag should be the actual url aka: http://example.org/bar/foo.gif
141 if ($this->includedUrl($tag)) {
142 $newVal .= "('" . $this->getProxyUrl($tag) . "')";
144 $newVal .= "('$tag')";
146 $content = substr($content, $endTag +
1);
149 // append what's left
155 * Rewrites <script src="http://example.org/foo.js" /> tags into their proxied versions
157 * @param DOMElement $node
159 public function rewriteScript(DOMElement
&$node) {
160 if (($src = $node->getAttribute('src')) != null && $this->includedUrl($src)) {
161 // make sure not to rewrite our forcedJsLibs src tag, else things break
162 if (strpos($src, '/gadgets/js') === false) {
163 $node->setAttribute('src', $this->getProxyUrl($src));
169 * Rewrites <link href="http://example.org/foo.css" /> tags into their proxied versions
171 * @param DOMElement $node
173 public function rewriteStyleLink(DOMElement
&$node) {
174 if (($src = $node->getAttribute('href')) != null && $this->includedUrl($src)) {
175 $node->setAttribute('href', $this->getProxyUrl($src));