diff --git a/bin/quickstart.sh b/bin/quickstart.sh --- a/bin/quickstart.sh +++ b/bin/quickstart.sh @@ -78,6 +78,6 @@ rm -rf database/database.sqlite ./artisan db:ping --wait php -dmemory_limit=512M ./artisan migrate:refresh --seed -./artisan serve +./artisan swoole:http start popd diff --git a/docker/swoole/Dockerfile b/docker/swoole/Dockerfile new file mode 100644 --- /dev/null +++ b/docker/swoole/Dockerfile @@ -0,0 +1,66 @@ +FROM fedora:31 + +MAINTAINER Jeroen van Meeuwen + +LABEL io.k8s.description="Platform for serving PHP applications under Swoole" \ + io.k8s.display-name="Swoole 4.4.x" \ + io.openshift.expose-services="8000:http" \ + io.openshift.tags="builder,php,swoole" + +ENV SWOOLE_VERSION=4.4.x + +RUN dnf -y install \ + composer \ + diffutils \ + file \ + git \ + make \ + npm \ + openssl-devel \ + php-cli \ + php-common \ + php-devel \ + php-ldap \ + php-opcache \ + php-pecl-apcu \ + re2c && \ + git clone -b v4.4.x https://github.com/swoole/swoole-src.git/ /swoole-src.git/ && \ + cd /swoole-src.git/ && \ + git clean -d -f -x && \ + phpize --clean && \ + phpize && \ + ./configure \ + --enable-sockets \ + --disable-mysqlnd \ + --enable-http2 \ + --enable-openssl && \ + --enable-debug \ + --enable-debug-log \ + --enable-trace-log && \ + make -j4 && \ + make install && \ + cd / && \ + rm -rf /swoole-src.git/ && \ + dnf -y remove \ + diffutils \ + file \ + make \ + openssl-devel \ + php-devel \ + re2c && \ + dnf clean all && \ + echo "extension=swoole.so" >> /etc/php.d/swoole.ini && \ + php -m 2>&1 | grep -q swoole + +RUN groupadd -g 1001 default && \ + useradd -d /opt/app-root/ -u 1001 -g 1001 default + +USER default + +WORKDIR /opt/app-root/ + +COPY /rootfs / + +EXPOSE 8000 + +CMD [ "/usr/local/bin/usage" ] diff --git a/docker/swoole/do-it-again.sh b/docker/swoole/do-it-again.sh new file mode 100755 --- /dev/null +++ b/docker/swoole/do-it-again.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +export APP_DEBUG="true" +export APP_KEY= +export APP_PUBLIC_URL=http://127.0.0.1:8000/ +export APP_SRC=src/ +export APP_URL=http://127.0.0.1:8000/ +export CACHE_DRIVER="array" +export COMPOSER_ARGS="--no-dev" +export DB_CONNECTION="sqlite" +export DB_DATABASE=":memory:" +export GIT_URI=https://git.kolab.org/source/kolab.git +export LARAVEL_ENV=production +export LOG_CHANNEL="stderr" +export MAIL_DRIVER="array" +export QUEUE_CONNECTION="sync" +export SESSION_DRIVER="array" +export SWOOLE_HTTP_HOST=0.0.0.0 +export SWOOLE_HTTP_PORT=8000 +export SWOOLE_HTTP_REACTOR_NUM=6 +export SWOOLE_HTTP_WORKER_NUM=6 + +docker build -t swoole . + +docker kill swoole + +docker rm swoole + +docker_opts="\ + -e APP_DEBUG=${APP_DEBUG} \ + -e APP_KEY=${APP_KEY} \ + -e APP_PUBLIC_URL=${APP_PUBLIC_URL} \ + -e APP_SRC=${APP_SRC} \ + -e APP_URL=${APP_URL} \ + -e CACHE_DRIVER=${CACHE_DRIVER} \ + -e COMPOSER_ARGS=${COMPOSER_ARGS} \ + -e DB_CONNECTION=${DB_CONNECTION} \ + -e DB_DATABASE=${DB_DATABASE} \ + -e GIT_URI=${GIT_URI} \ + -e LARAVEL_ENV=${LARAVEL_ENV} \ + -e LOG_CHANNEL=${LOG_CHANNEL} \ + -e MAIL_DRIVER=${MAIL_DRIVER} \ + -e QUEUE_CONNECTION=${QUEUE_CONNECTION} \ + -e SESSION_DRIVER=${SESSION_DRIVER} \ + -e SWOOLE_HTTP_HOST=${SWOOLE_HTTP_HOST} \ + -e SWOOLE_HTTP_PORT=${SWOOLE_HTTP_PORT} \ + -e SWOOLE_HTTP_REACTOR_NUM=${SWOOLE_HTTP_REACTOR_NUM} \ + -e SWOOLE_HTTP_WORKER_NUM=${SWOOLE_HTTP_WORKER_NUM}" + +docker run -it \ + ${docker_opts} \ + --name swoole swoole /usr/local/bin/build-image + +docker commit swoole swoole-s2i + +docker kill swoole-s2i + +docker rm swoole-s2i + +docker run -it -p 8000:8000 \ + ${docker_opts} \ + --name swoole-s2i swoole-s2i /usr/local/bin/run-container diff --git a/docker/swoole/rootfs/usr/local/bin/build-image b/docker/swoole/rootfs/usr/local/bin/build-image new file mode 100755 --- /dev/null +++ b/docker/swoole/rootfs/usr/local/bin/build-image @@ -0,0 +1,34 @@ +#!/bin/bash + +set -x + +set -e + +pwd + +if [ -z "${GIT_URI}" ]; then + echo "No GIT_URI specified. Exiting." + exit 1 +fi + +git clone -b ${GIT_BRANCH:-master} ${GIT_URI} + +cd $(basename ${GIT_URI} .git) + +if [ ! -z "${APP_SRC}" ]; then + cd ${APP_SRC} +fi + +if [ -f "composer.json" ]; then + echo "Detected composer.json, running install" + php -dmemory_limit=${COMPOSER_MEMORY_LIMIT:--1} /usr/bin/composer install ${COMPOSER_ARGS} + rm -rf ~/.cache/composer/ +fi + +if [ -z "${LARAVEL_ENV}" ]; then + LARAVEL_ENV=prod +fi + +npm install + +npm run ${LARAVEL_ENV} && rm -rf ~/.npm/ diff --git a/docker/swoole/rootfs/usr/local/bin/run-container b/docker/swoole/rootfs/usr/local/bin/run-container new file mode 100755 --- /dev/null +++ b/docker/swoole/rootfs/usr/local/bin/run-container @@ -0,0 +1,42 @@ +#!/bin/bash + +set -x + +set -e + +if [ -z "$@" ]; then + cd $(basename ${GIT_URI} .git) + + if [ ! -z "${APP_SRC}" ]; then + cd ${APP_SRC} + fi + + if [ ! -f ".env" -a -f ".env.example" ]; then + mv .env.example .env + fi + + if [ -z "${APP_KEY}" ]; then + ./artisan key:generate + unset APP_KEY + fi + + if [ -z "${JWT_SECRET}" ]; then + ./artisan jwt:secret -f + fi + + ./artisan clear-compiled + + # This should not occur in production + #./artisan cache:clear + + # A standalone environment doesn't have anything to ping + #timeout 10m ./artisan db:ping --wait + + ./artisan migrate + + env + + exec ./artisan swoole:http start +else + exec $@ +fi diff --git a/src/.env.example b/src/.env.example --- a/src/.env.example +++ b/src/.env.example @@ -19,7 +19,7 @@ DB_PORT=3306 DB_USERNAME=kolabdev -BROADCAST_DRIVER=log +BROADCAST_DRIVER=redis CACHE_DRIVER=redis QUEUE_CONNECTION=redis @@ -61,8 +61,14 @@ REDIS_PASSWORD=null REDIS_PORT=6379 +SWOOLE_HOT_RELOAD_ENABLE=true +SWOOLE_HTTP_ACCESS_LOG=true SWOOLE_HTTP_HOST=127.0.0.1 SWOOLE_HTTP_PORT=8000 +SWOOLE_HTTP_REACTOR_NUM=1 +SWOOLE_HTTP_WEBSOCKET=true +SWOOLE_HTTP_WORKER_NUM=1 +SWOOLE_OB_OUTPUT=true PAYMENT_PROVIDER= MOLLIE_KEY= diff --git a/src/app/Http/Controllers/WebsocketController.php b/src/app/Http/Controllers/WebsocketController.php new file mode 100644 --- /dev/null +++ b/src/app/Http/Controllers/WebsocketController.php @@ -0,0 +1,18 @@ +emit("message", $data); + } + + public function ping($websocket, $data) + { + $websocket->emit("pong", $data); + } +} diff --git a/src/app/Http/Middleware/RequestLogger.php b/src/app/Http/Middleware/RequestLogger.php --- a/src/app/Http/Middleware/RequestLogger.php +++ b/src/app/Http/Middleware/RequestLogger.php @@ -6,8 +6,14 @@ class RequestLogger { + private static $start; + public function handle($request, Closure $next) { + // FIXME: This is not really a request start, but we can't + // use LARAVEL_START constant when working with swoole + self::$start = microtime(true); + return $next($request); } @@ -16,8 +22,8 @@ if (\App::environment('local')) { $url = $request->fullUrl(); $method = $request->getMethod(); - $time = microtime(true) - LARAVEL_START; $mem = round(memory_get_peak_usage() / 1024 / 1024, 1); + $time = microtime(true) - self::$start; \Log::debug(sprintf("C: %s %s [%sM]: %.4f sec.", $method, $url, $mem, $time)); } diff --git a/src/config/swoole_http.php b/src/config/swoole_http.php --- a/src/config/swoole_http.php +++ b/src/config/swoole_http.php @@ -90,7 +90,7 @@ |-------------------------------------------------------------------------- */ 'instances' => [ - // + 'auth', ], /* @@ -100,6 +100,8 @@ */ 'providers' => [ Illuminate\Pagination\PaginationServiceProvider::class, + App\Providers\AuthServiceProvider::class, + Tymon\JWTAuth\Providers\LaravelServiceProvider::class, ], /* diff --git a/src/config/swoole_websocket.php b/src/config/swoole_websocket.php --- a/src/config/swoole_websocket.php +++ b/src/config/swoole_websocket.php @@ -30,9 +30,9 @@ |-------------------------------------------------------------------------- */ 'middleware' => [ - // SwooleTW\Http\Websocket\Middleware\DecryptCookies::class, - // SwooleTW\Http\Websocket\Middleware\StartSession::class, - // SwooleTW\Http\Websocket\Middleware\Authenticate::class, + SwooleTW\Http\Websocket\Middleware\DecryptCookies::class, + SwooleTW\Http\Websocket\Middleware\StartSession::class, + SwooleTW\Http\Websocket\Middleware\Authenticate::class, ], /* diff --git a/src/package-lock.json b/src/package-lock.json --- a/src/package-lock.json +++ b/src/package-lock.json @@ -1083,45 +1083,45 @@ } }, "@fortawesome/fontawesome-common-types": { - "version": "0.2.29", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.29.tgz", - "integrity": "sha512-cY+QfDTbZ7XVxzx7jxbC98Oxr/zc7R2QpTLqTxqlfyXDrAJjzi/xUIqAUsygELB62JIrbsWxtSRhayKFkGI7MA==", + "version": "0.2.30", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.30.tgz", + "integrity": "sha512-TsRwpTuKwFNiPhk1UfKgw7zNPeV5RhNp2Uw3pws+9gDAkPGKrtjR1y2lI3SYn7+YzyfuNknflpBA1LRKjt7hMg==", "dev": true }, "@fortawesome/fontawesome-svg-core": { - "version": "1.2.29", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.29.tgz", - "integrity": "sha512-xmPmP2t8qrdo8RyKihTkGb09RnZoc+7HFBCnr0/6ZhStdGDSLeEd7ajV181+2W29NWIFfylO13rU+s3fpy3cnA==", + "version": "1.2.30", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.30.tgz", + "integrity": "sha512-E3sAXATKCSVnT17HYmZjjbcmwihrNOCkoU7dVMlasrcwiJAHxSKeZ+4WN5O+ElgO/FaYgJmASl8p9N7/B/RttA==", "dev": true, "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.29" + "@fortawesome/fontawesome-common-types": "^0.2.30" } }, "@fortawesome/free-brands-svg-icons": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.13.1.tgz", - "integrity": "sha512-dKwF+NpIV2LVCNBA7hibH53k+ChF4Wu59P2z35gu3zwRBZpmpLVhS9k1/RiSqUqkyXUQvA2rSv48GY6wp5axZQ==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.14.0.tgz", + "integrity": "sha512-WsqPFTvJFI7MYkcy0jeFE2zY+blC4OrnB9MJOcn1NxRXT/sSfEEhrI7CwzIkiYajLiVDBKWeErYOvpsMeodmCQ==", "dev": true, "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.29" + "@fortawesome/fontawesome-common-types": "^0.2.30" } }, "@fortawesome/free-regular-svg-icons": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.13.1.tgz", - "integrity": "sha512-sSeaqqmv2ovA5LKcrbh3VnEDZHVhaxijWKm4R0AdT0eG21pgxNsJbStD8lW9z6bgSuWXRNHhbhOmARuRCLS8tw==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.14.0.tgz", + "integrity": "sha512-6LCFvjGSMPoUQbn3NVlgiG4CY5iIY8fOm+to/D6QS/GvdqhDt+xZklQeERdCvVRbnFa1ITc1rJHPRXqkX5wztQ==", "dev": true, "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.29" + "@fortawesome/fontawesome-common-types": "^0.2.30" } }, "@fortawesome/free-solid-svg-icons": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.13.1.tgz", - "integrity": "sha512-LQH/0L1p4+rqtoSHa9qFYR84hpuRZKqaQ41cfBQx8b68p21zoWSekTAeA54I/2x9VlCHDLFlG74Nmdg4iTPQOg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.14.0.tgz", + "integrity": "sha512-M933RDM8cecaKMWDSk3FRYdnzWGW7kBBlGNGfvqLVwcwhUPNj9gcw+xZMrqBdRqxnSXdl3zWzTCNNGEtFUq67Q==", "dev": true, "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.29" + "@fortawesome/fontawesome-common-types": "^0.2.30" } }, "@fortawesome/vue-fontawesome": { diff --git a/src/resources/views/layouts/app.blade.php b/src/resources/views/layouts/app.blade.php --- a/src/resources/views/layouts/app.blade.php +++ b/src/resources/views/layouts/app.blade.php @@ -10,7 +10,7 @@ {{-- TODO: PWA disabled for now: @laravelPWA --}} - +
@@ -18,6 +18,6 @@
- + diff --git a/src/routes/websocket.php b/src/routes/websocket.php --- a/src/routes/websocket.php +++ b/src/routes/websocket.php @@ -1,6 +1,5 @@ emit( + 'message', + 'welcome' + ); + } +); + +Websocket::on( + 'open', + function ($websocket, Request $request) { + \Log::debug("socket opened"); + } +); -Websocket::on('disconnect', function ($websocket) { - // called while socket on disconnect -}); +Websocket::on( + 'disconnect', + function ($websocket) { + \Log::debug("someone disconnected"); + } +); -Websocket::on('example', function ($websocket, $data) { - $websocket->emit('message', $data); -}); +Websocket::on('message', 'App\Http\Controllers\WebsocketController@message'); +Websocket::on('ping', 'App\Http\Controllers\WebsocketController@ping'); diff --git a/src/websocket_test.py b/src/websocket_test.py new file mode 100755 --- /dev/null +++ b/src/websocket_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 -tt + +import json +import time +import websocket + +try: + import thread +except ImportError: + import _thread as thread + + +def on_message(ws, message): + print("message: %s" % (message)) + + +def on_pong(ws, message): + print("pong: %s" % (message)) + + +def on_error(ws, error): + print(error) + + +def on_close(ws): + print("### closed ###") + + +def on_open(ws): + def run(*args): + for i in range(3): + time.sleep(1) + ws.send(json.dumps(['ping', i])) + time.sleep(1) + ws.close() + print("thread terminating...") + thread.start_new_thread(run, ()) + + +if __name__ == "__main__": + websocket.enableTrace(True) + ws = websocket.WebSocketApp( + "ws://127.0.0.1:8000", + on_message=on_message, + on_pong=on_pong, + on_error=on_error, + on_close=on_close + ) + + ws.on_open = on_open + ws.run_forever()