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,67 @@ +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 \ + HOME=/opt/app-root/src + +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 \ + php-mysqlnd \ + 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 id default || (groupadd -g 1001 default && useradd -d /opt/app-root/ -u 1001 -g 1001 default) + +USER 1001 + +WORKDIR ${HOME} + +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 @@ -22,7 +22,7 @@ DB_PORT=3306 DB_USERNAME=kolabdev -BROADCAST_DRIVER=log +BROADCAST_DRIVER=redis CACHE_DRIVER=redis QUEUE_CONNECTION=redis @@ -64,8 +64,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/.s2i/bin/assemble b/src/.s2i/bin/assemble --- a/src/.s2i/bin/assemble +++ b/src/.s2i/bin/assemble @@ -1,10 +1,25 @@ #!/bin/bash +set -x set -e +function composer_install () { + echo "--->> Detected composer.json, running install" + php -dmemory_limit=${COMPOSER_MEMORY_LIMIT:--1} /usr/bin/composer install ${COMPOSER_ARGS} + rm -rf ~/.cache/composer/ +} + shopt -s dotglob echo "--->> $(rm -vrf vendor/ composer.lock)" +echo "---> Installing application source..." +rm -fR /tmp/src/.git +mv /tmp/src/* ./ + +pushd /opt/app-root/src + +fix-permissions ./ + if [ -f ".env.local" ]; then # Ensure there's a line ending echo "---->> Append .env.local" @@ -12,22 +27,17 @@ cat .env.local >> .env fi -#env +env -/usr/libexec/s2i/assemble - -#cat >> /opt/app-root/etc/conf.d/99-loglevel.conf << EOF -#LogLevel warn mod_rewrite.c:trace4 -#EOF - -# Won't work due to: -# Cannot install, php_dir for channel "pecl.php.net" is not writeable by the current user -#pecl channel-update pecl.php.net -#pecl install swoole +if [ -f "composer.json" ]; then + echo "--->> Detected composer.json, running install" + composer_install +fi -pushd /opt/app-root/src +pwd echo "---->> Run npm run prod" -npm install cross-env -npm run prod +npm install +npm run ${LARAVEL_ENV:=prod} && rm -rf ~/.npm/ +fix-permissions ./ diff --git a/src/.s2i/bin/run b/src/.s2i/bin/run --- a/src/.s2i/bin/run +++ b/src/.s2i/bin/run @@ -10,6 +10,7 @@ if [ -z ${APP_KEY} ]; then echo "----> Run artisan key:generate" ./artisan key:generate + unset APP_KEY fi if [ -z ${JWT_SECRET} ]; then @@ -20,8 +21,10 @@ echo "----> Run artisan clear-compiled" ./artisan clear-compiled -echo "----> Run artisan cache:clear" -./artisan ${ARTISAN_VERBOSITY} cache:clear || true +if [[ "${LARAVEL_ENV}" != "production" || "$LARAVEL_ENV{}" != "prod" ]]; then + echo "----> Run artisan cache:clear" + ./artisan ${ARTISAN_VERBOSITY} cache:clear || true +fi # rpm -qv chromium # if [ ! -z "$(rpm -qv chromium 2>/dev/null)" ]; then @@ -44,17 +47,22 @@ ./artisan migrate --force || : #./artisan db:seed --force || : +env + case ${HKCCP_APP} in worker|WORKER ) echo "----> Running worker " - ./artisan queue:work;; + exec ./artisan queue:work;; server|SERVER ) echo "----> Running server " - ./artisan serve;; + exec ./artisan serve;; apache|APACHE|httpd|HTTPD ) echo "----> Starting httpd " /usr/libexec/s2i/run 2>&1;; + swoole|SWOOLE ) + echo "----> Running swoole" + exec ./artisan swoole:http start;; * ) echo "----> Sleeping" - sleep 10000;; + exec sleep 10000;; esac 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/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 @@ -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');