Giter Site home page Giter Site logo

runtime's People

Contributors

alexander-schranz avatar ash-m avatar bafs avatar brettmc avatar chalasr avatar cviniciussdias avatar dmerchier avatar dunglas avatar eidenschink avatar ferror avatar grahamcampbell avatar gsdevme avatar junowilderness avatar leocavalcante avatar mleczakm avatar nyholm avatar ocramius avatar piku235 avatar pistej avatar rickysu avatar sy-records avatar tamcy avatar vhenzl avatar yoann-tyt 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

runtime's Issues

Bref\SymfonyRequestBridge using proxy settings instead of sourceIp

I'm using the Bref runtime to run a Symfony application in Lambda with CloudFront in front of it. Getting the source ip address with that layout is hard, esp. because the SymfonyRequestBridge is overriding the proxy configuration when starting up.

While reading the code I was wondering, why the Bridge is using the proxy configuration at all, instead of using the Lambda provided clientIp which already contains the correct value.

By replacing (SymfonyRequestBridge:40)

            'REMOTE_ADDR' => '127.0.0.1',

with

            'REMOTE_ADDR' => $event->getRequestContext()['http']['sourceIp'],

I was able to fix this issue for me while testing.

[Swoole] Support Static File Handler

I just try to use runtime using swoole runner but i got 404 when serving static files

No route found for "GET http://localhost:9876/favicon.ico"

Basically i use https://github.com/k911/swoole-bundle and have no issue about it and want to try to use symfony runtime

Layer information

When reading the docs on deploying a Bref runtime, there is a custom layer for PHP runtime. Bref provides a list of layer ARNs at https://runtimes.bref.sh. Is there somewhere similar the layer ARNs for the PHP runtimes are published?

`StreamedResponse` not streaming with swoole integration, when using a `symfony/http-kernel`-based application

While investigating the suitability of runtime/swoole:0.3.0, I ran into a blocker caused by streamed responses.

I was evaluating runtime/swoole because I ran into a problem similar to #50, and as of roadrunner-server/roadrunner#923 (comment), I also couldn't use RoadRunner (which I much prefer to Swoole, at first glance).

A controller producing large, slow responses

Testing this in automation is a bit of a mess (suggestions welcome though), but the idea is that I can stream a response as follows:

final class MyGiantCsvDownloadController
{
    public function someAction(): Response
    {
        return new StreamedResponse(function () {
            foreach (range(1, 5) as $i) {
                echo str_repeat((string) $i, 100000), "\n";
                sleep(1);
            }
        }, Response::HTTP_OK, [
            'Content-Type' => 'text/plain',
        ]);
    }
}

First problem: Symfony\Component\HttpKernel\EventListener\StreamedResponseListener

When interacting with this controller, through runtime/swoole, the respone is not sent via \Runtime\Swoole\SymfonyHttpBridge, where it should happen:

case $sfResponse instanceof StreamedResponse:
ob_start(function ($buffer) use ($response) {
$response->write($buffer);
return '';
});
$sfResponse->sendContent();
ob_end_clean();
$response->end();
break;

In this block, $response->write($buffer); should receive a massive chunk of 111111...22222...<SNIP>...55555, but instead, all output is sent to STDOUT in the worker (not to the response object), and a warning is produced:

<SNIP>55555555555
PHP Warning:  Swoole\Http\Response::write(): data to send is empty in /srv/share/vendor/runtime/swoole/src/SymfonyHttpBridge.php on line 50

Effectively, this write produces nothing, because the response was already sent by symfony/http-kernel:

$response->write($buffer);

After some investigation, I found that the culprit is that symfony/http-kernel sends StreamedResponse through a listener:

https://github.com/symfony/symfony/blob/82e8d23788940421e0ad6e30163242db3ba27a02/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php#L27-L51

I disabled this listener by monkey-patching (while trying out stuff), but if you know a "clean" way to remove it completely from my application, lemme know.

Second problem: response buffer is completely sent in one shot

Assuming we disabled the StreamedResponseListener (clean solution pending), the response content now makes it to Swoole\Http\Response#write($buffer);, but in one big chunk: the HTTP client sees the first byte when swoole finished collecting the whole response.

This is because of this ob_start() call not having a defined buffer size specified:

ob_start(function ($buffer) use ($response) {
$response->write($buffer);
return '';
});

According to the documentation for ob_start():

If the optional parameter chunk_size is passed, the buffer will be flushed after any output call which causes the buffer's length to equal or exceed chunk_size. The default value 0 means that the output function will only be called when the output buffer is closed.

Given that PHP uses a default buffer size of 4096, perhaps it would be a good idea to add one here too? I tried it locally, and my HTTP client starts receiving bytes much earlier than 5 seconds, this way :)

Swoole runtime for Symfony - remember_me does not work.

With this runtime remember_me is not set. I changed few lines of method reflectSymfonyResponse of SymfonyHttpBridge from:

  foreach ($sfResponse->headers->all() as $name => $values) {
      foreach ((array) $values as $value) {
          $response->header((string) $name, $value);
      }
  }

to:

  foreach ($sfResponse->headers->all() as $name => $values) {
      $response->header((string) $name, $values);
  }

And it works now.

Psr-17 does not work with Runtime 6.0 on PHP 8.0

Fatal error: Declaration of Runtime\Psr17\Runtime::getArgument(ReflectionParameter $parameter, ?string $type)
must be compatible with Symfony\Component\Runtime\GenericRuntime::getArgument(ReflectionParameter $parameter, ?string
$type): mixed in /srv/middleware/vendor/runtime/psr-17/src/Runtime.php on line 53

[Swoole] Support Symfony Session

When using Swoole, we need to maintain session (cookie)

Ref: https://github.com/k911/swoole-bundle/blob/develop/src/Bridge/Symfony/HttpFoundation/Session/SetSessionCookieEventListener.php

Create a tag for Symfony 7 support

Hi,

Would it be possible to create a new tag, at least for runtime/frankenphp-symfony?

Currently, it's not possible to install the package with Symfony 7, and breaks projects relying on FrankenPHP such as Symfony Docker.

Thanks!

Swoole runtime error on stress test

When recieving a big number of requests (via stress test, for example) while having hook_flags set to SWOOLE_HOOK_ALL, the Runtime triggers the following Swoole error:

app-1 | [2024-04-16 22:31:36 $7.0] WARNING Server::check_worker_exit_status(): worker(pid=19, id=3) abnormal exit, status=0, signal=11
app-1 | A bug occurred in Swoole-v5.1.1, please report it.
app-1 | The Swoole developers probably don't know about it,
app-1 | and unless you report it, chances are it won't be fixed.
app-1 | You can read How to report a bug doc before submitting any bug reports:
app-1 | >> https://github.com/swoole/swoole-src/blob/master/.github/ISSUE.md
app-1 | Please do not send bug reports in the mailing list or personal letters.
app-1 | The issue page is also suitable to submit feature requests.
app-1 |
app-1 | OS: Linux 5.15.146.1-microsoft-standard-WSL2 #1 SMP Thu Jan 11 04:09:03 UTC 2024 x86_64
app-1 | GCC_VERSION: 12.2.0
app-1 | PHP_VERSION : 8.3.4
app-1 |

If I try to use the hook_flag in a bare Swoole server, without Symfony runtime, I get no error. Example:

<?php

use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Http\Server;

$server = new Server('0.0.0.0', 8000, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);
$server->set([
    'hook_flags' => SWOOLE_HOOK_ALL
]);
$server->on('request', function (Request $request, Response $response) {
    $response->end('Ok');
});

$server->start();

This does not trigger any error on the same stress test.

Steps to reproduce:

  1. Create a Symfony project;
  2. Install Swoole Runtime;
  3. Create a simple controller (just having return $this->json('Ok'); is enough) to /example;
  4. Run the following stress test: docker run --rm -it williamyeh/wrk -c 100 -d 1 -t8 http://host.docker.internal:8000/example;
  5. Check the application logs and see the error.

Simply changing the hook flags to something like SWOOLE_HOOK_ALL & ~SWOOLE_HOOK_FILE & ~SWOOLE_HOOK_STDIO makes the error go away, so I assume it's a problem that happens when logs are added.

I also opened an issue here: swoole/swoole-src#5294

Difference between swoole and swoole-nyholm

It would be nice were the difference between the two runtimes documented; I'm unsure which runtime to select, or whether they are production-ready (the version number doesn't indicate so).

Could you clarify the documentation a little?

PHAR runtime

Requires a few tweaks: symfony/symfony#49443

Some of these could be solved in the runtime instead of relying on the user to do them: https://github.com/box-project/box/blob/main/doc/symfony.md#symfony-support

