Filename | /home/mickey/git_tree/PONAPI/Server/lib/PONAPI/Server.pm |
Statements | Executed 9079006 statements in 35.1s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
100001 | 1 | 1 | 8.47s | 102s | _ponapi_check_headers | PONAPI::Server::
100001 | 1 | 1 | 6.56s | 871s | call | PONAPI::Server::
100001 | 1 | 1 | 5.45s | 144s | _ponapi_params | PONAPI::Server::
100001 | 1 | 1 | 4.94s | 10.4s | _ponapi_route_match | PONAPI::Server::
100001 | 1 | 1 | 4.92s | 34.1s | _response | PONAPI::Server::
100001 | 1 | 1 | 4.75s | 23.0s | _ponapi_query_params | PONAPI::Server::
299514 | 5 | 1 | 2.69s | 4.68s | _is_get_like | PONAPI::Server::
501622 | 3 | 1 | 1.21s | 1.21s | CORE:match (opcode) | PONAPI::Server::
100001 | 1 | 1 | 830ms | 145s | __ANON__[lib/PONAPI/Server.pm:68] | PONAPI::Server::
100001 | 1 | 1 | 779ms | 1.48s | _ponapi_data | PONAPI::Server::
100001 | 1 | 1 | 365ms | 365ms | CORE:regcomp (opcode) | PONAPI::Server::
1 | 1 | 1 | 2.07ms | 2.67ms | BEGIN@34 | PONAPI::Server::
1 | 1 | 1 | 927µs | 1.31ms | BEGIN@13 | PONAPI::Server::
1 | 1 | 1 | 870µs | 2.12ms | BEGIN@14 | PONAPI::Server::
1 | 1 | 1 | 781µs | 1.71ms | BEGIN@10 | PONAPI::Server::
1 | 1 | 1 | 717µs | 1.40s | BEGIN@17 | PONAPI::Server::
1 | 1 | 1 | 634µs | 756µs | BEGIN@11 | PONAPI::Server::
1 | 1 | 1 | 392µs | 11.5ms | BEGIN@18 | PONAPI::Server::
1 | 1 | 1 | 66µs | 811ms | prepare_app | PONAPI::Server::
1 | 1 | 1 | 62µs | 62µs | CORE:print (opcode) | PONAPI::Server::
1 | 1 | 1 | 31µs | 810ms | _load_dao | PONAPI::Server::
1 | 1 | 1 | 18µs | 133µs | BEGIN@22 | PONAPI::Server::
1 | 1 | 1 | 14µs | 25µs | BEGIN@4 | PONAPI::Server::
1 | 1 | 1 | 9µs | 35µs | BEGIN@20 | PONAPI::Server::
1 | 1 | 1 | 8µs | 43µs | BEGIN@15 | PONAPI::Server::
1 | 1 | 1 | 7µs | 11µs | BEGIN@5 | PONAPI::Server::
1 | 1 | 1 | 7µs | 7µs | BEGIN@9 | PONAPI::Server::
1 | 1 | 1 | 4µs | 4µs | BEGIN@12 | PONAPI::Server::
1 | 1 | 1 | 2µs | 2µs | CORE:qr (opcode) | PONAPI::Server::
0 | 0 | 0 | 0s | 0s | __ANON__[lib/PONAPI/Server.pm:38] | PONAPI::Server::
0 | 0 | 0 | 0s | 0s | _error_response | PONAPI::Server::
0 | 0 | 0 | 0s | 0s | _options_response | PONAPI::Server::
0 | 0 | 0 | 0s | 0s | _validate_data_members | PONAPI::Server::
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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 | ||||
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 # 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 | ||||
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 | ||||
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 | ||||
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 | ||||
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 | ||||
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 | ||||
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 | ||||
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 | ||||
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 | ||||
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 | ||||
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 | ||||
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 | |||||
# 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 | |||||
# spent 2µs within PONAPI::Server::CORE:qr which was called:
# once (2µs+0s) by PONAPI::CLI::RunServer::BEGIN@9 at line 40 | |||||
# 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 |