roadrunner-php / laravel-bridge

🌉 RoadRunner ⇆ Laravel bridge 🇺🇦❤️
https://roadrunner.dev/docs/integration-laravel
MIT License
372 stars 25 forks source link

Laminas\Diactoros\Exception\InvalidArgumentException: Invalid header value type; must be a string or numeric; #32

Closed sergey-yabloncev closed 3 years ago

sergey-yabloncev commented 3 years ago

Describe the bug

If you want to install laravel 8 in the configuration file cors.php change the value of supports_credentials to true we get the following error

Laminas\Diactoros\Exception\InvalidArgumentException: Invalid header value type; must be a string or numeric; received NULL in /var/www/vendor/laminas/laminas-diactoros/src/HeaderSecurity.php:140
Stack trace:
#0 /var/www/vendor/laminas/laminas-diactoros/src/MessageTrait.php(399): Laminas\Diactoros\HeaderSecurity::assertValid(NULL)
#1 [internal function]: Laminas\Diactoros\Response->Laminas\Diactoros\{closure}(NULL)
#2 /var/www/vendor/laminas/laminas-diactoros/src/MessageTrait.php(402): array_map(Object(Closure), Array)
#3 /var/www/vendor/laminas/laminas-diactoros/src/MessageTrait.php(213): Laminas\Diactoros\Response->filterHeaderValue(Array)
#4 /var/www/vendor/symfony/psr-http-message-bridge/Factory/PsrHttpFactory.php(163): Laminas\Diactoros\Response->withHeader('access-control-...', Array)
#5 /var/www/vendor/spiral/roadrunner-laravel/src/Worker.php(86): Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory->createResponse(Object(Illuminate\Http\Response))
#6 /var/www/vendor/spiral/roadrunner-laravel/bin/rr-worker(77): Spiral\RoadRunnerLaravel\Worker->start(false)
#7 {main}

it seems to me that the fruitcake/laravel-cors package supplied by default with laravel 8 should be removed since roadrunner solves this case, and it may be worth documenting this moment

System information

PHP version 8,0,1 Current package version 3.7 RoadRunner version 1.9.2 Environment docker

RoadRunner configuration file content

rpc:
  enable: false

http:
  address: 0.0.0.0:80
  ssl:
    port:     443
    redirect: false
  maxRequestSize: 200

  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 ./vendor/bin/rr-worker  --refresh-app --reset-redis-connections"
    relay: "pipes"
    user: ""
    pool:
      numWorkers: 16
      maxJobs: 64
      allocateTimeout: 60
      destroyTimeout: 60

headers:
  cors:
    allowedOrigin: "*"
    allowedHeaders: "*"
    allowedMethods: "GET,POST,PUT,DELETE"
    allowCredentials: true
    exposedHeaders: "Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma"
    maxAge: 600
  response:
    "X-Powered-By": "RoadRunner"

static:
  dir: "public"
  forbid: [ ".php", ".htaccess" ]
  response:
    "X-Powered-By": "RoadRunner"

reload:
  interval: 1s
  patterns: [ ".php" ]

  services:
    http:
      dirs: [ "" ]
      recursive: true

config/roadrunner.php - standart

Additional context

# https://hub.docker.com/_/php/
FROM php:8.0-cli-alpine

ENV RR_VERSION 1.9.2

# Install dev dependencies
RUN apk add --no-cache --virtual .build-deps \
    $PHPIZE_DEPS \
    curl-dev \
    libtool \
    libxml2-dev \
    postgresql-dev \
    sqlite-dev \

# Install production dependencies
&& apk add --no-cache \
    bash \
    shadow \
    nano \
    curl \
    wget \
    freetype-dev \
    icu-dev \
    icu-libs \
    libc-dev \
    libjpeg-turbo-dev \
    libpng-dev \
    libzip-dev \
    make \
    oniguruma-dev \
    openssh-client \
    postgresql-libs \
    rsync \
    jpegoptim optipng pngquant gifsicle \
    zlib-dev \

# Install PECL and PEAR extensions
&& pecl install \
    redis \

# Enable PECL and PEAR extensions
&& docker-php-ext-enable \
    redis \

# Configure php extensions
&& docker-php-ext-configure gd --with-freetype --with-jpeg \

# Install php extensions
&& docker-php-ext-install \
    bcmath \
    calendar \
    curl \
    exif \
    gd \
    iconv \
    intl \
    mbstring \
    opcache \
    pdo \
    pdo_pgsql \
    pdo_sqlite \
    pcntl \
    sockets \
    tokenizer \
    xml \
    zip

# Copy php.ini configuration
COPY php.ini /usr/local/etc/php/conf.d/40-custom.ini
# Copy opcache.ini configuration
COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini

# Install composer
ENV COMPOSER_HOME /composer
ENV PATH ./vendor/bin:/composer/vendor/bin:$PATH
ENV COMPOSER_ALLOW_SUPERUSER 1
RUN curl -s https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin/ --filename=composer

# Download RoadRunner
RUN mkdir /tmp/rr \
  && cd /tmp/rr \
  && echo "{\"require\":{\"spiral/roadrunner\":\"${RR_VERSION}\"}}" >> composer.json \
  && composer install \
  && vendor/bin/rr get-binary -l /usr/local/bin \
  && rm -rf /tmp/rr

# Cleanup dev dependencies
RUN apk del -f .build-deps && rm -rf /var/cache/apk/* && docker-php-source delete && rm -rf /tmp/pear

RUN usermod -u 1000 www-data && groupmod -g 1000 www-data

WORKDIR /var/www

USER 1000:1000

EXPOSE 80
EXPOSE 443

CMD ["/usr/local/bin/rr", "serve", "-c", "/var/www/.rr.yml", "-w", "/var/www", "-d"]
jetexe commented 3 years ago

I cannot reproduce bug.

I get clean laravel installation

$ composer create-project laravel/laravel example-app

change supports_credentials to true in config/cors.php file.

Get docker file and roadrunner config from this issue, (but i have to change http.address to 0.0.0.0:8080 to avoid permission error)

Build image

$ docker build -t lara-test .

And run

$ docker run --rm -v"${PWD}:/var/www" -u"1000:1000" -p"8081:8080" lara-test

Header Access-Control-Allow-Credentials is set to true

sergey-yabloncev commented 3 years ago

Sorry, I send not all parameters for cros.php. In cors.php file your need to change param paths on ['*'] below file with all configurations

<?php
return [
    'paths' => ['*'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['*'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true,
];
jetexe commented 3 years ago

Yes, i reproduce an error.

The bug is in the asm89/stack-cors. Response header value must be string or string[], but request header value is nulable. In this case header Origin is null.

PSR-7

@tarampampam, maybe add AfterRequestHandlingEvent handler that fix headers?

tarampampam commented 3 years ago

@jetexe sounds like good idea! Can you make a PR?

jetexe commented 3 years ago

I've created an issue (https://github.com/asm89/stack-cors/issues/85) and PR (https://github.com/asm89/stack-cors/pull/86) into asm89/stack-cors. I hope that bug fix will be applied soon.

@sergey-yabloncev, for now you can try this solution:

Put this listener into app/Listeners/FilterNullHeadersListener.php

<?php

declare(strict_types = 1);

namespace App\Listeners;

use Spiral\RoadRunnerLaravel\Events\Contracts\WithHttpResponse;
use Spiral\RoadRunnerLaravel\Listeners\ListenerInterface;

/**
 * FilterNullHeadersListener handle event with http response to clear null valued headers.
 */
class FilterNullHeadersListener implements ListenerInterface
{
    /**
     * {@inheritDoc}
     */
    public function handle($event): void
    {
        if ($event instanceof WithHttpResponse) {
            foreach ($event->httpResponse()->headers->all() as $header => $values) {
                $filteredArray = \array_filter($values);

                if ($filteredArray === []) {
                    $event->httpResponse()->headers->remove($header);
                } else {
                    $event->httpResponse()->headers->set($header, $filteredArray);
                }
            }
        }
    }
}

Add add it into config/roadrunner.php

<?php

return [
    ...
    'listeners' => [
        ...
        Events\AfterRequestHandlingEvent::class => [
            \App\Listeners\FilterNullHeadersListener::class,
        ],
       ...
    ]
    ...
]
sergey-yabloncev commented 3 years ago

@jetexe Thank's!

jetexe commented 3 years ago

@sergey-yabloncev fix was merged and released, can you try it?

sergey-yabloncev commented 3 years ago

Yes now, all work correctly! Thanks!