Perl script as a router between icecast2 and VLC client - perl

I wrote a Perl script to act as router between an icecast2 server and a VLC client. I start the script via
plackup --listen :8000 testStreamingServer.pl --debug
The problem is, I always get to the print "Starting server\n"; line but neither VLC, Firefox or curl can connect to the audio stream. There is no further info on the server side. No debug message, nothing.
Here is the code:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
use LWP::UserAgent;
use Plack::Runner;
use File::Temp qw(tempfile);
my $source_url = 'http://server1.example.com/stream.mp3';
my $redirect_url = 'http://server2.example.com/stream.mp3';
my $ua = LWP::UserAgent->new();
print "Starting server\n";
my $app = sub {
print "Request received\n";
my $response = $ua->get($source_url);
if ($response->is_success) {
print "Source server is online, stream is playable\n";
my ($fh, $filename) = tempfile();
my $content_response = $ua->get($source_url, ':content_file' => $filename);
my $headers = $content_response->headers();
print Dumper $headers;
my $size = -s $filename;
open(my $file, "<", $filename);
my $file_content = do { local $/; <$file> };
close $file;
return [
200,
[ 'Content-Type' => 'audio/mpeg',
'Content-Length' => $size,
'icy-metaint' => 8192,
'icy-name' => 'My Stream',
'icy-description' => 'My stream description',
'icy-genre' => 'variety',
'icy-pub' => 1,
'icy-br' => 128,
'Connection' => 'close' ],
[ $file_content ],
];
} else {
print "Source server is offline, redirecting to backup stream\n";
return [
302,
[ 'Location' => $redirect_url, 'Connection' => 'close' ],
[],
];
}
};
my $runner = Plack::Runner->new;
$runner->parse_options(qw(--listen :8000 --server HTTP::Server::PSGI));
$runner->run($app);
print "Server started\n";
I tried print debugging, line by line. I am clueless right now.

Related

How to write a client program for Net::WebSocket::Server program?

I have a server program that listens on 9000 port. But I can't find a way to write a client program for that server that connects server at 9000 port. Here is the main part of server program:
use strict;
use warnings;
use Net::WebSocket::Server;
my $port = "9000";
my $msg_count = 0;
print "starting server on $port \n\n";
my $count = 2400;
Net::WebSocket::Server->new(
listen => $port,
silence_max => 5,
tick_period => 300,
on_tick => sub {
my ($serv) = #_;
print "connections >> " . $serv->connections . "\n";
print $_->ip() for( $serv->connections() ); print "\n";
print $_->port() for( $serv->connections() ); print "\n\n";
$count++;
},
on_connect => sub {
my ($serv, $conn) = #_;
$conn->on(
handshake => sub {
my ($conn, $handshake) = #_;
my $tmp = $handshake->req->origin;
print "here ... $tmp \n\n";
},
utf8 => sub {
my ($conn, $msg) = #_;
my $IP = $conn->ip();
my $PORT = $conn->port();
my $SERVER = $conn->server();
my $SOCKET = $conn->socket();
my $str = Dumper $SOCKET;
I searched internet and what that sounds understandable to me is the following client program:
use strict;
use warnings;
use IO::Socket::SSL;
my $cl=IO::Socket::SSL->new("http://localhost:9000") or die "error=$!, ssl_error=$SSL_ERROR";
if($cl) {
$cl->connect_SSL or die $#;
# Something about certificates?
$cl->syswrite("Command");
close($cl);
}
But its not working. The error client program generates is as follows:
Expected 'PeerService' at client2.pl line 5.
I am newbie in Socket programming and currently understanding websockets programming in Perl.
Note: I am on windows platform.
I ran the example code suggested https://stackoverflow.com/questions/37318581/simple-perl-websocket-client. It gives error "Can't use an undefined value as a subroutine reference at C:/Strawberry/perl/site/lib/Protocol/WebSocket/Client.pm line 103.":
use strict;
use warnings;
use Protocol::WebSocket::Client;
my $client = Protocol::WebSocket::Client->new(url => 'ws://localhost:9000') or die "$!";
my $reply = "Free\n";
# Sends a correct handshake header
$client->connect or die "$!";
# Register on connect handler
$client->on(
connect => sub {
$client->write('hi there');
}
) or die "$!";
# Parses incoming data and on every frame calls on_read
$client->read($reply);
print "$reply\n";
# Sends correct close header
$client->disconnect;
Please investigate following demo code snippets for WebSocket Server and Client.
Note: please do not forget to alter code to match your server origin (ip address and port)
use strict;
use warnings;
use feature 'say';
use Net::WebSocket::Server;
my $origin = 'http://192.168.1.160:8080'; # server origin
my $port = 8080;
$| = 1;
say "Starting server on $port";
Net::WebSocket::Server->new(
listen => $port,
tick_period => 60,
on_tick => sub {
my ($serv) = #_;
my $stamp = 'Server time: ' . scalar localtime;
$_->send_utf8($stamp) for $serv->connections;
},
on_connect => sub {
my ($serv, $conn) = #_;
$conn->on(
handshake => sub {
my ($conn, $handshake) = #_;
$conn->disconnect() unless $handshake->req->origin eq $origin;
},
ready => sub {
my ($conn) = #_;
say "Client: connect IP $conn->{ip} PORT $conn->{port}";
my $msg = 'Connected server time is ' . scalar localtime . "\n";
$_->send_utf8($msg) for $conn->server->connections;
},
utf8 => sub {
my ($conn, $msg) = #_;
say "Client message: $conn->{ip} $msg";
$_->send_utf8('Server reply: ' . $msg)
for $conn->server->connections;
$conn->disconnect() if $msg eq 'exit';
},
binary => sub {
my ($conn, $msg) = #_;
$_->send_binary($msg) for $conn->server->connections;
},
pong => sub {
my ($conn, $msg) = #_;
$_->send_utf8($msg) for $conn->server->connections;
},
disconnect => sub {
my ($conn, $code, $reason) = #_;
say "Client: disconnect IP $conn->{ip} PORT $conn->{port}";
},
);
},
)->start;
Client
use strict;
use warnings;
use feature 'say';
use IO::Async::Loop;
use Net::Async::WebSocket::Client;
my $HOST = '192.168.1.160';
my $PORT = 8080;
my $loop = IO::Async::Loop->new;
my $client = Net::Async::WebSocket::Client->new(
on_text_frame => sub {
my ( $self, $frame ) = #_;
say $frame;
},
);
my $input = IO::Async::Stream->new_for_stdin(
on_read => sub {
my ( $self, $buffref, $eof ) = #_;
my $msg;
$msg = $1 while $$buffref =~ s/^(.*)\n//;
$client->send_text_frame( $msg );
$loop->loop_stop if $msg eq 'exit';
return 0;
},
);
$loop->add( $client );
$loop->add( $input );
$client->connect(
url => "ws://$HOST:$PORT/"
)->then( sub {
say 'Successfully connected to server';
$client->send_text_frame( scalar localtime );
})->get;
$loop->run;
say 'Bye, until next time';
exit 0;
References:
Net::WebSocket::Server
Net::Async::WebSocket::Client
IO::Async::Loop

mod_perl and CGI behavior

This has got to be something silly I'm doing wrong. It's such a newbie type problem.
The original script is something that sits and waits for a 3rd party to connect and POST some xml to it, it takes that xml, does some validation, and stores it in a db. That part is fine. The problem is my response. I'm trying to use the header() function from CGI and it's just not behaving. It comes up blank. Obviously I could just do this manually and just print the header string, but now I'm really curious why this is behaving so strangely.
Here is a stripped down test version of the cgi script:
use strict;
use warnings;
use Data::Dumper::Names;
use CGI qw(:standard);
use Apache2::Connection ();
use Apache2::RequestRec ();
$| = 1;
# Grab the request object provided by mod_perl.
our $request_obj = shift;
our $connection = $request_obj->connection;
our $remote_ip = $connection->client_ip();
my $cgi = CGI->new($request_obj->args());
print STDERR Dumper($cgi);
my $input = $cgi->param('POSTDATA');
print STDERR Dumper($input);
my $cgi_header = $cgi->header();
print STDERR Dumper($cgi_header);
my $cgi_full_header = $cgi->header(-type => 'application/xml');
print STDERR Dumper($cgi_full_header);
my $q = CGI->new({});
print STDERR Dumper($q);
my $q_header = $q->header();
print STDERR Dumper($q_header);
my $q_full_header = $q->header(-type => 'application/xml' );
print STDERR Dumper($q_full_header);
And the output:
$cgi = bless( {
'.r' => bless( do{\(my $o = '94118860562256')}, 'Apache2::RequestRec' ),
'param' => {
'POSTDATA' => [
'test'
],
'XForms:Model' => [
'test'
]
},
'use_tempfile' => 1,
'.fieldnames' => {},
'.charset' => 'ISO-8859-1',
'escape' => 1,
'.parameters' => [
'XForms:Model',
'POSTDATA'
]
}, 'CGI' );
$input = 'test';
$cgi_header = '';
$cgi_full_header = '';
$q = bless( {
'.parameters' => [
'XForms:Model',
'POSTDATA'
],
'escape' => 1,
'.fieldnames' => {},
'.charset' => 'ISO-8859-1',
'use_tempfile' => 1,
'.r' => bless( do{\(my $o = '94118860562256')}, 'Apache2::RequestRec' ),
'param' => {
'POSTDATA' => [
''
],
'XForms:Model' => [
''
]
}
}, 'CGI' );
$q_header = '';
$q_full_header = '';
And here is the simple test script I'm using to send the POST.
#!/perl/bin/perl
use strict;
use warnings;
use DBI;
use URI;
use LWP::UserAgent;
use Data::Dumper::Names;
my $ua = LWP::UserAgent->new;
$ua->max_size( 131072 );
$ua->agent('test_xml_pusher');
$ua->ssl_opts(verify_hostname => 0);
my $url = URI->new;
$url->scheme('https');
$url->host('xxxxxxxxxxxxxxxxxxxxxxxxx');
$url->port(443);
$url->path_segments('test.cgi');
# Yes, I know... it's not valid xml... don't care for the purposes of this test.
#
my $xml = 'test';
my $response = $ua->post( $url, Content => $xml, 'Content-Type' => 'application/xml' );
print Dumper($response);
my $status_line = $response->status_line;
print Dumper($status_line);
my $content = $response->content;
print Dumper($content);
So why is $cgi_header empty? And why does $q end up being a reference to the same thing as $cgi even though I tried initializing it as my $q = CGI->new({});? (I also tried empty quotes instead of empty brackets.)
Any thoughts?
Thanks!
My environment is a centos 7 server running apache httpd 2.4.34 with mod_perl 2.0.11 and perl 5.22.4. (httpd is installed from from SCL, but perl and mod_perl are installed from source.)
--
Andy

500 Can't connect to example.com:443 (LWP::Protocol::https::Socket: SSL connect attempt failed with unknown error SSL wants a read first)

I want to export issue from the GitLab CE using this script.
I ran the following commands on macOS 10.10.5:
sudo /usr/bin/perl -MCPAN -e'install Text::CSV_XS'
sudo cpan Mozilla::CA
sudo cpan install LWP
sudo cpan LWP::Protocol::https
However, I still get this error:
500 Can't connect to gitlab.lrz.de:443 (LWP::Protocol::https::Socket: SSL connect attempt failed with unknown error SSL wants a read first) /Users/kiaora/Downloads/get-all-project-issues.pl line 41.`
This is literally my first experience with perl. Any help is greatly appreciated.
I only changed these lines:
my $PROJECT_ID="myProjectID"; # numeric project id, can be found in project -> general settings
my $GITLAB_API_PRIVATE_TOKEN='myToken(scope read_repository)'; # obtained from https://gitlab.lrz.de/profile/personal_access_tokens
my $baseurl = "https://gitlab.lrz.de/"; # change if using a private install
EDIT:
#!/usr/bin/perl -w
use strict;
use LWP::UserAgent;
use Text::CSV_XS qw( csv );
use JSON::PP qw(decode_json);
# Uncomment these for debugging
# use LWP::ConsoleLogger::Easy qw( debug_ua );
# use Data::Dumper;
my $PROJECT_ID="--my-project-id--"; # numeric project id, can be found in project -> general settings
my $GITLAB_API_PRIVATE_TOKEN='--my-api-private-token--'; # obtained from https://gitlab.com/profile/personal_access_tokens
my $baseurl = "https://gitlab.lrz.de/"; # change if using a private install
$baseurl .= "api/v4/";
my $issuesurl = $baseurl."projects/".$PROJECT_ID."/issues";
my #issues = ();
my $page = 1;
my $totalpages;
do
{
my %query_hash = (
'per_page' => 100,
'page' => $page
);
print "Fetching page $page".(defined($totalpages)?" (of $totalpages)":"")."\n";
my $ua = LWP::UserAgent->new();
# debug_ua($ua);
$ua->default_header('PRIVATE-TOKEN' => $GITLAB_API_PRIVATE_TOKEN);
my $uri = URI->new($issuesurl);
$uri->query_form(%query_hash);
my $resp = $ua->get($uri);
if (!$resp->is_success) {
die $resp->status_line;
}
$totalpages = int($resp->header("X-Total-Pages"));
my $resptext;
$resptext = $resp->decoded_content;
my $issuedata = decode_json($resptext);
push(#issues, #{$issuedata});
}
while ($page++ < $totalpages);
my $outputfname = "issues.csv";
my $csv = Text::CSV_XS->new ({ binary => 1, eol => $/ });
open my $fh, ">", $outputfname or die "$outputfname: $!";
my #headings = [
"URL",
"Milestone",
"Author",
"Title",
"Description",
"State",
"Assignees",
"Labels",
"Created At",
"Updated At",
"Closed At",
"Due date",
"Confidental",
"Weight",
"Locked",
"Time estimate",
"Time spent",
"Human Time estimate",
"Human Time spent",
];
$csv->print ($fh, #headings) or $csv->error_diag;
foreach my $i (#issues)
{
# print Dumper([$i])."\n";
my #values = [
$i->{'web_url'},
$i->{'milestone'}->{'title'},
$i->{'author'}->{'username'},
$i->{'title'},
$i->{'description'},
$i->{'state'},
join(',', map {$_->{'username'}} #{$i->{'assignees'}}),
join(',', #{$i->{'labels'}}),
$i->{'created_at'},
$i->{'updated_at'},
$i->{'closed_at'},
$i->{'due_date'},
$i->{'confidential'},
$i->{'weight'},
$i->{'discussion_locked'},
$i->{'time_stats'}->{'time_estimate'},
$i->{'time_stats'}->{'total_time_spent'},
$i->{'time_stats'}->{'human_time_estimate'},
$i->{'time_stats'}->{'human_total_time_spent'},
$i->{'closed_by'}->{'username'},
];
$csv->print ($fh, #values) or $csv->error_diag;
}
close $fh or die "$outputfname: $!";
print "Issues saved to $outputfname\n";

Using a proxy with WWW::Mechanize

Im trying to perform tests on my servers upload function using several proxies. By the time I get my ip through api.ipify.org. The console outputs my true ip, not the proxies ip.
use strict; use warnings;
use WWW::Mechanize;
my $file = "proxies.txt";
open (FH, "< $file") or die "Can't open $file for read: $!";
my #lines = <FH>;
close FH or die "Cannot close $file: $!";
print "Loaded proxy list";
my $m = WWW::Mechanize->new(
autocheck => 1,
agent_alias => 'Mozilla',
cookie_jar => {},
ssl_opts => {verify_hostname => 0},
quiet => 0,
);
my $httpl = "http://";
$m->no_proxy('localhost');
my $ua = LWP::UserAgent->new;
for(my $i=0; $i < 47; $i++)
{
$m->proxy('http', $httpl . '' . $lines[$i]);
print "Connecting to proxy " . $lines[$i];
$m->get("https://api.ipify.org?format=json");
print $m->content;
for(my $j = 0; $j <= 10; $j++){
$m->get("http://example.com");
system("node genran.js");
$m->post('http://example.com/upload.php',
Content_Type => "form-data",
Content => [
'password' => '',
'public' => 'yes',
'uploadContent' => [ 'spam.txt', 'Love pecons', 'Content_$
file => [ 'x86.png', 'image_name', 'Content-Type' => 'ima$
]
);
print $m->content;
}}
$m->proxy('http', $httpl . '' . $lines[$i]);
print "Connecting to proxy " . $lines[$i];
$m->get("https://api.ipify.org?format=json");
You set only a proxy for http but make a https request. You need to set the proxy for https too like this:
$m->proxy('https', ... put your https proxy here ...);
Or to use the same proxy for multiple protocols:
$m->proxy(['http','https'], ... );
Also, make sure that you use at least version 6.06 of LWP::UserAgent and LWP::Protocol::https for proper support of proxies with https, i.e.
use LWP::UserAgent;
print LWP::UserAgent->VERSION,"\n";
use LWP::Protocol::https;
print LWP::Protocol::https->VERSION,"\n";

Web Server with HTTP::Daemon, HTML not rendering

I figured out a way to create a quick web server in Perl:
#!/usr/bin/env perl -s -wl
use strict;
use HTTP::Daemon;
use HTTP::Headers;
use HTTP::Response;
sub help {
print "$0 -port=<port-number>";
}
our $port;
our $addr = "localhost";
$port = 9000 unless defined $port;
my $server = HTTP::Daemon->new(
LocalAddr => $addr,
LocalPort => $port,
Listen => 1,
Reuse => 1,
);
die "$0: Could not setup server" unless $server;
print "$0: http://$addr:$port Accepting clients";
while (my $client = $server->accept()) {
print "$0: Client received";
$client->autoflush(1);
my $request = $client->get_request;
print "$0: Client's Request Received";
print "$0: Request: " . $request->method;
if ($request->method eq 'GET') {
my $header = HTTP::Headers->new;
$header->date( time );
$header->server("$0");
$header->content_type('text/html');
my $content = "<!doctype html><html><head><title>Hello World</title></head><body><h1>Hello World!</h1></body></html>";
my $response = HTTP::Response->new(200);
$response->content($content);
$response->header("Content-Type" => "text/html");
$client->send_response($response);
}
print "$0: Closed";
$client->close;
undef($client);
}
But for some reason, every time I access localhost:9000 it displays part of the HTTP Header - date, server, content-length and content-type - and the content. It doesn't render it as an HTML page. Is there something I'm missing?
This is caused by the -l switch:
#!/usr/bin/env perl -s -wl
^
It sets the output record separator to the value of the input record separator (a newline), which results in additional newlines being added to HTTP server output, and a broken HTTP response.