From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 5E02593169 for ; Fri, 17 Feb 2023 16:25:29 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 429F68026 for ; Fri, 17 Feb 2023 16:25:29 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Fri, 17 Feb 2023 16:25:27 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id B50FD47782 for ; Fri, 17 Feb 2023 16:25:27 +0100 (CET) From: Max Carrara To: pve-devel@lists.proxmox.com Date: Fri, 17 Feb 2023 16:25:17 +0100 Message-Id: <20230217152517.84874-3-m.carrara@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20230217152517.84874-1-m.carrara@proxmox.com> References: <20230217152517.84874-1-m.carrara@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.050 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [PATCH http-server 2/2] http-server: anyevent: replace request processing subroutine X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 17 Feb 2023 15:25:29 -0000 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 --- 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