SHINDIG-1056 by lipeng, BasicRemoteContentTest doesn't depend on static private key...
[shindig.git] / php / src / gadgets / rewrite / ContentRewriter.php
blob4a0f6402bdc14b64bd5b807ae7dc96a5d8e2438b
1 <?php
3 /**
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
19 * under the License.
22 /**
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 {
29 private $rewrite;
30 private $baseUrl;
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;
38 } else {
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);
45 /**
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');
57 /**
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
62 * @param string $url
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());
71 return $url;
74 /**
75 * Checks the URL against the include-url and exclude-url params
77 * @param string $url
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) {
84 $included = true;
85 break;
89 if (isset($this->rewrite['exclude-url'])) {
90 foreach ($this->rewrite['exclude-url'] as $excludeUrl) {
91 if ($excludeUrl == '*' || strpos($url, $excludeUrl) !== false) {
92 $excluded = true;
93 break;
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
126 * @return string
128 public function rewriteCSS($content) {
129 $newVal = '';
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) . "')";
143 } else {
144 $newVal .= "('$tag')";
146 $content = substr($content, $endTag + 1);
149 // append what's left
150 $newVal .= $content;
151 return $newVal;
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));