Skip to content

Commit

Permalink
php-proxy 5.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Athlon1600 committed Dec 25, 2017
1 parent b104ec0 commit 6232d52
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 65 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 1 addition & 5 deletions src/Event/ProxyEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()){
Expand Down
49 changes: 25 additions & 24 deletions src/Plugin/AbstractPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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'
);
}
}

?>
28 changes: 13 additions & 15 deletions src/Plugin/ProxifyPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}

Expand All @@ -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];
}

Expand Down Expand Up @@ -101,7 +101,7 @@ private function meta_refresh($matches){

// <title>, <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').'</title>', $str);
Expand Down Expand Up @@ -137,29 +137,27 @@ 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');

$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);
}
}

Expand Down
63 changes: 43 additions & 20 deletions src/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -28,15 +23,14 @@ class Proxy {
private $status_found = false;

public function __construct(){
$this->dispatcher = new EventDispatcher();
// do nothing for now
}

public function setOutputBuffering($output_buffering){
$this->output_buffering = $output_buffering;
}

private function header_callback($ch, $headers){

$parts = explode(":", $headers, 2);

// extract status code
Expand All @@ -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);
Expand All @@ -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
)));
Expand All @@ -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){
Expand Down Expand Up @@ -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 {

Expand All @@ -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);
}

Expand All @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions src/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down

0 comments on commit 6232d52

Please sign in to comment.