forked from os-autoinst/os-autoinst
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4b05d9d
commit b0e33d6
Showing
3 changed files
with
181 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
#!/usr/bin/perl | ||
# Copyright 2022 SUSE LLC | ||
# SPDX-License-Identifier: GPL-2.0-or-later | ||
# | ||
|
||
use Mojo::Base -strict, -signatures; | ||
|
||
use Getopt::Long; | ||
use Mojo::Cookie::Request; | ||
use Mojo::IOLoop; | ||
use Mojo::IOLoop::Server; | ||
use Mojo::IOLoop::Stream; | ||
use Mojo::Log; | ||
use Mojo::UserAgent; | ||
|
||
package OpenQA::Isotovideo::dewebsockify; | ||
use Exporter 'import'; | ||
our @EXPORT_OK = ('main'); | ||
|
||
sub main { | ||
my ($args) = @_; | ||
die "Arguments must be a hash reference\n" unless defined $args && ref($args) eq 'HASH'; | ||
|
||
my $ws_url = $args->{websocketurl} or die "websocket URL missing\n"; | ||
my $port = $args->{listenport} // 5900; | ||
my $cookie = $args->{cookie} // undef; | ||
my $log = Mojo::Log->new(level => $args->{loglevel} // 'info'); | ||
|
||
$log->debug("websocket url: $ws_url"); | ||
$log->debug("listen port: $port"); | ||
$log->debug("cookie: " . $cookie) if $cookie; | ||
|
||
# create listen socket | ||
my $ua = Mojo::UserAgent->new; | ||
my $server = Mojo::IOLoop::Server->new; | ||
my $ws_connection; | ||
my $stream; | ||
my @tosend; | ||
$ua->insecure($args->{insecure} // 0); | ||
|
||
# accept new connections | ||
$server->on(accept => sub ($server, $handle) { | ||
if ($stream) { | ||
$log->info('Rejecting new client; already one client connected'); | ||
return undef; | ||
} | ||
$stream = Mojo::IOLoop::Stream->new($handle); | ||
$stream->start; | ||
$stream->reactor->start unless $stream->reactor->is_running; | ||
|
||
# establish websocket connection | ||
if (!$ws_connection) { | ||
$log->info("Establishing WebSocket connection to $ws_url"); | ||
@tosend = (); | ||
my $tx = $ua->build_websocket_tx($ws_url); | ||
my $req = $tx->req; | ||
my $headers = $tx->req->headers; | ||
$req->cookies($cookie) if $cookie; | ||
$headers->add(Pragma => 'no-cache'); | ||
$headers->add('Sec-WebSocket-Protocol' => 'binary, vmware-vvc'); | ||
$ua->start($tx => sub ($ua, $tx) { | ||
|
||
# handle errors | ||
if (!$tx->is_websocket) { | ||
if (my $err = $tx->error) { | ||
$log->error($err->{code} ? "WebSocket $err->{code} response: $err->{message}" | ||
: "WebSocket connection error: $err->{message}"); | ||
} | ||
else { | ||
$log->error('Unable to upgrade to WebSocket connection'); | ||
} | ||
my $body = $tx->res->body; | ||
$log->trace($body) if $body; | ||
$ws_connection = undef; | ||
$stream->close_gracefully if $stream; | ||
return undef; | ||
} | ||
$log->info('WebSocket connection established'); | ||
$tx->max_websocket_size(1024**3); # required to avoid 1009 error, at least when using raw encoding | ||
$ws_connection = $tx; | ||
|
||
# pass data from websocket to raw socket | ||
$tx->on(text => sub ($tx, $text) { | ||
$log->trace("WebSocket text message: $text"); | ||
}); | ||
$tx->on(binary => sub ($tx, $bytes) { | ||
$log->trace("WebSocket binary message:\n" . sprintf("%v02X", $bytes)); | ||
$stream->write($bytes) if $stream; | ||
}); | ||
|
||
# pass pending data from raw socket to websocket | ||
$tx->send($_) for @tosend; | ||
@tosend = (); | ||
|
||
# handle websocket connection finish | ||
# note: Terminating here because at least for VMWare one needed a new URL/ticket anyways. | ||
$tx->on(finish => sub ($tx, $code, $reason) { | ||
$log->info("WebSocket closed with status $code."); | ||
$ws_connection = undef; | ||
$stream->close_gracefully if $stream; | ||
Mojo::IOLoop->stop_gracefully; | ||
}); | ||
}); | ||
} | ||
|
||
# pass data from raw socket to websocket | ||
$stream->on(read => sub ($s, $bytes) { | ||
if ($ws_connection) { | ||
$log->debug("Raw socket message:\n" . sprintf("%v02X", $bytes)); # uncoverable statement | ||
$ws_connection->send({binary => $bytes}); # uncoverable statement | ||
} | ||
else { | ||
$log->debug("Raw socket message (forwarding later):\n" . sprintf("%v02X", $bytes)); # uncoverable statement | ||
push @tosend, $bytes; # uncoverable statement | ||
} | ||
}); | ||
|
||
# handle raw socket close/error | ||
$stream->on(close => sub ($s) { | ||
$log->info('Client closed connection'); | ||
$stream = undef; | ||
$ws_connection ? $ws_connection->finish : Mojo::IOLoop->stop_gracefully; | ||
}); | ||
$stream->on(error => sub ($stream, $err) { | ||
$log->error("Client error: $err"); # uncoverable statement | ||
}); | ||
|
||
$log->info('Client accepted'); | ||
}); | ||
$server->listen(port => $port); | ||
$server->start; | ||
Mojo::IOLoop->start unless Mojo::IOLoop->is_running; | ||
} | ||
|
||
# Entry point for script execution | ||
sub parse_args { | ||
my %options; | ||
GetOptions(\%options, 'websocketurl=s', 'listenport=s', 'cookie=s', 'loglevel=s', 'insecure', 'help|h|?') | ||
or usage(1); | ||
usage(0) if $options{help}; | ||
return \%options; | ||
} | ||
|
||
sub usage ($r = 0) { | ||
say 'Listens on a TCP port forwarding data to the specified websocket server | ||
example: --websocketurl wss://... --listenport 590x --cookie "vmware_client=VMware; some_session=foobar" --insecure'; | ||
exit $r; | ||
} | ||
|
||
main(parse_args()) if not caller(); | ||
1; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,134 +1,29 @@ | ||
#!/usr/bin/perl | ||
# Copyright 2022 SUSE LLC | ||
# SPDX-License-Identifier: GPL-2.0-or-later | ||
# | ||
|
||
use Mojo::Base -strict, -signatures; | ||
|
||
use Getopt::Long; | ||
use Mojo::Cookie::Request; | ||
use Mojo::IOLoop; | ||
use Mojo::IOLoop::Server; | ||
use Mojo::IOLoop::Stream; | ||
use Mojo::Log; | ||
use Mojo::UserAgent; | ||
|
||
sub usage ($r = 0) { | ||
say 'Listens on a TCP port forwarding data to the specified websocket server | ||
example: --websocketurl wss://... --listenport 590x --cookie "vmware_client=VMware; some_session=foobar" --insecure'; | ||
exit $r; | ||
#!/usr/bin/env perl | ||
use strict; | ||
use warnings; | ||
|
||
use FindBin; | ||
use lib "$FindBin::Bin/../lib"; # Ensure OpenQA namespace is in @INC | ||
use OpenQA::Isotovideo::dewebsockify; | ||
|
||
my %args; | ||
GetOptions(\%args, 'websocketurl=s', 'listenport=s', 'cookie=s', 'loglevel=s', 'insecure', 'help|h|?') | ||
or usage(1); | ||
usage(0) if $args{help}; | ||
|
||
OpenQA::Isotovideo::dewebsockify::main(\%args); | ||
|
||
sub usage { | ||
print <<"END_USAGE"; | ||
Usage: $0 --websocketurl <URL> [--listenport <PORT>] [--cookie <COOKIE>] [--loglevel <LEVEL>] [--insecure] | ||
Options: | ||
--websocketurl The WebSocket server URL (required). | ||
--listenport Port to listen on (default: 5900). | ||
--cookie Cookie to include in WebSocket requests. | ||
--loglevel Log level (default: 'info'). | ||
--insecure Allow insecure WebSocket connections. | ||
--help, -h, -? Show this help message. | ||
END_USAGE | ||
exit $_[0]; | ||
} | ||
|
||
# read CLI args | ||
my %options; | ||
GetOptions(\%options, 'websocketurl=s', 'listenport=s', 'cookie=s', 'loglevel=s', 'insecure', 'help|h|?') or usage(1); | ||
usage if $options{help}; | ||
my $ws_url = $options{websocketurl} or die "websocket URL missing\n"; | ||
my $port = $options{listenport} // 5900; | ||
my $cookie = $options{cookie} ? $options{cookie} : undef; | ||
my $log = Mojo::Log->new(level => $options{loglevel} // 'info'); | ||
$log->debug("websocket url: $ws_url"); | ||
$log->debug("listen port: $port"); | ||
$log->debug("cookie: " . $cookie) if $cookie; | ||
|
||
# create listen socket | ||
my $ua = Mojo::UserAgent->new; | ||
my $server = Mojo::IOLoop::Server->new; | ||
my $ws_connection; | ||
my $stream; | ||
my @tosend; | ||
$ua->insecure($options{insecure} // 0); | ||
|
||
# accept new connections | ||
$server->on(accept => sub ($server, $handle) { | ||
if ($stream) { | ||
$log->info('Rejecting new client; already one client connected'); | ||
return undef; | ||
} | ||
$stream = Mojo::IOLoop::Stream->new($handle); | ||
$stream->start; | ||
$stream->reactor->start unless $stream->reactor->is_running; | ||
|
||
# establish websocket connection | ||
if (!$ws_connection) { | ||
$log->info("Establishing WebSocket connection to $ws_url"); | ||
@tosend = (); | ||
my $tx = $ua->build_websocket_tx($ws_url); | ||
my $req = $tx->req; | ||
my $headers = $tx->req->headers; | ||
$req->cookies($cookie) if $cookie; | ||
$headers->add(Pragma => 'no-cache'); | ||
$headers->add('Sec-WebSocket-Protocol' => 'binary, vmware-vvc'); | ||
$ua->start($tx => sub ($ua, $tx) { | ||
|
||
# handle errors | ||
if (!$tx->is_websocket) { | ||
if (my $err = $tx->error) { | ||
$log->error($err->{code} ? "WebSocket $err->{code} response: $err->{message}" | ||
: "WebSocket connection error: $err->{message}"); | ||
} | ||
else { | ||
$log->error('Unable to upgrade to WebSocket connection'); | ||
} | ||
my $body = $tx->res->body; | ||
$log->trace($body) if $body; | ||
$ws_connection = undef; | ||
$stream->close_gracefully if $stream; | ||
return undef; | ||
} | ||
$log->info('WebSocket connection established'); | ||
$tx->max_websocket_size(1024**3); # required to avoid 1009 error, at least when using raw encoding | ||
$ws_connection = $tx; | ||
|
||
# pass data from websocket to raw socket | ||
$tx->on(text => sub ($tx, $text) { | ||
$log->trace("WebSocket text message: $text"); | ||
}); | ||
$tx->on(binary => sub ($tx, $bytes) { | ||
$log->trace("WebSocket binary message:\n" . sprintf("%v02X", $bytes)); | ||
$stream->write($bytes) if $stream; | ||
}); | ||
|
||
# pass pending data from raw socket to websocket | ||
$tx->send($_) for @tosend; | ||
@tosend = (); | ||
|
||
# handle websocket connection finish | ||
# note: Terminating here because at least for VMWare one needed a new URL/ticket anyways. | ||
$tx->on(finish => sub ($tx, $code, $reason) { | ||
$log->info("WebSocket closed with status $code."); | ||
$ws_connection = undef; | ||
$stream->close_gracefully if $stream; | ||
Mojo::IOLoop->stop_gracefully; | ||
}); | ||
}); | ||
} | ||
|
||
# pass data from raw socket to websocket | ||
$stream->on(read => sub ($s, $bytes) { | ||
if ($ws_connection) { | ||
$log->debug("Raw socket message:\n" . sprintf("%v02X", $bytes)); # uncoverable statement | ||
$ws_connection->send({binary => $bytes}); # uncoverable statement | ||
} | ||
else { | ||
$log->debug("Raw socket message (forwarding later):\n" . sprintf("%v02X", $bytes)); # uncoverable statement | ||
push @tosend, $bytes; # uncoverable statement | ||
} | ||
}); | ||
|
||
# handle raw socket close/error | ||
$stream->on(close => sub ($s) { | ||
$log->info('Client closed connection'); | ||
$stream = undef; | ||
$ws_connection ? $ws_connection->finish : Mojo::IOLoop->stop_gracefully; | ||
}); | ||
$stream->on(error => sub ($stream, $err) { | ||
$log->error("Client error: $err"); # uncoverable statement | ||
}); | ||
|
||
$log->info('Client accepted'); | ||
}); | ||
|
||
$server->listen(port => $port); | ||
$server->start; | ||
Mojo::IOLoop->start unless Mojo::IOLoop->is_running; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters