├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config └── swoole.php ├── docs ├── chinese.md └── english.md └── src ├── Commands └── HttpServerCommand.php ├── Exceptions └── UnexpectedFramework.php ├── Foundation ├── Application.php ├── Request.php └── ResponseFactory.php ├── Servers └── HttpServer.php └── SwooleServiceProvider.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Files 2 | phpunit.xml 3 | composer.lock 4 | 5 | # Folders 6 | vendor 7 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Yi Huang 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 | ## Warning 2 | 3 | This package is no longer maintained. Please use the new package [huang-yi/laravel-swoole-http](https://github.com/huang-yi/laravel-swoole-http). 4 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "huang-yi/swoole-laravel", 3 | "description": "Using Swoole to make your web applications which based on Laravel or Lumen framework with higher performance.", 4 | "keywords": ["swoole", "laravel", "lumen", "performance", "faster"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Huang Yi", 9 | "email": "coodeer@163.com" 10 | } 11 | ], 12 | "require": { 13 | "illuminate/console": "~5.1", 14 | "illuminate/container": "~5.1", 15 | "illuminate/http": "~5.1", 16 | "illuminate/support": "~5.1" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "HuangYi\\Swoole\\": "src" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /config/swoole.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | return [ 12 | 13 | 'name' => env('SWOOLE_NAME', ''), 14 | 15 | /* 16 | |-------------------------------------------------------------------------- 17 | | The IP address of the Swoole server listening. 18 | |-------------------------------------------------------------------------- 19 | | 20 | | Listening on 127.0.0.1(localhost) by default. 21 | | You can also use 0.0.0.0 to listen on all IP addresses, 22 | | or specify a LAN/WAN IP address. 23 | | 24 | | @see https://wiki.swoole.com/wiki/page/14.html 25 | | 26 | */ 27 | 28 | 'host' => env('SWOOLE_HOST', '127.0.0.1'), 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | The port of the Swoole server listening. 33 | |-------------------------------------------------------------------------- 34 | | 35 | | Listening on 1215 by default. 36 | | 37 | | @see https://wiki.swoole.com/wiki/page/14.html 38 | | 39 | */ 40 | 41 | 'port' => env('SWOOLE_PORT', '1215'), 42 | 43 | /* 44 | |-------------------------------------------------------------------------- 45 | | Extend Swoole server configuration options. 46 | |-------------------------------------------------------------------------- 47 | | 48 | | If the Swoole of new version add more new configuration options, 49 | | we can extend it by using this configuration. This option will merged 50 | | into \HuangYi\Swoole\Servers\HttpServer::$options 51 | | 52 | | @see https://wiki.swoole.com/wiki/page/274.html 53 | | 54 | */ 55 | 56 | 'options' => [], 57 | 58 | /* 59 | |-------------------------------------------------------------------------- 60 | | Swoole server configuration options. 61 | |-------------------------------------------------------------------------- 62 | | 63 | | You can freely add configuration options according to your requirements. 64 | | 65 | | @see https://wiki.swoole.com/wiki/page/274.html 66 | | 67 | */ 68 | 69 | 'server' => [ 70 | 71 | 'daemonize' => env('SWOOLE_SERVER_DAEMONIZE', 1), 72 | 73 | 'log_file' => env('SWOOLE_SERVER_LOG_FILE', storage_path('logs/swoole.log')), 74 | 75 | 'pid_file' => env('SWOOLE_SERVER_PID_FILE', storage_path('logs/swoole.pid')), 76 | 77 | ], 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Before swoole http server start handler. 82 | |-------------------------------------------------------------------------- 83 | | 84 | | You can do something before starting a swoole http server. By default, we 85 | | will clear the APC or OPcache. This option only supports closure. 86 | | 87 | */ 88 | 89 | 'before_start' => null, 90 | 91 | ]; 92 | -------------------------------------------------------------------------------- /docs/chinese.md: -------------------------------------------------------------------------------- 1 | ## 警告 2 | 3 | 这个拓展包已不再维护,请使用新拓展包 [huang-yi/laravel-swoole-http](https://github.com/huang-yi/laravel-swoole-http). 4 | -------------------------------------------------------------------------------- /docs/english.md: -------------------------------------------------------------------------------- 1 | ## Warning 2 | 3 | This package This package is no longer maintained. Please use the new package [huang-yi/laravel-swoole-http](https://github.com/huang-yi/laravel-swoole-http). 4 | -------------------------------------------------------------------------------- /src/Commands/HttpServerCommand.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace HuangYi\Swoole\Commands; 12 | 13 | use HuangYi\Swoole\Servers\HttpServer; 14 | use Illuminate\Console\Command; 15 | use Swoole\Process; 16 | 17 | class HttpServerCommand extends Command 18 | { 19 | /** 20 | * The name and signature of the console command. 21 | * 22 | * @var string 23 | */ 24 | protected $signature = 'swoole:http {action : start|stop|restart|reload}'; 25 | 26 | /** 27 | * The console command description. 28 | * 29 | * @var string 30 | */ 31 | protected $description = 'Swoole http server controller.'; 32 | 33 | /** 34 | * The console command action. start|stop|restart|reload 35 | * 36 | * @var string 37 | */ 38 | protected $action; 39 | 40 | /** 41 | * 42 | * The PID. 43 | * 44 | * @var int 45 | */ 46 | protected $pid; 47 | 48 | /** 49 | * Execute the console command. 50 | * 51 | * @return void 52 | */ 53 | public function fire() 54 | { 55 | $this->initAction(); 56 | 57 | $this->runAction(); 58 | } 59 | 60 | /** 61 | * Run action. 62 | */ 63 | protected function runAction() 64 | { 65 | $this->{$this->action}(); 66 | } 67 | 68 | /** 69 | * Run swoole http server. 70 | */ 71 | protected function start() 72 | { 73 | if ($this->isRunning($this->getPID())) { 74 | $this->error('Failed! swoole_http_server process is already running.'); 75 | exit(1); 76 | } 77 | 78 | $this->info('Starting swoole http server...'); 79 | 80 | $this->info('> (You can run this command to ensure the ' . 81 | 'swoole_http_server process is running: ps aux|grep "swoole")'); 82 | 83 | $this->beforeStart(); 84 | 85 | $httpServer = new HttpServer(); 86 | 87 | $httpServer->run(); 88 | } 89 | 90 | /** 91 | * Stop swoole http server. 92 | */ 93 | protected function stop() 94 | { 95 | $pid = $this->getPID(); 96 | 97 | if (! $this->isRunning($pid)) { 98 | $this->error("Failed! There is no swoole_http_server process running."); 99 | exit(1); 100 | } 101 | 102 | $this->info('Stopping swoole http server...'); 103 | 104 | $isRunning = $this->killProcess($pid, SIGTERM, 15); 105 | 106 | if ($isRunning) { 107 | $this->error('Unable to stop the swoole_http_server process.'); 108 | exit(1); 109 | } 110 | 111 | // I don't known why Swoole didn't trigger onShutdown after sending SIGTERM. 112 | // So we should manually remove the pid file. 113 | $this->removePIDFile(); 114 | 115 | $this->info('> success'); 116 | } 117 | 118 | /** 119 | * Restart swoole http server. 120 | */ 121 | protected function restart() 122 | { 123 | $this->stop(); 124 | $this->start(); 125 | } 126 | 127 | /** 128 | * Reload. 129 | */ 130 | protected function reload() 131 | { 132 | $pid = $this->getPID(); 133 | 134 | if (! $this->isRunning($pid)) { 135 | $this->error("Failed! There is no swoole_http_server process running."); 136 | exit(1); 137 | } 138 | 139 | $this->info('Reloading swoole http server...'); 140 | 141 | $isRunning = $this->killProcess($pid, SIGUSR1); 142 | 143 | if (! $isRunning) { 144 | $this->error('> failure'); 145 | exit(1); 146 | } 147 | 148 | $this->info('> success'); 149 | } 150 | 151 | /** 152 | * Before start handler. 153 | */ 154 | protected function beforeStart() 155 | { 156 | $callback = app('config')->get('swoole.before_start'); 157 | 158 | if ($callback instanceof \Closure) { 159 | $callback(); 160 | } 161 | } 162 | 163 | /** 164 | * Initialize command action. 165 | */ 166 | protected function initAction() 167 | { 168 | $this->action = $this->argument('action'); 169 | 170 | if (! in_array($this->action, ['start', 'stop', 'restart', 'reload'])) { 171 | $this->error("Invalid argument '{$this->action}'. Expected 'start', 'stop', 'restart' or 'reload'."); 172 | exit(1); 173 | } 174 | } 175 | 176 | /** 177 | * If Swoole process is running. 178 | * 179 | * @param int $pid 180 | * @return bool 181 | */ 182 | protected function isRunning($pid) 183 | { 184 | if (! $pid) { 185 | return false; 186 | } 187 | 188 | Process::kill($pid, 0); 189 | 190 | return ! swoole_errno(); 191 | } 192 | 193 | /** 194 | * Kill process. 195 | * 196 | * @param int $pid 197 | * @param int $sig 198 | * @param int $wait 199 | * @return bool 200 | */ 201 | protected function killProcess($pid, $sig, $wait = 0) 202 | { 203 | Process::kill($pid, $sig); 204 | 205 | if ($wait) { 206 | $start = time(); 207 | 208 | do { 209 | if (! $this->isRunning($pid)) { 210 | break; 211 | } 212 | 213 | usleep(100000); 214 | } while (time() < $start + $wait); 215 | } 216 | 217 | return $this->isRunning($pid); 218 | } 219 | 220 | /** 221 | * Get PID. 222 | * 223 | * @return int|null 224 | */ 225 | protected function getPID() 226 | { 227 | if ($this->pid) { 228 | return $this->pid; 229 | } 230 | 231 | $pid = null; 232 | $path = $this->getPIDPath(); 233 | 234 | if (file_exists($path)) { 235 | $pid = (int) file_get_contents($path); 236 | 237 | if (! $pid) { 238 | $this->removePIDFile(); 239 | } else { 240 | $this->pid = $pid; 241 | } 242 | } 243 | 244 | return $this->pid; 245 | } 246 | 247 | /** 248 | * Get PID file path. 249 | * 250 | * @return string 251 | */ 252 | protected function getPIDPath() 253 | { 254 | return app('config')->get('swoole.server.pid_file'); 255 | } 256 | 257 | /** 258 | * Remove PID file. 259 | */ 260 | protected function removePIDFile() 261 | { 262 | if (file_exists($this->getPIDPath())) { 263 | unlink($this->getPIDPath()); 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /src/Exceptions/UnexpectedFramework.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace HuangYi\Exceptions; 12 | 13 | use Exception; 14 | 15 | class UnexpectedFramework extends Exception 16 | { 17 | 18 | } -------------------------------------------------------------------------------- /src/Foundation/Application.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace HuangYi\Swoole\Foundation; 12 | 13 | use HuangYi\Exceptions\UnexpectedFramework; 14 | use Illuminate\Contracts\Container\Container; 15 | use Illuminate\Contracts\Http\Kernel; 16 | use Illuminate\Foundation\Application as LaravelApplication; 17 | use Laravel\Lumen\Application as LumenApplication; 18 | 19 | class Application 20 | { 21 | /** 22 | * Current framework: 'laravel' or 'lumen'. 23 | * 24 | * @var string 25 | */ 26 | protected $framework; 27 | 28 | /** 29 | * Laravel Application. 30 | * 31 | * @var \Illuminate\Contracts\Container\Container 32 | */ 33 | protected $application; 34 | 35 | /** 36 | * @var \Illuminate\Contracts\Http\Kernel 37 | */ 38 | protected $kernel; 39 | 40 | /** 41 | * Return an Application instance. 42 | * 43 | * @return \HuangYi\Swoole\Foundation\Application 44 | * @throws \HuangYi\Exceptions\UnexpectedFramework 45 | */ 46 | public static function make() 47 | { 48 | return new static(); 49 | } 50 | 51 | /** 52 | * Application constructor. 53 | * 54 | * @throws \HuangYi\Exceptions\UnexpectedFramework 55 | */ 56 | public function __construct() 57 | { 58 | $this->create(); 59 | } 60 | 61 | /** 62 | * Run the Laravel application. 63 | * 64 | * @param \Illuminate\Http\Request $request 65 | * @return mixed 66 | * @throws \HuangYi\Exceptions\UnexpectedFramework 67 | */ 68 | public function run($request) 69 | { 70 | if ($this->framework == 'laravel') { 71 | $response = $this->runLaravel($request); 72 | } elseif ($this->framework == 'lumen') { 73 | $response = $this->runLumen($request); 74 | } else { 75 | throw new UnexpectedFramework('Only support Laravel or Lumen framework!'); 76 | } 77 | 78 | return $response; 79 | } 80 | 81 | /** 82 | * Run Laravel framework. 83 | * 84 | * @param \Illuminate\Http\Request $request 85 | * @return \Symfony\Component\HttpFoundation\Response 86 | */ 87 | protected function runLaravel($request) 88 | { 89 | $kernel = $this->getLaravelApplication()->make(Kernel::class); 90 | 91 | $response = $kernel->handle($request); 92 | 93 | $kernel->terminate($request, $response); 94 | 95 | return $response; 96 | } 97 | 98 | /** 99 | * Run Lumen framework. 100 | * 101 | * @param \Illuminate\Http\Request $request 102 | * @return mixed 103 | */ 104 | protected function runLumen($request) 105 | { 106 | $application = $this->getLaravelApplication(); 107 | 108 | // Reflections 109 | $reflection = new \ReflectionObject($application); 110 | 111 | $middleware = $reflection->getProperty('middleware'); 112 | $middleware->setAccessible(true); 113 | 114 | $dispatch = $reflection->getMethod('dispatch'); 115 | 116 | $callTerminableMiddleware = $reflection->getMethod('callTerminableMiddleware'); 117 | $callTerminableMiddleware->setAccessible(true); 118 | 119 | // Run 120 | $response = $dispatch->invoke($application, $request); 121 | 122 | if (count($middleware->getValue($application)) > 0) { 123 | $callTerminableMiddleware->invoke($application, $response); 124 | } 125 | 126 | return $response; 127 | } 128 | 129 | /** 130 | * Create the Laravel application. 131 | * 132 | * @throws \HuangYi\Exceptions\UnexpectedFramework 133 | */ 134 | protected function create() 135 | { 136 | if (! ($this->framework = $this->detectFramework())) { 137 | throw new UnexpectedFramework('Only support Laravel or Lumen framework!'); 138 | } 139 | 140 | $this->application = $this->getLaravelApplication(); 141 | 142 | if ($this->framework == 'laravel') { 143 | $bootstrappers = $this->getLaravelBootstrappers(); 144 | $this->application->bootstrapWith($bootstrappers); 145 | } 146 | } 147 | 148 | /** 149 | * Detect framework. 150 | * 151 | * @return string|bool 152 | */ 153 | public function detectFramework() 154 | { 155 | if (class_exists(LaravelApplication::class)) { 156 | $this->framework = 'laravel'; 157 | } elseif (class_exists(LumenApplication::class)) { 158 | $this->framework = 'lumen'; 159 | } else { 160 | return false; 161 | } 162 | 163 | return $this->framework; 164 | } 165 | 166 | /** 167 | * @return \Illuminate\Contracts\Container\Container 168 | */ 169 | protected function getLaravelApplication() 170 | { 171 | if ($this->application instanceof Container) { 172 | return $this->application; 173 | } 174 | 175 | return $this->application = require base_path() . '/bootstrap/app.php'; 176 | } 177 | 178 | /** 179 | * @return \Illuminate\Contracts\Http\Kernel 180 | */ 181 | protected function getLaravelHttpKernel() 182 | { 183 | if ($this->kernel instanceof Kernel) { 184 | return $this->kernel; 185 | } 186 | 187 | return $this->kernel = $this->getLaravelApplication()->make(Kernel::class); 188 | } 189 | 190 | /** 191 | * Get Laravel bootstrappers. 192 | * 193 | * @return array 194 | */ 195 | protected function getLaravelBootstrappers() 196 | { 197 | $kernel = $this->getLaravelHttpKernel(); 198 | 199 | // Reflect Kernel 200 | $reflection = new \ReflectionObject($kernel); 201 | 202 | $bootstrappersMethod = $reflection->getMethod('bootstrappers'); 203 | $bootstrappersMethod->setAccessible(true); 204 | 205 | $bootstrappers = $bootstrappersMethod->invoke($kernel); 206 | 207 | array_splice($bootstrappers, -2, 0, ['Illuminate\Foundation\Bootstrap\SetRequestForConsole']); 208 | 209 | return $bootstrappers; 210 | } 211 | 212 | } -------------------------------------------------------------------------------- /src/Foundation/Request.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace HuangYi\Swoole\Foundation; 12 | 13 | use Illuminate\Http\Request as IlluminateRequest; 14 | use Swoole\Http\Request as SwooleRequest; 15 | use Symfony\Component\HttpFoundation\ParameterBag; 16 | 17 | class Request extends IlluminateRequest 18 | { 19 | /** 20 | * This function is copy from 21 | * \Illuminate\Http\Request::capture() 22 | * 23 | * @param \Swoole\Http\Request $swooleRequest 24 | * @return \Illuminate\Http\Request 25 | * @throws \LogicException 26 | */ 27 | public static function swooleCapture(SwooleRequest $swooleRequest) 28 | { 29 | list($get, $post, $cookie, $files, $server, $content) 30 | = self::formatSwooleRequest($swooleRequest); 31 | 32 | static::enableHttpMethodParameterOverride(); 33 | 34 | $request = self::createFromSwooleGlobal($get, $post, $cookie, $files, $server, $content); 35 | 36 | return static::createFromBase($request); 37 | } 38 | 39 | /** 40 | * This function is copy from 41 | * \Symfony\Component\HttpFoundation\Request::createFromGlobals() 42 | * 43 | * @param array $get 44 | * @param array $post 45 | * @param array $cookie 46 | * @param array $files 47 | * @param array $server 48 | * @param mixed $content 49 | * @return array|mixed|static 50 | * @throws \LogicException 51 | */ 52 | protected static function createFromSwooleGlobal($get, $post, $cookie, $files, $server, $content) 53 | { 54 | // With the php's bug #66606, the php's built-in web server 55 | // stores the Content-Type and Content-Length header values in 56 | // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields. 57 | if ('cli-server' === PHP_SAPI) { 58 | if (array_key_exists('HTTP_CONTENT_LENGTH', $server)) { 59 | $server['CONTENT_LENGTH'] = $server['HTTP_CONTENT_LENGTH']; 60 | } 61 | if (array_key_exists('HTTP_CONTENT_TYPE', $server)) { 62 | $server['CONTENT_TYPE'] = $server['HTTP_CONTENT_TYPE']; 63 | } 64 | } 65 | 66 | $request = new static($get, $post, [], $cookie, $files, $server, $content); 67 | 68 | if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') 69 | && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) 70 | ) { 71 | parse_str($request->getContent(), $data); 72 | $request->request = new ParameterBag($data); 73 | } 74 | 75 | return $request; 76 | } 77 | 78 | /** 79 | * Format swoole request parameters. 80 | * 81 | * @param \Swoole\Http\Request $request 82 | * @return array 83 | */ 84 | protected static function formatSwooleRequest(SwooleRequest $request) 85 | { 86 | $get = isset($request->get) ? $request->get : []; 87 | $post = isset($request->post) ? $request->post : []; 88 | $files = isset($request->files) ? $request->files : []; 89 | $cookie = isset($request->cookie) ? $request->cookie : []; 90 | $header = isset($request->header) ? $request->header : []; 91 | $server = isset($request->server) ? $request->server : []; 92 | $content = $request->rawContent(); 93 | 94 | $server = self::formatSwooleServerArray($server, $header); 95 | 96 | return [$get, $post, $cookie, $files, $server, $content]; 97 | } 98 | 99 | /** 100 | * Format swoole's server array. 101 | * This function will merge headers into SERVER array, 102 | * and set REQUEST_START value. 103 | * 104 | * @param array $server 105 | * @param array $header 106 | * @return array 107 | */ 108 | protected static function formatSwooleServerArray(array $server, array $header) 109 | { 110 | $__SERVER = []; 111 | 112 | foreach ($server as $key => $value) { 113 | $key = strtoupper($key); 114 | $__SERVER[$key] = $value; 115 | } 116 | 117 | foreach ($header as $key => $value) { 118 | $key = str_replace('-', '_', $key); 119 | $key = strtoupper($key); 120 | 121 | if (! in_array($key, ['REMOTE_ADDR', 'SERVER_PORT', 'HTTPS'])) { 122 | $key = 'HTTP_' . $key; 123 | } 124 | 125 | $__SERVER[$key] = $value; 126 | } 127 | 128 | return $__SERVER; 129 | } 130 | } -------------------------------------------------------------------------------- /src/Foundation/ResponseFactory.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace HuangYi\Swoole\Foundation; 12 | 13 | use Swoole\Http\Response as SwooleResponse; 14 | use Symfony\Component\HttpFoundation\BinaryFileResponse; 15 | use Symfony\Component\HttpFoundation\Response as SymfonyResponse; 16 | use Symfony\Component\HttpFoundation\StreamedResponse; 17 | 18 | class ResponseFactory 19 | { 20 | /** 21 | * @var \Swoole\Http\Response 22 | */ 23 | protected $response; 24 | 25 | /** 26 | * Response type. 27 | * 28 | * @var string 29 | */ 30 | protected $type = 'text'; 31 | 32 | /** 33 | * Response content. 34 | * 35 | * @var string 36 | */ 37 | protected $content = ''; 38 | 39 | /** 40 | * Create swoole response from illuminate response. 41 | * 42 | * @param \Swoole\Http\Response $swooleResponse 43 | * @param mixed $illuminateResponse 44 | * @return \HuangYi\Swoole\Foundation\ResponseFactory 45 | * @throws \InvalidArgumentException 46 | */ 47 | public static function createFromIlluminate(SwooleResponse $swooleResponse, $illuminateResponse) 48 | { 49 | $response = new static; 50 | 51 | $response->setResponse($swooleResponse); 52 | 53 | if ($illuminateResponse instanceof SymfonyResponse) { 54 | $response->initFromIlluminateResponse($illuminateResponse); 55 | } else { 56 | $response->setContent((string) $illuminateResponse); 57 | } 58 | 59 | return $response; 60 | } 61 | 62 | /** 63 | * Init swoole response data from illuminate response. 64 | * 65 | * @param \Symfony\Component\HttpFoundation\Response $illuminateResponse 66 | * @throws \InvalidArgumentException 67 | */ 68 | protected function initFromIlluminateResponse(SymfonyResponse $illuminateResponse) 69 | { 70 | // headers 71 | foreach ($illuminateResponse->headers->allPreserveCase() as $name => $values) { 72 | foreach ($values as $value) { 73 | $this->response->header($name, $value); 74 | } 75 | } 76 | 77 | // cookies 78 | foreach ($illuminateResponse->headers->getCookies() as $cookie) { 79 | $this->response->cookie( 80 | $cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), 81 | $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), 82 | $cookie->isHttpOnly() 83 | ); 84 | } 85 | 86 | // status 87 | $this->response->status($illuminateResponse->getStatusCode()); 88 | 89 | // stream 90 | if ($illuminateResponse instanceof StreamedResponse) { 91 | // No processing currently. 92 | $this->setType('stream'); 93 | $this->setContent(''); 94 | } // file 95 | elseif ($illuminateResponse instanceof BinaryFileResponse) { 96 | $this->setType('file'); 97 | $this->setContent($illuminateResponse->getFile()->getPathname()); 98 | } // text 99 | else { 100 | $this->setType('text'); 101 | $this->setContent($illuminateResponse->getContent()); 102 | } 103 | } 104 | 105 | /** 106 | * End. 107 | */ 108 | public function send() 109 | { 110 | $type = $this->getType(); 111 | $content = $this->getContent(); 112 | 113 | if ($type == 'file') { 114 | $this->response->sendfile($content); 115 | } else { 116 | $this->response->end($content); 117 | } 118 | } 119 | 120 | /** 121 | * @return \Swoole\Http\Response 122 | */ 123 | public function getResponse() 124 | { 125 | return $this->response; 126 | } 127 | 128 | /** 129 | * @param \Swoole\Http\Response $response 130 | */ 131 | public function setResponse($response) 132 | { 133 | $this->response = $response; 134 | } 135 | 136 | /** 137 | * @return string 138 | */ 139 | public function getType() 140 | { 141 | return $this->type; 142 | } 143 | 144 | /** 145 | * @param string $type 146 | */ 147 | public function setType($type) 148 | { 149 | $this->type = $type; 150 | } 151 | 152 | /** 153 | * @return string 154 | */ 155 | public function getContent() 156 | { 157 | return $this->content; 158 | } 159 | 160 | /** 161 | * @param string $content 162 | */ 163 | public function setContent($content) 164 | { 165 | $this->content = $content; 166 | } 167 | } -------------------------------------------------------------------------------- /src/Servers/HttpServer.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace HuangYi\Swoole\Servers; 12 | 13 | use HuangYi\Swoole\Foundation\Application; 14 | use HuangYi\Swoole\Foundation\Request as IlluminateRequest; 15 | use HuangYi\Swoole\Foundation\ResponseFactory; 16 | use Swoole\Http\Request; 17 | use Swoole\Http\Response; 18 | use Swoole\Http\Server; 19 | 20 | class HttpServer 21 | { 22 | /** 23 | * @var \Swoole\Http\Server 24 | */ 25 | protected $server; 26 | 27 | /** 28 | * @var \HuangYi\Swoole\Foundation\Application 29 | */ 30 | protected $application; 31 | 32 | /** 33 | * Swoole Server allowed configuration options. 34 | * 35 | * @var array 36 | */ 37 | public static $options = [ 38 | 'reactor_num', 'worker_num', 'max_request', 'max_conn', 39 | 'task_worker_num', 'task_ipc_mode', 'task_max_request', 'task_tmpdir', 40 | 'dispatch_mode', 'message_queue_key', 'daemonize', 'backlog', 41 | 'log_file', 'log_level', 'heartbeat_check_interval', 42 | 'heartbeat_idle_time', 'open_eof_check', 'open_eof_split', 43 | 'package_eof', 'open_length_check', 'package_length_type', 44 | 'package_length_func', 'package_max_length', 'open_cpu_affinity', 45 | 'cpu_affinity_ignore', 'open_tcp_nodelay', 'tcp_defer_accept', 46 | 'ssl_cert_file', 'ssl_method', 'user', 'group', 'chroot', 'pid_file', 47 | 'pipe_buffer_size', 'buffer_output_size', 'socket_buffer_size', 48 | 'enable_unsafe_event', 'discard_timeout_request', 'enable_reuse_port', 49 | 'ssl_ciphers', 'enable_delay_receive', 'open_http_protocol', 50 | 'open_http2_protocol', 'open_websocket_protocol' 51 | ]; 52 | 53 | /** 54 | * Run the HttpServer. 55 | */ 56 | public function run() 57 | { 58 | $this->init(); 59 | $this->bindHandlers(); 60 | $this->start(); 61 | } 62 | 63 | /** 64 | * Initialize. 65 | */ 66 | protected function init() 67 | { 68 | define('RUN_IN_SWOOLE', true); 69 | 70 | $this->setProcessName('manager process'); 71 | $this->createServer(); 72 | $this->setConfig(); 73 | } 74 | 75 | /** 76 | * Create SwooleHttpServer. 77 | */ 78 | protected function createServer() 79 | { 80 | $host = app('config')->get('swoole.host'); 81 | $port = app('config')->get('swoole.port'); 82 | 83 | $this->server = new Server($host, $port); 84 | } 85 | 86 | /** 87 | * Set configurations. 88 | */ 89 | protected function setConfig() 90 | { 91 | $config = app('config')->get('swoole.server'); 92 | 93 | $envConfig = []; 94 | $options = $this->getOptions(); 95 | 96 | foreach ($options as $option) { 97 | $envKey = 'SWOOLE_SERVER_' . strtoupper($option); 98 | $envValue = env($envKey); 99 | 100 | if (! is_null($envValue)) { 101 | $envConfig[$option] = $envValue; 102 | } 103 | } 104 | 105 | $config = array_merge($config, $envConfig); 106 | 107 | $this->server->set($config); 108 | } 109 | 110 | /** 111 | * Get Swoole Server allowed configuration options. 112 | * 113 | * @return array 114 | */ 115 | protected function getOptions() 116 | { 117 | $extendOptions = (array) app('config')->get('swoole.options'); 118 | 119 | $envOptions = env('SWOOLE_OPTIONS'); 120 | 121 | if (empty($envOptions)) { 122 | $envOptions = []; 123 | } else { 124 | $envOptions = explode(',', $envOptions); 125 | } 126 | 127 | $extendOptions = array_merge($extendOptions, $envOptions); 128 | 129 | return array_merge(self::$options, $extendOptions); 130 | } 131 | 132 | /** 133 | * Bind SwooleHttpServer events' handlers. 134 | */ 135 | protected function bindHandlers() 136 | { 137 | $this->setStartHandler(); 138 | $this->setWorkerStartHandler(); 139 | $this->setRequestHandler(); 140 | $this->setShutdownHandler(); 141 | } 142 | 143 | /** 144 | * Set SwooleHttpServer onStartEvent handler. 145 | */ 146 | protected function setStartHandler() 147 | { 148 | $this->server->on('Start', [$this, 'onStart']); 149 | } 150 | 151 | /** 152 | * SwooleHttpServer onStartEvent handler. 153 | */ 154 | public function onStart() 155 | { 156 | $this->setProcessName('master process'); 157 | 158 | $pidFile = $this->getPIDFile(); 159 | $pid = $this->server->master_pid; 160 | 161 | file_put_contents($pidFile, $pid); 162 | } 163 | 164 | /** 165 | * Set SwooleHttpServer onWorkerStartEvent handler. 166 | */ 167 | protected function setWorkerStartHandler() 168 | { 169 | $this->server->on('WorkerStart', [$this, 'onWorkerStart']); 170 | } 171 | 172 | /** 173 | * SwooleHttpServer onWorkerStartEvent handler. 174 | */ 175 | public function onWorkerStart() 176 | { 177 | $this->clearCache(); 178 | 179 | $this->setProcessName('worker process'); 180 | 181 | $this->createApplication(); 182 | } 183 | 184 | /** 185 | * Set SwooleHttpServer onRequestEvent handler. 186 | */ 187 | protected function setRequestHandler() 188 | { 189 | $this->server->on('Request', [$this, 'onRequest']); 190 | } 191 | 192 | /** 193 | * SwooleHttpServer onRequestEvent handler. 194 | * 195 | * @param \Swoole\Http\Request $request 196 | * @param \Swoole\Http\Response $response 197 | * @throws \HuangYi\Exceptions\UnexpectedFramework 198 | * @throws \InvalidArgumentException 199 | * @throws \LogicException 200 | */ 201 | public function onRequest(Request $request, Response $response) 202 | { 203 | $illuminateRequest = IlluminateRequest::swooleCapture($request); 204 | $illuminateResponse = $this->runApplication($illuminateRequest); 205 | $swooleResponse = ResponseFactory::createFromIlluminate($response, $illuminateResponse); 206 | 207 | $swooleResponse->send(); 208 | } 209 | 210 | /** 211 | * Set SwooleHttpServer onShutdownEvent handler. 212 | */ 213 | protected function setShutdownHandler() 214 | { 215 | $this->server->on('Shutdown', [$this, 'onShutdown']); 216 | } 217 | 218 | /** 219 | * SwooleHttpServer onShutdownEvent handler. 220 | * 221 | * @param \Swoole\Http\Server $server 222 | */ 223 | public function onShutdown(Server $server) 224 | { 225 | unlink($this->getPIDFile()); 226 | } 227 | 228 | /** 229 | * Create application. 230 | * 231 | * @return \HuangYi\Swoole\Foundation\Application 232 | * @throws \HuangYi\Exceptions\UnexpectedFramework 233 | */ 234 | protected function createApplication() 235 | { 236 | return $this->application = Application::make(); 237 | } 238 | 239 | /** 240 | * Get Application. 241 | * 242 | * @return \HuangYi\Swoole\Foundation\Application 243 | * @throws \HuangYi\Exceptions\UnexpectedFramework 244 | */ 245 | protected function getApplication() 246 | { 247 | if ($this->application instanceof Application) { 248 | return $this->application; 249 | } 250 | 251 | return $this->createApplication(); 252 | } 253 | 254 | /** 255 | * Run Laravel application. 256 | * 257 | * @param \HuangYi\Swoole\Foundation\Request $request 258 | * @return mixed 259 | * @throws \HuangYi\Exceptions\UnexpectedFramework 260 | */ 261 | protected function runApplication($request) 262 | { 263 | return $this->getApplication()->run($request); 264 | } 265 | 266 | /** 267 | * Get pid file path. 268 | * 269 | * @return string 270 | */ 271 | protected function getPIDFile() 272 | { 273 | return app('config')->get('swoole.server.pid_file'); 274 | } 275 | 276 | /** 277 | * Start SwooleHttpServer. 278 | */ 279 | protected function start() 280 | { 281 | $this->server->start(); 282 | } 283 | 284 | /** 285 | * Clear APC or OPCache. 286 | */ 287 | protected function clearCache() 288 | { 289 | if (function_exists('apc_clear_cache')) { 290 | apc_clear_cache(); 291 | } 292 | 293 | if (function_exists('opcache_reset')) { 294 | opcache_reset(); 295 | } 296 | } 297 | 298 | /** 299 | * Set process name. 300 | * 301 | * @param string $type 302 | */ 303 | protected function setProcessName($type) 304 | { 305 | $name = $this->getConfigName(); 306 | $process = sprintf('swoole_http_server: %s%s', $type, $name); 307 | 308 | swoole_set_process_name($process); 309 | } 310 | 311 | /** 312 | * @return string 313 | */ 314 | protected function getConfigName() 315 | { 316 | $name = app('config')->get('swoole.name'); 317 | 318 | return $name ? ' for ' . $name : ''; 319 | } 320 | 321 | } 322 | -------------------------------------------------------------------------------- /src/SwooleServiceProvider.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace HuangYi\Swoole; 12 | 13 | use HuangYi\Swoole\Commands\HttpServerCommand; 14 | use Illuminate\Support\ServiceProvider; 15 | 16 | class SwooleServiceProvider extends ServiceProvider 17 | { 18 | /** 19 | * Indicates if loading of the provider is deferred. 20 | * 21 | * @var bool 22 | */ 23 | protected $defer = false; 24 | 25 | /** 26 | * Boot the service provider. 27 | * 28 | * @return void 29 | */ 30 | public function boot() 31 | { 32 | if (function_exists('config_path')) { 33 | $this->publishes([ 34 | __DIR__ . '/../config/swoole.php' => config_path('swoole.php') 35 | ], 'config'); 36 | } 37 | } 38 | 39 | /** 40 | * Register the service provider. 41 | * 42 | * @return void 43 | */ 44 | public function register() 45 | { 46 | $this->mergeConfigFrom(__DIR__ . '/../config/swoole.php', 'swoole'); 47 | 48 | $this->commands([HttpServerCommand::class]); 49 | } 50 | 51 | } 52 | --------------------------------------------------------------------------------