use HTTP::Request;
use HTTP::Response;
use Data::Dumper;
+use JSON;
my $limit_max_headers = 30;
my $limit_max_header_size = 8*1024;
die "websocket_proxy: missing port or socket\n";
}
+ my $encode = sub {
+ my ($data, $opcode) = @_;
+
+ my $string;
+ my $payload;
+ if ($binary) {
+ $string = $opcode ? $opcode : "\x82"; # binary frame
+ $payload = $$data;
+ } else {
+ $string = $opcode ? $opcode : "\x81"; # text frame
+ $payload = encode_base64($$data, '');
+ }
+
+ my $payload_len = length($payload);
+ if ($payload_len <= 125) {
+ $string .= pack 'C', $payload_len;
+ } elsif ($payload_len <= 0xffff) {
+ $string .= pack 'C', 126;
+ $string .= pack 'n', $payload_len;
+ } else {
+ $string .= pack 'C', 127;
+ $string .= pack 'Q>', $payload_len;
+ }
+ $string .= $payload;
+ return $string;
+ };
+
tcp_connect $remhost, $remport, sub {
my ($fh) = @_
or die "connect to '$remhost:$remport' failed: $!";
my $len = length($hdl->{rbuf});
my $data = substr($hdl->{rbuf}, 0, $len > $max_payload_size ? $max_payload_size : $len, '');
- my $string;
- my $payload;
-
- if ($binary) {
- $string = "\x82"; # binary frame
- $payload = $data;
- } else {
- $string = "\x81"; # text frame
- $payload = encode_base64($data, '');
- }
-
- my $payload_len = length($payload);
- if ($payload_len <= 125) {
- $string .= pack 'C', $payload_len;
- } elsif ($payload_len <= 0xffff) {
- $string .= pack 'C', 126;
- $string .= pack 'n', $payload_len;
- } else {
- $string .= pack 'C', 127;
- $string .= pack 'Q>', $payload_len;
- }
- $string .= $payload;
+ my $string = $encode->(\$data);
$reqstate->{hdl}->push_write($string) if $reqstate->{hdl};
};
my $data = substr($hdl->{rbuf}, 0, $offset + 4 + $payload_len, ''); # now consume data
- my @mask = (unpack('C', substr($data, $offset+0, 1)),
- unpack('C', substr($data, $offset+1, 1)),
- unpack('C', substr($data, $offset+2, 1)),
- unpack('C', substr($data, $offset+3, 1)));
-
+ my $mask = substr($data, $offset, 4);
$offset += 4;
my $payload = substr($data, $offset, $payload_len);
- for (my $i = 0; $i < $payload_len; $i++) {
- my $d = unpack('C', substr($payload, $i, 1));
- my $n = $d ^ $mask[$i % 4];
- substr($payload, $i, 1, pack('C', $n));
+ # NULL-mask might be used over TLS, skip to increase performance
+ if ($mask ne pack('N', 0)) {
+ # repeat 4 byte mask to payload length + up to 4 byte
+ $mask = $mask x (int($payload_len / 4) + 1);
+ # truncate mask to payload length
+ substr($mask, $payload_len) = "";
+ # (un-)apply mask
+ $payload ^= $mask;
}
$payload = decode_base64($payload) if !$binary;
} elsif ($opcode == 8) {
my $statuscode = unpack ("n", $payload);
print "websocket received close. status code: '$statuscode'\n" if $self->{debug};
- if ($reqstate->{proxyhdl}) {
- $reqstate->{proxyhdl}->push_shutdown();
- }
+ if ($reqstate->{proxyhdl}) {
+ $reqstate->{proxyhdl}->push_shutdown();
+ }
$hdl->push_shutdown();
+ } elsif ($opcode == 9) {
+ # ping received, schedule pong
+ $reqstate->{hdl}->push_write($encode->(\$payload, "\x8A")) if $reqstate->{hdl};
+ } elsif ($opcode == 0xA) {
+ # pong received, continue
} else {
die "received unhandled websocket opcode $opcode\n";
}
my $params = {};
if ($method eq 'PUT' || $method eq 'POST') {
- $params = decode_urlencoded($r->content);
+ my $ct;
+ if (my $ctype = $r->header('Content-Type')) {
+ $ct = parse_content_type($ctype);
+ }
+ if (defined($ct) && $ct eq 'application/json') {
+ $params = decode_json($r->content);
+ } else {
+ $params = decode_urlencoded($r->content);
+ }
}
my $query_params = decode_urlencoded($r->url->query());
eval {
my ($minport, $maxport) = PVE::Tools::spice_port_range();
- die "Port $spiceport is not allowed" if ($spiceport < $minport || $spiceport > $maxport);
+ if ($spiceport < $minport || $spiceport > $maxport) {
+ die "SPICE Port $spiceport is not in allowed range ($minport, $maxport)\n";
+ }
my $clientip = $reqstate->{peer_host};
my $r = $reqstate->{request};
}
my $ctype = $r->header('Content-Type');
- my ($ct, $boundary) = parse_content_type($ctype) if $ctype;
+ my ($ct, $boundary);
+ ($ct, $boundary)= parse_content_type($ctype) if $ctype;
if ($auth->{isUpload} && !$self->{trusted_env}) {
die "upload 'Content-Type '$ctype' not implemented\n"
return;
}
- if (!$ct || $ct eq 'application/x-www-form-urlencoded') {
+ 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);