roadrunner-server / roadrunner

🤯 High-performance PHP application server, process manager written in Go and powered with plugins
https://docs.roadrunner.dev
MIT License
7.91k stars 413 forks source link

Symfony: Default SESSION does not work (no cookie is set in Response) #18

Closed Richard87 closed 4 years ago

Richard87 commented 6 years ago

Hi!

I have this code:

<?php
/**
 * Created by PhpStorm.
 * User: richard
 * Date: 22.06.18
 * Time: 11:59
 */

require __DIR__ . '/vendor/autoload.php';

use App\Kernel;
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use Symfony\Component\Debug\Debug;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\HttpFoundation\Request;

if (getenv('APP_ENV') === false) {
    (new Dotenv())->load(__DIR__.'/.env');
}
$env = getenv('APP_ENV') ?: 'dev';
$debug = getenv('APP_DEBUG') ? ((bool) getenv('APP_DEBUG')) : !in_array($env, ['prod', 'k8s']);

if ($debug) {
    umask(0000);
    Debug::enable();
}
if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) {
    Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST);
}
if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) {
    Request::setTrustedHosts(explode(',', $trustedHosts));
}
$kernel = new Kernel($env, $debug);
$httpFoundationFactory = new HttpFoundationFactory();

$relay = new Spiral\Goridge\StreamRelay(STDIN, STDOUT);
$psr7 = new Spiral\RoadRunner\PSR7Client(new Spiral\RoadRunner\Worker($relay));

while ($req = $psr7->acceptRequest()) {
    try {
        $request = $httpFoundationFactory->createRequest($req);
        $response = $kernel->handle($request);

        $psr7factory = new DiactorosFactory();
        $psr7response = $psr7factory->createResponse($response);
        $psr7->respond($psr7response);

        $kernel->terminate($request, $response);
    } catch (\Throwable $e) {
        $psr7->getWorker()->error((string)$e);
    }
}

It's slighly modified Symfony code to handle env-variables :) (Also, it's stunningly fast!!!!)

But I don't get any cookies returned, so I can see my logg inn is accepted, and I'm redirected to the dashboard, but there I get a permission denied and redirect to login (because no cookies have been set)...

Any tips on how to troubleshoot, or what might be wrong?

wolfy-j commented 6 years ago

Check if you actually have cookies in PSR7 request, dump $r->getCookieParams() somewhere (do not dump them into STDOUT since it used for communication).

If you don't have cookies: make sure your cookies are not set on the different domain If you do have cookies in PSR7: most likely PSR-Symfony wrapper is incorrect.

We know for sure that cookies work (we have multiple application use them + there are a bunch of tests https://github.com/spiral/roadrunner/blob/master/service/http/handler_test.go#L113).

If you still have no idea what is going on try to download rr repository and run make test.

wolfy-j commented 6 years ago

Also check if your cookies are being returned in response (Chome Developer Console will help).

Richard87 commented 6 years ago

Strange... I Rebooted the computer because I couldn't get RoadRunner to start again, and it works perfect... Maybe it have some issues shutting down cleanly?

And wow, this is insanely fast!!! I just hope I don't get any memory issues since StreamedResponses haven't been impleneted yet...

Anyway, this is my latest version of the worker script:

<?php
/**
 * Created by PhpStorm.
 * User: richard
 * Date: 22.06.18
 * Time: 11:59
 */

require __DIR__ . '/vendor/autoload.php';

use App\Kernel;
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use Symfony\Component\Debug\Debug;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\HttpFoundation\Request;

if (getenv('APP_ENV') === false) {
    (new Dotenv())->load(__DIR__.'/.env');
}
$env = getenv('APP_ENV') ?: 'dev';
$debug = getenv('APP_DEBUG') ? ((bool) getenv('APP_DEBUG')) : !in_array($env, ['prod', 'k8s']);

if ($debug) {
    umask(0000);
    Debug::enable();
}
if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) {
    Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST);
}
if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) {
    Request::setTrustedHosts(explode(',', $trustedHosts));
}

$relay = new Spiral\Goridge\SocketRelay(__DIR__ . "/var/roadrunner.sock",null,\Spiral\Goridge\SocketRelay::SOCK_UNIX);
$worker = new Spiral\RoadRunner\Worker($relay);
$psr7 = new Spiral\RoadRunner\PSR7Client($worker);

$kernel = new Kernel($env, $debug);
$httpFoundationFactory = new HttpFoundationFactory();
$cacheDir = $kernel->getCacheDir();
while ($req = $psr7->acceptRequest()) {
    try {
        $request = $httpFoundationFactory->createRequest($req);
        $response = $kernel->handle($request);

        $psr7factory = new DiactorosFactory();
        $psr7response = $psr7factory->createResponse($response);
        $psr7->respond($psr7response);

        $kernel->terminate($request, $response);
        $kernel->reboot($cacheDir);
    } catch (\Throwable $e) {
        $worker->error((string)$e);
    }
}
Richard87 commented 6 years ago

(Also I changed to sockets because I don't want any errors if something is printed to STDOUT by accident ;) )

wolfy-j commented 6 years ago

Maybe it have some issues shutting down cleanly?

Can you please post your .rr file and name of your OS?

wolfy-j commented 6 years ago

And wow, this is insanely fast!!! I just hope I don't get any memory issues since StreamedResponses haven't been impleneted yet...

Output buffer will be cleared on every response, so you only risking using more memory during one request (no leaks is expected). Streaming responses are something we are closing looking to implement in next update.

Richard87 commented 6 years ago

Well this is embarasing :( after restart, I ran docker-compose up to get mysql and redis back online ,also starteed php (I didn't notice), and everytime I ran roadrunner I thought I was using that, but actually I was working via docker...

So, Cookies are still not sent... (but atleast ./rr http:workers is workign again!)

wolfy-j commented 6 years ago

Have you tried to debug using instructions attached earlier?

Richard87 commented 6 years ago

yup, just finished, seems like Symfony is not setting the cookies at all suddenly... (the same code works perfectly in Docker with Apache+mod-php and Apache + Php-fpm in production...

First it hits login_check, which is successful (if I check profiler, and by looking at the Location in the return header, next it hits /dashboard, but it fails, because it doesn't have any cookies :(

Check this out:

{
    "url": "\/login_check",
    "sf_response_headers": {
        "cache-control": [
            "max-age=0, must-revalidate, private"
        ],
        "date": [
            "Fri, 22 Jun 2018 13:26:47 GMT"
        ],
        "location": [
            "\/dashboard\/"
        ],
        "content-type": [
            "text\/html; charset=UTF-8"
        ]
    }
}
{
    "url": "\/dashboard\/",
    "sf_response_headers": {
        "cache-control": [
            "max-age=0, must-revalidate, private"
        ],
        "date": [
            "Fri, 22 Jun 2018 13:26:47 GMT"
        ],
        "location": [
            "\/"
        ],
        "content-type": [
            "text\/html; charset=UTF-8"
        ]
    }
}
{
    "url": "\/",
    "sf_response_headers": {
        "cache-control": [
            "max-age=0, must-revalidate, private"
        ],
        "date": [
            "Fri, 22 Jun 2018 13:26:47 GMT"
        ],
        "content-type": [
            "text\/html; charset=UTF-8"
        ]
    }
}
wolfy-j commented 6 years ago

Hmmmmm. Try to set host in request uri to the localhost (or host you use), it might be empty in PSR7 but Symfony might be requiring it.

$req = $req->withUri($req->getUri()->withHost('localhost'));
Richard87 commented 6 years ago

Okay, after a lot of stupidity, Symfony is setting the cookie in the symfony response:

{
    "url": "\/login_check",
    "headers": [
        "sf_redirect:{\"token\":\"b20886\",\"route\":\"login_check\",\"method\":\"POST\",\"controller\":\"n\\\/a\",\"status_code\":302,\"status_text\":\"Found\"}",
        "Hello:World!"
    ]
}

The PSR7 Response headers:

{
    "url": "\/login_check",
    "headers": {
        "cache-control": [
            "max-age=0, must-revalidate, private"
        ],
        "date": [
            "Fri, 22 Jun 2018 13:49:34 GMT"
        ],
        "location": [
            "\/dashboard\/"
        ],
        "content-type": [
            "text\/html; charset=UTF-8"
        ],
        "x-debug-token": [
            "dd6430"
        ],
        "x-debug-token-link": [
            "http:\/\/localhost:8000\/_profiler\/dd6430"
        ],
        "set-cookie": [
            "sf_redirect=%7B%22token%22%3A%22dd6430%22%2C%22route%22%3A%22login_check%22%2C%22method%22%3A%22POST%22%2C%22controller%22%3A%22n%5C%2Fa%22%2C%22status_code%22%3A302%2C%22status_text%22%3A%22Found%22%7D; path=\/; httponly"
        ],
        "Set-Cookie": [
            "sf_redirect=%7B%22token%22%3A%22dd6430%22%2C%22route%22%3A%22login_check%22%2C%22method%22%3A%22POST%22%2C%22controller%22%3A%22n%5C%2Fa%22%2C%22status_code%22%3A302%2C%22status_text%22%3A%22Found%22%7D; path=\/; httponly"
        ]
    }
}

But somehow the auth-cookie/session id is not set... Google Chrome also see the cookie: sf_redirect=%7B%22token%22%3A%22ed1ed6%22%2C%22route%22%3A%22login_check%22%2C%22method%22%3A%22POST%22%2C%22controller%22%3A%22n%5C%2Fa%22%2C%22status_code%22%3A302%2C%22status_text%22%3A%22Found%22%7D; path=/; httponly

Richard87 commented 6 years ago

Adding the Uri didn't work:

$kernel = new \App\Kernel("dev", true);
$httpFoundationFactory = new HttpFoundationFactory();
$cacheDir = $kernel->getCacheDir();
while ($req = $psr7->acceptRequest()) {
    try {
        $req = $req->withUri($req->getUri()->withHost('localhost'));
        $request = $httpFoundationFactory->createRequest($req);
        $response = $kernel->handle($request);

        $psr7factory = new DiactorosFactory();
        $psr7response = $psr7factory->createResponse($response);

        $cookies = $psr7response->getHeaders();
        file_put_contents(__DIR__ . "/headers.json",json_encode([
                'url' => $request->getPathInfo(),
                'headers' => $cookies,
            ],JSON_PRETTY_PRINT)."\n", FILE_APPEND);

        $psr7->respond($psr7response);

        $kernel->terminate($request, $response);
        $kernel->reboot($cacheDir);
    } catch (\Throwable $e) {
        $worker->error((string)$e);
    }
}
wolfy-j commented 6 years ago

So one cookie is set and another don't? Do you see cookie headers in chrome debug panel on a response from your login?

Richard87 commented 6 years ago

Yup

It's insanely weird... The "Debug toolbar" cookies, and the custom cookies I set to test is stored and sent, BUT not auth cookies...

Richard87 commented 6 years ago

It seems the session storage doesn't work...

Richard87 commented 6 years ago

I'm using redis backend for session storage.. .does that have any implications with Roadrunner?

wolfy-j commented 6 years ago

Not at all, only http layer is different.

Richard87 commented 6 years ago

Strange, session does seems to work-ish, but it's weird:

{"id":"7noncegfojm7slhu31pucoko0b","test":"2018-06-22T14:38:05+00:00","name":"eportal_auth"}
{"id":"pno6dppgmk36sb673rprt9b6ig","test":"2018-06-22T14:38:06+00:00","name":"eportal_auth"}
{"id":"78clifqv3ov8ve5t9pvvrc1jlu","test":"2018-06-22T14:38:06+00:00","name":"eportal_auth"}
{"id":"1obeqdstigeqbhagoui8rjid76","test":"2018-06-22T14:38:07+00:00","name":"eportal_auth"}
{"id":"7noncegfojm7slhu31pucoko0b","test":"2018-06-22T14:38:05+00:00","name":"eportal_auth"}
{"id":"pno6dppgmk36sb673rprt9b6ig","test":"2018-06-22T14:38:06+00:00","name":"eportal_auth"}
{"id":"78clifqv3ov8ve5t9pvvrc1jlu","test":"2018-06-22T14:38:06+00:00","name":"eportal_auth"}
{"id":"1obeqdstigeqbhagoui8rjid76","test":"2018-06-22T14:38:07+00:00","name":"eportal_auth"}
{"id":"7noncegfojm7slhu31pucoko0b","test":"2018-06-22T14:38:05+00:00","name":"eportal_auth"}
{"id":"pno6dppgmk36sb673rprt9b6ig","test":"2018-06-22T14:38:06+00:00","name":"eportal_auth"}

I saved a item in session:

  $session = $kernel->getContainer()->get("session");
        if (!$session->has("test"))
            $session->set("test", date("c"));

        file_put_contents(__DIR__ . "/headers.json",json_encode([
                'id' => $session->getId(),
                'test' => $session->get("test"),
                'name' => $session->getName(),
            ])."\n", FILE_APPEND);

The ID is changing constantly, and therefor reading slighly different times, anyhow, the session id never reaches the browser :/

wolfy-j commented 6 years ago

I think it's related to cookies not being emitted by Symfony, is there any sort of Symfony option to disable them? Maybe it requires any specific setting?

Richard87 commented 6 years ago

I don't think so, it works perfectly everywhere else...

{"id":"movsub0sitiab507i9plg936ju","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:54:50+00:00","uri":"\/login_check"}
{"id":"8mrbbtunenbrodp752iipfgrra","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:54:50+00:00","uri":"\/dashboard\/"}
{"id":"qoalctg5bvae67jvq5n8f53rgj","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:54:51+00:00","uri":"\/"}
{"id":"idemsd1mihrpf13e5j4gi7eq14","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:54:51+00:00","uri":"\/_wdt\/949254"}
{"id":"movsub0sitiab507i9plg936ju","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:54:55+00:00","uri":"\/login_check"}
{"id":"8mrbbtunenbrodp752iipfgrra","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:54:55+00:00","uri":"\/"}
{"id":"qoalctg5bvae67jvq5n8f53rgj","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:54:56+00:00","uri":"\/_wdt\/3b2bf4"}
{"id":"idemsd1mihrpf13e5j4gi7eq14","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:54:58+00:00","uri":"\/"}
{"id":"movsub0sitiab507i9plg936ju","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:54:58+00:00","uri":"\/_wdt\/018964"}
{"id":"8mrbbtunenbrodp752iipfgrra","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:57:38+00:00","uri":"\/login_check"}
{"id":"qoalctg5bvae67jvq5n8f53rgj","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:57:38+00:00","uri":"\/"}
{"id":"idemsd1mihrpf13e5j4gi7eq14","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:57:38+00:00","uri":"\/_wdt\/796c97"}
{"id":"movsub0sitiab507i9plg936ju","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:57:48+00:00","uri":"\/login_check"}
{"id":"8mrbbtunenbrodp752iipfgrra","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:57:48+00:00","uri":"\/dashboard\/"}
{"id":"qoalctg5bvae67jvq5n8f53rgj","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:57:48+00:00","uri":"\/"}
{"id":"idemsd1mihrpf13e5j4gi7eq14","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:57:48+00:00","uri":"\/_wdt\/05835e"}
{"id":"movsub0sitiab507i9plg936ju","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:58:04+00:00","uri":"\/login_check"}
{"id":"8mrbbtunenbrodp752iipfgrra","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:58:04+00:00","uri":"\/"}
{"id":"qoalctg5bvae67jvq5n8f53rgj","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:58:04+00:00","uri":"\/_wdt\/c418cb"}
{"id":"idemsd1mihrpf13e5j4gi7eq14","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:59:14+00:00","uri":"\/"}
{"id":"movsub0sitiab507i9plg936ju","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:59:14+00:00","uri":"\/_wdt\/6e9522"}
{"id":"8mrbbtunenbrodp752iipfgrra","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T14:59:32+00:00","uri":"\/"}
{"id":"qoalctg5bvae67jvq5n8f53rgj","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T14:59:33+00:00","uri":"\/_wdt\/31da3a"}
{"id":"idemsd1mihrpf13e5j4gi7eq14","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T15:00:22+00:00","uri":"\/"}
{"id":"movsub0sitiab507i9plg936ju","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T15:00:23+00:00","uri":"\/_wdt\/d4bc9e"}
{"id":"8mrbbtunenbrodp752iipfgrra","test":"2018-06-22T14:54:50+00:00","now":"2018-06-22T15:00:35+00:00","uri":"\/"}
{"id":"qoalctg5bvae67jvq5n8f53rgj","test":"2018-06-22T14:54:51+00:00","now":"2018-06-22T15:00:35+00:00","uri":"\/_wdt\/84c22d"}

The strange this is it seems the ID's are recycled, and the session storage works. It feels like something the load balancer is doing? Is there some logic looking at cookies in there?

(Test is the time php first saw that session_id)

Richard87 commented 6 years ago

In our production env / live, I deleted all cookies, and went to the dashboard

Symfony immidiatly creates a new Cookie, and uses the "Set-cookie" header, on all sequent request, Chrome uses that cookie, and symfony never sends a Set-cookie again, not even when logging in (even with auth denied, it keeps the same cookie id).

So, when I run up docker-compose, everything works immidiatly, if I stop docker-php, and run the symfony built in dev-server, it reacts exactly the same way as the production env.

I can also see all cookies in Chrome dev-tools etc (1. set cookie on the first request, and then using that cookie on all subsequent requests).

When using roadrunner, the browser never sees the cookie, even though somehow the same id's are recycled?

wolfy-j commented 6 years ago

Can you try to dump the $response->getHeaders() somewhere? Are they being set in response?

Richard87 commented 6 years ago

Yeah, I don't have any clues what's happening...

It doesn't seem like symfony returns any headers, but everytime roadrunner crashes (without any description to why), I have to reboot the machine to be able to get it up and running again, so troubleshooting is pretty slow...

Also, is there any way of enabling xdebug in the workers?

wolfy-j commented 6 years ago

It doesn't seem like symfony returns any headers, but everytime roadrunner crashes (without any description to why), I have to reboot the machine to be able to get it up and running again, so troubleshooting is pretty slow...

What OS are you using?

Also, is there any way of enabling xdebug in the workers?

Yes, i will write an instruction next week how to configure it properly.

Richard87 commented 6 years ago

I'm on Linux (Fedora 28)

wolfy-j commented 6 years ago

Is it possible that error within this lines?

if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) {
    Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST);
}
if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) {
    Request::setTrustedHosts(explode(',', $trustedHosts));
}

Do you have any sort of IP filtering which might affect session creation or anything like that? Do you set this values using ENV? RR runs using php-cli mode and it might not nesessary provide same values as nginx.

Richard87 commented 6 years ago

Hi!

No, I'm not sure what does actually do! (I have put file_put_contents in strategic places, sometimes it crashes the roadrunner, but I don't now why).

I'll remove them and see what happens :)

There are no ip-filters or anything, it should be simple and straight forward :(

wolfy-j commented 6 years ago

Try adding ini_set('display_errors', 'stderr'); to your script, this part is missing in README but we have in tests and at our production. This might explain why you can't see any errors.

Richard87 commented 6 years ago

I tried with "xdebug_break()", but no erros are sent with display_errors on.

Id did modify Worker.php:

    /**
     * Respond to the server with result of task execution and execution context.
     *
     * Example:
     * $worker->respond((string)$response->getBody(), json_encode($response->getHeaders()));
     *
     * @param string|null $payload
     * @param string|null $header
     */
    public function send(string $payload = null, string $header = null)
    {
        if (is_null($header)) {
            $this->relay->send($header, Relay::PAYLOAD_CONTROL | Relay::PAYLOAD_NONE);
        } else {

            file_put_contents("/home/richard/Projects/eportal/headers.json", $header."\n",FILE_APPEND);
            $this->relay->send($header, Relay::PAYLOAD_CONTROL | Relay::PAYLOAD_RAW);
        }

        $this->relay->send($payload, Relay::PAYLOAD_RAW);
    }

But no cookies are set related to session id at all (only the cookie setting the sf_redirect is set) :(

wolfy-j commented 6 years ago

I'm currently preparing an update which will allow your to see the content of STDERR in realtime in debug mode, this might help, see #21

This is def some integration issue with Symfony. Do you see any cookies in Original Symfony response?

$psr7response = $psr7factory->createResponse($response);

Maybe the psr7 mapper does not work correctly.

wolfy-j commented 6 years ago

Use error_log with ini_set('display_errors', 'stderr'); for an easier debug.

Richard87 commented 6 years ago

Hi, no, currently I think there is something weird going on in symfony, it never sets any auth cookies when using road runner :/

Richard87 commented 6 years ago

Hi!

Thanks, just trying 1.0.3 now, and there is some awesome error reporting ;)

But I still can't restart RoadRunner after a php error.

^[
^CDEBU[0641] [rpc]: stopped                               
ERRO[0641] worker.23968 signal: interrupt 
ERRO[0641] worker.5693 signal: interrupt 
ERRO[0641] worker.5704 signal: interrupt 
ERRO[0641] worker.5697 signal: interrupt 

^C
^Z
[1]  + 5682 suspended  ./rr serve -vvv -d

~/Projects/eportal on  feature/road-runner ➜ kill 5682

~/Projects/eportal on  feature/road-runner ➜ ./rr serve -vvv -d         
DEBU[0000] [rpc]: started                               
DEBU[0000] [http]: started                              
DEBU[0000] [static]: started                            
^Z
[2]  + 24405 suspended  ./rr serve -vvv -d

~/Projects/eportal on  feature/road-runner ➜ bg
[2]  - 24405 continued  ./rr serve -vvv -d

~/Projects/eportal on  feature/road-runner ➜ ./rr http:workers 
^C
^C
^Z
[3]  + 24492 suspended  ./rr http:workers

~/Projects/eportal on  feature/road-runner ➜ bg
[3]  - 24492 continued  ./rr http:workers

~/Projects/eportal on  feature/road-runner ➜ curl localhost:8000
curl: (7) Failed to connect to localhost port 8000: Connection refused

~/Projects/eportal on  feature/road-runner ➜ curl 127.0.0.1:8000
curl: (7) Failed to connect to 127.0.0.1 port 8000: Connection refused

~/Projects/eportal on  feature/road-runner ➜ kill -9 24405
Error: unexpected EOF                                                                                                                                                                                              
[2]    24405 killed     ./rr serve -vvv -d
[3]  - 24492 exit 1     ./rr http:workers

~/Projects/eportal on  feature/road-runner ➜ ./rr serve -vvv -d &
[2] 24557
DEBU[0000] [rpc]: started                                                                                                                                                                                          
DEBU[0000] [http]: started                              
DEBU[0000] [static]: started                            

~/Projects/eportal on  feature/road-runner ➜ curl 127.0.0.1:8000 
curl: (7) Failed to connect to 127.0.0.1 port 8000: Connection refused

~/Projects/eportal on  feature/road-runner ➜ ./rr http:workers   
^Z
[3]  + 24597 suspended  ./rr http:workers

~/Projects/eportal on  feature/road-runner ➜ bg
[3]  - 24597 continued  ./rr http:workers

~/Projects/eportal on  feature/road-runner ➜ kill 24557
DEBU[0015] [rpc]: stopped                                                                                                                                                                                          
DEBU[0015] [http]: stopped                              

~/Projects/eportal on  feature/road-runner ➜ 

(I'm still trying to get Cookies to work, but it goes slightly slower when I have to reboot everytime... )

Richard87 commented 6 years ago

I set up a new repository to test https://github.com/Richard87/roadrunner-test

I have only installed symfony/security-bundle, symfony/psr-http-message-bridge and spiral/roadrunner to make test it out...

wolfy-j commented 6 years ago

I think we identified one of the potential issues with locking down pipes, we will release this patch today.

Richard87 commented 6 years ago

The problem is that the session_id is not stored in the symfony Response object, it's handled seperatly by PHP.

Therefor DiactorosFactory never includes the session id / set Cookie... https://github.com/symfony/http-foundation/blob/master/Session/Storage/NativeSessionStorage.php#L142

session_start() sets the Set-cookie with the correct properties depending on the active handler that implements \SessionHandlerInterface.

To fix/hack it we need to fetch the Session Storage from the Symfony kernel and build our own cookie, it's included, but I still have some problems...

First off all, all cookies are duplicated(!):

POST http://localhost:8007/login_check

HTTP/1.1 302 Found
Cache-Control: max-age=0, must-revalidate, private
Content-Type: text/html; charset=UTF-8
Date: Mon, 25 Jun 2018 15:29:10 GMT
Location: /dashboard/
Set-Cookie: sf_redirect=%7B%22token%22%3A%22706c85%22%2C%22route%22%3A%22login_check%22%2C%22method%22%3A%22POST%22%2C%22controller%22%3A%22n%5C%2Fa%22%2C%22status_code%22%3A302%2C%22status_text%22%3A%22Found%22%7D; path=/; httponly
Set-Cookie: eportal_auth=slfj100pcgchcb69h7n6abdrgo; path=/; httponly
Set-Cookie: sf_redirect=%7B%22token%22%3A%22706c85%22%2C%22route%22%3A%22login_check%22%2C%22method%22%3A%22POST%22%2C%22controller%22%3A%22n%5C%2Fa%22%2C%22status_code%22%3A302%2C%22status_text%22%3A%22Found%22%7D; path=/; httponly
Set-Cookie: eportal_auth=slfj100pcgchcb69h7n6abdrgo; path=/; httponly
X-Debug-Token: 706c85
X-Debug-Token-Link: http://localhost:8007/_profiler/706c85
Content-Length: 288

My current code:

while ($req = $psr7->acceptRequest()) {
    try {
        $httpFoundationFactory = new HttpFoundationFactory();
        $request = $httpFoundationFactory->createRequest($req);
        $response = $kernel->handle($request);

        $session = $request->getSession();
        if ($session) {
            $name = $session->getName();
            $sessionId = $session->getId();

            if (!$request->cookies->has($name)) {
                $cookie = new Cookie($name, $sessionId);
                $response->headers->setCookie($cookie);
            }
        }

        $psr7factory = new DiactorosFactory();
        $psr7response = $psr7factory->createResponse($response);
        $psr7->respond($psr7response);

        $kernel->terminate($request, $response);
        $kernel->shutdown();
    } catch (\Throwable $e) {
        $worker->error((string)$e);
    }
}
wolfy-j commented 6 years ago

Try 1.0.4, it should help with busy sockets and potential locks on resources.

Richard87 commented 6 years ago

Thanks :)

I got the session to work, but it's not pretty :/

kernel = new \App\Kernel("dev", true);
$kernel->boot();
$cacheDir = $kernel->getCacheDir();
while ($req = $psr7->acceptRequest()) {
    try {
        $httpFoundationFactory = new HttpFoundationFactory();
        $request = $httpFoundationFactory->createRequest($req);

        // If session is missing from cookies, generate a new Id
        $sessionId = $request->cookies->get(session_name(), session_create_id());
        session_id($sessionId);

        $response = $kernel->handle($request);
        $kernel->terminate($request, $response);

        $session = $request->getSession();
        if ($session) {
            if (!$request->cookies->has(session_name())) {
                // TODO: build real cookie with proper
                // TODO: domain,path,lifetime,secure,httpOnly etc
                $cookie = new Cookie(session_name(), $sessionId);
                $response->headers->setCookie($cookie);
            }
            $session->save();
        }

        $psr7factory = new DiactorosFactory();
        $psr7response = $psr7factory->createResponse($response);
        $psr7->respond($psr7response);
        $kernel->shutdown();
    } catch (\Throwable $e) {
        $worker->error((string)$e);
        $worker->stop();
    }
}
wolfy-j commented 6 years ago

We are using session middleware which fallbacks to default PHP session mechanism with a custom cookie, this might explain why we do not have the same issue. Native PHP sessions do not work with long-running scripts, you must to start and stop them manually. Are you sure there is no Symfony bundle to manage sessions on framework level?

Richard87 commented 6 years ago

There probably is, but I haven't have time to look into it (and get it to work!)

Pomyk commented 6 years ago

To make sessions work in a silex app (using symfony components):

in the request loop:

$session_id = $req->cookies->get('session_cookie_name');
if (!empty($session_id)) {
     session_id($session_id);
}
$resp = $app->handle($req);
$app['session.storage']->save();

For session storage use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage

Richard87 commented 6 years ago

Hi!

In Symfony 4 we don't have access to the Session storage service, also we need to set the session id in the cookie if it's missing. How does Silex handle that?

Pomyk commented 6 years ago

Actually it's not working 100% for me either... back to the drawing board.

Richard87 commented 6 years ago

I assume $session->save() (either it's Silex or Symfony) only saves the session to the designated session storage.

But the session cookie is never set on the Symfony request, I assume it's the same on Silex?

wolfy-j commented 6 years ago

I'm closing this request due to inactivity.

mnavarrocarter commented 6 years ago

@wolfy-j I would like to to consider reopening this and I'm willing to provide more feedback on the issue, since something similar is happening with my application as well.

AFAIK, Symfony 4 does not use php native session solutions as pointed out here and here.

Mi issue is a bit different though. I configured 4 workers to my web app, and when I authenticate with one of them successfully, then another one grabs the redirect request, and tries to access the protected page, but unauthenticated.

I know that this is due to the fact that a different worker handles the redirect, because if I keep authenticating, eventually I kinda hit a worker that is authenticated and I'm able to enter. And because if I run the app with just one worker, it authenticates and works fine. Any idea of what could I be missing?

wolfy-j commented 6 years ago

I would recommend asking advice from Symfony users, we use the almost identical mechanism of auto-managing sessions via middleware and it works perfectly. Is there any issue with request mapping between Symfony Request and PSR7?

mnavarrocarter commented 6 years ago

I'll do a little bit of research. Symfony does not use PSR7 Requests by default. They developed some sort of factory bridge to be able to work with PSR 7. There may be something missing there. Or maybe Zend Diactoros uses native session functions.

wolfy-j commented 6 years ago

Zend Diactoros does not manage sessions at all (only data-carrying), since Symfony control session internally it must read/write session cookie. I assume the issue can be with some security or "trusted proxy" mechanism which either does not allow to set or to get cookie value to/from PSR7.

wolfy-j commented 6 years ago

@dunglas would you mind providing any advice what might be the case? From PSR7 and RR standpoint we are 100% confident that it works properly cos, well, it works on production for over 6 months now and we have this tests:

https://github.com/spiral/roadrunner/blob/master/tests/http/cookie.php https://github.com/spiral/roadrunner/blob/master/service/http/handler_test.go#L170