← Index
NYTProf Performance Profile   « block view • line view • sub view »
For /usr/local/src/github.com/foswiki/core/bin/view
  Run on Sun Dec 4 17:17:59 2011
Reported on Sun Dec 4 17:26:34 2011

Filename/usr/local/src/github.com/foswiki/core/lib/Foswiki/UI/Rest.pm
StatementsExecuted 26 statements in 2.37ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
61183µs83µsFoswiki::UI::Rest::::registerRESTHandlerFoswiki::UI::Rest::registerRESTHandler
11137µs47µsFoswiki::UI::Rest::::BEGIN@13Foswiki::UI::Rest::BEGIN@13
11122µs46µsFoswiki::UI::Rest::::BEGIN@14Foswiki::UI::Rest::BEGIN@14
11120µs439µsFoswiki::UI::Rest::::BEGIN@16Foswiki::UI::Rest::BEGIN@16
11119µs54µsFoswiki::UI::Rest::::BEGIN@253Foswiki::UI::Rest::BEGIN@253
11117µs46µsFoswiki::UI::Rest::::BEGIN@256Foswiki::UI::Rest::BEGIN@256
11110µs10µsFoswiki::UI::Rest::::BEGIN@15Foswiki::UI::Rest::BEGIN@15
0000s0sFoswiki::UI::Rest::::__ANON__[:153]Foswiki::UI::Rest::__ANON__[:153]
0000s0sFoswiki::UI::Rest::::__ANON__[:160]Foswiki::UI::Rest::__ANON__[:160]
0000s0sFoswiki::UI::Rest::::restFoswiki::UI::Rest::rest
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5---+ package Foswiki::UI::Rest
6
7UI delegate for REST interface
8
9=cut
10
11package Foswiki::UI::Rest;
12
13258µs257µs
# spent 47µs (37+10) within Foswiki::UI::Rest::BEGIN@13 which was called: # once (37µs+10µs) by Foswiki::Func::registerRESTHandler at line 13
use strict;
# spent 47µs making 1 call to Foswiki::UI::Rest::BEGIN@13 # spent 10µs making 1 call to strict::import
14251µs270µs
# spent 46µs (22+24) within Foswiki::UI::Rest::BEGIN@14 which was called: # once (22µs+24µs) by Foswiki::Func::registerRESTHandler at line 14
use warnings;
# spent 46µs making 1 call to Foswiki::UI::Rest::BEGIN@14 # spent 24µs making 1 call to warnings::import
15254µs110µs
# spent 10µs within Foswiki::UI::Rest::BEGIN@15 which was called: # once (10µs+0s) by Foswiki::Func::registerRESTHandler at line 15
use Foswiki ();
# spent 10µs making 1 call to Foswiki::UI::Rest::BEGIN@15
1621.74ms2858µs
# spent 439µs (20+419) within Foswiki::UI::Rest::BEGIN@16 which was called: # once (20µs+419µs) by Foswiki::Func::registerRESTHandler at line 16
use Error qw( :try );
# spent 439µs making 1 call to Foswiki::UI::Rest::BEGIN@16 # spent 419µs making 1 call to Error::import
17
1812µsour %restDispatch;
19
20=begin TML=
21
22---++ StaticMethod registerRESTHandler( $subject, $verb, \&fn, %options )
23
24Adds a function to the dispatch table of the REST interface
25for a given subject. See System.CommandAndCGIScripts#rest for more info.
26
27 * =$subject= - The subject under which the function will be registered.
28 * =$verb= - The verb under which the function will be registered.
29 * =\&fn= - Reference to the function.
30
31The handler function must be of the form:
32<verbatim>
33sub handler(\%session, $subject, $verb) -> $text
34</verbatim>
35where:
36 * =\%session= - a reference to the Foswiki session object (may be ignored)
37 * =$subject= - The invoked subject (may be ignored)
38 * =$verb= - The invoked verb (may be ignored)
39
40Additional options are set in the =%options= hash. These options are important
41to ensuring that requests to your handler can't be used in cross-scripting
42attacks, or used for phishing.
43 * =authenticate= - use this boolean option to require authentication for the
44 handler. If this is set, then an authenticated session must be in place
45 or the REST call will be rejected with a 401 (Unauthorized) status code.
46 By default, rest handlers do *not* require authentication.
47 * =validate= - use this boolean option to require validation of any requests
48 made to this handler.
49 By default, requests made to REST handlers are not validated.
50 * =http_allow= use this option to specify the HTTP methods that can
51 be used to invoke the handler.
52
53=cut=
54
55
# spent 83µs within Foswiki::UI::Rest::registerRESTHandler which was called 6 times, avg 14µs/call: # 6 times (83µs+0s) by Foswiki::Func::registerRESTHandler at line 669 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Func.pm, avg 14µs/call
sub registerRESTHandler {
561292µs my ( $subject, $verb, $fnref, %options ) = @_;
57
58 $restDispatch{$subject}{$verb} = {
59 function => $fnref,
60 %options
61 };
62}
63
64sub rest {
65 my ( $session, %initialContext ) = @_;
66
67 my $req = $session->{request};
68 my $res = $session->{response};
69 my $err;
70
71 # Must define topic param in the query to avoid plugins being
72 # passed the path_info when the are initialised. We can't affect
73 # the path_info, but we *can* persuade Foswiki to ignore it.
74 my $topic = $req->param('topic');
75 if ($topic) {
76 unless ( $topic =~ /\.|\// ) {
77 $res->header( -type => 'text/html', -status => '400' );
78 $err = 'ERROR: (400) Invalid REST invocation'
79 . " - Invalid topic parameter $topic\n";
80 $res->print($err);
81 throw Foswiki::EngineException( 400, $err, $res );
82 }
83 }
84 else {
85
86 # No topic specified, but we still have to set a topic to stop
87 # plugins being passed the subject and verb in place of a topic.
88 $session->{webName} = $Foswiki::cfg{UsersWebName};
89 $session->{topicName} = $Foswiki::cfg{HomeTopicName};
90 }
91
92 my $cache = $session->{cache};
93 my $cachedPage;
94 $cachedPage = $cache->getPage( $session->{webName}, $session->{topicName} )
95 if $cache;
96
97 if ($cachedPage) {
98 print STDERR
99 "found REST for $session->{webName}.$session->{topicName} in cache\n"
100 if $Foswiki::cfg{Cache}{Debug};
101
102 # render uncacheable areas
103 my $text = $cachedPage->{text};
104 $cache->renderDirtyAreas( \$text ) if $cachedPage->{isDirty};
105
106 # set status
107 my $status = $cachedPage->{status};
108 if ( $status == 302 ) {
109 $session->{response}->redirect( $cachedPage->{location} );
110 }
111 else {
112 $session->{response}->status($status);
113 }
114
115 # set headers
116 $session->generateHTTPHeaders( 'rest', $cachedPage->{contentType},
117 $text, $cachedPage );
118
119 # send it out
120 $session->{response}->print($text);
121
122 $session->logEvent( 'rest',
123 $session->{webName} . '.' . $session->{topicName}, '(cached)' );
124
125 return;
126 }
127
128 print STDERR
129 "computing REST for $session->{webName}.$session->{topicName}\n"
130 if $Foswiki::cfg{Cache}{Debug};
131
132 # If there's login info, try and apply it
133 my $login = $req->param('username');
134 if ($login) {
135 my $pass = $req->param('password');
136 my $validation = $session->{users}->checkPassword( $login, $pass );
137 unless ($validation) {
138 $res->header( -type => 'text/html', -status => '401' );
139 $err = "ERROR: (401) Can't login as $login";
140 $res->print($err);
141 throw Foswiki::EngineException( 401, $err, $res );
142 }
143
144 my $cUID = $session->{users}->getCanonicalUserID($login);
145 my $WikiName = $session->{users}->getWikiName($cUID);
146 $session->{users}->getLoginManager()->userLoggedIn( $login, $WikiName );
147 }
148
149 # Check that the REST script is authorised under the standard
150 # {AuthScripts} contract
151 try {
152 $session->getLoginManager()->checkAccess();
153 }
154 catch Error with {
155 my $e = shift;
156 $res->header( -type => 'text/html', -status => '401' );
157 $err = "ERROR: (401) $e";
158 $res->print($err);
159 throw Foswiki::EngineException( 401, $err, $res );
160 };
161
162 my $pathInfo = $req->path_info();
163
164 # Foswiki rest invocations are defined as having a subject (pluginName)
165 # and verb (restHandler in that plugin). Make sure the path_info is
166 # well-structured.
167 unless ( $pathInfo =~ m#/(.*?)[./]([^/]*)# ) {
168
169 $res->header( -type => 'text/html', -status => '400' );
170 $err =
171 "ERROR: (400) Invalid REST invocation - $pathInfo is malformed\n";
172 $res->print($err);
173
174 $res->print(
175 "\nuseage: ./rest /PluginName/restHandler param=value\n\n" . join(
176 "\n",
177 map {
178 $_ . ' : '
179 . join( ' , ', keys( %{ $restDispatch{$_} } ) )
180 } keys(%restDispatch)
181 )
182 . "\n\n"
183 ) if $session->inContext('command_line');
184
185 throw Foswiki::EngineException( 400, $err, $res );
186 }
187
188 # Implicit untaint OK - validated later
189 my ( $subject, $verb ) = ( $1, $2 );
190
191 my $record = $restDispatch{$subject}{$verb};
192
193 # Check we have this handler
194 unless ($record) {
195 $res->header( -type => 'text/html', -status => '404' );
196 $err =
197 'ERROR: (404) Invalid REST invocation - '
198 . $pathInfo
199 . ' does not refer to a known handler';
200 $res->print($err);
201 throw Foswiki::EngineException( 404, $err, $res );
202 }
203
204 # Check the method is allowed
205 if ( $record->{http_allow} && defined $req->method() ) {
206 my %allowed = map { $_ => 1 } split( /[,\s]+/, $record->{http_allow} );
207 unless ( $allowed{ uc( $req->method() ) } ) {
208 $res->header( -type => 'text/html', -status => '405' );
209 $err =
210 'ERROR: (405) Bad Request: ' . uc( $req->method() ) . ' denied';
211 $res->print($err);
212 throw Foswiki::EngineException( 404, $err, $res );
213 }
214 }
215
216 # Check someone is logged in
217 if ( $record->{authenticate} ) {
218 unless ( $session->inContext('authenticated')
219 || $Foswiki::cfg{LoginManager} eq 'none' )
220 {
221 $res->header( -type => 'text/html', -status => '401' );
222 $err = "ERROR: (401) $pathInfo requires you to be logged in";
223 $res->print($err);
224 throw Foswiki::EngineException( 401, $err, $res );
225 }
226 }
227
228 # Validate the request
229 if ( $record->{validate} ) {
230 my $nonce = $req->param('validation_key');
231 if (
232 !defined($nonce)
233 || !Foswiki::Validation::isValidNonce(
234 $session->getCGISession(), $nonce
235 )
236 )
237 {
238 $res->header( -type => 'text/html', -status => '403' );
239 $err = "ERROR: (403) Invalid validation code";
240 $res->print($err);
241 throw Foswiki::EngineException( 403, $err, $res );
242 }
243
244 # SMELL: Note we don't expire the validation code. If we expired it,
245 # then subsequent requests using the same code would have to be
246 # interactively confirmed, which isn't really an option with
247 # an XHR.
248 }
249
250 $session->logEvent( 'rest',
251 $session->{webName} . '.' . $session->{topicName} );
252
253287µs290µs
# spent 54µs (19+36) within Foswiki::UI::Rest::BEGIN@253 which was called: # once (19µs+36µs) by Foswiki::Func::registerRESTHandler at line 253
no strict 'refs';
# spent 54µs making 1 call to Foswiki::UI::Rest::BEGIN@253 # spent 36µs making 1 call to strict::unimport
254 my $function = $record->{function};
255 my $result = &$function( $session, $subject, $verb, $session->{response} );
2562273µs276µs
# spent 46µs (17+30) within Foswiki::UI::Rest::BEGIN@256 which was called: # once (17µs+30µs) by Foswiki::Func::registerRESTHandler at line 256
use strict 'refs';
# spent 46µs making 1 call to Foswiki::UI::Rest::BEGIN@256 # spent 30µs making 1 call to strict::import
257
258 # SMELL: does anyone use endPoint? I can't find any evidence of it.
259 my $endPoint = $req->param('endPoint');
260 if ( defined($endPoint) ) {
261 my $nurl = $session->getScriptUrl( 1, 'view', '', $endPoint );
262 $session->redirect($nurl);
263 }
264 elsif ($result) {
265
266 # If the handler doesn't want to handle all the details of the
267 # response, they can return a page here and get it 200'd
268 $session->writeCompletePage($result);
269 }
270
271 # Otherwise it's assumed that the handler dealt with the response.
272}
273
27417µs1;
275__END__