Profile of lib/PONAPI/Server.pm

← 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
Statements | Executed 9079006 statements in 35.1s

Subroutines
Calls | P | F | Exclusive Time | Inclusive Time | Subroutine
--- | --- | --- | --- | --- | ---
100001 | 1 | 1 | 8.47s | 102s | PONAPI::Server::::_ponapi_check_headersPONAPI::Server::_ponapi_check_headers
100001 | 1 | 1 | 6.56s | 871s | PONAPI::Server::::callPONAPI::Server::call
100001 | 1 | 1 | 5.45s | 144s | PONAPI::Server::::_ponapi_paramsPONAPI::Server::_ponapi_params
100001 | 1 | 1 | 4.94s | 10.4s | PONAPI::Server::::_ponapi_route_matchPONAPI::Server::_ponapi_route_match
100001 | 1 | 1 | 4.92s | 34.1s | PONAPI::Server::::_responsePONAPI::Server::_response
100001 | 1 | 1 | 4.75s | 23.0s | PONAPI::Server::::_ponapi_query_paramsPONAPI::Server::_ponapi_query_params
299514 | 5 | 1 | 2.69s | 4.68s | PONAPI::Server::::_is_get_likePONAPI::Server::_is_get_like
501622 | 3 | 1 | 1.21s | 1.21s | PONAPI::Server::::CORE:matchPONAPI::Server::CORE:match (opcode)
100001 | 1 | 1 | 830ms | 145s | PONAPI::Server::::__ANON__[lib/PONAPI/Server.pm:68]PONAPI::Server::__ANON__[lib/PONAPI/Server.pm:68]
100001 | 1 | 1 | 779ms | 1.48s | PONAPI::Server::::_ponapi_dataPONAPI::Server::_ponapi_data
100001 | 1 | 1 | 365ms | 365ms | PONAPI::Server::::CORE:regcompPONAPI::Server::CORE:regcomp (opcode)
1 | 1 | 1 | 2.07ms | 2.67ms | PONAPI::Server::::BEGIN@34PONAPI::Server::BEGIN@34
1 | 1 | 1 | 927µs | 1.31ms | PONAPI::Server::::BEGIN@13PONAPI::Server::BEGIN@13
1 | 1 | 1 | 870µs | 2.12ms | PONAPI::Server::::BEGIN@14PONAPI::Server::BEGIN@14
1 | 1 | 1 | 781µs | 1.71ms | PONAPI::Server::::BEGIN@10PONAPI::Server::BEGIN@10
1 | 1 | 1 | 717µs | 1.40s | PONAPI::Server::::BEGIN@17PONAPI::Server::BEGIN@17
1 | 1 | 1 | 634µs | 756µs | PONAPI::Server::::BEGIN@11PONAPI::Server::BEGIN@11
1 | 1 | 1 | 392µs | 11.5ms | PONAPI::Server::::BEGIN@18PONAPI::Server::BEGIN@18
1 | 1 | 1 | 66µs | 811ms | PONAPI::Server::::prepare_appPONAPI::Server::prepare_app
1 | 1 | 1 | 62µs | 62µs | PONAPI::Server::::CORE:printPONAPI::Server::CORE:print (opcode)
1 | 1 | 1 | 31µs | 810ms | PONAPI::Server::::_load_daoPONAPI::Server::_load_dao
1 | 1 | 1 | 18µs | 133µs | PONAPI::Server::::BEGIN@22PONAPI::Server::BEGIN@22
1 | 1 | 1 | 14µs | 25µs | PONAPI::Server::::BEGIN@4PONAPI::Server::BEGIN@4
1 | 1 | 1 | 9µs | 35µs | PONAPI::Server::::BEGIN@20PONAPI::Server::BEGIN@20
1 | 1 | 1 | 8µs | 43µs | PONAPI::Server::::BEGIN@15PONAPI::Server::BEGIN@15
1 | 1 | 1 | 7µs | 11µs | PONAPI::Server::::BEGIN@5PONAPI::Server::BEGIN@5
1 | 1 | 1 | 7µs | 7µs | PONAPI::Server::::BEGIN@9PONAPI::Server::BEGIN@9
1 | 1 | 1 | 4µs | 4µs | PONAPI::Server::::BEGIN@12PONAPI::Server::BEGIN@12
1 | 1 | 1 | 2µs | 2µs | PONAPI::Server::::CORE:qrPONAPI::Server::CORE:qr (opcode)
0 | 0 | 0 | 0s | 0s | PONAPI::Server::::__ANON__[lib/PONAPI/Server.pm:38]PONAPI::Server::__ANON__[lib/PONAPI/Server.pm:38]
0 | 0 | 0 | 0s | 0s | PONAPI::Server::::_error_responsePONAPI::Server::_error_response
0 | 0 | 0 | 0s | 0s | PONAPI::Server::::_options_responsePONAPI::Server::_options_response
0 | 0 | 0 | 0s | 0s | PONAPI::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
2 |  |  |  |  | package PONAPI::Server;
3 |  |  |  |  |
4 | 2 | 21µs | 2 | 36µ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
5 | 2 | 31µs | 2 | 15µ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 |  |  |  |  |
7 | 1 | 500ns |  |  | our $VERSION = '0.002013';
8 |  |  |  |  |
9 | 2 | 23µs | 1 | 7µ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
10 | 2 | 81µs | 1 | 1.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
11 | 2 | 84µs | 1 | 756µ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
12 | 2 | 16µs | 1 | 4µ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
13 | 2 | 71µs | 1 | 1.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
14 | 2 | 109µs | 1 | 2.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
15 | 2 | 21µs | 2 | 78µ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 |  |  |  |  |
17 | 2 | 98µs | 1 | 1.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
18 | 2 | 137µs | 2 | 11.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 |  |  |  |  |
20 | 2 | 71µs | 2 | 61µ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 {
23 | 1 | 14µs | 1 | 115µ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" ] },
32 | 1 | 23µs | 1 | 133µs | }; # spent 133µs making 1 call to PONAPI::Server::BEGIN@22
33 |  |  |  |  |
34 | 2 | 1.59ms | 1 | 2.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 {
36 | 1 | 91µs | 1 | 62µs | print "closing...\n"; # spent 62µs making 1 call to PONAPI::Server::CORE:print
37 |  |  |  |  | POSIX::_exit(0);
38 | 1 | 11µs |  |  | };
39 |  |  |  |  |
40 | 1 | 8µs | 1 | 2µs | my $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 {
43 | 1 | 200ns |  |  | my $self = shift;
44 |  |  |  |  |
45 | 1 | 300ns |  |  | my %conf;
46 | 1 | 200ns |  |  | local $@;
47 | 1 | 4µs |  |  | eval {
48 | 1 | 36µs | 2 | 562µ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 |  |  |  |  | };
52 | 1 | 8µs | 1 | 7µs | $self->{$_} //= $conf{$_} for keys %conf; # spent 7µs making 1 call to PONAPI::Server::ConfigReader::DESTROY
53 |  |  |  |  |
54 |  |  |  |  | # Some defaults
55 | 1 | 300ns |  |  | my $default_media_type = 'application/vnd.api+json';
56 | 1 | 300ns |  |  | $self->{'ponapi.spec_version'} //= '1.0';
57 | 1 | 300ns |  |  | $self->{'ponapi.mediatype'} //= $default_media_type;
58 |  |  |  |  |
59 | 1 | 13µs | 1 | 810ms | $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 {
63 | 100001 | 66.2ms |  |  | my ( $self, $env ) = @_;
64 | 100001 | 782ms | 100001 | 788ms | 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 {
67 | 100001 | 996ms | 100001 | 144s | $self->_ponapi_params( shift, $req ) # spent 144s making 100001 calls to PONAPI::Server::_ponapi_params, avg 1.44ms/call
68 | 100001 | 1.68s | 100001 | 148s | }; # spent 148s making 100001 calls to Return::MultiLevel::__ANON__[Return/MultiLevel.pm:25], avg 1.48ms/call
69 |  |  |  |  |
70 | 100001 | 102ms |  |  | return $self->_options_response( $ponapi_params->{__options__} )
71 |  |  |  |  | if $ponapi_params->{__options__};
72 |  |  |  |  |
73 | 100001 | 57.5ms |  |  | return $self->_error_response( $ponapi_params->{__error__} )
74 |  |  |  |  | if $ponapi_params->{__error__};
75 |  |  |  |  |
76 | 100001 | 185ms |  |  | my $action = delete $ponapi_params->{action};
77 | 100001 | 777ms | 100001 | 681s | 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
78 | 100001 | 2.05s | 200002 | 35.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 {
85 | 1 | 200ns |  |  | my $self = shift;
86 |  |  |  |  |
87 |  |  |  |  | my $repository =
88 | 1 | 7µs | 2 | 809ms | 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 |  |  |  |  |
91 | 1 | 20µs | 1 | 710µ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 {
98 | 299514 | 99.8ms |  |  | my $req = shift;
99 | 299514 | 2.97s | 599028 | 1.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 {
104 | 100001 | 82.6ms |  |  | my ( $self, $wr, $req ) = @_;
105 |  |  |  |  |
106 |  |  |  |  | # THE HEADERS
107 | 100001 | 447ms | 100001 | 102s | $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
110 | 100001 | 458ms | 100001 | 10.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
113 | 100001 | 425ms | 100001 | 23.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
116 | 100001 | 384ms | 100001 | 1.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.
119 | 100001 | 229ms |  |  | my $req_base = $self->{'ponapi.relative_links'} eq 'full' ? "".$req->base : '/';
120 | 100001 | 371ms | 100001 | 735ms | 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
121 | 100001 | 149ms |  |  | my $update_200 = !!$self->{'ponapi.respond_to_updates_with_200'};
122 | 100001 | 337ms | 100001 | 1.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 |  |  |  |  |
124 | 100001 | 715ms |  |  | 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 |  |  |  |  |
134 | 100001 | 617ms |  |  | 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 {
138 | 100001 | 73.6ms |  |  | my ( $self, $wr, $req ) = @_;
139 | 100001 | 329ms | 100001 | 921ms | my $method = $req->method; # spent 921ms making 100001 calls to Plack::Request::method, avg 9µs/call
140 |  |  |  |  |
141 | 100001 | 256ms |  |  | $wr->(ERR_BAD_REQ) unless grep { $_ eq $method } qw< GET POST PATCH DELETE HEAD OPTIONS >;
142 |  |  |  |  |
143 | 100001 | 725ms | 100001 | 719ms | 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`
146 | 100001 | 1.48s | 200002 | 563ms | $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`
149 | 100001 | 73.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 |  |  |  |  |
157 | 100001 | 65.5ms |  |  | my $def_rel_type = defined $rel_type;
158 |  |  |  |  |
159 | 100001 | 35.6ms |  |  | $wr->(ERR_BAD_REQ) if $def_rel_type and $rel_type !~ /$qr_member_name_prefix/;
160 |  |  |  |  |
161 |  |  |  |  | # set `action`
162 | 100001 | 31.3ms |  |  | my $action;
163 | 100001 | 75.2ms |  |  | if ( defined $id ) {
164 | 49756 | 35.6ms |  |  | $action = 'create_relationships' if $method eq 'POST' and $relationships and $def_rel_type;
165 | 49756 | 201ms | 49756 | 917ms | $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
166 | 49756 | 89.9ms | 49756 | 613ms | $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
167 | 49756 | 90.4ms | 49756 | 674ms | $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
168 | 49756 | 31.5ms |  |  | $action = 'update' if $method eq 'PATCH' and !$relationships and !$def_rel_type;
169 | 49756 | 24.1ms |  |  | $action = 'update_relationships' if $method eq 'PATCH' and $relationships and $def_rel_type;
170 | 49756 | 21.6ms |  |  | $action = 'delete' if $method eq 'DELETE' and !$relationships and !$def_rel_type;
171 | 49756 | 28.7ms |  |  | $action = 'delete_relationships' if $method eq 'DELETE' and $relationships and $def_rel_type;
172 |  |  |  |  | }
173 |  |  |  |  | else {
174 | 50245 | 176ms | 50245 | 1.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
175 | 50245 | 39.2ms |  |  | $action = 'create' if $method eq 'POST';
176 |  |  |  |  | }
177 |  |  |  |  |
178 | 100001 | 52.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 |  |  |  |  |
189 | 100001 | 35.8ms |  |  | $wr->(ERR_NO_MATCHING_ROUTE) unless $action;
190 |  |  |  |  |
191 |  |  |  |  | # return ( action, type, id?, rel_type? )
192 | 100001 | 183ms |  |  | my @ret = ( action => $action, type => $type );
193 | 100001 | 53.8ms |  |  | defined $id and push @ret => id => $id;
194 | 100001 | 13.5ms |  |  | $def_rel_type and push @ret => rel_type => $rel_type;
195 | 100001 | 578ms |  |  | 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 {
199 | 100001 | 76.4ms |  |  | my ( $self, $wr, $req ) = @_;
200 |  |  |  |  |
201 | 100001 | 379ms | 100001 | 1.20s | return if $req->method eq 'OPTIONS'; # spent 1.20s making 100001 calls to Plack::Request::method, avg 12µs/call
202 |  |  |  |  |
203 | 100001 | 273ms |  |  | my $mt = $self->{'ponapi.mediatype'};
204 |  |  |  |  |
205 | 100001 | 74.8ms |  |  | my $has_mediatype = 0;
206 |  |  |  |  |
207 |  |  |  |  | # check Content-Type
208 | 100001 | 335ms | 100001 | 818ms | 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
218 | 100001 | 655ms | 200002 | 39.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
219 | 100001 | 622ms | 100001 | 4.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 =
222 | 100001 | 1.44s | 300003 | 46.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 |  |  |  |  |
225 | 100001 | 883ms |  |  | if ( @jsonapi_accept ) {
226 | 100001 | 240ms | 100001 | 994ms | $wr->(ERR_WRONG_HEADER_ACCEPT) # spent 994ms making 100001 calls to HTTP::Headers::ActionPack::Core::BaseHeaderWithParams::params_are_empty, avg 10µs/call
227 | 100001 | 158ms |  |  | unless grep { $_->params_are_empty } @jsonapi_accept;
228 |  |  |  |  |
229 | 100001 | 29.8ms |  |  | $has_mediatype++;
230 |  |  |  |  | }
231 |  |  |  |  | }
232 |  |  |  |  |
233 | 100001 | 625ms |  |  | $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 {
237 | 100001 | 58.5ms |  |  | my ( $self, $wr, $req ) = @_;
238 |  |  |  |  |
239 | 100001 | 48.3ms |  |  | my %params;
240 | 100001 | 301ms | 100001 | 13.5s | my $query_params = $req->query_parameters; # spent 13.5s making 100001 calls to Plack::Request::query_parameters, avg 135µs/call
241 |  |  |  |  |
242 | 100001 | 505ms | 200002 | 3.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)
245 | 100001 | 246ms |  |  | for my $k ( keys %{ $query_params } ) {
246 | 102107 | 1.02s | 102107 | 523ms | 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)
250 | 102107 | 231ms |  |  | unless grep { $p eq $_ } qw< fields filter page include sort >;
251 |  |  |  |  |
252 |  |  |  |  | # "complex" parameters have the correct structre
253 |  |  |  |  | $wr->(ERR_BAD_REQ)
254 | 102107 | 118ms |  |  | if !defined $f and grep { $p eq $_ } qw< page fields filter >;
255 |  |  |  |  |
256 |  |  |  |  | # 'sort' requested but not supported
257 | 102107 | 95.4ms |  |  | $wr->(ERR_SORT_NOT_ALLOWED)
258 |  |  |  |  | if $p eq 'sort' and !$self->{'ponapi.sort_allowed'};
259 |  |  |  |  |
260 |  |  |  |  | # values can be passed as CSV
261 | 260257 | 187ms |  |  | my @values = map { $unesacpe_values ? uri_unescape($_) : $_ }
262 | 102107 | 520ms | 102107 | 1.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)
266 | 102107 | 113ms |  |  | $wr->(ERR_BAD_REQ)
267 |  |  |  |  | if $p ne 'fields' and exists $query_params->{$k} and !@values;
268 |  |  |  |  |
269 |  |  |  |  | # values passed on in array-ref
270 | 102107 | 184ms |  |  | grep { $p eq $_ } qw< fields filter >
271 |  |  |  |  | and $params{$p}{$f} = \@values;
272 |  |  |  |  |
273 |  |  |  |  | # page info has one value per request
274 | 102107 | 35.6ms |  |  | $p eq 'page' and $params{$p}{$f} = $values[0];
275 |  |  |  |  |
276 |  |  |  |  | # values passed on in hash-ref
277 | 102107 | 90.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.
282 | 102107 | 143ms |  |  | $p eq 'sort' and $params{'sort'} = \@values;
283 |  |  |  |  | }
284 |  |  |  |  |
285 | 100001 | 655ms |  |  | 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 {
289 | 100001 | 69.8ms |  |  | my ( $self, $wr, $req ) = @_;
290 |  |  |  |  |
291 | 100001 | 715ms | 100001 | 701ms | 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 |  |  |  |  |
309 |  |  |  |  | sub _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 {
357 | 100001 | 88.3ms |  |  | my ( $self, $status, $headers, $content, $is_head ) = @_;
358 | 100001 | 659ms | 100001 | 1.67s | my $res = Plack::Response->new( $status || 200 ); # spent 1.67s making 100001 calls to Plack::Response::new, avg 17µs/call
359 |  |  |  |  |
360 | 100001 | 203ms | 100001 | 1.66s | $res->headers($headers); # spent 1.66s making 100001 calls to Plack::Response::headers, avg 17µs/call
361 | 100001 | 457ms | 100001 | 4.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 |  |  |  |  |
364 | 100001 | 63.8ms |  |  | if ( ref $content ) {
365 | 100001 | 2.13s | 100001 | 1.65s | my $enc_content = JSON::XS::encode_json $content; # spent 1.65s making 100001 calls to JSON::XS::encode_json, avg 17µs/call
366 | 100001 | 281ms | 100001 | 3.42s | $res->content_length( length($enc_content) ); # spent 3.42s making 100001 calls to Plack::Response::content_length, avg 34µs/call
367 | 100001 | 303ms | 100001 | 1.51s | $res->content_type( $self->{'ponapi.mediatype'} ); # spent 1.51s making 100001 calls to Plack::Response::content_type, avg 15µs/call
368 | 100001 | 296ms | 100001 | 1.09s | $res->content($enc_content) unless $is_head; # spent 1.09s making 100001 calls to Plack::Response::content, avg 11µs/call
369 |  |  |  |  | }
370 |  |  |  |  |
371 | 100001 | 696ms | 100001 | 13.9s | $res->finalize; # spent 13.9s making 100001 calls to Plack::Response::finalize, avg 139µs/call
372 |  |  |  |  | }
373 |  |  |  |  |
374 |  |  |  |  | sub _options_response {
375 |  |  |  |  | my ( $self, $options ) = @_;
376 |  |  |  |  | return +[ 200, [ Allow => join( ', ' => @{$options} ) ], [] ];
377 |  |  |  |  | }
378 |  |  |  |  |
379 |  |  |  |  | sub _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 |  |  |  |  |
388 | 1 | 4µs |  |  | 1;
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