Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
pve-manager
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
pve-manager
Commits
23e0c0ba
Commit
23e0c0ba
authored
Apr 30, 2014
by
Dietmar Maurer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add generic formater support
parent
9bbf4e4b
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
299 additions
and
127 deletions
+299
-127
API2.pm
PVE/API2.pm
+188
-0
HTTPServer.pm
PVE/HTTPServer.pm
+31
-7
REST.pm
PVE/REST.pm
+80
-120
No files found.
PVE/API2.pm
View file @
23e0c0ba
...
...
@@ -4,7 +4,12 @@ use strict;
use
warnings
;
use
PVE::
pvecfg
;
use
PVE::
REST
;
use
PVE::
RESTHandler
;
use
HTTP::
Status
;
use
JSON
;
use
HTML::
Entities
;
use
PVE::
JSONSchema
;
use
base
qw(PVE::RESTHandler)
;
...
...
@@ -108,4 +113,187 @@ __PACKAGE__->register_method ({
return
$res
;
}});
# register result formaters
my
$prepare_response_data
=
sub
{
my
(
$format
,
$res
)
=
@_
;
my
$success
=
1
;
my
$new
=
{
data
=>
$res
->
{
data
},
};
if
(
scalar
(
keys
%
{
$res
->
{
errors
}}))
{
$success
=
0
;
$new
->
{
errors
}
=
$res
->
{
errors
};
}
if
(
$format
eq
'
extjs
'
||
$format
eq
'
htmljs
')
{
# HACK: extjs wants 'success' property instead of useful HTTP status codes
if
(
HTTP::Status::
is_error
(
$res
->
{
status
}))
{
$success
=
0
;
$new
->
{
message
}
=
$res
->
{
message
}
||
status_message
(
$res
->
{
status
});
$new
->
{
status
}
=
$res
->
{
status
}
||
200
;
$res
->
{
message
}
=
undef
;
$res
->
{
status
}
=
200
;
}
$new
->
{
success
}
=
$success
;
}
if
(
$success
&&
$res
->
{
total
})
{
$new
->
{
total
}
=
$res
->
{
total
};
}
if
(
$success
&&
$res
->
{
changes
})
{
$new
->
{
changes
}
=
$res
->
{
changes
};
}
$res
->
{
data
}
=
$new
;
};
PVE::REST::
register_formater
('
json
',
sub
{
my
(
$res
,
$data
,
$param
,
$path
,
$auth
)
=
@_
;
my
$nocomp
=
0
;
my
$ct
=
'
application/json;charset=UTF-8
';
&
$prepare_response_data
('
json
',
$res
);
my
$raw
=
to_json
(
$res
->
{
data
},
{
utf8
=>
1
,
allow_nonref
=>
1
});
return
(
$raw
,
$ct
,
$nocomp
);
});
PVE::REST::
register_formater
('
extjs
',
sub
{
my
(
$res
,
$data
,
$param
,
$path
,
$auth
)
=
@_
;
my
$nocomp
=
0
;
my
$ct
=
'
application/json;charset=UTF-8
';
&
$prepare_response_data
('
extjs
',
$res
);
my
$raw
=
to_json
(
$res
->
{
data
},
{
utf8
=>
1
,
allow_nonref
=>
1
});
return
(
$raw
,
$ct
,
$nocomp
);
});
PVE::REST::
register_formater
('
htmljs
',
sub
{
my
(
$res
,
$data
,
$param
,
$path
,
$auth
)
=
@_
;
my
$nocomp
=
0
;
# we use this for extjs file upload forms
my
$ct
=
'
text/html;charset=UTF-8
';
&
$prepare_response_data
('
htmljs
',
$res
);
my
$raw
=
encode_entities
(
to_json
(
$res
->
{
data
},
{
allow_nonref
=>
1
}));
return
(
$raw
,
$ct
,
$nocomp
);
});
PVE::REST::
register_formater
('
spiceconfig
',
sub
{
my
(
$res
,
$data
,
$param
,
$path
,
$auth
)
=
@_
;
my
$nocomp
=
0
;
my
$ct
=
'
application/x-virt-viewer;charset=UTF-8
';
&
$prepare_response_data
('
spiceconfig
',
$res
);
$data
=
$res
->
{
data
};
my
$raw
;
if
(
$data
&&
ref
(
$data
)
&&
ref
(
$data
->
{
data
}))
{
$raw
=
"
[virt-viewer]
\n
";
while
(
my
(
$key
,
$value
)
=
each
%
{
$data
->
{
data
}})
{
$raw
.=
"
$key
=
$value
\n
"
if
defined
(
$value
);
}
}
return
(
$raw
,
$ct
,
$nocomp
);
});
PVE::REST::
register_formater
('
png
',
sub
{
my
(
$res
,
$data
,
$param
,
$path
,
$auth
)
=
@_
;
my
$nocomp
=
1
;
my
$ct
=
'
image/png
';
&
$prepare_response_data
('
png
',
$res
);
$data
=
$res
->
{
data
};
# fixme: better to revove that whole png thing ?
my
$filename
;
my
$raw
=
'';
if
(
$data
&&
ref
(
$data
)
&&
ref
(
$data
->
{
data
})
&&
$data
->
{
data
}
->
{
filename
}
&&
defined
(
$data
->
{
data
}
->
{
image
}))
{
$filename
=
$data
->
{
data
}
->
{
filename
};
$raw
=
$data
->
{
data
}
->
{
image
};
}
return
(
$raw
,
$ct
,
$nocomp
);
});
PVE::REST::
register_formater
('
html
',
sub
{
my
(
$res
,
$data
,
$param
,
$path
,
$auth
)
=
@_
;
my
$nocomp
=
0
;
my
$ct
=
'
text/html;charset=UTF-8
';
&
$prepare_response_data
('
html
',
$res
);
$data
=
$res
->
{
data
};
my
$info
=
$res
->
{
info
};
my
$raw
=
"
<html><body>
";
if
(
!
HTTP::Status::
is_success
(
$res
->
{
status
}))
{
my
$msg
=
$res
->
{
message
}
||
'';
$raw
.=
"
<h1>ERROR
$res
->{status}
$msg
</h1>
";
}
my
$lnk
=
PVE::JSONSchema::
method_get_child_link
(
$info
);
if
(
$lnk
&&
$data
&&
$data
->
{
data
}
&&
HTTP::Status::
is_success
(
$res
->
{
status
}))
{
my
$href
=
$lnk
->
{
href
};
if
(
$href
=~
m/^\{(\S+)\}$/
)
{
my
$prop
=
$1
;
$path
=~
s/\/+$//
;
# remove trailing slash
foreach
my
$elem
(
sort
{
$a
->
{
$prop
}
cmp
$b
->
{
$prop
}}
@
{
$data
->
{
data
}})
{
next
if
!
ref
(
$elem
);
if
(
defined
(
my
$value
=
$elem
->
{
$prop
}))
{
if
(
$value
ne
'')
{
if
(
scalar
(
keys
%
$elem
)
>
1
)
{
my
$tv
=
to_json
(
$elem
,
{
allow_nonref
=>
1
,
canonical
=>
1
});
$raw
.=
"
<a href='
$path
/
$value
'>
$value
</a> <pre>
$tv
</pre><br>
";
}
else
{
$raw
.=
"
<a href='
$path
/
$value
'>
$value
</a><br>
";
}
}
}
}
}
}
else
{
$raw
.=
"
<pre>
";
$raw
.=
encode_entities
(
to_json
(
$data
,
{
allow_nonref
=>
1
,
pretty
=>
1
}));
$raw
.=
"
</pre>
";
}
$raw
.=
"
</body></html>
";
return
(
$raw
,
$ct
,
$nocomp
);
});
1
;
PVE/HTTPServer.pm
View file @
23e0c0ba
...
...
@@ -49,7 +49,7 @@ my $baseuri = "/api2";
sub
split_abs_uri
{
my
(
$abs_uri
)
=
@_
;
my
(
$format
,
$rel_uri
)
=
$abs_uri
=~
m/^\Q$baseuri\E\/+(
html|text|json|extjs|png|htmljs|spiceconfig
)(\/.*)?$/
;
my
(
$format
,
$rel_uri
)
=
$abs_uri
=~
m/^\Q$baseuri\E\/+(
[a-z][a-z0-9]+
)(\/.*)?$/
;
$rel_uri
=
'
/
'
if
!
$rel_uri
;
return
wantarray
?
(
$rel_uri
,
$format
)
:
$rel_uri
;
...
...
@@ -445,8 +445,11 @@ sub handle_api2_request {
my
$path
=
$r
->
uri
->
path
();
my
(
$rel_uri
,
$format
)
=
split_abs_uri
(
$path
);
if
(
!
$format
)
{
$self
->
error
(
$reqstate
,
HTTP_NOT_IMPLEMENTED
,
"
no such uri
");
my
$formater
=
PVE::REST::
get_formater
(
$format
);
if
(
!
defined
(
$formater
))
{
$self
->
error
(
$reqstate
,
HTTP_NOT_IMPLEMENTED
,
"
no such uri
$rel_uri
,
$format
");
return
;
}
...
...
@@ -501,9 +504,14 @@ sub handle_api2_request {
$delay
=
0
if
$delay
<
0
;
}
PVE::REST::
prepare_response_data
(
$format
,
$res
);
my
(
$raw
,
$ct
,
$nocomp
)
=
PVE::REST::
format_response_data
(
$format
,
$res
,
$path
);
if
(
$res
->
{
info
}
&&
$res
->
{
info
}
->
{
formater
})
{
if
(
defined
(
my
$func
=
$res
->
{
info
}
->
{
formater
}
->
{
$format
}))
{
$formater
=
$func
;
}
}
my
(
$raw
,
$ct
,
$nocomp
)
=
&
$formater
(
$res
,
$res
->
{
data
},
$path
,
$auth
);
my
$resp
;
if
(
ref
(
$raw
)
&&
(
ref
(
$raw
)
eq
'
HTTP::Response
'))
{
$resp
=
$raw
;
...
...
@@ -954,7 +962,23 @@ sub unshift_read_header {
$rel_uri
,
$ticket
,
$token
);
};
if
(
my
$err
=
$@
)
{
$self
->
error
(
$reqstate
,
HTTP_UNAUTHORIZED
,
$err
);
# always delay unauthorized calls by 3 seconds
my
$delay
=
3
;
if
(
my
$formater
=
PVE::REST::
get_login_formater
(
$format
))
{
my
(
$raw
,
$ct
,
$nocomp
)
=
&
$formater
(
$path
,
$auth
);
my
$resp
;
if
(
ref
(
$raw
)
&&
(
ref
(
$raw
)
eq
'
HTTP::Response
'))
{
$resp
=
$raw
;
}
else
{
$resp
=
HTTP::
Response
->
new
(
HTTP_UNAUTHORIZED
,
"
Login Required
");
$resp
->
header
("
Content-Type
"
=>
$ct
);
$resp
->
content
(
$raw
);
}
$self
->
response
(
$reqstate
,
$resp
,
undef
,
$nocomp
,
3
);
}
else
{
my
$resp
=
HTTP::
Response
->
new
(
HTTP_UNAUTHORIZED
,
$err
);
$self
->
response
(
$reqstate
,
$resp
,
undef
,
0
,
$delay
);
}
return
;
}
}
...
...
PVE/REST.pm
View file @
23e0c0ba
...
...
@@ -52,125 +52,6 @@ sub create_auth_cookie {
return
"
${cookie_name}
=
$encticket
; path=/; secure;
";
}
sub
format_response_data
{
my
(
$format
,
$res
,
$uri
)
=
@_
;
my
$data
=
$res
->
{
data
};
my
$info
=
$res
->
{
info
};
my
(
$ct
,
$raw
,
$nocomp
);
if
(
$format
eq
'
json
')
{
$ct
=
'
application/json;charset=UTF-8
';
$raw
=
to_json
(
$data
,
{
utf8
=>
1
,
allow_nonref
=>
1
});
}
elsif
(
$format
eq
'
html
')
{
$ct
=
'
text/html;charset=UTF-8
';
$raw
=
"
<html><body>
";
if
(
!
is_success
(
$res
->
{
status
}))
{
my
$msg
=
$res
->
{
message
}
||
'';
$raw
.=
"
<h1>ERROR
$res
->{status}
$msg
</h1>
";
}
my
$lnk
=
PVE::JSONSchema::
method_get_child_link
(
$info
);
if
(
$lnk
&&
$data
&&
$data
->
{
data
}
&&
is_success
(
$res
->
{
status
}))
{
my
$href
=
$lnk
->
{
href
};
if
(
$href
=~
m/^\{(\S+)\}$/
)
{
my
$prop
=
$1
;
$uri
=~
s/\/+$//
;
# remove trailing slash
foreach
my
$elem
(
sort
{
$a
->
{
$prop
}
cmp
$b
->
{
$prop
}}
@
{
$data
->
{
data
}})
{
next
if
!
ref
(
$elem
);
if
(
defined
(
my
$value
=
$elem
->
{
$prop
}))
{
if
(
$value
ne
'')
{
if
(
scalar
(
keys
%
$elem
)
>
1
)
{
my
$tv
=
to_json
(
$elem
,
{
allow_nonref
=>
1
,
canonical
=>
1
});
$raw
.=
"
<a href='
$uri
/
$value
'>
$value
</a> <pre>
$tv
</pre><br>
";
}
else
{
$raw
.=
"
<a href='
$uri
/
$value
'>
$value
</a><br>
";
}
}
}
}
}
}
else
{
$raw
.=
"
<pre>
";
$raw
.=
encode_entities
(
to_json
(
$data
,
{
allow_nonref
=>
1
,
pretty
=>
1
}));
$raw
.=
"
</pre>
";
}
$raw
.=
"
</body></html>
";
}
elsif
(
$format
eq
'
png
')
{
$ct
=
'
image/png
';
$nocomp
=
1
;
# fixme: better to revove that whole png thing ?
my
$filename
;
$raw
=
'';
if
(
$data
&&
ref
(
$data
)
&&
ref
(
$data
->
{
data
})
&&
$data
->
{
data
}
->
{
filename
}
&&
defined
(
$data
->
{
data
}
->
{
image
}))
{
$filename
=
$data
->
{
data
}
->
{
filename
};
$raw
=
$data
->
{
data
}
->
{
image
};
}
}
elsif
(
$format
eq
'
extjs
')
{
$ct
=
'
application/json;charset=UTF-8
';
$raw
=
to_json
(
$data
,
{
utf8
=>
1
,
allow_nonref
=>
1
});
}
elsif
(
$format
eq
'
htmljs
')
{
# we use this for extjs file upload forms
$ct
=
'
text/html;charset=UTF-8
';
$raw
=
encode_entities
(
to_json
(
$data
,
{
allow_nonref
=>
1
}));
}
elsif
(
$format
eq
'
spiceconfig
')
{
$ct
=
'
application/x-virt-viewer;charset=UTF-8
';
if
(
$data
&&
ref
(
$data
)
&&
ref
(
$data
->
{
data
}))
{
$raw
=
"
[virt-viewer]
\n
";
while
(
my
(
$key
,
$value
)
=
each
%
{
$data
->
{
data
}})
{
$raw
.=
"
$key
=
$value
\n
"
if
defined
(
$value
);
}
}
}
else
{
$ct
=
'
text/plain;charset=UTF-8
';
$raw
=
to_json
(
$data
,
{
utf8
=>
1
,
allow_nonref
=>
1
,
pretty
=>
1
});
}
return
wantarray
?
(
$raw
,
$ct
,
$nocomp
)
:
$raw
;
}
sub
prepare_response_data
{
my
(
$format
,
$res
)
=
@_
;
my
$success
=
1
;
my
$new
=
{
data
=>
$res
->
{
data
},
};
if
(
scalar
(
keys
%
{
$res
->
{
errors
}}))
{
$success
=
0
;
$new
->
{
errors
}
=
$res
->
{
errors
};
}
if
(
$format
eq
'
extjs
'
||
$format
eq
'
htmljs
')
{
# HACK: extjs wants 'success' property instead of useful HTTP status codes
if
(
is_error
(
$res
->
{
status
}))
{
$success
=
0
;
$new
->
{
message
}
=
$res
->
{
message
}
||
status_message
(
$res
->
{
status
});
$new
->
{
status
}
=
$res
->
{
status
}
||
200
;
$res
->
{
message
}
=
undef
;
$res
->
{
status
}
=
200
;
}
$new
->
{
success
}
=
$success
;
}
if
(
$success
&&
$res
->
{
total
})
{
$new
->
{
total
}
=
$res
->
{
total
};
}
if
(
$success
&&
$res
->
{
changes
})
{
$new
->
{
changes
}
=
$res
->
{
changes
};
}
$res
->
{
data
}
=
$new
;
}
my
$exc_to_res
=
sub
{
my
(
$info
,
$err
,
$status
)
=
@_
;
...
...
@@ -201,7 +82,7 @@ sub auth_handler {
# explicitly allow some calls without auth
if
((
$rel_uri
eq
'
/access/domains
'
&&
$method
eq
'
GET
')
||
(
$rel_uri
eq
'
/access/ticket
'
&&
$method
eq
'
POST
'
))
{
(
$rel_uri
eq
'
/access/ticket
'
&&
(
$method
eq
'
GET
'
||
$method
eq
'
POST
')
))
{
$require_auth
=
0
;
}
...
...
@@ -318,4 +199,83 @@ sub rest_handler {
return
$resp
;
}
# generic formater support
my
$formater_hash
=
{};
sub
register_formater
{
my
(
$format
,
$func
)
=
@_
;
die
"
formater '
$format
' already defined
"
if
$formater_hash
->
{
$format
};
$formater_hash
->
{
$format
}
=
{
func
=>
$func
,
};
}
sub
get_formater
{
my
(
$format
)
=
@_
;
return
undef
if
!
$format
;
my
$info
=
$formater_hash
->
{
$format
};
return
undef
if
!
$info
;
return
$info
->
{
func
};
}
my
$login_formater_hash
=
{};
sub
register_login_formater
{
my
(
$format
,
$func
)
=
@_
;
die
"
login formater '
$format
' already defined
"
if
$login_formater_hash
->
{
$format
};
$login_formater_hash
->
{
$format
}
=
{
func
=>
$func
,
};
}
sub
get_login_formater
{
my
(
$format
)
=
@_
;
return
undef
if
!
$format
;
my
$info
=
$login_formater_hash
->
{
$format
};
return
undef
if
!
$info
;
return
$info
->
{
func
};
}
sub
register_page_formater
{
my
(
%
config
)
=
@_
;
my
$base_handler_class
=
$config
{
base_handler_class
}
||
die
"
missing base_handler_class
";
my
$format
=
$config
{
format
}
||
die
"
missing format
";
die
"
format '
$format
' is not registered
"
if
!
$formater_hash
->
{
$format
};
my
$path
=
$config
{
path
}
||
die
"
missing path
";
my
$method
=
$config
{
method
}
||
die
"
missing method
";
my
$code
=
$config
{
code
}
||
die
"
missing formater code
";
my
$uri_param
=
{};
my
(
$handler
,
$info
)
=
$base_handler_class
->
find_handler
(
$method
,
$path
,
$uri_param
);
die
"
unabe to find handler for '
$method
:
$path
'
"
if
!
(
$handler
&&
$info
);
die
"
duplicate formater for '
$method
:
$path
'
"
if
$info
->
{
formater
}
&&
$info
->
{
formater
}
->
{
$format
};
$info
->
{
formater
}
->
{
$format
}
=
$code
;
}
1
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment