public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
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





  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
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal