From: Max Carrara <m.carrara@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH http-server 2/2] http-server: anyevent: replace request processing subroutine
Date: Fri, 17 Feb 2023 16:25:17 +0100 [thread overview]
Message-ID: <20230217152517.84874-3-m.carrara@proxmox.com> (raw)
In-Reply-To: <20230217152517.84874-1-m.carrara@proxmox.com>
All usages of the `push_request_header` subroutine are replaced with
`begin_process_next_request`. Consequently, the `push_request_header`
and `unshift_read_header` subroutines are removed, as they are not
used anywhere anymore.
Signed-off-by: Max Carrara <m.carrara@proxmox.com>
---
src/PVE/APIServer/AnyEvent.pm | 277 +---------------------------------
1 file changed, 2 insertions(+), 275 deletions(-)
diff --git a/src/PVE/APIServer/AnyEvent.pm b/src/PVE/APIServer/AnyEvent.pm
index bf7957a..86f5e07 100644
--- a/src/PVE/APIServer/AnyEvent.pm
+++ b/src/PVE/APIServer/AnyEvent.pm
@@ -181,7 +181,7 @@ sub finish_response {
if (!$self->{end_loop} && $reqstate->{keep_alive} > 0) {
# print "KEEPALIVE $reqstate->{keep_alive}\n" if $self->{debug};
$hdl->on_read(sub {
- eval { $self->push_request_header($reqstate); };
+ eval { $self->begin_process_next_request($reqstate); };
warn $@ if $@;
});
} else {
@@ -1297,279 +1297,6 @@ sub get_upload_filename {
return "/var/tmp/pveupload-" . Digest::MD5::md5_hex($tmpfile_seq_no . time() . $$);
}
-sub unshift_read_header {
- my ($self, $reqstate, $state) = @_;
-
- $state = { size => 0, count => 0 } if !$state;
-
- $reqstate->{hdl}->unshift_read(line => sub {
- my ($hdl, $line) = @_;
-
- eval {
- # print "$$: got header: $line\n" if $self->{debug};
-
- die "too many http header lines (> $limit_max_headers)\n" if ++$state->{count} >= $limit_max_headers;
- die "http header too large\n" if ($state->{size} += length($line)) >= $limit_max_header_size;
-
- my $r = $reqstate->{request};
- if ($line eq '') {
-
- my $path = uri_unescape($r->uri->path());
- my $method = $r->method();
-
- $r->push_header($state->{key}, $state->{val})
- if $state->{key};
-
- if (!$known_methods->{$method}) {
- my $resp = HTTP::Response->new(HTTP_NOT_IMPLEMENTED, "method '$method' not available");
- $self->response($reqstate, $resp);
- return;
- }
-
- my $conn = $r->header('Connection');
- my $accept_enc = $r->header('Accept-Encoding');
- $reqstate->{accept_gzip} = ($accept_enc && $accept_enc =~ m/gzip/) ? 1 : 0;
-
- if ($conn) {
- $reqstate->{keep_alive} = 0 if $conn =~ m/close/oi;
- } else {
- if ($reqstate->{proto}->{ver} < 1001) {
- $reqstate->{keep_alive} = 0;
- }
- }
-
- my $te = $r->header('Transfer-Encoding');
- if ($te && lc($te) eq 'chunked') {
- # Handle chunked transfer encoding
- $self->error($reqstate, 501, "chunked transfer encoding not supported");
- return;
- } elsif ($te) {
- $self->error($reqstate, 501, "Unknown transfer encoding '$te'");
- return;
- }
-
- my $pveclientip = $r->header('PVEClientIP');
- my $base_uri = $self->{base_uri};
-
- # fixme: how can we make PVEClientIP header trusted?
- if ($self->{trusted_env} && $pveclientip) {
- $reqstate->{peer_host} = $pveclientip;
- } else {
- $r->header('PVEClientIP', $reqstate->{peer_host});
- }
-
- my $len = $r->header('Content-Length');
-
- my $host_header = $r->header('Host');
- if (my $rpcenv = $self->{rpcenv}) {
- $rpcenv->set_request_host($host_header);
- }
-
- # header processing complete - authenticate now
-
- my $auth = {};
- if ($self->{spiceproxy}) {
- my $connect_str = $host_header;
- my ($vmid, $node, $port) = $self->verify_spice_connect_url($connect_str);
- if (!(defined($vmid) && $node && $port)) {
- $self->error($reqstate, HTTP_UNAUTHORIZED, "invalid ticket");
- return;
- }
- $self->handle_spice_proxy_request($reqstate, $connect_str, $vmid, $node, $port);
- return;
- } elsif ($path =~ m/^\Q$base_uri\E/) {
- my $token = $r->header('CSRFPreventionToken');
- my $cookie = $r->header('Cookie');
- my $auth_header = $r->header('Authorization');
-
- # prefer actual cookie
- my $ticket = PVE::APIServer::Formatter::extract_auth_value($cookie, $self->{cookie_name});
-
- # fallback to cookie in 'Authorization' header
- $ticket = PVE::APIServer::Formatter::extract_auth_value($auth_header, $self->{cookie_name})
- if !$ticket;
-
- # finally, fallback to API token if no ticket has been provided so far
- my $api_token;
- $api_token = PVE::APIServer::Formatter::extract_auth_value($auth_header, $self->{apitoken_name})
- if !$ticket;
-
- my ($rel_uri, $format) = &$split_abs_uri($path, $self->{base_uri});
- if (!$format) {
- $self->error($reqstate, HTTP_NOT_IMPLEMENTED, "no such uri");
- return;
- }
-
- eval {
- $auth = $self->auth_handler($method, $rel_uri, $ticket, $token, $api_token,
- $reqstate->{peer_host});
- };
- if (my $err = $@) {
- # HACK: see Note 1
- Net::SSLeay::ERR_clear_error();
- # always delay unauthorized calls by 3 seconds
- my $delay = 3;
-
- if (ref($err) eq "PVE::Exception") {
-
- $err->{code} ||= HTTP_INTERNAL_SERVER_ERROR,
- my $resp = HTTP::Response->new($err->{code}, $err->{msg});
- $self->response($reqstate, $resp, undef, 0, $delay);
-
- } elsif (my $formatter = PVE::APIServer::Formatter::get_login_formatter($format)) {
- my ($raw, $ct, $nocomp) =
- $formatter->($path, $auth, $self->{formatter_config});
- my $resp;
- if (ref($raw) && (ref($raw) eq 'HTTP::Response')) {
- $resp = $raw;
- } else {
- $resp = HTTP::Response->new(HTTP_UNAUTHORIZED, "Login Required");
- $resp->header("Content-Type" => $ct);
- $resp->content($raw);
- }
- $self->response($reqstate, $resp, undef, $nocomp, $delay);
- } else {
- my $resp = HTTP::Response->new(HTTP_UNAUTHORIZED, $err);
- $self->response($reqstate, $resp, undef, 0, $delay);
- }
- return;
- }
- }
-
- $reqstate->{log}->{userid} = $auth->{userid};
-
- if ($len) {
-
- if (!($method eq 'PUT' || $method eq 'POST')) {
- $self->error($reqstate, 501, "Unexpected content for method '$method'");
- return;
- }
-
- my $ctype = $r->header('Content-Type');
- my ($ct, $boundary) = $ctype ? parse_content_type($ctype) : ();
-
- if ($auth->{isUpload} && !$self->{trusted_env}) {
- die "upload 'Content-Type '$ctype' not implemented\n"
- if !($boundary && $ct && ($ct eq 'multipart/form-data'));
-
- die "upload without content length header not supported" if !$len;
-
- die "upload without content length header not supported" if !$len;
-
- $self->dprint("start upload $path $ct $boundary");
-
- my $tmpfilename = get_upload_filename();
- my $outfh = IO::File->new($tmpfilename, O_RDWR|O_CREAT|O_EXCL, 0600) ||
- die "unable to create temporary upload file '$tmpfilename'";
-
- $reqstate->{keep_alive} = 0;
-
- my $boundlen = length($boundary) + 8; # \015?\012--$boundary--\015?\012
-
- my $state = {
- size => $len,
- boundary => $boundary,
- ctx => Digest::MD5->new,
- boundlen => $boundlen,
- maxheader => 2048 + $boundlen, # should be large enough
- params => decode_urlencoded($r->url->query()),
- phase => 0,
- read => 0,
- post_size => 0,
- starttime => [gettimeofday],
- outfh => $outfh,
- };
- $reqstate->{tmpfilename} = $tmpfilename;
- $reqstate->{hdl}->on_read(sub {
- $self->file_upload_multipart($reqstate, $auth, $method, $path, $state);
- });
- return;
- }
-
- if ($len > $limit_max_post) {
- $self->error($reqstate, 501, "for data too large");
- return;
- }
-
- if (!$ct || $ct eq 'application/x-www-form-urlencoded' || $ct eq 'application/json') {
- $reqstate->{hdl}->unshift_read(chunk => $len, sub {
- my ($hdl, $data) = @_;
- $r->content($data);
- $self->handle_request($reqstate, $auth, $method, $path);
- });
- } else {
- $self->error($reqstate, 506, "upload 'Content-Type '$ctype' not implemented");
- }
- } else {
- $self->handle_request($reqstate, $auth, $method, $path);
- }
- } elsif ($line =~ /^([^:\s]+)\s*:\s*(.*)/) {
- $r->push_header($state->{key}, $state->{val}) if $state->{key};
- ($state->{key}, $state->{val}) = ($1, $2);
- $self->unshift_read_header($reqstate, $state);
- } elsif ($line =~ /^\s+(.*)/) {
- $state->{val} .= " $1";
- $self->unshift_read_header($reqstate, $state);
- } else {
- $self->error($reqstate, 506, "unable to parse request header");
- }
- };
- warn $@ if $@;
- });
-};
-
-sub push_request_header {
- my ($self, $reqstate) = @_;
-
- eval {
- $reqstate->{hdl}->push_read(line => sub {
- my ($hdl, $line) = @_;
-
- eval {
- # print "got request header: $line\n" if $self->{debug};
-
- $reqstate->{keep_alive}--;
-
- if ($line =~ /(\S+)\040(\S+)\040HTTP\/(\d+)\.(\d+)/o) {
- my ($method, $url, $maj, $min) = ($1, $2, $3, $4);
-
- if ($maj != 1) {
- $self->error($reqstate, 506, "http protocol version $maj.$min not supported");
- return;
- }
- if ($url =~ m|^[^/]*@|) {
- # if an '@' comes before the first slash proxy forwarding might consider
- # the frist part of the url to be part of an authority...
- $self->error($reqstate, 400, "invalid url");
- return;
- }
-
- $self->{request_count}++; # only count valid request headers
- if ($self->{request_count} >= $self->{max_requests}) {
- $self->{end_loop} = 1;
- }
- $reqstate->{log} = { requestline => $line };
- $reqstate->{proto}->{str} = "HTTP/$maj.$min";
- $reqstate->{proto}->{maj} = $maj;
- $reqstate->{proto}->{min} = $min;
- $reqstate->{proto}->{ver} = $maj*1000+$min;
- $reqstate->{request} = HTTP::Request->new($method, $url);
- $reqstate->{starttime} = [gettimeofday],
-
- $self->unshift_read_header($reqstate);
- } elsif ($line eq '') {
- # ignore empty lines before requests (browser bugs?)
- $self->push_request_header($reqstate);
- } else {
- $self->error($reqstate, 400, 'bad request');
- }
- };
- warn $@ if $@;
- });
- };
- warn $@ if $@;
-}
-
sub begin_process_next_request {
my ($self, $reqstate) = @_;
@@ -2243,7 +1970,7 @@ sub accept_connections {
$self->dprint("ACCEPT FH" . $clientfh->fileno() . " CONN$self->{conn_count}");
- $self->push_request_header($reqstate);
+ $self->begin_process_next_request($reqstate);
}
};
--
2.30.2
next prev parent reply other threads:[~2023-02-17 15:25 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-02-17 15:25 [pve-devel] [PATCH http-server 0/2] refactor HTTP request processing Max Carrara
2023-02-17 15:25 ` [pve-devel] [PATCH http-server 1/2] http-server: anyevent: add new subroutine for processing requests Max Carrara
2023-02-28 14:05 ` Fabian Grünbichler
2023-02-17 15:25 ` Max Carrara [this message]
2023-02-17 17:15 ` [pve-devel] [PATCH http-server 0/2] refactor HTTP request processing Thomas Lamprecht
2023-02-20 9:40 ` Max Carrara
2023-02-20 9:59 ` [pve-devel] [PATCH] http-server: redirect HTTP to HTTPS Max Carrara
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230217152517.84874-3-m.carrara@proxmox.com \
--to=m.carrara@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox