Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it possible to pass functions to be run prior to flushing the response #114

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

thekid
Copy link
Member

@thekid thekid commented May 19, 2024

This makes it easy to implement Server timing, which appears in the browser's developer console, as follows:

// Use performance.getEntriesByType('navigation')[0].serverTiming[0] to access via JS
// or check the browser's development console, network tab, "Timing" for a request
class WithServerTiming implements Filter {

  public function filter($req, $res, $invocation) {
    $start= microtime(true);
    $res->flushing(function($res) use($start) {
      $res->header('Server-Timing', sprintf('total;dur=%.3f', 1000 * (microtime(true) - $start)));
    });

    // This function will now run as soon as either a handler down the chain explicitely flushes the
    // response, or when this happens at the end of the request/response lifecycle in the server.
    return $invocation->proceed($req, $res);
  }
}

Ideally, we would do this via HTTP trailers which can be appended to the response, but this only works in Firefox, see https://www.fastly.com/blog/supercharging-server-timing-http-trailers/, so we need a way to append this information at the last moment possible: before finishing sending the headers. This is roughly the equivalent of https://github.com/jshttp/on-headers (see how NuxtJS does this) and intercepting Ktor's send pipeline, see https://ktor.io/docs/server-custom-plugins-base-api.html


For server metrics in ExpressJS, see https://www.npmjs.com/package/server-timing. They add a setMetric() method to response. While I don't like monkey-patching, I'd like to have an easy way of adding metrics. Maybe something like this?

// Inside above filter
$metrics= new Metrics();
$res->flushing(function($res) use($metrics) {
  foreach ($metrics as $name => $value) {
    $res->header('Server-Timing', sprintf('%s;dur=%.3f', $name, $value), true);
  }
});

return $invocation->proceed($metrics->attach($req), $res);

// Inside handler
function($req, $res) {
  $metrics= Metrics::for($req) ?? Metrics::$NULL;

  $metrics->start('db');
  $this->db->query(/* ... */);
  $metrics->stop('db');

  $res->send('OK', 'text/plain');
}

@thekid thekid changed the title Make it possible to pass a function to be run prior to flushing the response Make it possible to pass functions to be run prior to flushing the response May 19, 2024
@thekid
Copy link
Member Author

thekid commented May 20, 2024

Question is whether we need to be able to hook into any other parts of the send pipeline, like writing, flushing or closing; or if we should leave that up to something wrapping around web.io.Output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant