From a55a38db0276a8ce1a09392573af6e1305cbaba9 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 1 Aug 2022 21:24:42 +0000 Subject: httpd: make internals slightly more generic This brings the HTTP server closer to the IMAP/NNTP/POP3 implementations and eliminates package-wide globals in PublicInbox::HTTPD. The end goal is to be able to host completely different PSGI applications on different listen ports. --- lib/PublicInbox/Daemon.pm | 10 ++++----- lib/PublicInbox/HTTP.pm | 10 ++++----- lib/PublicInbox/HTTPD.pm | 55 ++++++++++++++++++++++++++--------------------- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm index bceae6e5..1af03cc4 100644 --- a/lib/PublicInbox/Daemon.pm +++ b/lib/PublicInbox/Daemon.pm @@ -81,11 +81,11 @@ sub load_mod ($) { my $mod = $modc.'D'; eval "require $mod"; # IMAPD|HTTPD|NNTPD|POP3D die $@ if $@; - my %xn = map { $_ => $mod->can($_) } qw(refresh post_accept); - $xn{tlsd} = $mod->new if $mod->can('refresh_groups'); #!HTTPD - my $tlsd = $xn{tlsd}; - $xn{refresh} //= sub { $tlsd->refresh_groups(@_) }; - $xn{post_accept} //= sub { $modc->new($_[0], $tlsd) }; + my %xn; + my $tlsd = $xn{tlsd} = $mod->new; + $xn{refresh} = sub { $tlsd->refresh_groups(@_) }; + $xn{post_accept} = $tlsd->can('post_accept_cb') ? + $tlsd->post_accept_cb : sub { $modc->new($_[0], $tlsd) }; $xn{af_default} = 'httpready' if $modc eq 'PublicInbox::HTTP'; \%xn; } diff --git a/lib/PublicInbox/HTTP.pm b/lib/PublicInbox/HTTP.pm index 76e978a2..669211e3 100644 --- a/lib/PublicInbox/HTTP.pm +++ b/lib/PublicInbox/HTTP.pm @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2021 all contributors +# Copyright (C) all contributors # License: AGPL-3.0+ # # Generic PSGI server for convenience. It aims to provide @@ -52,8 +52,8 @@ sub http_date () { } sub new ($$$) { - my ($class, $sock, $addr, $httpd) = @_; - my $self = bless { httpd => $httpd }, $class; + my ($class, $sock, $addr, $srv_env) = @_; + my $self = bless { srv_env => $srv_env }, $class; my $ev = EPOLLIN; my $wbuf; if ($sock->can('accept_SSL') && !$sock->accept_SSL) { @@ -78,7 +78,7 @@ sub event_step { # called by PublicInbox::DS return read_input($self) if ref($self->{env}); my $rbuf = $self->{rbuf} // (\(my $x = '')); - my %env = %{$self->{httpd}->{env}}; # full hash copy + my %env = %{$self->{srv_env}}; # full hash copy my $r; while (($r = parse_http_request($$rbuf, \%env)) < 0) { # We do not support Trailers in chunked requests, for @@ -145,7 +145,7 @@ sub app_dispatch { # note: NOT $self->{sock}, we want our close (+ PublicInbox::DS::close), # to do proper cleanup: $env->{'psgix.io'} = $self; # for ->close or async_pass - my $res = Plack::Util::run_app($self->{httpd}->{app}, $env); + my $res = Plack::Util::run_app($env->{'pi-httpd.app'}, $env); eval { if (ref($res) eq 'CODE') { $res->(sub { response_write($self, $env, $_[0]) }); diff --git a/lib/PublicInbox/HTTPD.pm b/lib/PublicInbox/HTTPD.pm index 715e4538..bcdbb9f9 100644 --- a/lib/PublicInbox/HTTPD.pm +++ b/lib/PublicInbox/HTTPD.pm @@ -13,12 +13,17 @@ use PublicInbox::HTTPD::Async; sub pi_httpd_async { PublicInbox::HTTPD::Async->new(@_) } -sub new { - my ($class, $sock, $app, $client) = @_; - my $n = getsockname($sock) or die "not a socket: $sock $!\n"; - my ($host, $port) = PublicInbox::Daemon::host_with_port($n); +# we have a different env for ever listener socket for +# SERVER_NAME, SERVER_PORT and psgi.url_scheme +# envs: listener FD => PSGI env +sub new { bless { envs => {} }, __PACKAGE__ } - my %env = ( +# this becomes {srv_env} in PublicInbox::HTTP +sub env_for ($$$) { + my ($self, $srv, $client) = @_; + my $n = getsockname($srv) or die "not a socket: $srv $!\n"; + my ($host, $port) = PublicInbox::Daemon::host_with_port($n); + { SERVER_NAME => $host, SERVER_PORT => $port, SCRIPT_NAME => '', @@ -40,26 +45,24 @@ sub new { # this to limit git-http-backend(1) parallelism. # We also check for the truthiness of this to # detect when to use async paths for slow blobs - 'pi-httpd.async' => \&pi_httpd_async - ); - bless { app => $app, env => \%env }, $class; + 'pi-httpd.async' => \&pi_httpd_async, + 'pi-httpd.app' => $self->{app}, + } } -my %httpds; # per-listen-FD mapping for HTTPD->{env}->{SERVER_} -my $default_app; # ugh... - -sub refresh { +sub refresh_groups { + my ($self) = @_; + my $app; if (@main::ARGV) { - eval { $default_app = Plack::Util::load_psgi(@ARGV) }; - if ($@) { - die $@, -"$0 runs in /, command-line paths must be absolute\n"; - } + eval { $app = Plack::Util::load_psgi(@ARGV) }; + die $@, <new; $www->preload; - $default_app = builder { + $app = builder { eval { enable 'ReverseProxy' }; $@ and warn <call(@_) }; }; } - %httpds = (); # invalidate cache + $_->{'pi-httpd.app'} = $app for values %{$self->{envs}}; + $self->{app} = $app; } -sub post_accept { # Listener->{post_accept} - my ($client, $addr, $srv) = @_; # $_[3] - tls_wrap (unused) - my $httpd = $httpds{fileno($srv)} //= - __PACKAGE__->new($srv, $default_app, $client); - PublicInbox::HTTP->new($client, $addr, $httpd), +sub post_accept_cb { # for Listener->{post_accept} + my ($self) = @_; + sub { + my ($client, $addr, $srv) = @_; # $_[4] - tls_wrap (unused) + PublicInbox::HTTP->new($client, $addr, + $self->{envs}->{fileno($srv)} //= + env_for($self, $srv, $client)); + } } 1; -- cgit v1.2.3-24-ge0c7