Skip to content

Connection leak when using HTTP/1 #390

@danog

Description

@danog

When using HTTP/1, connections are never released, leading to a pileup in the connection pool.

This is caused by the fact that the trailersDeferred created in Http1Connection::readResponse is never resolved.

Reproducer:

#!/usr/bin/env php
<?php declare(strict_types=1);

require dirname(__DIR__) . "/vendor/autoload.php";

use Amp\ByteStream;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request as ClientRequest;
use Amp\Http\HttpStatus;
use Amp\Http\Server\DefaultErrorHandler;
use Amp\Http\Server\Driver\SocketClientFactory;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;
use Amp\Http\Server\SocketHttpServer;
use Amp\Log\ConsoleFormatter;
use Amp\Log\StreamHandler;
use Amp\Socket;
use Monolog\Logger;
use Monolog\Processor\PsrLogMessageProcessor;

use function Amp\async;
use function Amp\Future\await;
use function Amp\trapSignal;

$logHandler = new StreamHandler(ByteStream\getStdout());
$logHandler->pushProcessor(new PsrLogMessageProcessor());
$logHandler->setFormatter(new ConsoleFormatter());
$logger = new Logger('server');
$logger->pushHandler($logHandler);
$logger->useLoggingLoopDetection(false);

$server = new SocketHttpServer(
    $logger,
    new Socket\ResourceServerSocketFactory(),
    new SocketClientFactory($logger),
);

$server->expose("0.0.0.0:1337");
$server->expose("[::]:1337");

$server->start(new class implements RequestHandler {
    public function handleRequest(Request $request): Response
    {
        return new Response(
            status: HttpStatus::OK,
            headers: ["content-type" => "text/plain; charset=utf-8"],
            body: "Hello, World!",
        );
    }
}, new DefaultErrorHandler());

$client = HttpClientBuilder::buildDefault();
for ($x = 0; $x < 10; $x++) {
    $f = [];
    for ($y = 0; $y < 100; $y++) {
        $f []= async(function () use ($client) {
            $client->request(new ClientRequest("http://127.0.0.1:1337"));
        });
    }
    await($f);
}

// Await a termination signal to be received.
$signal = trapSignal([\SIGHUP, \SIGINT, \SIGQUIT, \SIGTERM]);

$logger->info(sprintf("Received signal %d, stopping HTTP server", $signal));

$server->stop();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions