From 6232d524c4e2d109ee5a32cc6ecd2def61ca6e35 Mon Sep 17 00:00:00 2001 From: Athlon1600 Date: Mon, 25 Dec 2017 13:26:51 -0600 Subject: [PATCH] php-proxy 5.1 --- composer.json | 2 +- src/Event/ProxyEvent.php | 6 +--- src/Plugin/AbstractPlugin.php | 49 ++++++++++++++------------- src/Plugin/ProxifyPlugin.php | 28 ++++++++-------- src/Proxy.php | 63 ++++++++++++++++++++++++----------- src/helpers.php | 4 +++ 6 files changed, 87 insertions(+), 65 deletions(-) diff --git a/composer.json b/composer.json index 2bf58ab..5695afb 100644 --- a/composer.json +++ b/composer.json @@ -2,10 +2,10 @@ "name": "athlon1600/php-proxy", "type": "library", "keywords": ["php proxy", "proxy script", "php web proxy", "web proxy", "php proxy script"], + "license": "MIT", "homepage": "https://www.php-proxy.com/", "require": { "ext-curl": "*", - "symfony/event-dispatcher": "~3.2" }, "suggest": { "predis/predis": "For caching purposes" diff --git a/src/Event/ProxyEvent.php b/src/Event/ProxyEvent.php index 1cfddec..8c707bb 100644 --- a/src/Event/ProxyEvent.php +++ b/src/Event/ProxyEvent.php @@ -2,11 +2,7 @@ namespace Proxy\Event; -use Symfony\Component\EventDispatcher\Event; - -// http://symfony.com/doc/current/components/event_dispatcher/generic_event.html -class ProxyEvent extends Event implements \ArrayAccess { - +class ProxyEvent implements \ArrayAccess { private $data; public function __construct($data = array()){ diff --git a/src/Plugin/AbstractPlugin.php b/src/Plugin/AbstractPlugin.php index 405c33b..605cee3 100644 --- a/src/Plugin/AbstractPlugin.php +++ b/src/Plugin/AbstractPlugin.php @@ -2,12 +2,10 @@ namespace Proxy\Plugin; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Proxy\Event\ProxyEvent; -abstract class AbstractPlugin implements EventSubscriberInterface { - +abstract class AbstractPlugin { + // apply these methods only to those events whose request URL passes this filter protected $url_pattern; @@ -27,26 +25,40 @@ public function onCompleted(ProxyEvent $event){ // fired after the full response=headers+body has been read - will only be called on "non-streaming" responses } - // dispatch based on filter - final public function route(ProxyEvent $event, $event_name, EventDispatcherInterface $dispatcher){ + final public function subscribe($dispatcher){ + + $dispatcher->addListener('request.before_send', function($event){ + $this->route('request.before_send', $event); + }); + + $dispatcher->addListener('request.sent', function($event){ + $this->route('request.sent', $event); + }); + + $dispatcher->addListener('curl.callback.write', function($event){ + $this->route('curl.callback.write', $event); + }); + + $dispatcher->addListener('request.complete', function($event){ + $this->route('request.complete', $event); + }); + } + // dispatch based on filter + final private function route($event_name, ProxyEvent $event){ $url = $event['request']->getUri(); // url filter provided and current request url does not match it if($this->url_pattern){ - if(strpos($this->url_pattern, '/') === 0){ - if(!preg_match($this->url_pattern, $url)) + if(starts_with($this->url_pattern, '/') && preg_match($this->url_pattern, $url) !== 1){ return; - } - else - { - if(stripos($url, $this->url_pattern) === false) + } else if(stripos($url, $this->url_pattern) === false){ return; } } switch($event_name){ - + case 'request.before_send': $this->onBeforeRequest($event); break; @@ -64,17 +76,6 @@ final public function route(ProxyEvent $event, $event_name, EventDispatcherInter break; } } - - // This method returns an array indexed by event names and whose values are either the method name to call - // or an array composed of the method name to call and a priority. - final public static function getSubscribedEvents(){ - return array( - 'request.before_send' => 'route', - 'request.sent' => 'route', - 'curl.callback.write' => 'route', - 'request.complete' => 'route' - ); - } } ?> diff --git a/src/Plugin/ProxifyPlugin.php b/src/Plugin/ProxifyPlugin.php index cf82354..3165428 100644 --- a/src/Plugin/ProxifyPlugin.php +++ b/src/Plugin/ProxifyPlugin.php @@ -12,10 +12,9 @@ class ProxifyPlugin extends AbstractPlugin { private $base_url = ''; private function css_url($matches){ - - $url = trim($matches[1]); - if(stripos($url, 'data:') === 0){ + $url = trim($matches[1]); + if(starts_with($url, 'data:')){ return $matches[0]; } @@ -33,7 +32,8 @@ private function html_attr($matches){ // could be empty? $url = trim($matches[2]); - if(stripos($url, 'data:') === 0 || stripos($url, 'magnet:') === 0 || stripos($url, 'about:') === 0 || stripos($url, 'javascript:') === 0 || stripos($url, 'mailto:') === 0 || stripos($url, 'tel:') === 0 || stripos($url, 'ios-app:') === 0 || stripos($url, 'android-app:') === 0){ + $schemes = array('data:', 'magnet:', 'about:', 'javascript:', 'mailto:', 'tel:', 'ios-app:', 'android-app:'); + if(starts_with($url, $schemes)){ return $matches[0]; } @@ -101,7 +101,7 @@ private function meta_refresh($matches){ // , <base>, <link>, <style>, <meta>, <script>, <noscript> private function proxify_head($str){ - + // let's replace page titles with something custom if(Config::get('replace_title')){ $str = preg_replace('/<title[^>]*>(.*?)<\/title>/is', '<title>'.Config::get('replace_title').'', $str); @@ -137,9 +137,10 @@ private function proxify_css($str){ } public function onCompleted(ProxyEvent $event){ - + // to be used when proxifying all the relative links $this->base_url = $event['request']->getUri(); + $url_host = parse_url($this->base_url, PHP_URL_HOST); $response = $event['response']; $content_type = $response->headers->get('content-type'); @@ -147,19 +148,16 @@ public function onCompleted(ProxyEvent $event){ $str = $response->getContent(); // DO NOT do any proxification on .js files and text/plain content type - if($content_type == 'text/javascript' || $content_type == 'application/javascript' || $content_type == 'application/x-javascript' || $content_type == 'text/plain'){ + $no_proxify = array('text/javascript', 'application/javascript', 'application/x-javascript', 'text/plain'); + if(in_array($content_type, $no_proxify)){ return; } // remove JS from urls - $js_remove = Config::get('js_remove'); - if(is_array($js_remove)){ - $domain = parse_url($this->base_url, PHP_URL_HOST); - - foreach($js_remove as $pattern){ - if(strpos($domain, $pattern) !== false){ - $str = Html::remove_scripts($str); - } + $js_remove = (array)Config::get('js_remove'); + foreach($js_remove as $pattern){ + if(strpos($url_host, $pattern) !== false){ + $str = Html::remove_scripts($str); } } diff --git a/src/Proxy.php b/src/Proxy.php index cf2ae6d..5c1797d 100644 --- a/src/Proxy.php +++ b/src/Proxy.php @@ -2,20 +2,15 @@ namespace Proxy; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\GenericEvent; - -use Proxy\Config; use Proxy\Event\ProxyEvent; use Proxy\Http\Request; use Proxy\Http\Response; +use Proxy\Config; class Proxy { - - // proxy version! - const VERSION = '5.0.1'; + + // Proxy script version + const VERSION = '5.1.0'; private $dispatcher; @@ -28,7 +23,7 @@ class Proxy { private $status_found = false; public function __construct(){ - $this->dispatcher = new EventDispatcher(); + // do nothing for now } public function setOutputBuffering($output_buffering){ @@ -36,7 +31,6 @@ public function setOutputBuffering($output_buffering){ } private function header_callback($ch, $headers){ - $parts = explode(":", $headers, 2); // extract status code @@ -60,7 +54,7 @@ private function header_callback($ch, $headers){ $event = new ProxyEvent(array('request' => $this->request, 'response' => $this->response, 'proxy' => &$this)); // this is the end of headers - last line is always empty - notify the dispatcher about this - $this->dispatcher->dispatch('request.sent', $event); + $this->dispatch('request.sent', $event); } return strlen($headers); @@ -70,7 +64,7 @@ private function write_callback($ch, $str){ $len = strlen($str); - $this->dispatcher->dispatch('curl.callback.write', new ProxyEvent(array( + $this->dispatch('curl.callback.write', new ProxyEvent(array( 'request' => $this->request, 'data' => $str ))); @@ -83,8 +77,34 @@ private function write_callback($ch, $str){ return $len; } - public function getEventDispatcher(){ - return $this->dispatcher; + // TODO: move this all into its own Dispatcher class? + // https://github.com/guzzle/guzzle/blob/5.3/src/Event/Emitter.php + // https://github.com/laravel/framework/blob/5.0/src/Illuminate/Events/Dispatcher.php#L72 + private $listeners = array(); + + public function addListener($event, $callback, $priority = 0){ + $this->listeners[$event][$priority][] = $callback; + } + + public function addSubscriber($subscriber){ + if(method_exists($subscriber, 'subscribe')){ + $subscriber->subscribe($this); + } + } + + private function dispatch($event_name, $event){ + + if(isset($this->listeners[$event_name])){ + $temp = (array)$this->listeners[$event_name]; + + foreach($temp as $priority => $listeners){ + foreach( (array)$listeners as $listener){ + if(is_callable($listener) ){ + $listener($event); + } + } + } + } } public function forward(Request $request, $url){ @@ -122,11 +142,13 @@ public function forward(Request $request, $url){ $options[CURLOPT_WRITEFUNCTION] = array($this, 'write_callback'); // Notify any listeners that the request is ready to be sent, and this is your last chance to make any modifications. - $this->dispatcher->dispatch('request.before_send', new ProxyEvent(array('request' => $this->request, 'response' => $this->response))); + $this->dispatch('request.before_send', new ProxyEvent(array( + 'request' => $this->request, + 'response' => $this->response + ))); // We may not even need to send this request if response is already available somewhere (CachePlugin) if($this->request->params->has('request.complete')){ - // do nothing? } else { @@ -146,9 +168,7 @@ public function forward(Request $request, $url){ // there must have been an error if at this point if(!$result){ - $error = sprintf('(%d) %s', curl_errno($ch), curl_error($ch)); - throw new \Exception($error); } @@ -159,7 +179,10 @@ public function forward(Request $request, $url){ $this->output_buffer = null; } - $this->dispatcher->dispatch('request.complete', new ProxyEvent(array('request' => $this->request, 'response' => $this->response))); + $this->dispatch('request.complete', new ProxyEvent(array( + 'request' => $this->request, + 'response' => $this->response + ))); return $this->response; } diff --git a/src/helpers.php b/src/helpers.php index f71e392..1eaae4a 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -17,6 +17,10 @@ function starts_with($haystack, $needles){ return false; } +function str_before($subject, $search){ + return $search === '' ? $subject : explode($search, $subject)[0]; +} + function is_html($content_type){ return clean_content_type($content_type) == 'text/html';