]> git.proxmox.com Git - pve-http-server.git/blob - simple-demo.pl
set CN=$nodename for demo server certificate
[pve-http-server.git] / simple-demo.pl
1 #!/usr/bin/perl
2
3 package DemoServer;
4
5 use strict;
6 use warnings;
7 use HTTP::Status qw(:constants);
8 use URI::Escape;
9
10 use PVE::APIServer::AnyEvent;
11 use PVE::Exception qw(raise_param_exc);
12
13 use base('PVE::APIServer::AnyEvent');
14
15 use Digest::MD5;
16
17 my $secret = Digest::MD5::md5_base64($$ . time());
18
19 sub create_ticket {
20 my ($username) = @_;
21
22 return "$username:$secret";
23 }
24
25 sub auth_handler {
26 my ($self, $method, $rel_uri, $ticket, $token, $peer_host) = @_;
27
28 # explicitly allow some calls without authentication
29 if ($rel_uri eq '/access/ticket' &&
30 ($method eq 'POST' || $method eq 'GET')) {
31 return; # allow call to create ticket
32 }
33
34 die "no ticket" if !defined($ticket);
35
36 my ($userid, $rest) = split(/:/, $ticket, 2);
37 die "invalid unsername" if $userid ne 'demo';
38 die "invalid ticket" if $rest ne $secret;
39
40 return {
41 ticket => $ticket,
42 userid => $userid,
43 };
44 }
45
46 sub rest_handler {
47 my ($self, $clientip, $method, $rel_uri, $auth, $params) = @_;
48
49 my $resp = {
50 status => HTTP_NOT_IMPLEMENTED,
51 message => "Method '$method $rel_uri' not implemented",
52 };
53 if ($rel_uri eq '/access/ticket') {
54 if ($method eq 'POST') {
55 if ($params->{username} && $params->{username} eq 'demo' &&
56 $params->{password} && $params->{password} eq 'demo') {
57 return {
58 status => HTTP_OK,
59 data => {
60 ticket => create_ticket($params->{username}),
61 },
62 };
63 }
64 return $resp;
65 } elsif ($method eq 'GET') {
66 # this is allowed to display the login form
67 return { status => HTTP_OK, data => {} };
68 } else {
69 return $resp;
70 }
71 }
72
73 $resp = {
74 data => {
75 method => $method,
76 clientip => $clientip,
77 rel_uri => $rel_uri,
78 auth => $auth,
79 params => $params,
80 },
81 info => { description => "You called API method '$method $rel_uri'" },
82 status => HTTP_OK,
83 };
84
85 return $resp;
86 }
87
88
89 package main;
90
91 use strict;
92 use warnings;
93
94 use Socket qw(IPPROTO_TCP TCP_NODELAY SOMAXCONN);
95 use IO::Socket::IP;
96 use HTTP::Headers;
97 use HTTP::Response;
98
99 use PVE::Tools qw(run_command);
100 use PVE::INotify;
101 use PVE::APIServer::Formatter::Standard;
102 use PVE::APIServer::Formatter::HTML;
103
104 my $nodename = PVE::INotify::nodename();
105 my $port = 9999;
106
107 my $cert_file = "simple-demo.pem";
108
109 if (! -f $cert_file) {
110 print "generating demo server certificate\n";
111 my $cmd = ['openssl', 'req', '-batch', '-x509', '-newkey', 'rsa:4096',
112 '-nodes', '-keyout', $cert_file, '-out', $cert_file,
113 '-subj', "/CN=$nodename/",
114 '-days', '3650'];
115 run_command($cmd);
116 }
117
118 my $socket = IO::Socket::IP->new(
119 LocalAddr => $nodename,
120 LocalPort => $port,
121 Listen => SOMAXCONN,
122 Proto => 'tcp',
123 GetAddrInfoFlags => 0,
124 ReuseAddr => 1) ||
125 die "unable to create socket - $@\n";
126
127 # we often observe delays when using Nagle algorithm,
128 # so we disable that to maximize performance
129 setsockopt($socket, IPPROTO_TCP, TCP_NODELAY, 1);
130
131 my $accept_lock_fn = "simple-demo.lck";
132 my $lockfh = IO::File->new(">>${accept_lock_fn}") ||
133 die "unable to open lock file '${accept_lock_fn}' - $!\n";
134
135 my $server = DemoServer->new(
136 socket => $socket,
137 lockfile => $accept_lock_fn,
138 lockfh => $lockfh,
139 title => 'Simple Demo API',
140 logfh => \*STDOUT,
141 tls_ctx => { verify => 0, cert_file => $cert_file },
142 pages => {
143 '/' => sub { get_index($nodename, @_) },
144 },
145 );
146
147 # NOTE: Requests to non-API pages are not authenticated
148 # so you must be very careful here
149
150 my $root_page = <<__EOD__;
151 <!DOCTYPE html>
152 <html>
153 <head>
154 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
155 <meta http-equiv="X-UA-Compatible" content="IE=edge">
156 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
157 <title>Simple Demo Server</title>
158 </head>
159 <body>
160 <h1>Simple Demo Server ($nodename)</h1>
161
162 You can browse the API <a href='/api2/html' >here</a>. Please sign
163 in with usrename <b>demo</b> and passwort <b>demo</b>.
164
165 </body>
166 </html>
167 __EOD__
168
169 sub get_index {
170 my ($nodename, $server, $r, $args) = @_;
171
172 my $headers = HTTP::Headers->new(Content_Type => "text/html; charset=utf-8");
173 my $resp = HTTP::Response->new(200, "OK", $headers, $root_page);
174
175 }
176
177 print "demo server listens at: https://$nodename:$port/\n";
178
179 $server->run();