Runtime would need to ship:

  1. itself
  2. the decorated Symfony Kernel which accepts passing the project root to it, which it then returns via getProjectRoot()
  3. the fixed runtime template to allow supporting older versions of Symfony since they're likely to mislabel the bugfix for the existing template as a feature

/cc @theofidry

Problems integrating Functions when PHP74 is not preview anymore

Hi Tobias.

We are developing with Symfony and Google Functions. It's fresh so there are changes probably. Today we are facing some bad news.

We have problem from today as Coposer 1 run by deploy script from google forces to install their own router and crashes on it.

When I do it myself before, there starts to be conflict and google in autoload and router is invoked as the first so your library is out of work.

Maybe we are not the only one or we deploy differently. Do you have any suggestions?

Is there any way to share knowledge around your project than mailing you?

-------------------- Not installing Google's router:

ERROR: (gcloud.functions.deploy) OperationError: code=3, message=Build failed: Warning from https://repo.packagist.org: Support for Composer 1 is deprecated and some packages will not be available. You should upgrade to Composer 2. See https://blog.packagist.com/deprecating-composer-1-support/
Warning from https://repo.packagist.org: Support for Composer 1 is deprecated and some packages will not be available. You should upgrade to Composer 2. See https://blog.packagist.com/deprecating-composer-1-support/
./composer.json has been updated
Loading composer repositories with package information
Warning from https://repo.packagist.org: Support for Composer 1 is deprecated and some packages will not be available. You should upgrade to Composer 2. See https://blog.packagist.com/deprecating-composer-1-support/
Updating dependencies (including require-dev)
Restricting packages listed in "symfony/symfony" to "5.3.*"
Your requirements could not be resolved to an installable set of packages.

Problem 1
- Conclusion: don't install google/cloud-functions-framework v0.7.2
- Conclusion: don't install google/cloud-functions-framework v0.7.1
- Conclusion: remove guzzlehttp/psr7 2.0.0
- Installation request for google/cloud-functions-framework ^0.7 -> satisfiable by google/cloud-functions-framework[v0.7.0, v0.7.1, v0.7.2].
- Conclusion: don't install guzzlehttp/psr7 2.0.0
- google/cloud-functions-framework v0.7.0 requires guzzlehttp/psr7 ^1.6 -> satisfiable by guzzlehttp/psr7[1.6.0, 1.6.1, 1.7.0, 1.8.0, 1.8.1, 1.8.2].
- Can only install one of: guzzlehttp/psr7[1.7.0, 2.0.0].
- Can only install one of: guzzlehttp/psr7[1.8.0, 2.0.0].
- Can only install one of: guzzlehttp/psr7[1.8.1, 2.0.0].
- Can only install one of: guzzlehttp/psr7[1.8.2, 2.0.0].
- Can only install one of: guzzlehttp/psr7[1.6.0, 2.0.0].
- Can only install one of: guzzlehttp/psr7[1.6.1, 2.0.0].
- Installation request for guzzlehttp/psr7 (locked at 2.0.0) -> satisfiable by guzzlehttp/psr7[2.0.0].

Installation failed, reverting ./composer.json to its original content.; Error ID: caafae72

-------------- Installing Google's router

Nothing to install, update or remove
Generating optimized autoload files
Warning: Ambiguous class resolution, "Google\CloudFunctions\CloudEvent" was found 2x: in "D:/www/cg-platby-test/app/vendor/google/cloud-functions-framework/src/CloudEvent.php" and "D:/ww
w/cg-platby-test/app/vendor/runtime/google-cloud/google\CloudEvent.php", the first will be used.
Warning: Ambiguous class resolution, "Google\CloudFunctions\Context" was found 2x: in "D:/www/cg-platby-test/app/vendor/google/cloud-functions-framework/src/Context.php" and "D:/www/cg-p
latby-test/app/vendor/runtime/google-cloud/google\Context.php", the first will be used.
Warning: Ambiguous class resolution, "Google\CloudFunctions\LegacyEventMapper" was found 2x: in "D:/www/cg-platby-test/app/vendor/google/cloud-functions-framework/src/LegacyEventMapper.p
hp" and "D:/www/cg-platby-test/app/vendor/runtime/google-cloud/google\LegacyEventMapper.php", the first will be used.
37 packages you are using are looking for funding.
Use the composer fund command to find out more!

--------------- and then deployed:

