+ # disconnect the client so they may immediately connect again via HTTPS
+ $self->client_do_disconnect($reqstate);
+
+ return 0;
+}
+
+sub authenticate_and_handle_request {
+ my ($self, $reqstate) = @_;
+
+ my $request = $reqstate->{request};
+ my $method = $request->method();
+
+ my $path = uri_unescape($request->uri->path());
+ my $base_uri = $self->{base_uri};
+
+ my $auth = {};
+
+ if ($self->{spiceproxy}) {
+ my $connect_str = $request->header('Host');
+ 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 = $request->header('CSRFPreventionToken');
+ my $cookie = $request->header('Cookie');
+ my $auth_header = $request->header('Authorization');
+
+ # prefer actual cookie
+ my $ticket = PVE::APIServer::Formatter::extract_auth_value(
+ $cookie,
+ $self->{cookie_name}
+ );
+
+ # fallback to cookie in 'Authorization' header
+ if (!$ticket) {
+ $ticket = PVE::APIServer::Formatter::extract_auth_value(
+ $auth_header,
+ $self->{cookie_name}
+ );
+ }
+
+ # finally, fallback to API token if no ticket has been provided so far
+ my $api_token;
+ if (!$ticket) {
+ $api_token = PVE::APIServer::Formatter::extract_auth_value(
+ $auth_header,
+ $self->{apitoken_name}
+ );
+ }
+
+ 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;