1 | <?php |
||
2 | |||
3 | namespace Hhxsv5\LaravelS; |
||
4 | |||
5 | use Hhxsv5\LaravelS\Illuminate\Laravel; |
||
6 | use Hhxsv5\LaravelS\Illuminate\LaravelTrait; |
||
7 | use Hhxsv5\LaravelS\Illuminate\LogTrait; |
||
8 | use Hhxsv5\LaravelS\Swoole\DynamicResponse; |
||
9 | use Hhxsv5\LaravelS\Swoole\Events\ServerStartInterface; |
||
10 | use Hhxsv5\LaravelS\Swoole\Events\ServerStopInterface; |
||
11 | use Hhxsv5\LaravelS\Swoole\Events\WorkerErrorInterface; |
||
12 | use Hhxsv5\LaravelS\Swoole\Events\WorkerStartInterface; |
||
13 | use Hhxsv5\LaravelS\Swoole\Events\WorkerStopInterface; |
||
14 | use Hhxsv5\LaravelS\Swoole\InotifyTrait; |
||
15 | use Hhxsv5\LaravelS\Swoole\Process\CustomProcessTrait; |
||
16 | use Hhxsv5\LaravelS\Swoole\Process\ProcessTitleTrait; |
||
17 | use Hhxsv5\LaravelS\Swoole\Request; |
||
18 | use Hhxsv5\LaravelS\Swoole\Server; |
||
19 | use Hhxsv5\LaravelS\Swoole\StaticResponse; |
||
20 | use Hhxsv5\LaravelS\Swoole\Timer\TimerTrait; |
||
21 | use Illuminate\Http\Request as IlluminateRequest; |
||
22 | use Swoole\Http\Request as SwooleRequest; |
||
23 | use Swoole\Http\Response as SwooleResponse; |
||
24 | use Swoole\Http\Server as HttpServer; |
||
25 | use Swoole\Process; |
||
26 | use Swoole\Server\Port; |
||
27 | use Symfony\Component\Console\Style\OutputStyle; |
||
28 | use Symfony\Component\HttpFoundation\BinaryFileResponse; |
||
29 | |||
30 | |||
31 | /** |
||
32 | * Swoole Request => Laravel Request |
||
33 | * Laravel Request => Laravel handle => Laravel Response |
||
34 | * Laravel Response => Swoole Response |
||
35 | */ |
||
36 | class LaravelS extends Server |
||
37 | { |
||
38 | /** |
||
39 | * Fix conflicts of traits |
||
40 | */ |
||
41 | use InotifyTrait, LaravelTrait, LogTrait, ProcessTitleTrait, TimerTrait, CustomProcessTrait; |
||
42 | |||
43 | /**@var array */ |
||
44 | protected array $laravelConf; |
||
45 | |||
46 | /**@var Laravel */ |
||
47 | protected Laravel $laravel; |
||
48 | |||
49 | /**@var Process[] */ |
||
50 | protected static array $customProcesses = []; |
||
51 | |||
52 | public function __construct(array $svrConf, array $laravelConf) |
||
53 | { |
||
54 | parent::__construct($svrConf); |
||
55 | $this->laravelConf = $laravelConf; |
||
56 | |||
57 | $timerConf = $this->conf['timer'] ?? []; |
||
58 | $timerConf['process_prefix'] = $svrConf['process_prefix']; |
||
59 | $this->addTimerProcess($this->swoole, $timerConf, $this->laravelConf); |
||
60 | |||
61 | $inotifyConf = $this->conf['inotify_reload'] ?? []; |
||
62 | if (!isset($inotifyConf['watch_path'])) { |
||
63 | $inotifyConf['watch_path'] = $this->laravelConf['root_path']; |
||
64 | } |
||
65 | $inotifyConf['process_prefix'] = $svrConf['process_prefix']; |
||
66 | $this->addInotifyProcess($this->swoole, $inotifyConf, $this->laravelConf); |
||
67 | |||
68 | $processes = $this->conf['processes'] ?? []; |
||
69 | static::$customProcesses = $this->addCustomProcesses($this->swoole, $svrConf['process_prefix'], $processes, $this->laravelConf); |
||
70 | |||
71 | // Fire ServerStart event |
||
72 | if (isset($this->conf['event_handlers']['ServerStart'])) { |
||
73 | Laravel::autoload($this->laravelConf['root_path']); |
||
74 | $this->fireEvent('ServerStart', ServerStartInterface::class, [$this->swoole]); |
||
75 | } |
||
76 | } |
||
77 | |||
78 | public static function getCustomProcesses(): array |
||
79 | { |
||
80 | return static::$customProcesses; |
||
81 | } |
||
82 | |||
83 | protected function beforeWebSocketHandShake(SwooleRequest $request) |
||
84 | { |
||
85 | // Start Laravel's lifetime, then support session ...middleware. |
||
86 | $laravelRequest = $this->convertRequest($this->laravel, $request); |
||
87 | $this->laravel->bindRequest($laravelRequest); |
||
88 | $this->laravel->fireEvent('laravels.received_request', [$laravelRequest]); |
||
89 | $this->laravel->cleanProviders(); |
||
90 | $laravelResponse = $this->laravel->handleDynamic($laravelRequest); |
||
91 | $this->laravel->fireEvent('laravels.generated_response', [$laravelRequest, $laravelResponse]); |
||
92 | } |
||
93 | |||
94 | protected function afterWebSocketOpen(SwooleRequest $request) |
||
95 | { |
||
96 | // End Laravel's lifetime. |
||
97 | $this->laravel->saveSession(); |
||
98 | $this->laravel->clean(); |
||
99 | } |
||
100 | |||
101 | protected function triggerWebSocketEvent($event, array $params) |
||
102 | { |
||
103 | if ($event === 'onHandShake') { |
||
104 | $this->beforeWebSocketHandShake($params[0]); |
||
105 | if (!empty($this->conf['server'])) { |
||
106 | $params[1]->header('Server', $this->conf['server']); |
||
107 | } |
||
108 | } |
||
109 | |||
110 | parent::triggerWebSocketEvent($event, $params); |
||
111 | |||
112 | switch ($event) { |
||
113 | case 'onHandShake': |
||
114 | if (isset($params[1]->header['Sec-Websocket-Accept'])) { |
||
115 | // Successful handshake |
||
116 | parent::triggerWebSocketEvent('onOpen', [$this->swoole, $params[0]]); |
||
117 | } |
||
118 | $this->afterWebSocketOpen($params[0]); |
||
119 | break; |
||
120 | case 'onOpen': |
||
121 | $this->afterWebSocketOpen($params[1]); |
||
122 | break; |
||
123 | } |
||
124 | } |
||
125 | |||
126 | protected function triggerPortEvent(Port $port, $handlerClass, $event, array $params) |
||
127 | { |
||
128 | switch ($event) { |
||
129 | case 'onHandShake': |
||
130 | $this->beforeWebSocketHandShake($params[0]); |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
![]() |
|||
131 | case 'onRequest': |
||
132 | if (!empty($this->conf['server'])) { |
||
133 | $params[1]->header('Server', $this->conf['server']); |
||
134 | } |
||
135 | break; |
||
136 | } |
||
137 | |||
138 | parent::triggerPortEvent($port, $handlerClass, $event, $params); |
||
139 | |||
140 | switch ($event) { |
||
141 | case 'onHandShake': |
||
142 | if (isset($params[1]->header['Sec-Websocket-Accept'])) { |
||
143 | // Successful handshake |
||
144 | parent::triggerPortEvent($port, $handlerClass, 'onOpen', [$this->swoole, $params[0]]); |
||
145 | } |
||
146 | $this->afterWebSocketOpen($params[0]); |
||
147 | break; |
||
148 | case 'onOpen': |
||
149 | $this->afterWebSocketOpen($params[1]); |
||
150 | break; |
||
151 | } |
||
152 | } |
||
153 | |||
154 | public function onShutdown(HttpServer $server) |
||
155 | { |
||
156 | parent::onShutdown($server); |
||
157 | |||
158 | // Fire ServerStop event |
||
159 | if (isset($this->conf['event_handlers']['ServerStop'])) { |
||
160 | $this->laravel = $this->initLaravel($this->laravelConf, $this->swoole); |
||
161 | $this->fireEvent('ServerStop', ServerStopInterface::class, [$server]); |
||
162 | } |
||
163 | } |
||
164 | |||
165 | public function onWorkerStart(HttpServer $server, $workerId) |
||
166 | { |
||
167 | parent::onWorkerStart($server, $workerId); |
||
168 | |||
169 | // To implement gracefully reload |
||
170 | // Delay to create Laravel |
||
171 | // Delay to include Laravel's autoload.php |
||
172 | $this->laravel = $this->initLaravel($this->laravelConf, $this->swoole); |
||
173 | |||
174 | // Fire WorkerStart event |
||
175 | $this->fireEvent('WorkerStart', WorkerStartInterface::class, func_get_args()); |
||
176 | } |
||
177 | |||
178 | public function onWorkerStop(HttpServer $server, $workerId) |
||
179 | { |
||
180 | parent::onWorkerStop($server, $workerId); |
||
181 | |||
182 | // Fire WorkerStop event |
||
183 | $this->fireEvent('WorkerStop', WorkerStopInterface::class, func_get_args()); |
||
184 | } |
||
185 | |||
186 | public function onWorkerError(HttpServer $server, $workerId, $workerPId, $exitCode, $signal) |
||
187 | { |
||
188 | parent::onWorkerError($server, $workerId, $workerPId, $exitCode, $signal); |
||
189 | |||
190 | Laravel::autoload($this->laravelConf['root_path']); |
||
191 | |||
192 | // Fire WorkerError event |
||
193 | $this->fireEvent('WorkerError', WorkerErrorInterface::class, func_get_args()); |
||
194 | } |
||
195 | |||
196 | protected function convertRequest(Laravel $laravel, SwooleRequest $request) |
||
197 | { |
||
198 | $rawGlobals = $laravel->getRawGlobals(); |
||
199 | return (new Request($request))->toIlluminateRequest($rawGlobals['_SERVER'], $rawGlobals['_ENV']); |
||
200 | } |
||
201 | |||
202 | public function onRequest(SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) |
||
203 | { |
||
204 | try { |
||
205 | $laravelRequest = $this->convertRequest($this->laravel, $swooleRequest); |
||
206 | $this->laravel->bindRequest($laravelRequest); |
||
207 | $this->laravel->fireEvent('laravels.received_request', [$laravelRequest]); |
||
208 | $handleStaticSuccess = false; |
||
209 | if ($this->conf['handle_static']) { |
||
210 | // For Swoole < 1.9.17 |
||
211 | $handleStaticSuccess = $this->handleStaticResource($this->laravel, $laravelRequest, $swooleResponse); |
||
212 | } |
||
213 | if (!$handleStaticSuccess) { |
||
214 | $this->handleDynamicResource($this->laravel, $laravelRequest, $swooleResponse); |
||
215 | } |
||
216 | } catch (\Exception $e) { |
||
217 | $this->handleException($e, $swooleResponse); |
||
218 | } |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * @param \Exception $e |
||
223 | * @param SwooleResponse $response |
||
224 | */ |
||
225 | protected function handleException($e, SwooleResponse $response) |
||
226 | { |
||
227 | $error = sprintf( |
||
228 | 'onRequest: Uncaught exception "%s"([%d]%s) at %s:%s, %s%s', |
||
229 | get_class($e), |
||
230 | $e->getCode(), |
||
231 | $e->getMessage(), |
||
232 | $e->getFile(), |
||
233 | $e->getLine(), |
||
234 | PHP_EOL, |
||
235 | $e->getTraceAsString() |
||
236 | ); |
||
237 | $this->error($error); |
||
238 | try { |
||
239 | $response->status(500); |
||
240 | $response->end('Oops! An unexpected error occurred'); |
||
241 | } catch (\Exception $e) { |
||
242 | $this->logException($e); |
||
243 | } |
||
244 | } |
||
245 | |||
246 | protected function handleStaticResource(Laravel $laravel, IlluminateRequest $laravelRequest, SwooleResponse $swooleResponse) |
||
247 | { |
||
248 | $laravelResponse = $laravel->handleStatic($laravelRequest); |
||
249 | if ($laravelResponse === false) { |
||
250 | return false; |
||
251 | } |
||
252 | if (!empty($this->conf['server'])) { |
||
253 | $laravelResponse->headers->set('Server', $this->conf['server'], true); |
||
254 | } |
||
255 | $laravel->fireEvent('laravels.generated_response', [$laravelRequest, $laravelResponse]); |
||
256 | $response = new StaticResponse($swooleResponse, $laravelResponse); |
||
257 | $response->setChunkLimit($this->conf['swoole']['buffer_output_size']); |
||
258 | $response->send($this->conf['enable_gzip']); |
||
259 | return true; |
||
260 | } |
||
261 | |||
262 | protected function handleDynamicResource(Laravel $laravel, IlluminateRequest $laravelRequest, SwooleResponse $swooleResponse) |
||
263 | { |
||
264 | $laravel->cleanProviders(); |
||
265 | $laravelResponse = $laravel->handleDynamic($laravelRequest); |
||
266 | if (!empty($this->conf['server'])) { |
||
267 | $laravelResponse->headers->set('Server', $this->conf['server'], true); |
||
268 | } |
||
269 | $laravel->fireEvent('laravels.generated_response', [$laravelRequest, $laravelResponse]); |
||
270 | if ($laravelResponse instanceof BinaryFileResponse) { |
||
271 | $response = new StaticResponse($swooleResponse, $laravelResponse); |
||
272 | } else { |
||
273 | $response = new DynamicResponse($swooleResponse, $laravelResponse); |
||
274 | } |
||
275 | $response->setChunkLimit($this->conf['swoole']['buffer_output_size']); |
||
276 | $response->send($this->conf['enable_gzip']); |
||
277 | $laravel->clean(); |
||
278 | return true; |
||
279 | } |
||
280 | |||
281 | /**@var OutputStyle */ |
||
282 | protected static $outputStyle; |
||
283 | |||
284 | public static function setOutputStyle(OutputStyle $outputStyle) |
||
285 | { |
||
286 | static::$outputStyle = $outputStyle; |
||
287 | } |
||
288 | |||
289 | public static function getOutputStyle() |
||
290 | { |
||
291 | return static::$outputStyle; |
||
292 | } |
||
293 | } |
||
294 |