*5 FastCGI sent in stderr: "PHP message: PHP Fatal error: Uncaught InvalidArgumentException: Function target is not callable: "throwException" in /workspace/vendor/google/cloud-functions-framework/router.php:68
Stack trace:
#0 /workspace/vendor/google/cloud-functions-framework/router.php(77): {closure}()

The package name for runtime/psr-17 is confusing

This runtime is more a PSR-15 runtime than a PSR-17 one.

The implementation relies (optionally) on PSR-17 to know how to create the PSR-7 request. But the application logic being run is implemented using the PSR-15 interface.

I originally submitted this in php-runtime/psr-17#2 before realizing it was a subtree split (maybe you should deactivate issue trackers in the subtree repos, or add a config for the issue creation redirecting to the mono-repo)

[Bref] What's the correct configuration for Console app?

I'm trying to migrate my Symfony app with Bref to use Runtime, and I can't get the Lambda function with Symfony Console to work correctly. I also get the same behaviour for Nyholm/bref-runtime-demo.

By "work correctly", I mean that if I execute a command with vendor/bin/bref cli, I can't see any output in my console. The command seems to be executed correctly, its output is logged in CloudWatch, but I can't see the output on my machine.

The readme in this project implicates that console function should keep using bref:layer.console layer:

# pluggins and other options omitted
provider:
  environment:
    APP_RUNTIME: Runtime\Bref\Runtime

functions:
  console:
    handler: bin/console
    layers:
      - ${bref:layer.php-80}
      - ${bref:layer.console}

The demo project suggests that console functions should use bref-extra:symfony-runtime-php-80 layer:

# pluggins and other options omitted
provider:
  environment:
    APP_RUNTIME: Runtime\Bref\Runtime

functions:
  console:
    handler: bin/console
    layers:
      - ${bref:layer.php-80}
      - ${bref-extra:symfony-runtime-php-80}

Either way, the function seems to be executed correctly, but there is no local output.

I was able to display the function's output locally with the following config by forcing it to use GenericRuntime.

# pluggins and other options omitted
provider:
  environment:
    APP_RUNTIME: Runtime\Bref\Runtime

functions:
  console:
    environment:
      APP_RUNTIME: Symfony\Component\Runtime\GenericRuntime
    handler: bin/console
    layers:
      - ${bref:layer.php-80}
      - ${bref:layer.console}

With this setup, the function is executed correctly, its output is logged to CloudWatch, and I can see the output locally in my console. In other words, it works exactly in the same way as the "old" bin/console without Runtime did. But I'm not sure this is the correct approach.

It's probably fine for a console function to write only to stderr if it's a cron, but for functions executed remotely with vendor/bin/bref cli (e.g. DB migrations), their output should be transferred back to the client.

Am I missing something?

Cannot register middleware (or configure) server on runtime/react

The runtime/react instantiates an HttpServer for you which is where one would define middleware. Can we abstract this in a decent way? Maybe we can add an optional $middlewares argument to getRunner().

Or something a bit more long-term for people that want to tweak it different ways, perhaps there's a way were we can just extend the ServerFactory to use an HttpServer we configure. Not sure how that would work though. Maybe replace new HttpServer with $this->getHttpServer(), return a new one by default, or whatever the user decided if we've registered that class somehow... perhaps as an option argument to getRunner().

Session Handling with RoadRunnerSymfonyNyholm

Currently there is no session handling implemented in the Runner.php. Symfony itself does never write a session cookie the the Response object so the session cookie is never set.

Expected Behaviour

First I want to demonstrate the expected behaviour, my research here is based on https://sulu.rocks/admin which currently runs on a normal apache server and the symfony demo using dev php webserver.

1. Visiting the Login site

As we see the login page, and have a look at our Cookies. There are 0 cookies set. So if a route is behind a firewall but the route is anonymous available no session cookies are written by symfony.

State Value
Request Cookie
Response Set-Cookie

2. Log into the Application with a false user

If we login to the application but no user is set also no session is started.

State Value
Request Cookie
Response Set-Cookie

3. Log into the Application with a correct user

Log into an application with a correct user not having a session.

State Value
Request Cookie
Response Set-Cookie my_session_id

4. Navigating inside the application

Aslong as we are now navigating the session_id should stay the same and never again a Set-Cookie should be called aslong as the session does not expire.

State Value
Request Cookie my_session_id
Response Set-Cookie

5. Logout from the application

If we are logout of the application the session cookie value be set to deleted and automatically be expired so its also removed from the browser cookies.

