Giter Club home page Giter Club logo

roadrunner-bundle's Introduction

Roadrunner Bundle

RoadRunner is a high-performance PHP application server, load-balancer, and process manager written in Golang.

This bundle provides a RoadRunner Worker integrated in Symfony, it's easily configurable and extendable.

Installation

Run the following command:

composer require baldinof/roadrunner-bundle

If you don't use Symfony Flex:

  • register Baldinof\RoadRunnerBundle\BaldinofRoadRunnerBundle in your kernel
  • copy default RoadRunner configuration files: cp vendor/baldinof/roadrunner-bundle/.rr.* .

Usage

  • require the RoadRunner download utility: composer require --dev spiral/roadrunner-cli
  • get the RoadRunner binary: vendor/bin/rr get --location bin/
  • run RoadRunner with bin/rr serve or bin/rr serve -c .rr.dev.yaml (watch mode)
  • visit your app at http://localhost:8080

Integrations

Depending on installed bundle & your configuration, this bundles add some integrations:

  • Sentry: configure the request context (if the SentryBundle is installed)
  • Sessions: add the session cookie to the Symfony response (if framework.sessions.enabled config is true)
  • Doctrine Mongo Bundle: clear opened managers after each requests (if DoctrineMongoDBBundle is installed)
  • Doctrine ORM Bundle: clear opened managers and check connection is still usable after each requests (if DoctrineBundle is installed)
  • Blackfire: enable the probe when a profile is requested (if the blackfire extension is installed)

Even if it is not recommended, you can disable default integrations:

baldinof_road_runner:
  default_integrations: false

Middlewares

You can use middlewares to manipulate request & responses. Middlewares must implements Baldinof\RoadRunnerBundle\Http\MiddlewareInterface.

Example configuration:

baldinof_road_runner:
    middlewares:
        - App\Middleware\YourMiddleware

Be aware that

  • middlewares are run outside of Symfony Kernel::handle()
  • the middleware stack is always resolved at worker start (can be a performance issue if your middleware initialization takes time)

Kernel reboots

The Symfony kernel and the dependency injection container are preserved between requests. If an exception is thrown during the request handling, the kernel is rebooted and a fresh container is used.

The goal is to prevent services to be in a non-recoverable state after an error.

To optimize your worker you can allow exceptions that does not put your app in an errored state:

# config/packages/baldinof_road_runner.yaml
baldinof_road_runner:
    kernel_reboot:
      strategy: on_exception
      allowed_exceptions:
        - Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
        - Symfony\Component\Serializer\Exception\ExceptionInterface
        - App\Exception\YourDomainException

If some of your services are stateful, you can implement Symfony\Contracts\Service\ResetInterface and your service will be resetted on each request.

If you are seeing issues and want to use a fresh container on each request you can use the always reboot strategy:

# config/packages/baldinof_road_runner.yaml
baldinof_road_runner:
    kernel_reboot:
      strategy: always

If you are building long-running application and need to reboot it every XXX request to prevent memory leaks you can use max_jobs reboot strategy:

# config/packages/baldinof_road_runner.yaml
baldinof_road_runner:
    kernel_reboot:
      strategy: max_jobs
      max_jobs: 1000 # maximum number of request
      max_jobs_dispersion: 0.2 # dispersion 20% used to prevent simultaneous reboot of all active workers (kernel will rebooted between 800 and 1000 requests) 

You can combine reboot strategies:

# config/packages/baldinof_road_runner.yaml
baldinof_road_runner:
    kernel_reboot:
      strategy: [on_exception, max_jobs]
      allowed_exceptions:
        - Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
        - Symfony\Component\Serializer\Exception\ExceptionInterface
        - App\Exception\YourDomainException
      max_jobs: 1000
      max_jobs_dispersion: 0.2

Events

The following events are dispatched throughout the worker lifecycle:

  • Baldinof\RoadRunnerBundle\Event\WorkerStartEvent: Dispatched right before the worker starts listening to requests.
  • Baldinof\RoadRunnerBundle\Event\WorkerStopEvent: Dispatched right before the worker closes.
  • Baldinof\RoadRunnerBundle\Event\WorkerExceptionEvent: Dispatched after encountering an uncaught exception during request handling.
  • Baldinof\RoadRunnerBundle\Event\WorkerKernelRebootedEvent: Dispatched after the symfony kernel was rebooted (see Kernel reboots).

Development mode

Copy the dev config file if it's not present: cp vendor/baldinof/roadrunner-bundle/.rr.dev.yaml .

Start RoadRunner with the dev config file:

bin/rr serve -c .rr.dev.yaml

Reference: https://roadrunner.dev/docs/beep-beep-reload

If you use the Symfony VarDumper, dumps will not be shown in the HTTP Response body. You can view dumps with bin/console server:dump or in the profiler.

Metrics

Roadrunner can collect application metrics, and expose a prometheus endpoint.

Example configuration:

# config/packages/baldinof_road_runner.yaml
baldinof_road_runner:
  metrics:
    enabled: true
    collect:
      user_login:
        type: counter
        help: "Number of logged in user"

And configure RoadRunner:

# .rr.yaml
rpc:
  listen: "tcp:127.0.0.1:6001"

metrics:
  address: "0.0.0.0:9180" # prometheus endpoint

Then simply inject Spiral\RoadRunner\MetricsInterface to record metrics:

class YouController
{
    public function index(MetricsInterface $metrics): Response
    {
        $metrics->add('user_login', 1);

        return new Response("...");
    }
}

gRPC

gRPC support was added by the roadrunner-grpc plugin for RoadRunner 2 (https://github.com/spiral/roadrunner-grpc).

To configure Roadrunner for gRPC, refer to the configuration reference at https://roadrunner.dev/docs/beep-beep-grpc. Basic configuration example:

server:
  command: "php public/index.php"
  env:
    APP_RUNTIME: Baldinof\RoadRunnerBundle\Runtime\Runtime

grpc:
  listen: "tcp://:9001"

  proto:
    - "calculator.proto"

Once you have generated your PHP files from proto files, you just have to implement the service interfaces. GRPC services are registered automatically. Example service:

<?php

namespace App\Grpc;

use Spiral\RoadRunner\GRPC;
use App\Grpc\Generated\Calculator\Sum;
use App\Grpc\Generated\Calculator\Result;
use App\Grpc\Generated\Calculator\CalculatorInterface;

class Calculator implements CalculatorInterface
{
    public function Sum(GRPC\ContextInterface $ctx, Sum $in): Result
    {
        return (new Result())->setResult($in->getA() + $in->getB());
    }
}

KV caching

Roadrunner has a KV (Key-Value) plugin that can be used to cache data between requests.

To use it, refer to the configuration reference at https://roadrunner.dev/docs/kv-overview. This requires the spiral/roadrunner-kv, spiral/goridge and symfony/cache composer dependencies. Basic configuration example:

Example configuration:

# config/packages/baldinof_road_runner.yaml
baldinof_road_runner:
  kv:
    storages:
      - example

And configure RoadRunner:

# .rr.yaml
rpc:
  listen: tcp://127.0.0.1:6001

kv:
  example:
    driver: memory
    config: { }

An adapter service will now be created automatically for your storage with the name cache.adapter.roadrunner.kv_<YOUR_STORAGE_NAME>.

Basic usage example:

# config/packages/cache.yaml
framework:
  cache:
    pools:
      cache.example:
        adapter: cache.adapter.roadrunner.kv_example

Usage with Docker

# Dockerfile
FROM php:8.1-alpine

RUN apk add --no-cache linux-headers autoconf openssl-dev g++ make pcre-dev icu-dev zlib-dev libzip-dev && \
    docker-php-ext-install bcmath intl opcache zip sockets && \
    apk del --purge autoconf g++ make

WORKDIR /usr/src/app

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

COPY composer.json composer.lock ./

RUN composer install --no-dev --no-scripts --prefer-dist --no-progress --no-interaction

RUN ./vendor/bin/rr get-binary --location /usr/local/bin

COPY . .

ENV APP_ENV=prod

RUN composer dump-autoload --optimize && \
    composer check-platform-reqs && \
    php bin/console cache:warmup

EXPOSE 8080

CMD ["rr", "serve"]

roadrunner-bundle's People

Contributors

baldinof avatar bzajacc avatar endroid avatar fluffydiscord avatar friedrichroell avatar hugochinchilla avatar iborysenko avatar okorneliuk avatar punk-undead avatar rmikalkenas avatar sabrimarz avatar slytherincz avatar stanjansen avatar sweoggy avatar victor-truhanovich avatar vsychov avatar waylandace avatar xepozz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

roadrunner-bundle's Issues

Bug: Files cached in next POST Request

I've a weird issue when im testing my application.

Is it possible that this bundle caches files?

After our test sends a file using a POST request, the next POST request throws an unexpected 400. It received the file from the previous request. After debugging i found out that's not the test bundle (codeception) sending the file. I used an ngrok tunnel to verify this.
The Symfony profiler said it received the file.

So it somewhere in the application receiving the request and using the file from the previous request.

Im currently debugging, but if anybody knows something, im all ears! This issue started since i stopped running my migrations after each test. bin/console doctrine:migrations:migrate -n

Dumper Helper

Hey,
the roadrunner-laravel integration has released a useful utility: the Dumper.
That makes dump and dd available through rr\dump and rr\dd.

Is this something you have on the roadmap to do?

I already did some basic integration into one of our projects and could provide a PR if you wanted to. May take a few days though because I'm swamped a bit with work

Shared cache between workers

Hi everyone,

i have a question about Roadrunner and symfony and its behaviour regarding caching:

We are currently using symfony with two caching adapters in this order: array,redis

We noticed that this causes some problems and we get out-dated values from the cache. Are we right with our assumption that this is caused by none shared caches on the array level, so that all workers accessing their own representation and values?

If this is the case:

  • Can APCu solve this problem?
  • Are there other ways to solve this problem? What are you doing to solve this?

Thanks in advance!

Selcuk

goridge_frame_receive: CRC verification failed, bad header: RuntimeExcep

Hello,

I have an issue while running this package

[INFO] RoadRunner server started; version: 2.9.1, buildtime: 2022-04-11T10:26:46+0000
2022-04-14T00:53:10.206Z ERROR container/poller.go:16 vertex got an error {"id": "http.Plugin", "error": "static_pool_allocate_workers: WorkerAllocate:\n\tgoridge_frame_receive: CRC verification failed, bad header: \n\n \n <meta "}
github.com/roadrunner-server/endure/pkg/container.(*Endure).poll.func1
github.com/roadrunner-server/[email protected]/pkg/container/poller.go:16
error occurred: static_pool_allocate_workers: WorkerAllocate:
goridge_frame_receive: CRC verification failed, bad header:

I don't know what is the problem or what is missing.

Thanks

Symfony 4.4.x ArrayNode and BaseNode E_USER_DEPRECATED notice

Can the following deprecation notice be resolved?

  • Symfony: 4.4.18
  • PHP: 7.3.26
baldinof/roadrunner-bundle

[▼
  "exception" => Symfony\Component\ErrorHandler\Exception\SilencedErrorContext {#455 ▼
    +count: 2
    -severity: E_USER_DEPRECATED
    trace: {▼
      /data/smart/vendor/symfony/config/Definition/ArrayNode.php:238 {▼
        Symfony\Component\Config\Definition\ArrayNode->finalizeValue($value) …
        › if ($child->isDeprecated()) {
        ›     @trigger_error($child->getDeprecationMessage($name, $this->getPath()), \E_USER_DEPRECATED);
        › }
      }
      /data/smart/vendor/symfony/config/Definition/BaseNode.php:427 {▼
        › 
        › $value = $this->finalizeValue($value);
        › 
      }
    }
  }
]

Question: Blackfire

Hi!

We just recently started using blackfire.

The documentation says that the bundle supports blackfire, but how do i use it in an api application?

  • I cannot use the blackfire cli.
  • I installed the blackfire sdk
  • tried this, but it looks like register_shutdown_function is never called.
// let's create a client
$blackfire = new \Blackfire\Client();
// then start the probe
$probe = $blackfire->createProbe();

// When runtime shuts down, let's finish the profiling session
register_shutdown_function(function () use ($blackfire, $probe) {
    // See the PHP SDK documentation for using the $profile object
    $profile = $blackfire->endProbe($probe);
    dd('stopped');
});

Any help would be much appreciated!

How to force https?

In my local dev environment I use nginx-proxy. It allows running local virtual hosts with https support. And this bundle all works fine, except one thing: Symfony doesn't see connection protocol as https. If I manually force the following in HttpFoundationWorker:

$server['HTTPS'] = 'on';
$server['SERVER_PORT'] = 443;

it starts working, but how can I do that in a proper manner? Otherwise all the generated links have http as a scheme.

P.S. I also debugged deeper and discovered that this happens because $request->uri contains http instead of https. But how to tell it to use https?

An exception has been thrown during the rendering of a template ("Untrusted Host "localhost".").

Not sure if this is related to RoadRunner, but I don't get this issue when running as symfony serve.

Symfony: 4.4.24
PHP: 7.4.19

When trying to view the Symfony Profiler from the web debug toolbar, I get the following error;

An exception has been thrown during the rendering of a template ("Untrusted Host "localhost".").

I have the following set in .env

TRUSTED_PROXIES=127.0.0.1,localhost
TRUSTED_HOSTS='^localhost|127.0.0.1|rcdo\.co\.uk|ricardo-aea\.com$'

Stack Trace

Twig\Error\RuntimeError:
An exception has been thrown during the rendering of a template ("Untrusted Host "localhost".").

  at vendor/symfony/web-profiler-bundle/Resources/views/Profiler/layout.html.twig:111
  at Twig\Template->displayBlock()
     (var/cache/dev/twig/bf/bf12d20c267291994bfc0d5a4b846de275078686a9fccb5f5b0fd626ae1d0c88.php:76)
  at __TwigTemplate_9102b89c893e179c91ea85832b18f46998eea5494cd633f70b4cdd3e29ce6a17->doDisplay()
     (vendor/twig/twig/src/Template.php:405)
  at Twig\Template->displayWithErrorHandling()
     (vendor/twig/twig/src/Template.php:378)
  at Twig\Template->display()
     (var/cache/dev/twig/10/104477248445284e8d32cc7eefe233df929fb6d50050bcb9876c2e0a9f72a4bd.php:50)
  at __TwigTemplate_05509a347546813a6b94f12d615ba9543a7d7d8691d677c374ac16aac863a105->doDisplay()
     (vendor/twig/twig/src/Template.php:405)
  at Twig\Template->displayWithErrorHandling()
     (vendor/twig/twig/src/Template.php:378)
  at Twig\Template->display()
     (var/cache/dev/twig/6f/6f02f7ba6d86e2b9ca1bcb4ede44b100df6ea6f17e6e55fe4e11fb4bb62b8f54.php:50)
  at __TwigTemplate_829a4f5e0b60e96f12de1e08e8917f34f9c166d6e6a46d4986b0ad51984afdf2->doDisplay()
     (vendor/twig/twig/src/Template.php:405)
  at Twig\Template->displayWithErrorHandling()
     (vendor/twig/twig/src/Template.php:378)
  at Twig\Template->display()
     (vendor/twig/twig/src/Template.php:390)
  at Twig\Template->render()
     (vendor/twig/twig/src/TemplateWrapper.php:45)
  at Twig\TemplateWrapper->render()
     (vendor/twig/twig/src/Environment.php:318)
  at Twig\Environment->render()
     (vendor/symfony/web-profiler-bundle/Controller/ProfilerController.php:123)
  at Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController->panelAction()
     (vendor/symfony/http-kernel/HttpKernel.php:158)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw()
     (vendor/symfony/http-kernel/HttpKernel.php:80)
  at Symfony\Component\HttpKernel\HttpKernel->handle()
     (vendor/symfony/http-kernel/Kernel.php:201)
  at Symfony\Component\HttpKernel\Kernel->handle()
     (vendor/baldinof/roadrunner-bundle/src/Http/KernelHandler.php:45)
  at Baldinof\RoadRunnerBundle\Http\KernelHandler->handle()
  at Generator->current()
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:94)
  at Baldinof\RoadRunnerBundle\Http\Runner->getResponse()
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:72)
  at Baldinof\RoadRunnerBundle\Http\Runner->handle()
     (vendor/baldinof/roadrunner-bundle/src/Integration/PHP/NativeSessionMiddleware.php:34)
  at Baldinof\RoadRunnerBundle\Integration\PHP\NativeSessionMiddleware->process()
  at Generator->current()
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:94)
  at Baldinof\RoadRunnerBundle\Http\Runner->getResponse()
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:80)
  at Baldinof\RoadRunnerBundle\Http\Runner->handle()
     (vendor/baldinof/roadrunner-bundle/src/Integration/Doctrine/DoctrineORMMiddleware.php:59)
  at Baldinof\RoadRunnerBundle\Integration\Doctrine\DoctrineORMMiddleware->process()
  at Generator->current()
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:94)
  at Baldinof\RoadRunnerBundle\Http\Runner->getResponse()
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:80)
  at Baldinof\RoadRunnerBundle\Http\Runner->handle()
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:34)
  at Baldinof\RoadRunnerBundle\Http\MiddlewareStack->handle()
  at Generator->current()
     (vendor/baldinof/roadrunner-bundle/src/Worker/Worker.php:61)
  at Baldinof\RoadRunnerBundle\Worker\Worker->start()
     (vendor/baldinof/roadrunner-bundle/src/Command/WorkerCommand.php:68)
  at Baldinof\RoadRunnerBundle\Command\WorkerCommand->execute()
     (vendor/symfony/console/Command/Command.php:255)
  at Symfony\Component\Console\Command\Command->run()
     (vendor/symfony/console/Application.php:1027)
  at Symfony\Component\Console\Application->doRunCommand()
     (vendor/symfony/framework-bundle/Console/Application.php:97)
  at Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand()
     (vendor/symfony/console/Application.php:273)
  at Symfony\Component\Console\Application->doRun()
     (vendor/symfony/framework-bundle/Console/Application.php:83)
  at Symfony\Bundle\FrameworkBundle\Console\Application->doRun()
     (vendor/symfony/console/Application.php:149)
  at Symfony\Component\Console\Application->run()
     (bin/console:42)           

Runtime Error: in vendor/symfony/web-profiler-bundle/Resources/views/Profiler/layout.html.twig (line 111)


                        <a class="sf-toggle btn btn-sm" data-toggle-selector="#sidebar-search" {% if tokens is defined or about is defined %}data-toggle-initial="display"{% endif %}>                            {{ include('@WebProfiler/Icon/search.svg') }} <span class="hidden-small">Search</span>                        </a>                        {{ render(path('_profiler_search_bar', request.query.all)) }}                    </div>                </div>                {% if templates is defined %}                    <ul id="menu-profiler">

vertex got an error

Sometimes we have error, and api response is 502, what can we do?

  • php 8.0
  • symfony 5.2.12

Error log:

web    | github.com/spiral/endure/pkg/container.(*Endure).poll.func1
web    |        github.com/spiral/[email protected]/pkg/container/poller.go:16
web    | error occurred: http_plugin_serve: WorkerAllocate:
web    |        server_plugin_new_worker_pool:
web    |        static_pool_initialize:
web    |        allocate workers: context deadline exceeded, plugin: http.Plugin
web    | handle_serve_command: http_plugin_serve: WorkerAllocate:
web    |        server_plugin_new_worker_pool:
web    |        static_pool_initialize:
web    |        allocate workers: context deadline exceeded; fsm_recognizer: can't transition from state: Stopped by event Stop

.rr.yaml

rpc:
  listen: tcp://127.0.0.1:6001

server:
  command: "php -dopcache.enable_cli=1 bin/console baldinof:roadrunner:worker"
  user: "www-data"
  group: "www-data"
  env:
    - APP_RUNTIME: Baldinof\RoadRunnerBundle\Runtime\Runtime
  relay: "tcp://localhost:7000"
  relay_timeout: 60s

logs:
  mode: production
  level: error
  encoding: json
  output: stderr
  err_output: stderr

http:
  address: 0.0.0.0:80
  max_request_size: 10
  middleware: [ "headers", "static", "gzip" ]
  trusted_subnets: [
      "10.0.0.0/8",
      "127.0.0.0/8",
      "172.16.0.0/12",
      "192.168.0.0/16",
      "::1/128",
      "fc00::/7",
      "fe80::/10",
  ]

  uploads:
    dir: "/tmp"
    forbid: [ ".php", ".exe", ".bat", ".sh" ]

  static:
    dir: "public"
    forbid: []
  pool:
    num_workers: 0
    max_jobs: 20
    allocate_timeout: 60s
    destroy_timeout: 60s
    supervisor:
      watch_tick: 1s
      ttl: 60s
      idle_ttl: 10s
      max_worker_memory: 256
      exec_ttl: 60s

  http2:
    h2c: false
    max_concurrent_streams: 15

endure:
  grace_period: 30s
  print_graph: false
  log_level: error

config/packages/baldinof_road_runner.yaml

baldinof_road_runner:
    kernel_reboot:
        strategy: on_exception
        allowed_exceptions:
            - Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
            - Symfony\Component\Serializer\Exception\ExceptionInterface
            - Symfony\Contracts\HttpClient\Exception\ExceptionInterface

    metrics:
        enabled: false

Dockerfile

FROM core.harbor.k8s.devim.team/proxy/library/php:8-cli

ENV COMPOSER_ALLOW_SUPERUSER=1
ENV COMPOSER_CACHE_DIR=/var/www/.cache
ENV SUDO_FORCE_REMOVE=yes
WORKDIR /var/www/web
EXPOSE 80
ENTRYPOINT ["/entrypoint.sh"]

RUN usermod -u 1000 www-data \
    && groupmod -g 1000 www-data \
    && addgroup nobody www-data \
    && chown -R www-data:www-data /var/www

RUN apt-get update && apt-get install -y \
    sudo \
    curl \
    git \
    zip \
    wget \
    libzip-dev \
    libgmp-dev \
    libffi-dev \
    libssl-dev \
    gnupg2 \
    librabbitmq-dev \
    libicu-dev \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

RUN docker-php-ext-install -j$(nproc) \
    sockets \
    zip \
    gmp \
    pcntl \
    bcmath \
    ffi \
    intl \
    opcache \
    pdo \
    pdo_pgsql \
    pgsql

RUN docker-php-source extract \
    && mkdir /usr/src/php/ext/amqp \
    && curl -L https://github.com/php-amqp/php-amqp/archive/master.tar.gz | tar -xzC /usr/src/php/ext/amqp --strip-components=1 \
    && docker-php-ext-install amqp \
    && docker-php-ext-enable amqp

RUN pecl install \
    redis \
    && docker-php-ext-enable \
    redis

ADD docker/crypto/linux-amd64_deb.tgz /tmp
RUN bash /tmp/linux-amd64_deb/install.sh && rm -r /tmp/linux-amd64_deb

RUN curl -sS https://getcomposer.org/installer | php -- --version="2.0.8" --install-dir=/usr/local/bin --filename=composer
COPY --chown=www-data:www-data composer.* ./
RUN sudo -u www-data composer install --optimize-autoloader --no-interaction

COPY --chown=www-data:www-data . /var/www/web

