diff options
Diffstat (limited to 'Bugzilla/WebService/Server/REST.pm')
-rw-r--r-- | Bugzilla/WebService/Server/REST.pm | 781 |
1 files changed, 396 insertions, 385 deletions
diff --git a/Bugzilla/WebService/Server/REST.pm b/Bugzilla/WebService/Server/REST.pm index 8450a7a28..6a5e889a8 100644 --- a/Bugzilla/WebService/Server/REST.pm +++ b/Bugzilla/WebService/Server/REST.pm @@ -40,134 +40,135 @@ use MIME::Base64 qw(decode_base64); ########################### sub handle { - my ($self) = @_; - - # Determine how the data should be represented. We do this early so - # errors will also be returned with the proper content type. - # If no accept header was sent or the content types specified were not - # matched, we default to the first type in the whitelist. - $self->content_type($self->_best_content_type(REST_CONTENT_TYPE_WHITELIST())); - - # Using current path information, decide which class/method to - # use to serve the request. Throw error if no resource was found - # unless we were looking for OPTIONS - if (!$self->_find_resource($self->cgi->path_info)) { - if ($self->request->method eq 'OPTIONS' - && $self->bz_rest_options) - { - my $response = $self->response_header(STATUS_OK, ""); - my $options_string = join(', ', @{ $self->bz_rest_options }); - $response->header('Allow' => $options_string, - 'Access-Control-Allow-Methods' => $options_string); - return $self->response($response); - } - - ThrowUserError("rest_invalid_resource", - { path => $self->cgi->path_info, - method => $self->request->method }); + my ($self) = @_; + + # Determine how the data should be represented. We do this early so + # errors will also be returned with the proper content type. + # If no accept header was sent or the content types specified were not + # matched, we default to the first type in the whitelist. + $self->content_type($self->_best_content_type(REST_CONTENT_TYPE_WHITELIST())); + + # Using current path information, decide which class/method to + # use to serve the request. Throw error if no resource was found + # unless we were looking for OPTIONS + if (!$self->_find_resource($self->cgi->path_info)) { + if ($self->request->method eq 'OPTIONS' && $self->bz_rest_options) { + my $response = $self->response_header(STATUS_OK, ""); + my $options_string = join(', ', @{$self->bz_rest_options}); + $response->header( + 'Allow' => $options_string, + 'Access-Control-Allow-Methods' => $options_string + ); + return $self->response($response); } - # Dispatch to the proper module - my $class = $self->bz_class_name; - my ($path) = $class =~ /::([^:]+)$/; - $self->path_info($path); - delete $self->{dispatch_path}; - $self->dispatch({ $path => $class }); + ThrowUserError("rest_invalid_resource", + {path => $self->cgi->path_info, method => $self->request->method}); + } - my $params = $self->_retrieve_json_params; + # Dispatch to the proper module + my $class = $self->bz_class_name; + my ($path) = $class =~ /::([^:]+)$/; + $self->path_info($path); + delete $self->{dispatch_path}; + $self->dispatch({$path => $class}); - fix_credentials($params); + my $params = $self->_retrieve_json_params; - # Fix includes/excludes for each call - rest_include_exclude($params); + fix_credentials($params); - # Set callback name if exists - $self->_bz_callback($params->{'callback'}) if $params->{'callback'}; + # Fix includes/excludes for each call + rest_include_exclude($params); - Bugzilla->input_params($params); + # Set callback name if exists + $self->_bz_callback($params->{'callback'}) if $params->{'callback'}; - # Set the JSON version to 1.1 and the id to the current urlbase - # also set up the correct handler method - my $obj = { - version => '1.1', - id => correct_urlbase(), - method => $self->bz_method_name, - params => $params - }; + Bugzilla->input_params($params); - # Execute the handler - my $result = $self->_handle($obj); + # Set the JSON version to 1.1 and the id to the current urlbase + # also set up the correct handler method + my $obj = { + version => '1.1', + id => correct_urlbase(), + method => $self->bz_method_name, + params => $params + }; - if (!$self->error_response_header) { - return $self->response( - $self->response_header($self->bz_success_code || STATUS_OK, $result)); - } + # Execute the handler + my $result = $self->_handle($obj); + + if (!$self->error_response_header) { + return $self->response( + $self->response_header($self->bz_success_code || STATUS_OK, $result)); + } - $self->response($self->error_response_header); + $self->response($self->error_response_header); } sub response { - my ($self, $response) = @_; - - # If we have thrown an error, the 'error' key will exist - # otherwise we use 'result'. JSONRPC returns other data - # along with the result/error such as version and id which - # we will strip off for REST calls. - my $content = $response->content; - my $json_data = {}; - if ($content) { - $json_data = $self->json->decode($content); - } - - my $result = {}; - if (exists $json_data->{error}) { - $result = $json_data->{error}; - $result->{error} = $self->type('boolean', 1); - $result->{documentation} = REST_DOC; - delete $result->{'name'}; # Remove JSONRPCError - } - elsif (exists $json_data->{result}) { - $result = $json_data->{result}; - } - - # The result needs to be a valid JSON data structure - # and not a undefined or scalar value. - if (!ref $result - || blessed($result) - || (ref $result ne 'HASH' && ref $result ne 'ARRAY')) - { - $result = { result => $result }; - } - - Bugzilla::Hook::process('webservice_rest_response', - { rpc => $self, result => \$result, response => $response }); - - # Access Control - $response->header("Access-Control-Allow-Origin", "*"); - $response->header("Access-Control-Allow-Headers", "origin, content-type, accept, x-requested-with"); - - # ETag support - my $etag = $self->bz_etag; - $self->bz_etag($result) if !$etag; - - # If accessing through web browser, then display in readable format - if ($self->content_type eq 'text/html') { - $result = $self->json->pretty->canonical->allow_nonref->encode($result); - - my $template = Bugzilla->template; - $content = ""; - $template->process("rest.html.tmpl", { result => $result }, \$content) - || ThrowTemplateError($template->error()); - - $response->content_type('text/html'); - } - else { - $content = $self->json->encode($result); - } - - $response->content($content); - - $self->SUPER::response($response); + my ($self, $response) = @_; + + # If we have thrown an error, the 'error' key will exist + # otherwise we use 'result'. JSONRPC returns other data + # along with the result/error such as version and id which + # we will strip off for REST calls. + my $content = $response->content; + my $json_data = {}; + if ($content) { + $json_data = $self->json->decode($content); + } + + my $result = {}; + if (exists $json_data->{error}) { + $result = $json_data->{error}; + $result->{error} = $self->type('boolean', 1); + $result->{documentation} + = Bugzilla->params->{'urlbase'} . 'docs/en/html/api/index.html'; + delete $result->{'name'}; # Remove JSONRPCError + } + elsif (exists $json_data->{result}) { + $result = $json_data->{result}; + } + + # The result needs to be a valid JSON data structure + # and not a undefined or scalar value. + if ( !ref $result + || blessed($result) + || (ref $result ne 'HASH' && ref $result ne 'ARRAY')) + { + $result = {result => $result}; + } + + Bugzilla::Hook::process('webservice_rest_response', + {rpc => $self, result => \$result, response => $response}); + + # Access Control + $response->header("Access-Control-Allow-Origin", "*"); + $response->header("Access-Control-Allow-Headers", + "origin, content-type, accept, x-requested-with"); + + # ETag support + my $etag = $self->bz_etag; + $self->bz_etag($result) if !$etag; + + # If accessing through web browser, then display in readable format + if ($self->content_type eq 'text/html') { + $result = $self->json->pretty->canonical->allow_nonref->encode($result); + + my $template = Bugzilla->template; + $content = ""; + $template->process("rest.html.tmpl", {result => $result}, \$content) + || ThrowTemplateError($template->error()); + + $response->content_type('text/html'); + } + else { + $content = $self->json->encode($result); + } + + $response->content($content); + + $self->SUPER::response($response); } ####################################### @@ -175,36 +176,40 @@ sub response { ####################################### sub handle_login { - my $self = shift; - - # If we're being called using GET, we don't allow cookie-based or Env - # login, because GET requests can be done cross-domain, and we don't - # want private data showing up on another site unless the user - # explicitly gives that site their username and password. (This is - # particularly important for JSONP, which would allow a remote site - # to use private data without the user's knowledge, unless we had this - # protection in place.) We do allow this for GET /login as we need to - # for Bugzilla::Auth::Persist::Cookie to create a login cookie that we - # can also use for Bugzilla_token support. This is OK as it requires - # a login and password to be supplied and will fail if they are not - # valid for the user. - if (!grep($_ eq $self->request->method, ('POST', 'PUT')) - && !($self->bz_class_name eq 'Bugzilla::WebService::User' - && $self->bz_method_name eq 'login')) - { - # XXX There's no particularly good way for us to get a parameter - # to Bugzilla->login at this point, so we pass this information - # around using request_cache, which is a bit of a hack. The - # implementation of it is in Bugzilla::Auth::Login::Stack. - Bugzilla->request_cache->{'auth_no_automatic_login'} = 1; - } - - my $class = $self->bz_class_name; - my $method = $self->bz_method_name; - my $full_method = $class . "." . $method; - - # Bypass JSONRPC::handle_login - Bugzilla::WebService::Server->handle_login($class, $method, $full_method); + my $self = shift; + + # If we're being called using GET, we don't allow cookie-based or Env + # login, because GET requests can be done cross-domain, and we don't + # want private data showing up on another site unless the user + # explicitly gives that site their username and password. (This is + # particularly important for JSONP, which would allow a remote site + # to use private data without the user's knowledge, unless we had this + # protection in place.) We do allow this for GET /login as we need to + # for Bugzilla::Auth::Persist::Cookie to create a login cookie that we + # can also use for Bugzilla_token support. This is OK as it requires + # a login and password to be supplied and will fail if they are not + # valid for the user. + if ( + !grep($_ eq $self->request->method, ('POST', 'PUT')) + && !( + $self->bz_class_name eq 'Bugzilla::WebService::User' + && $self->bz_method_name eq 'login' + ) + ) + { + # XXX There's no particularly good way for us to get a parameter + # to Bugzilla->login at this point, so we pass this information + # around using request_cache, which is a bit of a hack. The + # implementation of it is in Bugzilla::Auth::Login::Stack. + Bugzilla->request_cache->{'auth_no_automatic_login'} = 1; + } + + my $class = $self->bz_class_name; + my $method = $self->bz_method_name; + my $full_method = $class . "." . $method; + + # Bypass JSONRPC::handle_login + Bugzilla::WebService::Server->handle_login($class, $method, $full_method); } ############################ @@ -214,79 +219,78 @@ sub handle_login { # We do not want to run Bugzilla::WebService::Server::JSONRPC->_find_prodedure # as it determines the method name differently. sub _find_procedure { - my $self = shift; - if ($self->isa('JSON::RPC::Server::CGI')) { - return JSON::RPC::Server::_find_procedure($self, @_); - } - else { - return JSON::RPC::Legacy::Server::_find_procedure($self, @_); - } + my $self = shift; + if ($self->isa('JSON::RPC::Server::CGI')) { + return JSON::RPC::Server::_find_procedure($self, @_); + } + else { + return JSON::RPC::Legacy::Server::_find_procedure($self, @_); + } } sub _argument_type_check { - my $self = shift; - my $params; - - if ($self->isa('JSON::RPC::Server::CGI')) { - $params = JSON::RPC::Server::_argument_type_check($self, @_); + my $self = shift; + my $params; + + if ($self->isa('JSON::RPC::Server::CGI')) { + $params = JSON::RPC::Server::_argument_type_check($self, @_); + } + else { + $params = JSON::RPC::Legacy::Server::_argument_type_check($self, @_); + } + + # JSON-RPC 1.0 requires all parameters to be passed as an array, so + # we just pull out the first item and assume it's an object. + my $params_is_array; + if (ref $params eq 'ARRAY') { + $params = $params->[0]; + $params_is_array = 1; + } + + taint_data($params); + + # Now, convert dateTime fields on input. + my $method = $self->bz_method_name; + my $pkg = $self->{dispatch_path}->{$self->path_info}; + my @date_fields = @{$pkg->DATE_FIELDS->{$method} || []}; + foreach my $field (@date_fields) { + if (defined $params->{$field}) { + my $value = $params->{$field}; + if (ref $value eq 'ARRAY') { + $params->{$field} = [map { $self->datetime_format_inbound($_) } @$value]; + } + else { + $params->{$field} = $self->datetime_format_inbound($value); + } } - else { - $params = JSON::RPC::Legacy::Server::_argument_type_check($self, @_); + } + my @base64_fields = @{$pkg->BASE64_FIELDS->{$method} || []}; + foreach my $field (@base64_fields) { + if (defined $params->{$field}) { + $params->{$field} = decode_base64($params->{$field}); } + } - # JSON-RPC 1.0 requires all parameters to be passed as an array, so - # we just pull out the first item and assume it's an object. - my $params_is_array; - if (ref $params eq 'ARRAY') { - $params = $params->[0]; - $params_is_array = 1; - } + # This is the best time to do login checks. + $self->handle_login(); - taint_data($params); - - # Now, convert dateTime fields on input. - my $method = $self->bz_method_name; - my $pkg = $self->{dispatch_path}->{$self->path_info}; - my @date_fields = @{ $pkg->DATE_FIELDS->{$method} || [] }; - foreach my $field (@date_fields) { - if (defined $params->{$field}) { - my $value = $params->{$field}; - if (ref $value eq 'ARRAY') { - $params->{$field} = - [ map { $self->datetime_format_inbound($_) } @$value ]; - } - else { - $params->{$field} = $self->datetime_format_inbound($value); - } - } - } - my @base64_fields = @{ $pkg->BASE64_FIELDS->{$method} || [] }; - foreach my $field (@base64_fields) { - if (defined $params->{$field}) { - $params->{$field} = decode_base64($params->{$field}); - } - } + # Bugzilla::WebService packages call internal methods like + # $self->_some_private_method. So we have to inherit from + # that class as well as this Server class. + my $new_class = ref($self) . '::' . $pkg; + my $isa_string = 'our @ISA = qw(' . ref($self) . " $pkg)"; + eval "package $new_class;$isa_string;"; + bless $self, $new_class; - # This is the best time to do login checks. - $self->handle_login(); + # Allow extensions to modify the params post login + Bugzilla::Hook::process('webservice_rest_request', + {rpc => $self, params => $params}); - # Bugzilla::WebService packages call internal methods like - # $self->_some_private_method. So we have to inherit from - # that class as well as this Server class. - my $new_class = ref($self) . '::' . $pkg; - my $isa_string = 'our @ISA = qw(' . ref($self) . " $pkg)"; - eval "package $new_class;$isa_string;"; - bless $self, $new_class; + if ($params_is_array) { + $params = [$params]; + } - # Allow extensions to modify the params post login - Bugzilla::Hook::process('webservice_rest_request', - { rpc => $self, params => $params }); - - if ($params_is_array) { - $params = [$params]; - } - - return $params; + return $params; } ################### @@ -294,46 +298,46 @@ sub _argument_type_check { ################### sub bz_method_name { - my ($self, $method) = @_; - $self->{_bz_method_name} = $method if $method; - return $self->{_bz_method_name}; + my ($self, $method) = @_; + $self->{_bz_method_name} = $method if $method; + return $self->{_bz_method_name}; } sub bz_class_name { - my ($self, $class) = @_; - $self->{_bz_class_name} = $class if $class; - return $self->{_bz_class_name}; + my ($self, $class) = @_; + $self->{_bz_class_name} = $class if $class; + return $self->{_bz_class_name}; } sub bz_success_code { - my ($self, $value) = @_; - $self->{_bz_success_code} = $value if $value; - return $self->{_bz_success_code}; + my ($self, $value) = @_; + $self->{_bz_success_code} = $value if $value; + return $self->{_bz_success_code}; } sub bz_rest_params { - my ($self, $params) = @_; - $self->{_bz_rest_params} = $params if $params; - return $self->{_bz_rest_params}; + my ($self, $params) = @_; + $self->{_bz_rest_params} = $params if $params; + return $self->{_bz_rest_params}; } sub bz_rest_options { - my ($self, $options) = @_; - $self->{_bz_rest_options} = $options if $options; - return $self->{_bz_rest_options}; + my ($self, $options) = @_; + $self->{_bz_rest_options} = $options if $options; + return $self->{_bz_rest_options}; } sub rest_include_exclude { - my ($params) = @_; + my ($params) = @_; - if ($params->{'include_fields'} && !ref $params->{'include_fields'}) { - $params->{'include_fields'} = [ split(/[\s+,]/, $params->{'include_fields'}) ]; - } - if ($params->{'exclude_fields'} && !ref $params->{'exclude_fields'}) { - $params->{'exclude_fields'} = [ split(/[\s+,]/, $params->{'exclude_fields'}) ]; - } + if ($params->{'include_fields'} && !ref $params->{'include_fields'}) { + $params->{'include_fields'} = [split(/[\s+,]/, $params->{'include_fields'})]; + } + if ($params->{'exclude_fields'} && !ref $params->{'exclude_fields'}) { + $params->{'exclude_fields'} = [split(/[\s+,]/, $params->{'exclude_fields'})]; + } - return $params; + return $params; } ########################## @@ -341,184 +345,191 @@ sub rest_include_exclude { ########################## sub _retrieve_json_params { - my $self = shift; - - # Make a copy of the current input_params rather than edit directly - my $params = {}; - %{$params} = %{ Bugzilla->input_params }; - - # First add any parameters we were able to pull out of the path - # based on the resource regexp and combine with the normal URL - # parameters. - if (my $rest_params = $self->bz_rest_params) { - foreach my $param (keys %$rest_params) { - # If the param does not already exist or if the - # rest param is a single value, add it to the - # global params. - if (!exists $params->{$param} || !ref $rest_params->{$param}) { - $params->{$param} = $rest_params->{$param}; - } - # If rest_param is a list then add any extra values to the list - elsif (ref $rest_params->{$param}) { - my @extra_values = ref $params->{$param} - ? @{ $params->{$param} } - : ($params->{$param}); - $params->{$param} - = [ uniq (@{ $rest_params->{$param} }, @extra_values) ]; - } - } + my $self = shift; + + # Make a copy of the current input_params rather than edit directly + my $params = {}; + %{$params} = %{Bugzilla->input_params}; + + # First add any parameters we were able to pull out of the path + # based on the resource regexp and combine with the normal URL + # parameters. + if (my $rest_params = $self->bz_rest_params) { + foreach my $param (keys %$rest_params) { + + # If the param does not already exist or if the + # rest param is a single value, add it to the + # global params. + if (!exists $params->{$param} || !ref $rest_params->{$param}) { + $params->{$param} = $rest_params->{$param}; + } + + # If rest_param is a list then add any extra values to the list + elsif (ref $rest_params->{$param}) { + my @extra_values + = ref $params->{$param} ? @{$params->{$param}} : ($params->{$param}); + $params->{$param} = [uniq(@{$rest_params->{$param}}, @extra_values)]; + } + } + } + + # Any parameters passed in in the body of a non-GET request will override + # any parameters pull from the url path. Otherwise non-unique keys are + # combined. + if ($self->request->method ne 'GET') { + my $extra_params = {}; + + # We do this manually because CGI.pm doesn't understand JSON strings. + my $json = delete $params->{'POSTDATA'} || delete $params->{'PUTDATA'}; + if ($json) { + eval { $extra_params = $self->json->decode($json); }; + if ($@) { + ThrowUserError('json_rpc_invalid_params', {err_msg => $@}); + } } - # Any parameters passed in in the body of a non-GET request will override - # any parameters pull from the url path. Otherwise non-unique keys are - # combined. - if ($self->request->method ne 'GET') { - my $extra_params = {}; - # We do this manually because CGI.pm doesn't understand JSON strings. - my $json = delete $params->{'POSTDATA'} || delete $params->{'PUTDATA'}; - if ($json) { - eval { $extra_params = $self->json->decode($json); }; - if ($@) { - ThrowUserError('json_rpc_invalid_params', { err_msg => $@ }); - } - } - - # Allow parameters in the query string if request was non-GET. - # Note: parameters in query string body override any matching - # parameters in the request body. - foreach my $param ($self->cgi->url_param()) { - $extra_params->{$param} = $self->cgi->url_param($param); - } - - %{$params} = (%{$params}, %{$extra_params}) if %{$extra_params}; + # Allow parameters in the query string if request was non-GET. + # Note: parameters in query string body override any matching + # parameters in the request body. + foreach my $param ($self->cgi->url_param()) { + $extra_params->{$param} = $self->cgi->url_param($param); } - return $params; + %{$params} = (%{$params}, %{$extra_params}) if %{$extra_params}; + } + + return $params; } sub _find_resource { - my ($self, $path) = @_; - - # Load in the WebService module from the dispatch map and then call - # $module->rest_resources to get the resources array ref. - my $resources = {}; - foreach my $module (values %{ $self->{dispatch_path} }) { - eval("require $module") || die $@; - next if !$module->can('rest_resources'); - $resources->{$module} = $module->rest_resources; - } - - Bugzilla::Hook::process('webservice_rest_resources', - { rpc => $self, resources => $resources }); - - # Use the resources hash from each module loaded earlier to determine - # which handler to use based on a regex match of the CGI path. - # Also any matches found in the regex will be passed in later to the - # handler for possible use. - my $request_method = $self->request->method; - - my (@matches, $handler_found, $handler_method, $handler_class); - foreach my $class (keys %{ $resources }) { - # The resource data for each module needs to be - # an array ref with an even number of elements - # to work correctly. - next if (ref $resources->{$class} ne 'ARRAY' - || scalar @{ $resources->{$class} } % 2 != 0); - - while (my $regex = shift @{ $resources->{$class} }) { - my $options_data = shift @{ $resources->{$class} }; - next if ref $options_data ne 'HASH'; - - if (@matches = ($path =~ $regex)) { - # If a specific path is accompanied by a OPTIONS request - # method, the user is asking for a list of possible request - # methods for a specific path. - $self->bz_rest_options([ keys %{ $options_data } ]); - - if ($options_data->{$request_method}) { - my $resource_data = $options_data->{$request_method}; - $self->bz_class_name($class); - - # The method key/value can be a simple scalar method name - # or a anonymous subroutine so we execute it here. - my $method = ref $resource_data->{method} eq 'CODE' - ? $resource_data->{method}->($self) - : $resource_data->{method}; - $self->bz_method_name($method); - - # Pull out any parameters parsed from the URL path - # and store them for use by the method. - if ($resource_data->{params}) { - $self->bz_rest_params($resource_data->{params}->(@matches)); - } - - # If a special success code is needed for this particular - # method, then store it for later when generating response. - if ($resource_data->{success_code}) { - $self->bz_success_code($resource_data->{success_code}); - } - $handler_found = 1; - } - } - last if $handler_found; + my ($self, $path) = @_; + + # Load in the WebService module from the dispatch map and then call + # $module->rest_resources to get the resources array ref. + my $resources = {}; + foreach my $module (values %{$self->{dispatch_path}}) { + eval("require $module") || die $@; + next if !$module->can('rest_resources'); + $resources->{$module} = $module->rest_resources; + } + + Bugzilla::Hook::process('webservice_rest_resources', + {rpc => $self, resources => $resources}); + + # Use the resources hash from each module loaded earlier to determine + # which handler to use based on a regex match of the CGI path. + # Also any matches found in the regex will be passed in later to the + # handler for possible use. + my $request_method = $self->request->method; + + my (@matches, $handler_found, $handler_method, $handler_class); + foreach my $class (keys %{$resources}) { + + # The resource data for each module needs to be + # an array ref with an even number of elements + # to work correctly. + next + if (ref $resources->{$class} ne 'ARRAY' + || scalar @{$resources->{$class}} % 2 != 0); + + while (my $regex = shift @{$resources->{$class}}) { + my $options_data = shift @{$resources->{$class}}; + next if ref $options_data ne 'HASH'; + + if (@matches = ($path =~ $regex)) { + + # If a specific path is accompanied by a OPTIONS request + # method, the user is asking for a list of possible request + # methods for a specific path. + $self->bz_rest_options([keys %{$options_data}]); + + if ($options_data->{$request_method}) { + my $resource_data = $options_data->{$request_method}; + $self->bz_class_name($class); + + # The method key/value can be a simple scalar method name + # or a anonymous subroutine so we execute it here. + my $method + = ref $resource_data->{method} eq 'CODE' + ? $resource_data->{method}->($self) + : $resource_data->{method}; + $self->bz_method_name($method); + + # Pull out any parameters parsed from the URL path + # and store them for use by the method. + if ($resource_data->{params}) { + $self->bz_rest_params($resource_data->{params}->(@matches)); + } + + # If a special success code is needed for this particular + # method, then store it for later when generating response. + if ($resource_data->{success_code}) { + $self->bz_success_code($resource_data->{success_code}); + } + $handler_found = 1; } - last if $handler_found; + } + last if $handler_found; } + last if $handler_found; + } - return $handler_found; + return $handler_found; } sub _best_content_type { - my ($self, @types) = @_; - return ($self->_simple_content_negotiation(@types))[0] || '*/*'; + my ($self, @types) = @_; + return ($self->_simple_content_negotiation(@types))[0] || '*/*'; } sub _simple_content_negotiation { - my ($self, @types) = @_; - my @accept_types = $self->_get_content_prefs(); - # Return the types as-is if no accept header sent, since sorting will be a no-op. - if (!@accept_types) { - return @types; - } - my $score = sub { $self->_score_type(shift, @accept_types) }; - return sort {$score->($b) <=> $score->($a)} @types; + my ($self, @types) = @_; + my @accept_types = $self->_get_content_prefs(); + + # Return the types as-is if no accept header sent, since sorting will be a no-op. + if (!@accept_types) { + return @types; + } + my $score = sub { $self->_score_type(shift, @accept_types) }; + return sort { $score->($b) <=> $score->($a) } @types; } sub _score_type { - my ($self, $type, @accept_types) = @_; - my $score = scalar(@accept_types); - for my $accept_type (@accept_types) { - return $score if $type eq $accept_type; - $score--; - } - return 0; + my ($self, $type, @accept_types) = @_; + my $score = scalar(@accept_types); + for my $accept_type (@accept_types) { + return $score if $type eq $accept_type; + $score--; + } + return 0; } sub _get_content_prefs { - my $self = shift; - my $default_weight = 1; - my @prefs; - - # Parse the Accept header, and save type name, score, and position. - my @accept_types = split /,/, $self->cgi->http('accept') || ''; - my $order = 0; - for my $accept_type (@accept_types) { - my ($weight) = ($accept_type =~ /q=(\d\.\d+|\d+)/); - my ($name) = ($accept_type =~ m#(\S+/[^;]+)#); - next unless $name; - push @prefs, { name => $name, order => $order++}; - if (defined $weight) { - $prefs[-1]->{score} = $weight; - } else { - $prefs[-1]->{score} = $default_weight; - $default_weight -= 0.001; - } + my $self = shift; + my $default_weight = 1; + my @prefs; + + # Parse the Accept header, and save type name, score, and position. + my @accept_types = split /,/, $self->cgi->http('accept') || ''; + my $order = 0; + for my $accept_type (@accept_types) { + my ($weight) = ($accept_type =~ /q=(\d\.\d+|\d+)/); + my ($name) = ($accept_type =~ m#(\S+/[^;]+)#); + next unless $name; + push @prefs, {name => $name, order => $order++}; + if (defined $weight) { + $prefs[-1]->{score} = $weight; + } + else { + $prefs[-1]->{score} = $default_weight; + $default_weight -= 0.001; } + } - # Sort the types by score, subscore by order, and pull out just the name - @prefs = map {$_->{name}} sort {$b->{score} <=> $a->{score} || - $a->{order} <=> $b->{order}} @prefs; - return @prefs; + # Sort the types by score, subscore by order, and pull out just the name + @prefs = map { $_->{name} } + sort { $b->{score} <=> $a->{score} || $a->{order} <=> $b->{order} } @prefs; + return @prefs; } 1; @@ -646,7 +657,7 @@ the client. =item B<handle_login> This method determines the proper WebService all to make based on class -and method name determined earlier. Then calls L<Bugzilla::WebService::Server::handle_login> +and method name determined earlier. Then calls L<Bugzilla::WebService::Server/handle_login> which will attempt to authenticate the client. =item B<bz_method_name> |