State Value
Request Cookie my_session_id
Response Set-Cookie deleted, expired 1970

6. Visiting with an expired session cookie

If we have an expired session cookie in our browser php will again add a Set-Cookie header that the browsers remove it.

State Value
Request Cookie my_session_id
Response Set-Cookie deleted, expired 1970

The Problems

Problem A: Session is not written to the Response Object (solved)

Currently the Session is not written to the Response Object.

Solution A1:

I currently did workaround this by doing this in my modified Runner.php by adding something like this:

                // ...
                $sessionName = $this->sessionOptions['name'] ?? \session_name();
                
                // ...
                    $sessionId = \session_id();

                    // ...
                        $sfResponse->headers->setCookie(
                            Cookie::create(
                                $sessionName,
                                $sessionId,
                                $expires,
                                $this->sessionOptions['cookie_path'] ?? '/',
                                $this->sessionOptions['cookie_domain'] ?? null,
                                $this->sessionOptions['cookie_secure'] ?? null,
                                $this->sessionOptions['cookie_httponly'] ?? true,
                                false,
                                $this->sessionOptions['cookie_samesite'] ?? Cookie::SAMESITE_LAX,
                            )
                        );

Solution A2: Symfony adds an option that the AbstractSessionListener will write this cookie when the session was saved here: https://github.com/symfony/symfony/blob/bbb0a694fbbd5a527b2236636271a47122b5d47c/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php#L110

Problem B: Cleanup session data at end of the request (solved)

As php is never shutdown we have the problem that we need cleanup all session based php states ourselve

Solution B1: I currently did this in the Runner.php by adding a finally block to the run method

finally {
    if (session_status() === PHP_SESSION_ACTIVE) {
         // abort the session if nobody did write it
         session_abort();
    }

    $_SESSION = []; // reset session array
    session_id(''); // else the next request would have the same session_id which would be a security problem
}

Solution B2: Something which maybe should be tackled with in PHP itself to add a function which would reset this. And so also avoid Problem C.

Problem C: SessionId is not read from new Request object (solved)

The session need also be read from the request object. As session_id will return empty string and php will not read it from the $_COOKIE variable.

Solution C1: In the Runner.php I did do this with:

$sessionName = $this->sessionOptions['name'] ?? \session_name();
$requestSessionId = $sfRequest->cookies->get($sessionName, '');
\session_id($requestSessionId);

Problem D: Use Symfony framework bundle options (solved)

As the runner is Symfony specific it should also use the Symfony Framework cookie configuration when it is set.

Solution D1: That the symfony configuration for the cookie is used, I booted once the kernel in the constructor to read the sessionOptions from it and use them to set the session cookie in the run method as seen above.

    /**
     * @var array<string, mixed>
     */
    private $sessionOptions;

    public function __construct(Kernel $kernel, ?HttpFoundationFactoryInterface $httpFoundationFactory = null, ?HttpMessageFactoryInterface $httpMessageFactory = null)
    {
        $this->kernel = $kernel;
        // ...
        
        $kernel->boot();
        $container = $kernel->getContainer();
        /** @var array<string, mixed> $sessionOptions */
        $this->sessionOptions = $container->getParameter('session.storage.options');
        $kernel->shutdown();
    }

Problem E: Write session only when really started or session id changed (unsolved)

Currently inside the Runner.php we don't know if a session is started or not this is because symfony does when calling session->save() inside the AbstractSessionListener, reseting the session state in its NativeSessionStorage. There would only be 1 hint that a session was started here which is the closed = true flag, sadly this variable is not exposed and so we never now in the Runner.php if a session was really started or not.

Solution E1: Symfony writes the session cookie at https://github.com/symfony/symfony/blob/bbb0a694fbbd5a527b2236636271a47122b5d47c/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php#L110 when a specific option is enabled.

Problem F: Unset Session Cookie when destroyed or expired (unsolved)

Currently we know never when a session is getting destroyed or should be removed so Set-Cookie with deleted and expire 1970 is never called.

I currently have yet a solution for this, see Point 5. and 6. above when this happens

Question: How do I create pools?

So I have php-runtime working with swoole and symfony. Works like a charm! The only thing I cant seem to work out is to create pools for lets say SQL or Redis.

Previously I had created a command that runs a HttpServer and also spun up some workers for pools etc to keep connections open.

Can't seem to find any answers here in the issue history or anywhere else for that matter.

Kind regards,
Rogier

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.