├── .vscode └── settings.json ├── LICENSE ├── README.md ├── app ├── config │ └── config.sample.ini ├── database │ └── cors-proxy.sql └── templates │ └── default │ ├── error.phtml │ ├── frontend.phtml │ └── sitemap.pxml ├── bin ├── cs ├── cs.bat ├── test └── test.bat ├── composer.json ├── src ├── Application.php ├── Config.php ├── Exception │ ├── FileNotFoundException.php │ └── MalformedConfigFileException.php ├── Helpers.php └── RequestHandler.php └── www ├── .htaccess ├── assets └── default │ ├── css │ └── main.css │ ├── img │ └── favicon.ico │ └── js │ └── main.js ├── index.php └── robots.txt /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.insertSpaces": true, 3 | "editor.tabSize": 4 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 HTMLDriven 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CORS proxy 2 | 3 | [![Build Status](https://travis-ci.org/htmldriven/cors-proxy.svg?branch=master)](https://travis-ci.org/htmldriven/cors-proxy) 4 | [![SensioLabs Insight](https://img.shields.io/sensiolabs/i/edb8e93a-2918-41e7-8667-7bf2dfe3b492.svg)](https://insight.sensiolabs.com/projects/edb8e93a-2918-41e7-8667-7bf2dfe3b492) 5 | [![Installs](https://img.shields.io/packagist/dt/htmldriven/cors-proxy.svg)](https://packagist.org/packages/htmldriven/cors-proxy) 6 | [![License](https://img.shields.io/packagist/l/htmldriven/cors-proxy.svg)](https://packagist.org/packages/htmldriven/cors-proxy) 7 | 8 | CORS proxy is a free service for developers who need to bypass same-origin policy related to performing standard AJAX requests to 3rd party services. 9 | 10 | ## Documentation 11 | 12 | Learn more in the [documentation](docs/en/index.md). 13 | 14 | ## Project home 15 | 16 | You can find more information about the CORS proxy on project website [cors-proxy.htmldriven.com](https://cors-proxy.htmldriven.com). 17 | -------------------------------------------------------------------------------- /app/config/config.sample.ini: -------------------------------------------------------------------------------- 1 | urlParameterName = 'url' 2 | userAgent = 'htmldriven/cors-proxy 1.0' 3 | templateFile = '../app/templates/default/frontend.phtml' 4 | sitemapPath = '/sitemap.xml' 5 | sitemapTemplateFile = '../app/templates/default/sitemap.pxml' 6 | errorTemplateFile = '../app/templates/default/error.phtml' 7 | -------------------------------------------------------------------------------- /app/database/cors-proxy.sql: -------------------------------------------------------------------------------- 1 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 2 | SET time_zone = "+00:00"; 3 | 4 | CREATE TABLE IF NOT EXISTS `request_log` ( 5 | `id` int(10) unsigned NOT NULL, 6 | `method` enum('GET','POST','PUT','DELETE','OPTIONS','HEAD','TRACE','CONNECT','PATCH') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'GET' COMMENT 'HTTP request method.', 7 | `scheme` varchar(16) COLLATE utf8_unicode_ci NOT NULL COMMENT 'HTTP request scheme.', 8 | `host` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'HTTP request host domain/ip.', 9 | `path` text COLLATE utf8_unicode_ci NOT NULL COMMENT 'HTTP request URL path relative to host. Path should always start with ''/'' character.', 10 | `query` text COLLATE utf8_unicode_ci COMMENT 'Query string part of the URL.', 11 | `user_agent` text COLLATE utf8_unicode_ci COMMENT 'Client string identifier.', 12 | `request_headers` text COLLATE utf8_unicode_ci COMMENT 'All HTTP request headers in JSON format.', 13 | `request_body` mediumtext COLLATE utf8_unicode_ci COMMENT 'Request payload data.', 14 | `response_headers` text COLLATE utf8_unicode_ci COMMENT 'All HTTP response headers in JSON format.', 15 | `response_body` mediumtext COLLATE utf8_unicode_ci COMMENT 'Response payload data.', 16 | `date_created` datetime NOT NULL COMMENT 'Date in UTC.' 17 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 18 | 19 | 20 | ALTER TABLE `request_log` 21 | ADD PRIMARY KEY (`id`), 22 | ADD KEY `path` (`path`(255)) USING BTREE; 23 | 24 | 25 | ALTER TABLE `request_log` 26 | MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT; 27 | -------------------------------------------------------------------------------- /app/templates/default/error.phtml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | CORS proxy Error 17 | 18 | 19 |

Error

20 |

21 |

Version:

22 | 23 | -------------------------------------------------------------------------------- /app/templates/default/frontend.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CORS proxy | HTMLDriven.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 50 | 51 |
52 | 55 | 56 |
57 |
58 |
59 | 60 |
61 | /?getUrlParameterName(); ?>= 62 | 63 |
64 | > 65 |
66 |
67 |
68 |
69 |
70 |

About

71 |

CORS proxy is a free service for developers who need to bypass same-origin policy related to performing standard AJAX requests to 3rd party services.

72 |

You can simply use this website as quickest way to finally start doing some cross-domain requests and even you can run this service on your own webserver. If you prefer running the service on your own, follow the instructions in Setup section.

73 |

You can customize the service parameters to fit your own needs (including this front-end page template).

74 |
75 |
76 |
77 |

How to use

78 |

The CORS proxy service expects you provide the URL of 3rd party service/page in url HTTP GET parameter by default. A final cross-domain request URL via the CORS proxy service can be handled, looks something like this:

79 |
/?getUrlParameterName(); ?>=https://www.htmldriven.com/sample.json
80 | 81 |

Try the CORS proxy now

82 |

You can test the CORS proxy service response using the form above.

83 |
84 |
85 |
86 |

Setup

87 |

Read the following section if you want to run the CORS proxy on your own webserver.

88 | 89 |

Requirements

90 |

Please, check the composer.json file or packagist.org to see the current list of requirements in terms of packages which are required by composer. Except for PHP version >=5.6 with CURL extension being installed, there are no more odd requirements

91 | 92 |

Installation

93 |

The best way to install the CORS proxy is using the composer. You can do that using the following command:

94 |
composer create-project htmldriven/cors-proxy my-cors-proxy
95 |

* Note that my-cors-proxy in the command above is the name of target directory for newly created CORS proxy project, so this name is totally up to you.

96 |

Then, create a destination database using the cors-proxy.sql initialization script.

97 | 98 |

Configuration

99 |

If you need, you can customize the CORS proxy by creating custom config.ini file. This file must be located at app/config directory. There's already config.sample.ini file, which you can just copy and edit some parts of it to match your needs.

100 |

There are several config items which you can change. The following list shows all supported options:

101 | 110 |
111 |
112 |
113 |

License

114 |

CORS proxy is totally free as it's available and being distributed under The MIT License.

115 |
116 |
117 | 118 | 123 | 124 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 160 | 161 | 162 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /app/templates/default/sitemap.pxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | format('Y-m-d'); ?> 7 | Weekly 8 | 0.5 9 | 10 | -------------------------------------------------------------------------------- /bin/cs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | _DIR=$(dirname $0); 4 | 5 | "$_DIR"/../vendor/bin/phpcs -p --standard=PSR2 "$_DIR"/../src "$_DIR"/../tests 6 | -------------------------------------------------------------------------------- /bin/cs.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call %~dp0\..\vendor\bin\phpcs.bat -p --standard=PSR2 %~dp0\..\src %~dp0\..\tests 4 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | _DIR=$(dirname $0); 4 | 5 | "$_DIR"/../vendor/bin/tester -s -c "$_DIR"/../tests/environment/php-unix.ini "$_DIR"/../tests -p php 6 | -------------------------------------------------------------------------------- /bin/test.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call %~dp0\..\vendor\bin\tester.bat -s -c %~dp0\..\tests\environment\php-win.ini %~dp0\..\tests -p php 4 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "htmldriven/cors-proxy", 3 | "description": "A simple proxy service for performing cross-domain AJAX requests.", 4 | "keywords": [ 5 | "cors", 6 | "proxy", 7 | "cross-domain", 8 | "htmldriven" 9 | ], 10 | "license": "MIT", 11 | "homepage": "https://www.htmldriven.com", 12 | "authors": [ 13 | { 14 | "name": "RebendaJiri", 15 | "homepage": "https://www.htmldriven.com", 16 | "email": "jiri.rebenda@htmldriven.com" 17 | } 18 | ], 19 | "type": "project", 20 | "require": { 21 | "php": ">=5.6.0", 22 | "guzzlehttp/guzzle": "~3.8", 23 | "dibi/dibi": "^3.1" 24 | }, 25 | "require-dev": { 26 | "nette/tester": "^1.6", 27 | "squizlabs/php_codesniffer": "^3.1" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "HtmlDriven\\CorsProxy\\": "src/" 32 | } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "HtmlDriven\\CorsProxyTests\\": "tests/CorsProxyTests" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Application.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class Application 21 | { 22 | /** @var string */ 23 | const VERSION = '1.4.0'; 24 | 25 | /** @var string */ 26 | const DEFAULT_DOMAIN = 'cors-proxy.htmldriven.com'; 27 | 28 | /** @var array */ 29 | public $configDefaults = []; 30 | 31 | /** @var Config */ 32 | private $config; 33 | 34 | /** @var string[] */ 35 | private $allowedActions = [ 36 | 'frontend', 37 | 'sitemap', 38 | ]; 39 | 40 | /** 41 | * @param Config|NULL If NULL, default config is created 42 | */ 43 | public function __construct(Config $config = null) 44 | { 45 | $this->configDefaults = $this->createConfigDefaults(); 46 | $this->config = ($config === null ? $this->createConfig() : $config); 47 | } 48 | 49 | /** 50 | * Handles the request. 51 | * 52 | * @return void 53 | * @throws MalformedConfigFileException If configuration file parsing fails. 54 | * @throws FileNotFoundException If template file does not exist. 55 | * @throws RuntimeException If HTTP method cannot be detected. 56 | */ 57 | public function run() 58 | { 59 | $config = $this->config; 60 | 61 | // handle CORS proxy request if URL is set 62 | if (isset($_GET[$config->getUrlParameterName()])) { 63 | if ($config->isEnabled()) { 64 | $url = (string) $_GET[$config->getUrlParameterName()]; 65 | 66 | $client = $this->createClient($config); 67 | 68 | $dibiConnection = $this->createDibiConnection($config); 69 | 70 | $requestHandler = new RequestHandler( 71 | $config, 72 | $client, 73 | $dibiConnection 74 | ); 75 | 76 | $dibiConnection->disconnect(); 77 | 78 | $method = $this->detectHttpMethod(); 79 | 80 | $requestHandler->handleRequest($method, $url); 81 | } else { 82 | $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'); 83 | header($protocol . ' ' . 404 . ' Not Found'); 84 | echo 'Service has been disabled'; 85 | } 86 | } else { 87 | // show frontend 88 | $this->showFrontEnd($config); 89 | } 90 | } 91 | 92 | /** 93 | * @return Config 94 | * @throws MalformedConfigFileException If configuration file parsing fails. 95 | * @throws FileNotFoundException If template file does not exist. 96 | */ 97 | private function createConfig() 98 | { 99 | // config file is optional 100 | $iniFile = __DIR__ . '/../app/config/config.ini'; 101 | 102 | if (is_file($iniFile)) { 103 | if (false === ($iniConfig = parse_ini_file($iniFile, true))) { 104 | throw new MalformedConfigFileException(sprintf("Unable to parse configuration file '%s'.", $iniFile)); 105 | } 106 | $config = array_replace_recursive($this->configDefaults, $iniConfig); 107 | } else { 108 | $config = $this->configDefaults; 109 | } 110 | 111 | date_default_timezone_set($config['timezone']); 112 | 113 | // absolutize filepaths 114 | $config['templateFile'] = Helpers::absolutizeFilepath(__DIR__, $config['templateFile']); 115 | $config['sitemapTemplateFile'] = Helpers::absolutizeFilepath(__DIR__, $config['sitemapTemplateFile']); 116 | $config['errorTemplateFile'] = Helpers::absolutizeFilepath(__DIR__, $config['errorTemplateFile']); 117 | 118 | return new Config( 119 | $config['enabled'], 120 | $config['urlParameterName'], 121 | $config['userAgent'], 122 | $config['templateFile'], 123 | $config['sitemapPath'], 124 | $config['sitemapTemplateFile'], 125 | $config['errorTemplateFile'], 126 | $config['database'], 127 | $config['requestLogEnabled'] 128 | ); 129 | } 130 | 131 | /** 132 | * @param Config 133 | * @return ClientInterface 134 | */ 135 | private function createClient(Config $config) 136 | { 137 | $client = new Client(); 138 | $client->setUserAgent($config->getUserAgent()); 139 | return $client; 140 | } 141 | 142 | /** 143 | * @param Config 144 | * @return DibiConnection 145 | */ 146 | private function createDibiConnection(Config $config) 147 | { 148 | $dibiConnection = new DibiConnection( 149 | $config->getDatabaseConfig() 150 | ); 151 | return $dibiConnection; 152 | } 153 | 154 | /** 155 | * Detects HTTP method from global server params. 156 | * 157 | * @return string 158 | * @throws RuntimeException If HTTP method cannot be detected. 159 | */ 160 | private function detectHttpMethod() 161 | { 162 | if (!isset($_SERVER['REQUEST_METHOD'])) { 163 | throw new RuntimeException('Unable to detect HTTP method.'); 164 | } 165 | return strtoupper($_SERVER['REQUEST_METHOD']); 166 | } 167 | 168 | /** 169 | * @param Config 170 | * @return void 171 | */ 172 | private function showFrontEnd(Config $config) 173 | { 174 | $isServiceEnabled = $config->isEnabled(); 175 | 176 | $isSecured = $this->isSecured(); 177 | $domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : static::DEFAULT_DOMAIN; 178 | $protocol = ($isSecured ? 'https' : 'http'); 179 | $domainUrl = $protocol . '://' . $domain; 180 | $basePath = ''; 181 | 182 | $lastUpdate = $this->getLastUpdate($config); 183 | 184 | $action = null; 185 | 186 | $error = null; 187 | 188 | $requestUri = isset($_SERVER['REQUEST_URI']) ? rtrim($_SERVER['REQUEST_URI'], '/') : null; 189 | if (isset($requestUri)) { 190 | if ($requestUri === '/index.php') { 191 | header('HTTP/1.1 301 Moved Permanently'); 192 | header('Location: ' . $domainUrl . $basePath); 193 | return; 194 | } elseif ($requestUri === '') { 195 | $action = 'frontend'; 196 | } elseif ($requestUri === $config->getSitemapPath()) { 197 | $action = 'sitemap'; 198 | } 199 | } else { 200 | $error = 500; 201 | header('HTTP/1.1 500 Internal Server Error'); 202 | } 203 | 204 | if (!in_array($action, $this->allowedActions, true)) { 205 | $action = 'error'; 206 | $error = 404; 207 | header('HTTP/1.1 404 Not Found'); 208 | } 209 | 210 | switch ($action) { 211 | case 'sitemap': 212 | require $config->getSitemapTemplateFile(); 213 | break; 214 | case 'frontend': 215 | require $config->getTemplateFile(); 216 | break; 217 | case 'error': 218 | require $config->getErrorTemplateFile(); 219 | break; 220 | } 221 | } 222 | 223 | /** 224 | * Checks whether HTTPs is used. 225 | * 226 | * @return bool 227 | */ 228 | private function isSecured() 229 | { 230 | return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') 231 | || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443); 232 | } 233 | 234 | /** 235 | * @param Config 236 | * @return DateTime 237 | */ 238 | private function getLastUpdate(Config $config) 239 | { 240 | return new DateTime(date('Y-m-d H:i:s', filemtime($config->getTemplateFile()))); 241 | } 242 | 243 | /** 244 | * @return array 245 | */ 246 | private function createConfigDefaults() 247 | { 248 | return [ 249 | 'enabled' => true, 250 | 'urlParameterName' => 'url', 251 | 'userAgent' => 'htmldriven/cors-proxy ' . static::VERSION, 252 | 'templateFile' => __DIR__ . '/../app/templates/default/frontend.phtml', 253 | 'sitemapPath' => '/sitemap.xml', 254 | 'sitemapTemplateFile' => __DIR__ . '/../app/templates/default/sitemap.pxml', 255 | 'errorTemplateFile' => __DIR__ . '/../app/templates/default/error.phtml', 256 | 'timezone' => 'UTC', 257 | 'database' => [ 258 | 'driver' => 'pdo', 259 | 'dsn' => 'mysql:dbname=cors_proxy;host=' . $this->detectMySqlHost() . ';charset=utf8mb4', 260 | 'username' => 'cors_proxy', 261 | 'password' => '', 262 | 'lazy' => true, 263 | 'timezone' => '+00:00', 264 | 'options' => [ 265 | PDO::MYSQL_ATTR_INIT_COMMAND => 'SET time_zone = \'+00:00\'', 266 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 267 | ], 268 | ], 269 | 'requestLogEnabled' => false, 270 | ]; 271 | } 272 | 273 | /** 274 | * Detects MySQL host. 275 | * 276 | * @return string 277 | */ 278 | private function detectMySqlHost() 279 | { 280 | $host = getenv('_MYSQL_HOST'); 281 | if ($host === false) { 282 | $host = '127.0.0.1'; 283 | } 284 | return $host; 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Config 13 | { 14 | /** @var boolean */ 15 | private $enabled; 16 | 17 | /** @var string */ 18 | private $urlParameterName; 19 | 20 | /** @var string */ 21 | private $userAgent; 22 | 23 | /** @var string */ 24 | private $templateFile; 25 | 26 | /** @var string */ 27 | private $sitemapPath; 28 | 29 | /** @var string */ 30 | private $sitemapTemplateFile; 31 | 32 | /** @var string */ 33 | private $errorTemplateFile; 34 | 35 | /** @var array */ 36 | private $databaseConfig; 37 | 38 | /** @var boolean */ 39 | private $requestLogEnabled; 40 | 41 | /** 42 | * @param string 43 | * @param string 44 | * @param string 45 | * @param string 46 | * @param string 47 | * @param string 48 | * @param array 49 | * @throws FileNotFoundException If template/sitemap/error file does not exist. 50 | */ 51 | public function __construct( 52 | $enabled, 53 | $urlParameterName, 54 | $userAgent, 55 | $templateFile, 56 | $sitemapPath, 57 | $sitemapTemplateFile, 58 | $errorTemplateFile, 59 | $databaseConfig, 60 | $requestLogEnabled 61 | ) { 62 | 63 | Helpers::checkFileExists($templateFile); 64 | Helpers::checkFileExists($sitemapTemplateFile); 65 | Helpers::checkFileExists($errorTemplateFile); 66 | 67 | $this->enabled = (bool) $enabled; 68 | $this->urlParameterName = $urlParameterName; 69 | $this->userAgent = $userAgent; 70 | $this->templateFile = $templateFile; 71 | $this->sitemapPath = $sitemapPath; 72 | $this->sitemapTemplateFile = $sitemapTemplateFile; 73 | $this->errorTemplateFile = $errorTemplateFile; 74 | $this->databaseConfig = $databaseConfig; 75 | $this->requestLogEnabled = $requestLogEnabled; 76 | } 77 | 78 | /** 79 | * @return boolean 80 | */ 81 | public function isEnabled() 82 | { 83 | return $this->enabled; 84 | } 85 | 86 | /** 87 | * @return string 88 | */ 89 | public function getUrlParameterName() 90 | { 91 | return $this->urlParameterName; 92 | } 93 | 94 | /** 95 | * @return string 96 | */ 97 | public function getUserAgent() 98 | { 99 | return $this->userAgent; 100 | } 101 | 102 | /** 103 | * @return string 104 | */ 105 | public function getTemplateFile() 106 | { 107 | return $this->templateFile; 108 | } 109 | 110 | /** 111 | * @return string 112 | */ 113 | public function getSitemapPath() 114 | { 115 | return $this->sitemapPath; 116 | } 117 | 118 | /** 119 | * @return string 120 | */ 121 | public function getSitemapTemplateFile() 122 | { 123 | return $this->sitemapTemplateFile; 124 | } 125 | 126 | /** 127 | * @return string 128 | */ 129 | public function getErrorTemplateFile() 130 | { 131 | return $this->errorTemplateFile; 132 | } 133 | 134 | /** 135 | * @return array 136 | */ 137 | public function getDatabaseConfig() 138 | { 139 | return $this->databaseConfig; 140 | } 141 | 142 | /** 143 | * @return boolean 144 | */ 145 | public function isRequestLogEnabled() 146 | { 147 | return $this->requestLogEnabled; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Exception/FileNotFoundException.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Helpers 13 | { 14 | /** 15 | * Check whether given file exists. 16 | * 17 | * @param string 18 | * @return void 19 | * @throws FileNotFoundException 20 | */ 21 | public static function checkFileExists($file) 22 | { 23 | if (!is_file($file)) { 24 | throw new FileNotFoundException(sprintf("File '%s' does not exist or not accessible.", $file)); 25 | } 26 | } 27 | 28 | /** 29 | * Makes sure the path is absolute. 30 | * 31 | * @param string 32 | * @param string 33 | * @return string 34 | */ 35 | public static function absolutizeFilepath($rootPath, $filepath) 36 | { 37 | if (substr($filepath, 0, 1) === '.') { 38 | $filepath = $rootPath . DIRECTORY_SEPARATOR . $filepath; 39 | } 40 | return $filepath; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/RequestHandler.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class RequestHandler 18 | { 19 | /** @var Config */ 20 | private $config; 21 | 22 | /** @var ClientInterface */ 23 | private $client; 24 | 25 | /** @var DibiConnection */ 26 | private $dibiConnection; 27 | 28 | public function __construct( 29 | Config $config, 30 | ClientInterface $client, 31 | DibiConnection $dibiConnection 32 | ) { 33 | $this->config = $config; 34 | $this->client = $client; 35 | $this->dibiConnection = $dibiConnection; 36 | } 37 | 38 | /** 39 | * Sends the request to given URL and returns JSON response. 40 | * 41 | * @param string 42 | * @param string 43 | * @return void 44 | */ 45 | public function handleRequest($method, $url) 46 | { 47 | $request = $this->client->createRequest($method, $url); 48 | 49 | $json = [ 50 | 'success' => true, 51 | 'error' => null, 52 | 'body' => null, 53 | ]; 54 | 55 | $this->enableCrossDomainRequests(); 56 | 57 | $response = null; 58 | try { 59 | $response = $request->send(true); 60 | } catch (RequestException $e) { 61 | $json['success'] = false; 62 | $json['error'] = sprintf("Unable to handle request: CURL failed with message '%s'.", $e->getMessage()); 63 | 64 | http_response_code(400); 65 | } 66 | 67 | if (isset($response)) { 68 | $json['body'] = $response->getBody(true); 69 | } else { 70 | $json['body'] = null; 71 | } 72 | 73 | if ($this->config->isRequestLogEnabled()) { 74 | try { 75 | $this->dibiConnection 76 | ->insert('request_log', [ 77 | 'method' => $request->getMethod(), 78 | 'scheme' => $request->getScheme(), 79 | 'host' => $request->getHost(), 80 | 'path' => $request->getPath(), 81 | 'query' => (string) $request->getQuery(), 82 | 'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? 83 | $_SERVER['HTTP_USER_AGENT'] : null, 84 | 'request_headers' => null, // TODO: not yet supported 85 | 'request_body' => null, // TODO: not yet supported 86 | 'response_headers' => isset($response) ? 87 | json_encode($this->getHeadersFromResponse($response)) : null, 88 | 'response_body' => $json['body'], 89 | 'date_created' => (new DateTime())->format('Y-m-d H:i:s'), 90 | ]) 91 | ->execute(); 92 | } catch (DibiException $e) { 93 | // TODO: use custom error logging 94 | error_log($e->getMessage()); 95 | } 96 | } 97 | 98 | header('Content-Type: application/json'); 99 | echo json_encode($json); 100 | } 101 | 102 | /** 103 | * Enables cross-domain requests. 104 | * 105 | * @return void 106 | */ 107 | private function enableCrossDomainRequests() 108 | { 109 | header_remove('Access-Control-Allow-Origin'); 110 | header('Access-Control-Allow-Origin: *'); 111 | } 112 | 113 | /** 114 | * @param Response 115 | * @return array 116 | */ 117 | private function getHeadersFromResponse(Response $response) 118 | { 119 | $headers = []; 120 | foreach ($response->getHeaders() as $header => $value) { 121 | $headers[$header] = (string) $value; 122 | } 123 | return $headers; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /www/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache configuration file (see httpd.apache.org/docs/current/mod/quickreference.html) 2 | 3 | # disable directory listing 4 | 5 | Options -Indexes 6 | 7 | 8 | # enable cool URL 9 | 10 | RewriteEngine On 11 | # RewriteBase / 12 | 13 | # prevents files starting with dot to be viewed by browser 14 | RewriteRule /\.|^\. - [F] 15 | 16 | # front controller 17 | RewriteCond %{REQUEST_FILENAME} !-f 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteRule !\.(pdf|js|ico|gif|jpg|png|css|rar|zip|tar\.gz|map)$ index.php [L] 20 | 21 | 22 | # enable gzip compression 23 | 24 | 25 | AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/x-javascript text/javascript application/javascript application/json 26 | 27 | 28 | -------------------------------------------------------------------------------- /www/assets/default/css/main.css: -------------------------------------------------------------------------------- 1 | html{position:relative;min-height:100%;} 2 | body{margin-bottom:60px;} 3 | 4 | .navbar-nav .fb-like, 5 | .navbar-nav .twitter-follow-button, 6 | .navbar-nav .github-button-wrapper{margin-top:15px;margin-left:15px;margin-right:15px;} 7 | 8 | .footer{position:absolute;bottom:0;width:100%;height:60px;background-color:#f5f5f5;} 9 | .footer .copyright{display:block;margin:20px 0;} 10 | 11 | .modal-dialog{overflow-y:initial !important} 12 | .modal-body{height:250px;overflow-y:auto;} 13 | -------------------------------------------------------------------------------- /www/assets/default/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htmldriven/cors-proxy/e386b4cd97f1149891c71e36815b7f9e26a27708/www/assets/default/img/favicon.ico -------------------------------------------------------------------------------- /www/assets/default/js/main.js: -------------------------------------------------------------------------------- 1 | (function($, document, undefined) { 2 | $(document).ready(function() { 3 | var $requestResponseModal = $('#request-response-modal'); 4 | 5 | $('.copy-request-url').on('click', function(e) { 6 | e.preventDefault(); 7 | 8 | var requestUrl = $(this).closest('form').data('requestUrl'); 9 | var url = $(this).closest('form').find('[name="url"]').val(); 10 | 11 | requestUrl = requestUrl.replace('{url}', url); 12 | 13 | window.prompt('Copy to clipboard: Ctrl+C, Enter', requestUrl); 14 | }); 15 | 16 | $('.request-form').on('submit', function(e) { 17 | e.preventDefault(); 18 | 19 | var $submitButton = $(this).find('[type="submit"]'); 20 | 21 | var $url = $(this).find('[name="url"]'); 22 | 23 | var requestUrl = $(this).data('requestUrl'); 24 | var url = $url.val(); 25 | 26 | if (url.trim() === '') { 27 | alert('Please enter the URL before sending the request.'); 28 | $url.focus(); 29 | return false; 30 | } 31 | 32 | $submitButton.addClass('disabled'); 33 | 34 | requestUrl = requestUrl.replace('{url}', url); 35 | 36 | $.ajax(requestUrl, { 37 | before: function() { 38 | alert('test'); 39 | }, 40 | success: function(data) { 41 | var body = data.body; 42 | 43 | $requestResponseModal.find('.request-url').text(requestUrl); 44 | $requestResponseModal.find('.response-body').text(body); 45 | 46 | $requestResponseModal.modal({ 47 | show: true 48 | }); 49 | }, 50 | error: function(jqXHR, textStatus) { 51 | var responseJson = JSON.parse(jqXHR.responseText); 52 | var errorText = responseJson.error || textStatus || 'Unknown error.'; 53 | 54 | alert('Request failed: ' + errorText + ' We apologize for any inconvenience.'); 55 | }, 56 | complete: function() { 57 | $submitButton.removeClass('disabled'); 58 | } 59 | }); 60 | }); 61 | 62 | $('body').scrollspy({ 63 | target: '#main-navbar', 64 | offset: 80 65 | }); 66 | }); 67 | })(jQuery, document); 68 | -------------------------------------------------------------------------------- /www/index.php: -------------------------------------------------------------------------------- 1 | run(); 8 | -------------------------------------------------------------------------------- /www/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | --------------------------------------------------------------------------------