RUN set -eux; \
	sudo -u www-data mkdir -p var/cache var/log; \
	sudo -u www-data composer dump-autoload --no-dev --classmap-authoritative; \
	sudo -u www-data composer dump-env prod --no-cache --profile; \
	sudo -u www-data php bin/console cache:clear

COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh; \
    apt-get remove sudo -y

RUN ./vendor/bin/rr get-binary --location /usr/local/bin

COPY docker/prod/php-cli/php.ini /usr/local/etc/php/php.ini

Missing dependency nyholm/psr7

Hi,

first of all: Thank you very much for your roadrunner bundle! Unfortunately, I discovered a potential bug.

How to reproduce

Require your library:

composer require \
        --update-no-dev \
        baldinof/roadrunner-bundle

Afterwards, install and configure roadrunner. Then start roadrunner.

Actual behavior

With current version 1.3.0 roadrunner is not able to start. Excerpt from the logs:

Error: [http]: unable to connect to worker: [2020-09-04T09:48:46.995637+02:00] console.ERROR: Error thrown while running command "'baldinof:roadrunner:worker'". Message: "Class 'Nyholm\Psr7\Factory\Psr17Factory' not found" {"exception":"[object] (Error(code: 0): Class 'Nyholm\\Psr7\\Factory\\Psr17Factory' not found at /path/to/app/var/cache/prod/app/ContainerFQvxROM/getWorkerCommandService.php:32)","command":"'baldinof:roadrunner:worker'","message":"Class 'Nyholm\\Psr7\\Factory\\Psr17Factory' not found"} []

Expected behavior

Since composer package nyholm/psr7 is only suggested but not required, roadrunner should start without any errors.

The previous version I've used (1.2.2) seems unaffected.

Workaround

Install the missing dependency right after installing your library:

composer require \
        --update-no-dev \
        nyholm/psr7

Fatal error upon start: incompatible versions passed: from: 2.6.0, to: 2.8.2

I get the following error when starting roadrunner:

handle_serve_command: Init error:
	endure_initialize:
	endure_internal_init: Function call error:
	endure_call_init_fn:
	config_plugin_init: incompatible versions passed: from: 2.6.0, to: 2.8.2

Perhaps not directly an issue with this bundle, however wanted to submit this as maybe others have experienced this as well.

Steps to reproduce:

  1. Start a new symfony project: symfony new <name>
  2. Install this bundle: composer require baldinof/roadrunner-bundle
  3. Download RoadRunner locally: vendor/bin/rr get --location bin/
  4. Run the application: bin/rr serve -c .rr.dev.yaml --debug

PHP Version: PHP 8.1.3 (cli) (built: Feb 16 2022 13:27:56) (NTS)
OS: Linux 5.16.12-arch1-1 #1 SMP PREEMPT Wed, 02 Mar 2022 12:22:51 +0000 x86_64 GNU/Linux

[FEATURE REQUEST] gc_collect_cycles()

Hello there!

Since we have long-lived queries, I suggest adding an additional configuration of collect_cycles_every. Its value will indicate how many times in how many queries the function gc_collect_cycles() should be called for the current worker.

That is, if we specify a value of 10, then this function will run every 10 requests.

I think you'll support me.

Working example with gRPC?

Hey,

I'm having some issues getting gRPC to work. I keep running into the error below. I got the example from RoadRunner to work just fine, I added in Symfony 6 and your bundle, which also run but as soon as I add the gRPC settings to the yaml configuration I keep getting this error. If it rings any bells I'd like to hear but otherwise a working example would be fine also, thanks.

roadrunner_1  |         endure_start:
roadrunner_1  |         endure_serve_internal: Function call error:
roadrunner_1  |         endure_call_serve_fn: got initial serve error from the Vertex grpc.Plugin, stopping execution, error:
 grpc_plugin_serve: WorkerAllocate:
roadrunner_1  |         static_pool_allocate_workers:
roadrunner_1  |         goridge_frame_receive: CRC verification failed, bad header: 
roadrunner_1  | RoadRunner Bundle
roadrunner_1  | =================
roadrunner_1  | 
roadrunner_1  |  [```

Bundle not load correct .env file

Hi,
thanks a lot for this beautiful package, but I have an annoying issue: even if I run rr in dev, with .rr.dev.yaml, setting APP_ENV=dev or any other possible option, symfony loads only .env file ignoring any other .env.* file (.env.local, .env.dev.local, .env.prod, etc...). I've also tried with composer dump-env dev but nothing.. it reads ONLY .env file.

Can you help me please?
Thanks a lot.

Dockerfile in README produces errors

In the main README file there is an example Dockerfile to use, but it produces the following error on build.

RUN vendor/bin/rr get-binary --location /usr/local/bin:
#17 0.201 You need to set up the project dependencies using Composer:
#17 0.201
#17 0.201     composer install
#17 0.201
#17 0.201 You can learn all about Composer on https://getcomposer.org/.

This issue is using --no-autoloader in the composer install line.

RUN composer install --no-dev --no-autoloader --no-scripts --no-plugins --prefer-dist --no-progress --no-interaction

RUN ./vendor/bin/rr get-binary --location /usr/local/bin

Removing it allows the build to work.

Too few arguments to function Baldinof\RoadRunnerBundle\Worker\Dependencies::__construct()

After updating to version 1.5.0 start receiving following exception

Too few arguments to function Baldinof\RoadRunnerBundle\Worker\Dependencies::__construct(), 2 passed in /var/www/var/cache/prod/ContainerXu7JtGX/getDependenciesService.php on line 20 and exactly 3 expected

All because in config file its still configuration for 2 arguments

    $services->set(Dependencies::class)
        ->public() // Manually retrieved on the DIC in the Worker if the kernel has been rebooted
        ->args([
            service(MiddlewareStack::class),
            service(KernelRebootStrategyInterface::class),
        ]);

Temporal.io support

Hi there,

roadrunner 2+ have native support of Temporal.io, do you have any plans to implement support of this?
If not - will you accept this feature as PR to this bundle?

Metrics initializes four times

Hi everyone. Is it a bug, or expected behaviour?
The roadrunner initialize kernel four times -> metrics also initializes four times -> as a result system throws errors:

weather    | 2021/12/15 05:38:42	INFO	metrics     	declaring new metric	{"name": "metno_provider_req_duration", "type": "histogram", "namespace": ""}
weather    | 2021/12/15 05:38:42	INFO	metrics     	metric successfully added	{"name": "metno_provider_req_duration", "type": "histogram", "namespace": ""}
weather    | 2021/12/15 05:38:42	INFO	metrics     	declaring new metric	{"name": "metno_provider_req_duration", "type": "histogram", "namespace": ""}
weather    | 2021/12/15 05:38:42	ERROR	metrics     	metric with provided name already exist	{"name": "metno_provider_req_duration", "type": "histogram", "namespace": ""}
weather    | 2021/12/15 05:38:42	INFO	metrics     	declaring new metric	{"name": "metno_provider_req_duration", "type": "histogram", "namespace": ""}
weather    | 2021/12/15 05:38:42	ERROR	metrics     	metric with provided name already exist	{"name": "metno_provider_req_duration", "type": "histogram", "namespace": ""}
weather    | 2021/12/15 05:38:42	INFO	metrics     	declaring new metric	{"name": "metno_provider_req_duration", "type": "histogram", "namespace": ""}
weather    | 2021/12/15 05:38:42	ERROR	metrics     	metric with provided name already exist	{"name": "metno_provider_req_duration", "type": "histogram", "namespace": ""}

How to avoid those errors? Or somehow fix them?

  • symfony: 5.3
  • baldinof/roadrunner-bundle: 2.1.3
  • roadrunner: 2.4.2

Get binary command don't worked

 - spiral/roadrunner-binary (v2.6.0): Downloading...
  [============================] 100% (1,520.54Kb/1,520.54Kb)
 Do you want create default ".rr.yaml" configuration file? (yes/no) [yes]:
 > 

 [WARNING] RoadRunner has not been installed                                    

The command '/bin/sh -c ./vendor/bin/rr get-binary  --verbose --location /app/;' returned a non-zero code: 1

Composer.lock:

    "packages": [
        {
            "name": "baldinof/roadrunner-bundle",
            "version": "2.1.1",
            "source": {
                "type": "git",
                "url": "https://github.com/Baldinof/roadrunner-bundle.git"

Running RoadRunner in ECS behind an ALB

Hey,

startup works fine, but i see the app somehow assumes the current domain is the internal ECS Container IP. How can we fix this?

So basically its a FORWARDED issue i guess.

My config:

version: "2.7"

server:
  env:
    - APP_RUNTIME: Baldinof\RoadRunnerBundle\Runtime\Runtime

  command: "php bin/console baldinof:roadrunner:worker"

http:
  address: "0.0.0.0:8000"
  middleware: [ "static", "gzip" ]
  #pool:
  #  num_workers: 8
  static:
    dir: "public"
    forbid: [ ".php", ".htaccess" ]

logs:
  level: warn

it works fine locally but not behind an LB

in our Symfony Front Controller we had to do this

$trustedProxies = $_ENV['TRUSTED_PROXIES'];
if ($trustedProxies) {
    Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_FORWARDED | Request::HEADER_X_FORWARDED_AWS_ELB);
}

using public/index.php somehow didnt work (the symfony/runtime is installed)

kernel_reboot: strategy: always does not work

Hi,

Having such configuration in baldinof_road_runner.yaml (2.1.0)

baldinof_road_runner:
    kernel_reboot:
        strategy: always

    # Integrations are automatically detected, depending on installed bundle & current configuration
    # See https://github.com/baldinof/roadrunner-bundle#integrations
    default_integrations: true

    # Allow to send prometheus metrics to the master RoadRunner process,
    # via a `Spiral\RoadRunner\MetricsInterface` service.
    # See https://github.com/baldinof/roadrunner-bundle#metrics
    metrics:
        enabled: false

I'm encountering situations where response seems to be mixed with previous responses - how can I confirm that kernel is rebooting and I have a problem somewhere else?

Support Symfony 5.4

As i update my project from 5.3 to 5.4 i obtain 3 deprecated on this repository.

  • Method "Symfony\Component\Console\Command\Command::execute()" might add "int" as a native return type declaration in the future. Do the same in child class "Baldinof\RoadRunnerBundle\Command\WorkerCommand" now to avoid errors or add an explicit @return annotation to suppress this message.
  • Method "Symfony\Component\EventDispatcher\EventSubscriberInterface::getSubscribedEvents()" might add "array" as a native return type declaration in the future. Do the same in implementation "Baldinof\RoadRunnerBundle\Reboot\OnExceptionRebootStrategy" now to avoid errors or add an explicit @return annotation to suppress this message.
  • Method "Symfony\Component\EventDispatcher\EventSubscriberInterface::getSubscribedEvents()" might add "array" as a native return type declaration in the future. Do the same in implementation "Baldinof\RoadRunnerBundle\Integration\Sentry\SentryListener" now to avoid errors or add an explicit @return annotation to suppress this message.

Could you please fix them?

Update symfony/psr-http-message-bridge dependency

Hi Baldinof, I ran into a dependency conflict while trying to use your bundle from its dependency on v1.2 of symfony/psr-http-message-bridge. I looked at the changelog for that package and this one's source and I didn't see anything that would break (although of course I might be wrong about that). Are you working on updating that dependency, or is it OK if I submit a PR to do so?

Thanks,
Vidur

Problem with StreamedResponse

Hi,

I found that StreamedResponse not work now, example code for reproduce problem:

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Annotation\Route;

class TestController extends AbstractController
{
    /**
     * @Route("/test")
     */
    public function test()
    {
        $response = new StreamedResponse(null, 200, ['Content-Type' => 'text/plain']);
        $response->setCallback(function () {
            var_dump('Hello World');
            flush();

            var_dump('Hello World');
            flush();
        });

        return $response;
    }
}

DoctrineBundle doesn't infer use of ORM

Use of DoctrineBundle doesn't mean an ORM and any ManagerRegistry is available and yet you assume it is here https://github.com/Baldinof/roadrunner-bundle/blob/2.x/src/DependencyInjection/BaldinofRoadRunnerExtension.php#L131-L142

At a container compile time an error like this is thrown:

In CheckExceptionOnInvalidReferenceBehaviorPass.php line 86:
                                                                                                                                                                         
  The service "Baldinof\RoadRunnerBundle\Integration\Doctrine\DoctrineORMMiddleware" has a dependency on a non-existent service "Doctrine\Persistence\ManagerRegistry".  
 

Whats a point of bundle?

Sorry for this so abstract question, but what purpose of bundle - maybe it's a lack of documentation? (index.php or worker.php examples could make it more clear)
I'm trying to get RR into symfony 6 and found this, but not really sure what problem it solves.
It's not quite merge with RR official doc for 2.8 version.

I'll be really glad for guide me to any examples of usage this bundle.

(Sorry for issue, it should be a discussion, but this is only one i can create)

Worker not creating unix socket

I have the bundle installed in a Symfony 5 based app and the command is showing up in the console as expected

$ php apps/rrtest/bin/console.php list
Symfony 5.1.5 (env: local, debug: true)

...snip...
 baldinof
  baldinof:roadrunner:worker    Run the roadrunner worker
...snip...

but running RoadRunner fails because the socket has not been created.

$ rr serve -d -v -c .rr.yaml
WARNING: Native build is an experimental feature and could change at any time
Creating busuu-dev_courses-service_run ... done
DEBU[0000] [metrics]: disabled
DEBU[0000] [headers]: disabled
DEBU[0000] [static]: disabled
DEBU[0000] [health]: disabled
DEBU[0000] [reload]: disabled
DEBU[0000] [rpc]: disabled
DEBU[0000] [http]: started
ERRO[0000] [http]: [http]: listen unix var/roadrunner.sock: bind: no such file or directory
DEBU[0000] [rpc]: stopped
Error: [http]: listen unix var/roadrunner.sock: bind: no such file or directory

My .rr.yaml is very basic

rpc:
  enabled: false
http:
  address: "0.0.0.0:8080"
  workers:
    command: php/apps/rrtest/bin/console.php baldinof:roadrunner:worker
    relay: unix://var/roadrunner.sock

Problem with http auth

Hi,

thank you for this great works. I found some strange behavior, with enabled http_auth provider.
It's doesn't work with roadrunner, steps to reproduce:

  1. create new symfony project composer create-project symfony/website-skeleton test
  2. add baldinof/roadrunner-bundle
  3. make file config/packages/security.yaml, that it looks like this:
security:
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext

    providers:
        users_in_memory:
            memory:
                users:
                    user:  { password: 'test', roles: [ 'ROLE_ADMIN' ] }

    firewalls:
        main:
            anonymous: lazy
            provider: users_in_memory
            http_basic:
                realm: Secured Area

    access_control:
         - { path: ^/, roles: ROLE_ADMIN }

  1. try open / route, with login user and password test, and authorization not work

After few days it does not recreate workers

bin/rr -c .rr.yaml http:workers
+---------+-----------+---------+---------+--------------------+
| PID | STATUS | EXECS | MEMORY | CREATED |
+---------+-----------+---------+---------+--------------------+
+---------+-----------+---------+---------+--------------------+

Supervisord config

[program:roadrunner]
command=/usr/bin/env NUM_WORKERS=30 RPC_ADDRESS=tcp://127.0.0.1:9080 LISTEN_ADDRESS=127.0.0.1:8080 /var/www/application/bin/rr serve -c .rr.yaml
process_name=%(program_name)s_%(process_num)02d
numprocs=1
autostart=true
autorestart=true
loglevel=debug
startsecs=4
startretries=999999
user=www-data
redirect_stderr=true
directory=/var/www/application/
stopwaitsecs=4

If i kill the process which states bin/rr -c .rr.yaml serve supervisord correctly restarts serve process which in turn recreates all http workers.

My environment

RoadRunner, PHP Application Server
Version: 1.9.2, 2021-01-14T18:50:40+0000

$ php --version
PHP 7.3.19-1deb10u1 (cli) (built: Jul 5 2020 06:46:45) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.19, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.19-1
deb10u1, Copyright (c) 1999-2018, by Zend Technologies

composer.json excerpt

"baldinof/roadrunner-bundle": "^1.5"
"symfony/framework-bundle": "4.4.*",

.rr.yaml

rpc:
  enable: true
  listen: ${RPC_ADDRESS}
http:
  address: ${LISTEN_ADDRESS}
  appErrorCode: 500
  internalErrorCode: 500
  http2:
    enabled: true
    h2c: true
    maxConcurrentStreams: 128
  maxRequestSize: 16
  uploads:
    forbid: [ ".php", ".exe", ".bat" ]
  trustedSubnets: [ "10.0.0.0/8", "127.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "::1/128", "fc00::/7", "fe80::/10" ]
  workers:
    command: "php bin/console baldinof:roadrunner:worker"
    relay: "unix://var/roadrunner.sock"
    pool:
      numWorkers: ${NUM_WORKERS}
      maxJobs: 500
      allocateTimeout: 60
      destroyTimeout: 60

static:
  dir:   "public"
  forbid: [".php", ".htaccess"]

limit:
  interval: 1
  services:
    http:
      maxMemory: 64
      TTL: 600
      idleTTL: 60
      execTTL: 60

How can i debug why serve does not recreate workers? It works after full restart (killing serve process which is recreated by supervisord) for a few days and after that I have to restart it to work as expected.

Since symfony/dependency-injection 5.1: The "Symfony\Component\DependencyInjection\ContainerInterface" autowiring alias is deprecated.

Found the following deprecation.

PHP Version: PHP 7.4.15 (cli) (built: Feb 8 2021 20:31:57) ( NTS )
Symfony Version: 5.2.3

Since symfony/dependency-injection 5.1: The "Symfony\Component\DependencyInjection\ContainerInterface" autowiring alias is deprecated. Define it explicitly in your app if you want to keep using it. It is being referenced by the "Baldinof\RoadRunnerBundle\Http\Middleware\DoctrineMiddleware" service.

[▼
  "exception" => Symfony\Component\ErrorHandler\Exception\SilencedErrorContext {#3920 ▶}
]
{▼
  /app/smart/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php:68 {▼
    Symfony\Component\DependencyInjection\Compiler\ResolveReferencesToAliasesPass->getDefinitionId(string $id, ContainerBuilder $container): string …
    ›     $deprecation = $alias->getDeprecation($id);
    ›     trigger_deprecation($deprecation['package'], $deprecation['version'], rtrim($deprecation['message'], '. ').'. It is being referenced by the "%s" '.($container->hasDefinition($this->currentId) ? 'service.' : 'alias.'), $this->currentId);
    › }
  }
  /app/smart/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php:51 {▼
    › 
    › $defId = $this->getDefinitionId($id = (string) $value, $this->container);
    › 
  }
}

Move vendor/bin/rr get --location bin/ into symfony recipe

Hi, thanks for creating this bundle!

I wonder if we could move this step:
vendor/bin/rr get --location bin/

into symfony flex recipe?

Or is there any reason not to do that?

My use case is that I run composer install on build environment and it would be nice if I don't need to do more manual steps.
Otherwise I will need to create some script to check if the rr binary exists, and execute this step if not.

Reboot kernel & Multi tenancy

Hi,
We are trying to use roadrunner on a multi tenant symfony project.
Multi-tenancy is handled by the kernel, we inject database url dynamically depending on the request. This works fine with the PHP dev server however we cannot get it to work with roadrunner (missing database url).
I guess this comes from the fact that the kernel is not always rebooted. So we tried to set the strategy to always but this didn't have any effect. Furthermore we tried to add $this->kernel->reboot(null); at the beginning of the while loop in the Worker.php to make sure the container is rebuild but didn't seem to solve our problem either.
Does anybody have any ideas on how to handle this ?
Thanks in advance !

Deploying an AMQP worker

My apologies if this is explained somewhere or if this is not the correct place to address this, but I recently started migrating my service to Roadrunner and I read through both Roadrunner and Roadrunner bundle instructions, but could not find an answer.

I've managed to get my API service running on Roadrunner using this bundle, but how can I run a custom command as a worker, in this case a Messanger consumer command like I have done with Supervisord in the past?

php bin/console messenger:consume 

Potential Session Collision with Symfony Guard

Hi,

I'm not sure if this is a problem with our particular implementation of Symfony Guard or whether the issue is somewhere else.

However, today, we put a new web app we developed to functional / UAT testing. What came back is that users were seeing other users login credentials (email in this case) in the login form whether they would have no knowledge of the email address. We also have one other user who logged in as themselves and ended-up seeing someone else's login.

Is there a known issue using Symfony Guard or would the following set-up/code causes this to happen?

Symfony 5.2.5

security.yaml

security:
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

    encoders:
        legacy:
            id: 'App\Security\Encoder\PasswordEncoder'

        App\Entity\User:
            algorithm: auto
            migrate_from:
                - legacy

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            switch_user: true
            anonymous: true
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator

                # redirect anonymous users to the login page
                entry_point: App\Security\LoginFormAuthenticator
            logout:
                path: app_logout
                target: app_login
            remember_me:
                secret:   '%kernel.secret%'
                lifetime: 604800 # 1 week in seconds
                path:     /
                samesite: strict

Symfony Guard Authenticator

<?php

declare(strict_types=1);

namespace App\Security;

use App\Entity\User;
use App\Repository\UserRepository;
use App\Service\AuditLogService;
use App\Service\MailerService;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use ReCaptcha\ReCaptcha;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mime\Address;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

final class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
    use TargetPathTrait;

    public const LOGIN_ROUTE = 'app_login';

    private EntityManagerInterface $entityManager;
    private UrlGeneratorInterface $urlGenerator;
    private CsrfTokenManagerInterface $csrfTokenManager;
    private UserPasswordEncoderInterface $passwordEncoder;
    private MailerService $mailerService;
    private AuditLogService $auditLogService;
    private LoggerInterface $logger;
    private string $recaptchaSecret;
    private float $recaptchaScore;
    private string $appEnv;

    public function __construct(
        EntityManagerInterface $entityManager,
        UrlGeneratorInterface $urlGenerator,
        CsrfTokenManagerInterface $csrfTokenManager,
        UserPasswordEncoderInterface $passwordEncoder,
        MailerService $mailerService,
        AuditLogService $auditLogService,
        LoggerInterface $logger,
        string $recaptchaSecret,
        float $recaptchaScore,
        string $appEnv
    ) {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
        $this->mailerService = $mailerService;
        $this->auditLogService = $auditLogService;
        $this->logger = $logger;
        $this->recaptchaSecret = $recaptchaSecret;
        $this->recaptchaScore = $recaptchaScore;
        $this->appEnv = $appEnv;
    }

    // Ensures that the login form submission comes from a supported route and is a POST request.
    public function supports(Request $request): bool
    {
        return (self::LOGIN_ROUTE === $request->attributes->get('_route')) && $request->isMethod('POST');
    }

    // Retrieves credentials from the request. Sets the last used username and email on the session.
    public function getCredentials(Request $request): array
    {
        $data = $request->request->get('user_login_form');

        $this->logger->info('Client login attempt.', ['email' => $data['email'], 'client_ip' => $request->getClientIp()]);

        $credentials = [
            'email' => $data['email'],
            'password' => $data['password'],
            'csrf_token' => $data['_token'] ?? '',
            'captcha' => $data['captcha'] ?? '',
            'client_ip' => $request->getClientIp(),
        ];
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials['email']
        );

        return $credentials;
    }

    /**
     * @param mixed $credentials
     *
     * @return UserInterface|mixed|null
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $captcha = new ReCaptcha($this->recaptchaSecret);
        if ($response = $captcha->verify($credentials['captcha'], $credentials['client_ip'])) {
            if ($response->getScore() < $this->recaptchaScore) {
                $this->logger->info('Invalid recaptcha.', ['email' => $credentials['email']]);

                throw new CustomUserMessageAuthenticationException('There were problems with your captcha. Please try again.');
            }
        } else {
            $this->logger->info('Invalid recaptcha.', ['email' => $credentials['email'], 'client_ip' => $credentials['client_ip']]);

            throw new CustomUserMessageAuthenticationException('There were problems with your captcha. Please try again.');
        }

        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            $this->logger->info('CSRF token is invalid.', ['email' => $credentials['email'], 'client_ip' => $credentials['client_ip']]);

            throw new InvalidCsrfTokenException('CSRF token is invalid.');
        }

        if (!$user = $this->entityManager->getRepository(User::class)->findOneByEmail($credentials['email'])) {
            // fail authentication with a custom error
            /* @var User $user */
            $this->logger->info('Login unsuccessful as email address does not exist.', ['email' => $credentials['email'], 'client_ip' => $credentials['client_ip']]);

            throw new CustomUserMessageAuthenticationException('Login unsuccessful.');
        }

        //check if user is archived and throw error
        if (!$user->getActive()) {
            // fail authentication with a custom error
            $this->logger->info('Login unsuccessful as account is in active.', ['email' => $credentials['email'], 'client_ip' => $credentials['client_ip']]);

            throw new CustomUserMessageAuthenticationException('Login unsuccessful.');
        }

        if ($user->getLocked()) {
            if (null !== $user->getLockTime()) {
                $unlockAccount = ((new DateTime())->getTimestamp() - $user->getLockTime()->getTimestamp()) / 60;
                if ($unlockAccount >= 15) {
                    $user->setLocked(false);
                    $user->setLockTime(null);
                    $user->setLoginAttempt(0);
                    $this->entityManager->flush();

                    return $user;
                }

                $this->logger->info('Login unsuccessful as account is currently locked due to failed attempt.', ['email' => $credentials['email'], 'client_ip' => $credentials['client_ip']]);

                throw new CustomUserMessageAuthenticationException('Login unsuccessful.');
            }

            $user->setLocked(false);
            $user->setLockTime(null);
            $user->setLoginAttempt(0);
            $this->entityManager->flush();
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user): bool
    {
        if (!$this->passwordEncoder->isPasswordValid($user, $credentials['password'])) {
            // fail authentication with a custom error
            $this->logger->info('Login unsuccessful as supplied password did not match.', ['email' => $credentials['email'], 'client_ip' => $credentials['client_ip']]);

            throw new CustomUserMessageAuthenticationException('Login unsuccessful.');
        }

        return true;
    }

    /*
     * Redirects the newly logged on user to an appropriate page. If the user came from another page, send them back
     * there, otherwise send them to their respective dashboard page.
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): RedirectResponse
    {
        $targetPath = $this->getTargetPath($request->getSession(), $providerKey);
        if (!$targetPath) {
            $targetPath = $this->urlGenerator->generate('app_welcome');
        }

        $this->auditLogService->log('Successful login', $token->getUser()->getId(), UserRepository::class);

        $this->logger->info('Successful login.', ['email' => $token->getUser()->getEmail(), 'client_ip' => $request->getClientIp()]);

        return new RedirectResponse($targetPath);
    }

    /*
     * If authentication fails, need to log the failure attempt, this checks if the email supplied exists, if so,
     * it will increment the login attempts.  If the threshold of 3 is reached, the account will be locked.
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): RedirectResponse
    {
        $data = $request->request->get('user_login_form');

        if ($user = $this->entityManager->getRepository(User::class)->findOneByEmail($data['email'], false)) {
            $user->incrementLoginAttempt();

            if (3 === $user->getLoginAttempt()) {
                $this->logger->info('Account locked after 3 unsuccessful attempt to login.', ['email' => $data['email'], 'client_ip' => $request->getClientIp()]);

                $user->setLocked(true);
                $user->setLockTime(new DateTime());

                $this->mailerService->send(
                    new Address(
                        $user->getEmail(),
                        sprintf('Foobar Site [%s]', $this->appEnv)
                    ),
                    'Foobar Site - User Locked',
                    'email/account-locked.txt.twig',
                    ['user' => $user]
                );
            }

            $this->entityManager->persist($user);
            $this->entityManager->flush();
        }

        if ($user) {
            $this->auditLogService->log('Login failure', $user->getId(), UserRepository::class);
        }

        $this->logger->info('Account authentication failed.', ['email' => $data['email']]);

        return parent::onAuthenticationFailure($request, $exception);
    }

    protected function getLoginUrl(): string
    {
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
    }

    public function getPassword($credentials): ?string
    {
        return $credentials['password'];
    }
}

Must be compatible with Symfony\Component\HttpKernel\HttpKernelInterface

Hi,

Is there an issue using Symfony 4.4.24 and PHP 7.4 with the ^2.0 release?

Thanks,
Kevin

Executing script cache:clear [KO]
 [KO]
Script cache:clear returned with error code 255
!!  Cannot load Xdebug - it was already loaded
!!  
!!  Fatal error: Declaration of Baldinof\RoadRunnerBundle\Http\Runner::handle(Symfony\Component\HttpFoundation\Request $request, int $type = self::MASTER_REQUEST, bool $catch = true): Symfony\Component\HttpFoundation\Response must be compatible with Symfony\Component\HttpKernel\HttpKernelInterface::handle(Symfony\Component\HttpFoundation\Request $request, $type = self::MASTER_REQUEST, $catch = true) in /Users/ks27/code/smt/vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php on line 67
!!  PHP Fatal error:  Declaration of Baldinof\RoadRunnerBundle\Http\Runner::handle(Symfony\Component\HttpFoundation\Request $request, int $type = self::MASTER_REQUEST, bool $catch = true): Symfony\Component\HttpFoundation\Response must be compatible with Symfony\Component\HttpKernel\HttpKernelInterface::handle(Symfony\Component\HttpFoundation\Request $request, $type = self::MASTER_REQUEST, $catch = true) in /Users/ks27/code/smt/vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php on line 67
!!  Symfony\Component\ErrorHandler\Error\FatalError {#10011
!!    -error: array:4 [
!!      "type" => 64
!!      "message" => "Declaration of Baldinof\RoadRunnerBundle\Http\Runner::handle(Symfony\Component\HttpFoundation\Request $request, int $type = self::MASTER_REQUEST, bool $catch = true): Symfony\Component\HttpFoundation\Response must be compatible with Symfony\Component\HttpKernel\HttpKernelInterface::handle(Symfony\Component\HttpFoundation\Request $request, $type = self::MASTER_REQUEST, $catch = true)"
!!      "file" => "/Users/ks27/code/smt/vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php"
!!      "line" => 67
!!    ]
!!    #message: "Compile Error: Declaration of Baldinof\RoadRunnerBundle\Http\Runner::handle(Symfony\Component\HttpFoundation\Request $request, int $type = self::MASTER_REQUEST, bool $catch = true): Symfony\Component\HttpFoundation\Response must be compatible with Symfony\Component\HttpKernel\HttpKernelInterface::handle(Symfony\Component\HttpFoundation\Request $request, $type = self::MASTER_REQUEST, $catch = true)"
!!    #code: 0
!!    #file: "./vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php"
!!    #line: 67
!!  }
!!  
Script @auto-scripts was called via post-update-cmd

Installation failed, reverting ./composer.json and ./composer.lock to their original content.

Kernel terminate event some times doesn't get fired? (web profiler)

Hi all 👋

I have a symfony 5.4 application using the symfony runtime component and this bundle.

The application itself seems to be working (based on some rough testing done so far) but I'm facing a weird problem with the symfony developer tools, more specifically, the web profiler & web developer toolbar.

I'm observing a situation where the kernel terminate event doesn't seem to be fired at all, which then causes a profiler report not to be saved to the profiler storage, consequently resulting in a permanent 404 when attempting to fetch the /_wdt/{token} url via xhr.

I've added some simple debug statements to the Symfony\Component\HttpKernel\EventListener\ProfilerListener class which allowed me to confirm that the onKernelTerminate function in this class is only called some times. The listener for this event has a priority of -1024, but changing the priority didn't seem to make any difference.
I have tried different kernel reboot strategies, including always, which also didn't make any difference.

I haven't been able to figure out more about this issue but I'm wondering if anyone in the community has any more insights about what could be going on here. Some tips about where to further look would also be appreciated.

I'm using symfony:5.4.4, roadrunner-bundle:2.2.0 and roadrunner:2.8.2

Thanks!

References:

No logs on stdout/err

Hi, i have to support a non-standard Symfony application and they can't seem to get logs to stdout or stderr.

It's using RoadRunner v1.8.4 in a docker container with a unix socket. I think it's using Monolog v2.0, but nothing logged is being directed to stdout or stderr.

The rr.yaml is:

rpc:
  enable: false
http:
  address: "0.0.0.0:8080"

  workers:
    command: php apps/courses/bin/console.php baldinof:roadrunner:worker
    relay: unix:///opt/app/roadrunner.sock

Scientific notation in Prometheus metrics

Prometheus metrics with gauge type and value bigger than 100000 are exposed in the scientific notation like 1e+06. Is it possible somehow to manage it and expose it in simple number format as it is?

Multitenancy: Kerner-per-Tenant option

Hi

Great stuff here :) one question: would it be possible to add some sort of KernelProvider? By default it would just return the same instance, but it would open up a possibility to have multiple Kernel instances and return a specific Kernel based on an env configuration, or a request param (for example we could have a TenantKernelProvider that would ensuer each Tenant has its own Kernel).

That would also allow to have codnitional services - depending on the Tenant.

nginx and subdomains

Sorry if there is explanation somewhere but couldn't find it even on RR repo.

The issue is that I have an application with URL like

http://client_1.something.flex
http://client_2.something.flex

// for super-admin
http://something.flex

Something like this:

Click to expand nginx config
server {
    listen *:80;
    server_name *.something.flex something.flex;
    root /home/zmitic/PhpstormProjects/something/public;
    location / {
        try_files $uri /index.php$is_args$args;
    }
    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        fastcgi_param BASE_URL something.flex;
	proxy_ignore_client_abort on;
    }
}

Subdomain is used to determine client and apply Doctrine filters when needed.

Is it possible to do the same with RR and your bundle and have URL like: http://client_1.something.rr?

Production uses HTTPS set by another person, that might be problem as well but I do want to use RR on real project. Current nginx setup takes about 70-90ms, it would be nice to cut 30-40ms.

Thanks.

Proposal: Introduce kernel_reboot.strategy: never configuration

First of all thanks a lot for this great package. Together with roadrunner it has given us insane possibilities to improve performance! :)

Mainly we are getting this performance boost by loading lots of data to runtime on this event:
Baldinof\RoadRunnerBundle\Event\WorkerStartEvent

With the introduction of v1.3 the kernel gets rebooted on exceptions (which caused a lot of WTFs on ours side because this is new behaviour 🤪 )

It would be great to have a configuration method where this behaviour can be turned off. As it was until v1.3. E.g. KERNEL_REBOOT_STRATEGY_NEVER

Right now we have an implicit "workaround" by configuring the bundle like so:

baldinof_road_runner:
    kernel_reboot:
        strategy: on_exception
        allowed_exceptions:
            - Throwable

The following would be a bit more readable

baldinof_road_runner:
    kernel_reboot:
        strategy: never

Thanks for considering and thanks a lot for all the work put into this project!

Connection Ping dbal deprecated

Hi,

Im just using ur bundle for a couple of months, it works great in our old Symfony 4.4 project!

I just started creating a new project with your bundle, but stumbled against a deprecated issue with DBAL.

DoctrineORMMiddleware.php:50 $connection->ping() is deprecated (application crashing as a result of this). if its to check if the conneciton is alive we could do a simple select query right?

Symfony stopped catching exceptions

Hello there,

I am using php 8.1.3, RR 2.8.2, RR-bundle 2.2.0
The problem is that the Symfony error handler has stopped catching \Error exceptions
Normal exceptions are intercepted in normal mode

To reproduce:
In any action: throw new \Error();

UploadedFile: The file could not be uploaded

Since upgrading to RoadRunner 2.0.4, I've noticed when attempting to upload a file to the server Symfony is reporting the error The file could not be uploaded (see; https://symfony.com/doc/current/reference/constraints/File.html#uploaderrormessage), but never got this with RR 1.9.2

When the form gets validated, like so;

        $form = $this->createForm(UploadF307CType::class);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {

I get this error. Has anyone else had this issue? Max file upload size is set to 32M and the file is only 115K.

Path: children[importTemplatePath].data
Message: The file could not be uploaded.
Invalid value: Symfony\Component\HttpFoundation\File\UploadedFile {#250 …}

Violation:

Symfony\Component\Validator\ConstraintViolation {#3507 ▼
  -message: "The file could not be uploaded."
  -messageTemplate: "The file could not be uploaded."
  -parameters: []
  -plural: null
  -root: Symfony\Component\Form\Form {#3179 ▼
    name: "upload_f307_c"
    type_class: "App\Form\UploadF307CType"
    data: App\Entity\WorkflowClaim {#2390 ▼
      -id: 3672
      -stage: Proxies\__CG__\App\Entity\WorkflowStage {#2459 …}
      -submittedBy: Proxies\__CG__\App\Entity\User {#2538 …}
      -dateSubmitted: DateTime @1617803076 {#2386 ▶}
      -invoice: Proxies\__CG__\App\Entity\TrainingProviderInvoice {#2492 …}
      -importTemplatePath: null
      -processClaimUploads: Doctrine\ORM\PersistentCollection {#2519 …}
      -disableListeners: false
      #createdAt: DateTime @1617803076 {#2387 ▶}
      #updatedAt: DateTime @1617803076 {#2388 ▶}
    }
  }
  -propertyPath: "children[importTemplatePath].data"
  -invalidValue: Symfony\Component\HttpFoundation\File\UploadedFile {#250 …}
  -constraint: Symfony\Component\Validator\Constraints\File {#3246 …}
  -code: "0"
  -cause: null
}

Contents of the POST request

+files: Symfony\Component\HttpFoundation\FileBag {#209 ▼
    #parameters: array:1 [▼
      "upload_f307_c" => array:1 [▼
        "importTemplatePath" => Symfony\Component\HttpFoundation\File\UploadedFile {#250 ▼
          -test: false
          -originalName: "F307C 1310884 2020-21 P12.xlsx"
          -mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
          -error: 0
          path: "/tmp"
          filename: "upload522885814"
          basename: "upload522885814"
          pathname: "/tmp/upload522885814"
          extension: ""
          realPath: "/tmp/upload522885814"
          aTime: 2021-04-15 19:46:53
          mTime: 2021-04-15 19:46:53
          cTime: 2021-04-15 19:46:53
          inode: 1475428
          size: 114590
          perms: 0100600
          owner: 1000
          group: 1000
          type: "file"
          writable: true
          readable: true
          executable: false
          file: true
          dir: false
          link: false
        }
      ]
    ]
  }

If I watch -d the /tmp directory, I can see the file is uploaded to the server and then is immediately deleted.

-rw------- 1 app app 114590 Apr 15 21:42 upload522885814

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.