← Index
NYTProf Performance Profile   « line view »
For script/ponapi
  Run on Wed Feb 10 15:51:26 2016
Reported on Thu Feb 11 09:43:10 2016

Filename/home/mickey/git_tree/PONAPI/Server/lib/PONAPI/Server.pm
StatementsExecuted 9079006 statements in 35.1s
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
100001118.47s102sPONAPI::Server::::_ponapi_check_headersPONAPI::Server::_ponapi_check_headers
100001116.56s871sPONAPI::Server::::callPONAPI::Server::call
100001115.45s144sPONAPI::Server::::_ponapi_paramsPONAPI::Server::_ponapi_params
100001114.94s10.4sPONAPI::Server::::_ponapi_route_matchPONAPI::Server::_ponapi_route_match
100001114.92s34.1sPONAPI::Server::::_responsePONAPI::Server::_response
100001114.75s23.0sPONAPI::Server::::_ponapi_query_paramsPONAPI::Server::_ponapi_query_params
299514512.69s4.68sPONAPI::Server::::_is_get_likePONAPI::Server::_is_get_like
501622311.21s1.21sPONAPI::Server::::CORE:matchPONAPI::Server::CORE:match (opcode)
10000111830ms145sPONAPI::Server::::__ANON__[lib/PONAPI/Server.pm:68]PONAPI::Server::__ANON__[lib/PONAPI/Server.pm:68]
10000111779ms1.48sPONAPI::Server::::_ponapi_dataPONAPI::Server::_ponapi_data
10000111365ms365msPONAPI::Server::::CORE:regcompPONAPI::Server::CORE:regcomp (opcode)
1112.07ms2.67msPONAPI::Server::::BEGIN@34PONAPI::Server::BEGIN@34
111927µs1.31msPONAPI::Server::::BEGIN@13PONAPI::Server::BEGIN@13
111870µs2.12msPONAPI::Server::::BEGIN@14PONAPI::Server::BEGIN@14
111781µs1.71msPONAPI::Server::::BEGIN@10PONAPI::Server::BEGIN@10
111717µs1.40sPONAPI::Server::::BEGIN@17PONAPI::Server::BEGIN@17
111634µs756µsPONAPI::Server::::BEGIN@11PONAPI::Server::BEGIN@11
111392µs11.5msPONAPI::Server::::BEGIN@18PONAPI::Server::BEGIN@18
11166µs811msPONAPI::Server::::prepare_appPONAPI::Server::prepare_app
11162µs62µsPONAPI::Server::::CORE:printPONAPI::Server::CORE:print (opcode)
11131µs810msPONAPI::Server::::_load_daoPONAPI::Server::_load_dao
11118µs133µsPONAPI::Server::::BEGIN@22PONAPI::Server::BEGIN@22
11114µs25µsPONAPI::Server::::BEGIN@4PONAPI::Server::BEGIN@4
1119µs35µsPONAPI::Server::::BEGIN@20PONAPI::Server::BEGIN@20
1118µs43µsPONAPI::Server::::BEGIN@15PONAPI::Server::BEGIN@15
1117µs11µsPONAPI::Server::::BEGIN@5PONAPI::Server::BEGIN@5
1117µs7µsPONAPI::Server::::BEGIN@9PONAPI::Server::BEGIN@9
1114µs4µsPONAPI::Server::::BEGIN@12PONAPI::Server::BEGIN@12
1112µs2µsPONAPI::Server::::CORE:qrPONAPI::Server::CORE:qr (opcode)
0000s0sPONAPI::Server::::__ANON__[lib/PONAPI/Server.pm:38]PONAPI::Server::__ANON__[lib/PONAPI/Server.pm:38]
0000s0sPONAPI::Server::::_error_responsePONAPI::Server::_error_response
0000s0sPONAPI::Server::::_options_responsePONAPI::Server::_options_response
0000s0sPONAPI::Server::::_validate_data_membersPONAPI::Server::_validate_data_members
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# ABSTRACT: PONAPI - Perl implementation of {JSON:API} (http://jsonapi.org/) v1.0
2package PONAPI::Server;
3
4221µs236µs
# spent 25µs (14+11) within PONAPI::Server::BEGIN@4 which was called: # once (14µs+11µs) by PONAPI::CLI::RunServer::BEGIN@9 at line 4
use strict;
# spent 25µs making 1 call to PONAPI::Server::BEGIN@4 # spent 11µs making 1 call to strict::import
5231µs215µs
# spent 11µs (7+4) within PONAPI::Server::BEGIN@5 which was called: # once (7µs+4µs) by PONAPI::CLI::RunServer::BEGIN@9 at line 5
use warnings;
# spent 11µs making 1 call to PONAPI::Server::BEGIN@5 # spent 4µs making 1 call to warnings::import
6
71500nsour $VERSION = '0.002013';
8
9223µs17µs
# spent 7µs within PONAPI::Server::BEGIN@9 which was called: # once (7µs+0s) by PONAPI::CLI::RunServer::BEGIN@9 at line 9
use Plack::Request;
# spent 7µs making 1 call to PONAPI::Server::BEGIN@9
10281µs11.71ms
# spent 1.71ms (781µs+933µs) within PONAPI::Server::BEGIN@10 which was called: # once (781µs+933µs) by PONAPI::CLI::RunServer::BEGIN@9 at line 10
use Plack::Response;
# spent 1.71ms making 1 call to PONAPI::Server::BEGIN@10
11284µs1756µs
# spent 756µs (634+122) within PONAPI::Server::BEGIN@11 which was called: # once (634µs+122µs) by PONAPI::CLI::RunServer::BEGIN@9 at line 11
use HTTP::Headers::ActionPack;
# spent 756µs making 1 call to PONAPI::Server::BEGIN@11
12216µs14µs
# spent 4µs within PONAPI::Server::BEGIN@12 which was called: # once (4µs+0s) by PONAPI::CLI::RunServer::BEGIN@9 at line 12
use Module::Runtime ();
# spent 4µs making 1 call to PONAPI::Server::BEGIN@12
13271µs11.31ms
# spent 1.31ms (927µs+381µs) within PONAPI::Server::BEGIN@13 which was called: # once (927µs+381µs) by PONAPI::CLI::RunServer::BEGIN@9 at line 13
use Return::MultiLevel ();
# spent 1.31ms making 1 call to PONAPI::Server::BEGIN@13
142109µs12.12ms
# spent 2.12ms (870µs+1.25) within PONAPI::Server::BEGIN@14 which was called: # once (870µs+1.25ms) by PONAPI::CLI::RunServer::BEGIN@9 at line 14
use JSON::XS ();
# spent 2.12ms making 1 call to PONAPI::Server::BEGIN@14
15221µs278µs
# spent 43µs (8+35) within PONAPI::Server::BEGIN@15 which was called: # once (8µs+35µs) by PONAPI::CLI::RunServer::BEGIN@9 at line 15
use URI::Escape qw( uri_unescape );
# spent 43µs making 1 call to PONAPI::Server::BEGIN@15 # spent 35µs making 1 call to Exporter::import
16
17298µs11.40s
# spent 1.40s (717µs+1.40) within PONAPI::Server::BEGIN@17 which was called: # once (717µs+1.40s) by PONAPI::CLI::RunServer::BEGIN@9 at line 17
use PONAPI::Server::ConfigReader;
# spent 1.40s making 1 call to PONAPI::Server::BEGIN@17
182137µs211.6ms
# spent 11.5ms (392µs+11.1) within PONAPI::Server::BEGIN@18 which was called: # once (392µs+11.1ms) by PONAPI::CLI::RunServer::BEGIN@9 at line 18
use PONAPI::Utils::Names qw( check_name );
# spent 11.5ms making 1 call to PONAPI::Server::BEGIN@18 # spent 45µs making 1 call to Exporter::import
19
20271µs261µs
# spent 35µs (9+26) within PONAPI::Server::BEGIN@20 which was called: # once (9µs+26µs) by PONAPI::CLI::RunServer::BEGIN@9 at line 20
use parent 'Plack::Component';
# spent 35µs making 1 call to PONAPI::Server::BEGIN@20 # spent 26µs making 1 call to parent::import
21
22
# spent 133µs (18+115) within PONAPI::Server::BEGIN@22 which was called: # once (18µs+115µs) by PONAPI::CLI::RunServer::BEGIN@9 at line 32
use constant {
23114µs1115µs ERR_MISSING_MEDIA_TYPE => +{ __error__ => +[ 415, "{JSON:API} No {json:api} Media-Type (Content-Type / Accept)" ] },
# spent 115µs making 1 call to constant::import
24 ERR_MISSING_CONTENT_TYPE => +{ __error__ => +[ 415, "{JSON:API} Missing Content-Type header" ] },
25 ERR_WRONG_CONTENT_TYPE => +{ __error__ => +[ 415, "{JSON:API} Invalid Content-Type header" ] },
26 ERR_WRONG_HEADER_ACCEPT => +{ __error__ => +[ 406, "{JSON:API} Invalid Accept header" ] },
27 ERR_BAD_REQ => +{ __error__ => +[ 400, "{JSON:API} Bad request" ] },
28 ERR_BAD_REQ_INVALID_NAME => +{ __error__ => +[ 400, "{JSON:API} Bad request (invalid member-name)" ] },
29 ERR_BAD_REQ_PARAMS => +{ __error__ => +[ 400, "{JSON:API} Bad request (unsupported parameters)" ] },
30 ERR_SORT_NOT_ALLOWED => +{ __error__ => +[ 400, "{JSON:API} Server-side sorting not allowed" ] },
31 ERR_NO_MATCHING_ROUTE => +{ __error__ => +[ 404, "{JSON:API} No matching route" ] },
32123µs1133µs};
# spent 133µs making 1 call to PONAPI::Server::BEGIN@22
33
3421.59ms12.67ms
# spent 2.67ms (2.07+608µs) within PONAPI::Server::BEGIN@34 which was called: # once (2.07ms+608µs) by PONAPI::CLI::RunServer::BEGIN@9 at line 34
use POSIX ();
# spent 2.67ms making 1 call to PONAPI::Server::BEGIN@34
35$SIG{INT} = sub {
36191µs162µs print "closing...\n";
# spent 62µs making 1 call to PONAPI::Server::CORE:print
37 POSIX::_exit(0);
38111µs};
39
4018µs12µsmy $qr_member_name_prefix = qr/^[a-zA-Z0-9]/;
# spent 2µs making 1 call to PONAPI::Server::CORE:qr
41
42
# spent 811ms (66µs+811) within PONAPI::Server::prepare_app which was called: # once (66µs+811ms) by Plack::Component::to_app at line 49 of Plack/Component.pm
sub prepare_app {
431200ns my $self = shift;
44
451300ns my %conf;
461200ns local $@;
4714µs eval {
48136µs2562µs %conf = PONAPI::Server::ConfigReader->new(
# spent 467µs making 1 call to PONAPI::Server::ConfigReader::read_config # spent 95µs making 1 call to PONAPI::Server::ConfigReader::new
49 dir => $self->{'ponapi.config_dir'} || 'conf'
50 )->read_config;
51 };
5218µs17µs $self->{$_} //= $conf{$_} for keys %conf;
# spent 7µs making 1 call to PONAPI::Server::ConfigReader::DESTROY
53
54 # Some defaults
551300ns my $default_media_type = 'application/vnd.api+json';
561300ns $self->{'ponapi.spec_version'} //= '1.0';
571300ns $self->{'ponapi.mediatype'} //= $default_media_type;
58
59113µs1810ms $self->_load_dao();
# spent 810ms making 1 call to PONAPI::Server::_load_dao
60}
61
62
# spent 871s (6.56+865) within PONAPI::Server::call which was called 100001 times, avg 8.71ms/call: # 100001 times (6.56s+865s) by Plack::Component::__ANON__[/usr/local/share/perl/5.18.2/Plack/Component.pm:50] at line 50 of Plack/Component.pm, avg 8.71ms/call
sub call {
6310000166.2ms my ( $self, $env ) = @_;
64100001782ms100001788ms my $req = Plack::Request->new($env);
# spent 788ms making 100001 calls to Plack::Request::new, avg 8µs/call
65
66
# spent 145s (830ms+144) within PONAPI::Server::__ANON__[lib/PONAPI/Server.pm:68] which was called 100001 times, avg 1.45ms/call: # 100001 times (830ms+144s) by Return::MultiLevel::__ANON__[/usr/local/share/perl/5.18.2/Return/MultiLevel.pm:25] at line 25 of Return/MultiLevel.pm, avg 1.45ms/call
my $ponapi_params = Return::MultiLevel::with_return {
67100001996ms100001144s $self->_ponapi_params( shift, $req )
# spent 144s making 100001 calls to PONAPI::Server::_ponapi_params, avg 1.44ms/call
681000011.68s100001148s };
# spent 148s making 100001 calls to Return::MultiLevel::__ANON__[Return/MultiLevel.pm:25], avg 1.48ms/call
69
70100001102ms return $self->_options_response( $ponapi_params->{__options__} )
71 if $ponapi_params->{__options__};
72
7310000157.5ms return $self->_error_response( $ponapi_params->{__error__} )
74 if $ponapi_params->{__error__};
75
76100001185ms my $action = delete $ponapi_params->{action};
77100001777ms100001681s my ( $status, $headers, $res ) = $self->{'ponapi.DAO'}->$action($ponapi_params);
# spent 448s making 50245 calls to PONAPI::DAO::retrieve_all, avg 8.92ms/call # spent 233s making 49756 calls to PONAPI::DAO::retrieve, avg 4.68ms/call
781000012.05s20000235.0s return $self->_response( $status, $headers, $res, $req->method eq 'HEAD' );
# spent 34.1s making 100001 calls to PONAPI::Server::_response, avg 341µs/call # spent 898ms making 100001 calls to Plack::Request::method, avg 9µs/call
79}
80
81
82### ...
83
84
# spent 810ms (31µs+810) within PONAPI::Server::_load_dao which was called: # once (31µs+810ms) by PONAPI::Server::prepare_app at line 59
sub _load_dao {
851200ns my $self = shift;
86
87 my $repository =
8817µs2809ms Module::Runtime::use_module( $self->{'repository.class'} )->new( @{ $self->{'repository.args'} } )
# spent 666ms making 1 call to Test::PONAPI::Repository::MockDB::new # spent 143ms making 1 call to Module::Runtime::use_module
89 || die "[PONAPI Server] failed to create a repository object\n";
90
91120µs1710µs $self->{'ponapi.DAO'} = PONAPI::DAO->new(
# spent 710µs making 1 call to PONAPI::DAO::new
92 repository => $repository,
93 version => $self->{'ponapi.spec_version'},
94 );
95}
96
97
# spent 4.68s (2.69+1.99) within PONAPI::Server::_is_get_like which was called 299514 times, avg 16µs/call: # 100001 times (807ms+662ms) by PONAPI::Server::_ponapi_params at line 122, avg 15µs/call # 50245 times (606ms+402ms) by PONAPI::Server::_ponapi_route_match at line 174, avg 20µs/call # 49756 times (535ms+382ms) by PONAPI::Server::_ponapi_route_match at line 165, avg 18µs/call # 49756 times (377ms+297ms) by PONAPI::Server::_ponapi_route_match at line 167, avg 14µs/call # 49756 times (364ms+249ms) by PONAPI::Server::_ponapi_route_match at line 166, avg 12µs/call
sub _is_get_like {
9829951499.8ms my $req = shift;
992995142.97s5990281.99s return 1 if $req->method =~ /^(?:GET|HEAD)$/;
# spent 1.51s making 299514 calls to Plack::Request::method, avg 5µs/call # spent 485ms making 299514 calls to PONAPI::Server::CORE:match, avg 2µs/call
100 return;
101}
102
103
# spent 144s (5.45+139) within PONAPI::Server::_ponapi_params which was called 100001 times, avg 1.44ms/call: # 100001 times (5.45s+139s) by PONAPI::Server::__ANON__[lib/PONAPI/Server.pm:68] at line 67, avg 1.44ms/call
sub _ponapi_params {
10410000182.6ms my ( $self, $wr, $req ) = @_;
105
106 # THE HEADERS
107100001447ms100001102s $self->_ponapi_check_headers($wr, $req);
# spent 102s making 100001 calls to PONAPI::Server::_ponapi_check_headers, avg 1.02ms/call
108
109 # THE PATH --> route matching
110100001458ms10000110.4s my @ponapi_route_params = $self->_ponapi_route_match($wr, $req);
# spent 10.4s making 100001 calls to PONAPI::Server::_ponapi_route_match, avg 104µs/call
111
112 # THE QUERY
113100001425ms10000123.0s my @ponapi_query_params = $self->_ponapi_query_params($wr, $req);
# spent 23.0s making 100001 calls to PONAPI::Server::_ponapi_query_params, avg 230µs/call
114
115 # THE BODY CONTENT
116100001384ms1000011.48s my @ponapi_data = $self->_ponapi_data($wr, $req);
# spent 1.48s making 100001 calls to PONAPI::Server::_ponapi_data, avg 15µs/call
117
118 # misc.
119100001229ms my $req_base = $self->{'ponapi.relative_links'} eq 'full' ? "".$req->base : '/';
120100001371ms100001735ms my $req_path = $self->{'ponapi.relative_links'} eq 'full' ? "".$req->uri : $req->request_uri;
# spent 735ms making 100001 calls to Plack::Request::request_uri, avg 7µs/call
121100001149ms my $update_200 = !!$self->{'ponapi.respond_to_updates_with_200'};
122100001337ms1000011.47s my $doc_self_link = _is_get_like($req) ? !!$self->{'ponapi.doc_auto_self_link'} : 0;
# spent 1.47s making 100001 calls to PONAPI::Server::_is_get_like, avg 15µs/call
123
124100001715ms my %params = (
125 @ponapi_route_params,
126 @ponapi_query_params,
127 @ponapi_data,
128 req_base => $req_base,
129 req_path => $req_path,
130 respond_to_updates_with_200 => $update_200,
131 send_doc_self_link => $doc_self_link,
132 );
133
134100001617ms return \%params;
135}
136
137
# spent 10.4s (4.94+5.41) within PONAPI::Server::_ponapi_route_match which was called 100001 times, avg 104µs/call: # 100001 times (4.94s+5.41s) by PONAPI::Server::_ponapi_params at line 110, avg 104µs/call
sub _ponapi_route_match {
13810000173.6ms my ( $self, $wr, $req ) = @_;
139100001329ms100001921ms my $method = $req->method;
# spent 921ms making 100001 calls to Plack::Request::method, avg 9µs/call
140
141100001256ms $wr->(ERR_BAD_REQ) unless grep { $_ eq $method } qw< GET POST PATCH DELETE HEAD OPTIONS >;
142
143100001725ms100001719ms my ( $type, $id, $relationships, $rel_type ) = split '/' => substr($req->path_info,1);
# spent 719ms making 100001 calls to Plack::Request::path_info, avg 7µs/call
144
145 # validate `type`
1461000011.48s200002563ms $wr->(ERR_BAD_REQ) unless defined $type and $type =~ /$qr_member_name_prefix/ ;
# spent 365ms making 100001 calls to PONAPI::Server::CORE:regcomp, avg 4µs/call # spent 197ms making 100001 calls to PONAPI::Server::CORE:match, avg 2µs/call
147
148 # validate `rel_type`
14910000173.9ms if ( defined $rel_type ) {
150 $wr->(ERR_BAD_REQ) if $relationships ne 'relationships';
151 }
152 elsif ( $relationships ) {
153 $rel_type = $relationships;
154 undef $relationships;
155 }
156
15710000165.5ms my $def_rel_type = defined $rel_type;
158
15910000135.6ms $wr->(ERR_BAD_REQ) if $def_rel_type and $rel_type !~ /$qr_member_name_prefix/;
160
161 # set `action`
16210000131.3ms my $action;
16310000175.2ms if ( defined $id ) {
1644975635.6ms $action = 'create_relationships' if $method eq 'POST' and $relationships and $def_rel_type;
16549756201ms49756917ms $action = 'retrieve' if _is_get_like($req) and !$relationships and !$def_rel_type;
# spent 917ms making 49756 calls to PONAPI::Server::_is_get_like, avg 18µs/call
1664975689.9ms49756613ms $action = 'retrieve_by_relationship' if _is_get_like($req) and !$relationships and $def_rel_type;
# spent 613ms making 49756 calls to PONAPI::Server::_is_get_like, avg 12µs/call
1674975690.4ms49756674ms $action = 'retrieve_relationships' if _is_get_like($req) and $relationships and $def_rel_type;
# spent 674ms making 49756 calls to PONAPI::Server::_is_get_like, avg 14µs/call
1684975631.5ms $action = 'update' if $method eq 'PATCH' and !$relationships and !$def_rel_type;
1694975624.1ms $action = 'update_relationships' if $method eq 'PATCH' and $relationships and $def_rel_type;
1704975621.6ms $action = 'delete' if $method eq 'DELETE' and !$relationships and !$def_rel_type;
1714975628.7ms $action = 'delete_relationships' if $method eq 'DELETE' and $relationships and $def_rel_type;
172 }
173 else {
17450245176ms502451.01s $action = 'retrieve_all' if _is_get_like($req);
# spent 1.01s making 50245 calls to PONAPI::Server::_is_get_like, avg 20µs/call
1755024539.2ms $action = 'create' if $method eq 'POST';
176 }
177
17810000152.0ms if ( $method eq 'OPTIONS' ) {
179 my @options = (qw< GET HEAD > );
180 if ( defined $id ) {
181 push @options => (qw< PATCH DELETE >) unless $def_rel_type;
182 }
183 else {
184 push @options => 'POST';
185 }
186 $wr->( +{ __options__ => \@options } );
187 }
188
18910000135.8ms $wr->(ERR_NO_MATCHING_ROUTE) unless $action;
190
191 # return ( action, type, id?, rel_type? )
192100001183ms my @ret = ( action => $action, type => $type );
19310000153.8ms defined $id and push @ret => id => $id;
19410000113.5ms $def_rel_type and push @ret => rel_type => $rel_type;
195100001578ms return @ret;
196}
197
198
# spent 102s (8.47+93.2) within PONAPI::Server::_ponapi_check_headers which was called 100001 times, avg 1.02ms/call: # 100001 times (8.47s+93.2s) by PONAPI::Server::_ponapi_params at line 107, avg 1.02ms/call
sub _ponapi_check_headers {
19910000176.4ms my ( $self, $wr, $req ) = @_;
200
201100001379ms1000011.20s return if $req->method eq 'OPTIONS';
# spent 1.20s making 100001 calls to Plack::Request::method, avg 12µs/call
202
203100001273ms my $mt = $self->{'ponapi.mediatype'};
204
20510000174.8ms my $has_mediatype = 0;
206
207 # check Content-Type
208100001335ms100001818ms if ( $req->content_length ) {
# spent 818ms making 100001 calls to Plack::Request::content_length, avg 8µs/call
209 if ( my $content_type = $req->headers->header('Content-Type') ) {
210 $wr->(ERR_WRONG_CONTENT_TYPE) unless $content_type eq $mt;
211 $has_mediatype++;
212 } else {
213 $wr->(ERR_MISSING_CONTENT_TYPE)
214 }
215 }
216
217 # check Accept
218100001655ms20000239.5s if ( my $accept = $req->headers->header('Accept') ) {
# spent 36.8s making 100001 calls to Plack::Request::headers, avg 368µs/call # spent 2.66s making 100001 calls to HTTP::Headers::Fast::header, avg 27µs/call
219100001622ms1000014.44s my $pack = HTTP::Headers::ActionPack->new;
# spent 4.44s making 100001 calls to HTTP::Headers::ActionPack::new, avg 44µs/call
220
221 my @jsonapi_accept =
2221000011.44s30000346.2s map { ( $_->[1]->type eq $mt ) ? $_->[1] : () }
# spent 42.7s making 100001 calls to HTTP::Headers::ActionPack::create_header, avg 427µs/call # spent 2.73s making 100001 calls to HTTP::Headers::ActionPack::MediaTypeList::iterable, avg 27µs/call # spent 810ms making 100001 calls to HTTP::Headers::ActionPack::MediaType::type, avg 8µs/call
223 $pack->create_header( 'Accept' => $accept )->iterable;
224
225100001883ms if ( @jsonapi_accept ) {
226100001240ms100001994ms $wr->(ERR_WRONG_HEADER_ACCEPT)
# spent 994ms making 100001 calls to HTTP::Headers::ActionPack::Core::BaseHeaderWithParams::params_are_empty, avg 10µs/call
227100001158ms unless grep { $_->params_are_empty } @jsonapi_accept;
228
22910000129.8ms $has_mediatype++;
230 }
231 }
232
233100001625ms $wr->(ERR_MISSING_MEDIA_TYPE) unless $has_mediatype;
234}
235
236
# spent 23.0s (4.75+18.2) within PONAPI::Server::_ponapi_query_params which was called 100001 times, avg 230µs/call: # 100001 times (4.75s+18.2s) by PONAPI::Server::_ponapi_params at line 113, avg 230µs/call
sub _ponapi_query_params {
23710000158.5ms my ( $self, $wr, $req ) = @_;
238
23910000148.3ms my %params;
240100001301ms10000113.5s my $query_params = $req->query_parameters;
# spent 13.5s making 100001 calls to Plack::Request::query_parameters, avg 135µs/call
241
242100001505ms2000023.03s my $unesacpe_values = !!$req->headers->header('X-PONAPI-Escaped-Values');
# spent 2.75s making 100001 calls to HTTP::Headers::Fast::header, avg 27µs/call # spent 280ms making 100001 calls to Plack::Request::headers, avg 3µs/call
243
244 # loop over query parameters (unique keys)
245100001246ms for my $k ( keys %{ $query_params } ) {
2461021071.02s102107523ms my ( $p, $f ) = $k =~ /^ (\w+?) (?:\[(\w+)\])? $/x;
# spent 523ms making 102107 calls to PONAPI::Server::CORE:match, avg 5µs/call
247
248 # valid parameter names
249 $wr->(ERR_BAD_REQ_PARAMS)
250102107231ms unless grep { $p eq $_ } qw< fields filter page include sort >;
251
252 # "complex" parameters have the correct structre
253 $wr->(ERR_BAD_REQ)
254102107118ms if !defined $f and grep { $p eq $_ } qw< page fields filter >;
255
256 # 'sort' requested but not supported
25710210795.4ms $wr->(ERR_SORT_NOT_ALLOWED)
258 if $p eq 'sort' and !$self->{'ponapi.sort_allowed'};
259
260 # values can be passed as CSV
261260257187ms my @values = map { $unesacpe_values ? uri_unescape($_) : $_ }
262102107520ms1021071.12s map { split /,/ } $query_params->get_all($k);
# spent 1.12s making 102107 calls to Hash::MultiValue::get_all, avg 11µs/call
263
264 # check we have values for a given key
265 # (for 'fields' an empty list is valid)
266102107113ms $wr->(ERR_BAD_REQ)
267 if $p ne 'fields' and exists $query_params->{$k} and !@values;
268
269 # values passed on in array-ref
270102107184ms grep { $p eq $_ } qw< fields filter >
271 and $params{$p}{$f} = \@values;
272
273 # page info has one value per request
27410210735.6ms $p eq 'page' and $params{$p}{$f} = $values[0];
275
276 # values passed on in hash-ref
27710210790.6ms $p eq 'include' and $params{include} = \@values;
278
279 # sort values: indicate direction
280 # Not doing any processing here to allow repos to support
281 # complex sorting, if they want to.
282102107143ms $p eq 'sort' and $params{'sort'} = \@values;
283 }
284
285100001655ms return %params;
286}
287
288
# spent 1.48s (779ms+701ms) within PONAPI::Server::_ponapi_data which was called 100001 times, avg 15µs/call: # 100001 times (779ms+701ms) by PONAPI::Server::_ponapi_params at line 116, avg 15µs/call
sub _ponapi_data {
28910000169.8ms my ( $self, $wr, $req ) = @_;
290
291100001715ms100001701ms return unless $req->content_length and $req->content_length > 0;
# spent 701ms making 100001 calls to Plack::Request::content_length, avg 7µs/call
292
293 $wr->(ERR_BAD_REQ) if _is_get_like($req);
294
295 my $body;
296 eval { $body = JSON::XS::decode_json( $req->content ); 1 };
297
298 $wr->(ERR_BAD_REQ) unless $body and ref $body eq 'HASH' and exists $body->{data};
299
300 my $data = $body->{data};
301
302 $wr->(ERR_BAD_REQ) if defined $data and ref($data) !~ /^(?:ARRAY|HASH)$/;
303
304 $self->_validate_data_members( $wr, $data ) if defined $data;
305
306 return ( data => $data );
307}
308
309sub _validate_data_members {
310 my ( $self, $wr, $data ) = @_;
311
312 my @recs = ref $data eq 'ARRAY' ? @{$data} : $data;
313
314 for my $r ( @recs ) {
315 return unless keys %{$r};
316
317 # `type`
318 $wr->(ERR_BAD_REQ) unless $r->{type};
319 $wr->(ERR_BAD_REQ_INVALID_NAME) unless check_name( $r->{type} );
320
321 # `attributes`
322 if ( exists $r->{attributes} ) {
323 $wr->(ERR_BAD_REQ) unless ref( $r->{attributes} ) eq 'HASH';
324 $wr->(ERR_BAD_REQ_INVALID_NAME)
325 if grep { !check_name($_) } keys %{ $r->{attributes} };
326 }
327
328 # `relationships`
329 if ( exists $r->{relationships} ) {
330 $wr->(ERR_BAD_REQ) unless ref( $r->{relationships} ) eq 'HASH';
331
332 for my $k ( keys %{ $r->{relationships} } ) {
333 $wr->(ERR_BAD_REQ_INVALID_NAME) unless check_name($k);
334
335 my $rel = $r->{relationships}{$k};
336 my @rels = ref($rel||'') eq 'ARRAY' ? @$rel : $rel;
337 foreach my $relationship ( @rels ) {
338 next unless defined $relationship;
339 # Some requests have relationships => { blah },
340 # others have relationships => { data => { blah } }
341 $relationship = $relationship->{data}
342 if exists $relationship->{data};
343
344 $wr->(ERR_BAD_REQ) unless ref( $relationship ) eq 'HASH';
345 $wr->(ERR_BAD_REQ) unless exists $relationship->{type};
346
347 $wr->(ERR_BAD_REQ_INVALID_NAME)
348 if !check_name( $relationship->{type} )
349 or grep { !check_name($_) } keys %$relationship;
350 }
351 }
352 }
353 }
354}
355
356
# spent 34.1s (4.92+29.2) within PONAPI::Server::_response which was called 100001 times, avg 341µs/call: # 100001 times (4.92s+29.2s) by PONAPI::Server::call at line 78, avg 341µs/call
sub _response {
35710000188.3ms my ( $self, $status, $headers, $content, $is_head ) = @_;
358100001659ms1000011.67s my $res = Plack::Response->new( $status || 200 );
# spent 1.67s making 100001 calls to Plack::Response::new, avg 17µs/call
359
360100001203ms1000011.66s $res->headers($headers);
# spent 1.66s making 100001 calls to Plack::Response::headers, avg 17µs/call
361100001457ms1000014.35s $res->header( 'X-PONAPI-Server-Version' => $self->{'ponapi.spec_version'} )
# spent 4.35s making 100001 calls to Plack::Response::header, avg 43µs/call
362 if $self->{'ponapi.send_version_header'};
363
36410000163.8ms if ( ref $content ) {
3651000012.13s1000011.65s my $enc_content = JSON::XS::encode_json $content;
# spent 1.65s making 100001 calls to JSON::XS::encode_json, avg 17µs/call
366100001281ms1000013.42s $res->content_length( length($enc_content) );
# spent 3.42s making 100001 calls to Plack::Response::content_length, avg 34µs/call
367100001303ms1000011.51s $res->content_type( $self->{'ponapi.mediatype'} );
# spent 1.51s making 100001 calls to Plack::Response::content_type, avg 15µs/call
368100001296ms1000011.09s $res->content($enc_content) unless $is_head;
# spent 1.09s making 100001 calls to Plack::Response::content, avg 11µs/call
369 }
370
371100001696ms10000113.9s $res->finalize;
# spent 13.9s making 100001 calls to Plack::Response::finalize, avg 139µs/call
372}
373
374sub _options_response {
375 my ( $self, $options ) = @_;
376 return +[ 200, [ Allow => join( ', ' => @{$options} ) ], [] ];
377}
378
379sub _error_response {
380 my ( $self, $args ) = @_;
381
382 return $self->_response( $args->[0], [], +{
383 jsonapi => { version => $self->{'ponapi.spec_version'} },
384 errors => [ { detail => $args->[1], status => $args->[0] } ],
385 });
386}
387
38814µs1;
389
390__END__
 
# spent 1.21s within PONAPI::Server::CORE:match which was called 501622 times, avg 2µs/call: # 299514 times (485ms+0s) by PONAPI::Server::_is_get_like at line 99, avg 2µs/call # 102107 times (523ms+0s) by PONAPI::Server::_ponapi_query_params at line 246, avg 5µs/call # 100001 times (197ms+0s) by PONAPI::Server::_ponapi_route_match at line 146, avg 2µs/call
sub PONAPI::Server::CORE:match; # opcode
# spent 62µs within PONAPI::Server::CORE:print which was called: # once (62µs+0s) by PONAPI::Server::__ANON__[lib/PONAPI/Server.pm:38] at line 36
sub PONAPI::Server::CORE:print; # opcode
# spent 2µs within PONAPI::Server::CORE:qr which was called: # once (2µs+0s) by PONAPI::CLI::RunServer::BEGIN@9 at line 40
sub PONAPI::Server::CORE:qr; # opcode
# spent 365ms within PONAPI::Server::CORE:regcomp which was called 100001 times, avg 4µs/call: # 100001 times (365ms+0s) by PONAPI::Server::_ponapi_route_match at line 146, avg 4µs/call
sub PONAPI::Server::CORE:regcomp; # opcode