JSON-RPC-Simple

 view release on metacpan or  search on metacpan

lib/JSON/RPC/Simple/Dispatcher.pm  view on Meta::CPAN

    my $h = HTTP::Headers->new();
    $h->header("Content-Type" => "application/json; charset=" . $self->charset);
    $h->header("Content-Length" => length $content);
    
    return HTTP::Response->new($code, $message, $h, $content);
}

sub errstr { 
    return shift->{errstr} || ""; 
}

sub errobj {
    return shift->{errobj};
}

sub handle {
    my ($self, $path, $request) = @_;
    
    $HTTP_ERROR_CODE = 500;
    
    # Clear any previous errors
    delete $self->{errstr};
    
    # Don't support GET or other methods
    unless ($request->method eq "POST") {
        $self->{errstr} = "I only do POST";
        return $self->_error($request, undef, 0, $self->errstr);
    }
    
    unless ($request->content_type =~ m{^application/json}) {
        $self->{errstr} = 
            "Invalid Content-Type, got '" . $request->content_type . "'";
        return $self->_error($request, undef, 0, $self->errstr);
    }

    # Some requests, like HTTP::Request lazy load content_length so we can't ->can("") it which is why the eval
    my $content_length = eval { $request->content_length };
    if ($@) {
        # Apache2::RequestReq
        $content_length = $request->headers_in->{'Content-Length'} if $request->can("headers_in");
        
        # Fallback
        $content_length = $request->headers->{'Content-Length'} if !defined $content_length && $request->can("headers");
    };
    
    unless (defined $content_length) {
        $self->{errstr} = 
            "JSON-RPC 1.1 requires header Content-Length to be specified";
        return $self->_error($request, undef, 0, $self->errstr);
    }
    
    # Find target
    my $target = $self->{target}->{$path};
    
    # Decode the call and trap errors because it might
    # be invalid JSON
    my $call;
    eval {
        my $content = $request->content;

        # Remove utf-8 BOM if present
        $content =~ s/^(?:\xef\xbb\xbf|\xfe\xff|\xff\xfe)//;
        
        $call = $self->json->decode($content);
    };
    if ($@) {
        $self->{errstr} = "$@";
        $self->{errobj} = $@;
        return $self->_error(
            $request, undef, 0, $self->errstr, undef, undef, $target
        );
    }
    
    my $id = $call->{id};
    my $version = $call->{version};
    unless (defined $version) {
        $self->{errstr} = "Missing 'version'";
        return $self->_error(
            $request, $id, 0, $self->errstr, undef, $call, $target
        );
    }
    unless ($version eq "1.1") {
        $self->{errstr} = "I only do JSON-RPC 1.1";
        return $self->_error(
            $request, $id, 0, $self->errstr, undef, $call, $target
        );
    }
    
    my $method = $call->{method};
    unless ($method) {
        $self->{errstr} = "Missing method";
        $self->_error($request, $id, 0, $self->errstr, undef, $call, $target);
    }
    
    
    my $params = $call->{params};
    unless ($params) {
        $self->_error($id, 0, $self->errstr, undef, $call, $target);
    }

    unless (ref $params eq "HASH" || ref $params eq "ARRAY") {
        $self->{errstr} = "Invalid params, expecting object or array";
        return $self->_error(
            $request, $id, 0, $self->errstr, undef, $call, $target
        );
    }    

    unless ($target) {
        $self->{errstr} = "No target for '${path}' exists";
        return $self->_error(
            $request, $id, 0, $self->errstr, undef, $call, $target
        );
    }
    
    my $cv = $target->can($method);
    my $check_attrs;
    if ($cv) {
        # Check that it's a JSONRpcMethod
        my @attrs = JSON::RPC::Simple->fetch_method_arguments($cv);
        unless (@attrs) {
            $self->{errstr} = "Procedure not found";

lib/JSON/RPC/Simple/Dispatcher.pm  view on Meta::CPAN

=back

=head2 INSTANCE METHODS

=over 4

=item json

=item json ( $json )
 
Gets/sets the json object to use for encoding/decoding

=item charset

=item charset ( $charset )

Gets/sets the charset to use when creating the HTTP::Response object.

=item error_handler ( \&handler )

Gets/sets the error handler to call when an error occurs.

=item dispatch_to ( \%targets )

Sets the dispatch table. The dispatch-table is a path to instance mapping where 
the key is a path and the value the instance of class for which to call the 
method on. For example

  $o->dispatch_to({
    "/API" => "MyApp::API",
    "/Other/API" => MyApp::OtherAPI->new(),
  });

=item handle ( $path, $request )

This method decodes the $request which should be a HTTP::Request look-a-like 
object and finds the appropriate target in the dispatch table for $path.

The $request object MUST provide the following methods:

=over 4

=item method

The HTTP method of the request such as GET, POST, HEAD, PUT in captial letters.

=item content_type

The Content-Type header from the request.

=item content_length

The Content-Length header from the request.

=item content

The content of the request as we only handle POST.

=back

The content is stripped from any unicode BOM before being passed to the JSON 
decoder. 

=back

=cut



( run in 0.769 second using v1.01-cache-2.11-cpan-3d66aa2751a )