Skip to content

About

A lightweight, fully spec-compliant handler for JSON-RPC 2.0 requests

Resources

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

56 watching

Forks

Repository files navigation

json_rpc_handler

Gem (including prereleases) CI

A lightweight, fully spec-compliant JSON-RPC 2.0 handler.

It only deals with the parsing and handling of JSON-RPC requests, producing appropriate JSON-RPC responses.

It does not deal with the transport layer (e.g. HTTP, WebSockets, etc.)

Installation

Add this line to your application's Gemfile:

gem 'json_rpc_handler'

And then execute:

bundle

Or install it yourself as:

gem install json_rpc_handler

Usage

If you already have a request object parsed from JSON, you can use the handle method. Pass a block to the handle call to look up methods by name:

require 'json_rpc_handler'

request = {
  jsonrpc: '2.0',
  id: 1,
  method: 'add',
  params: {a: 1, b: 2},
}

result = JsonRpcHandler.handle(request) do |method_name|
  case method_name
  when 'add'
    ->(params) { params[:a] + params[:b] }
  end
end

puts result.to_json
# {"jsonrpc":"2.0","id":1,"result":3}

Returning a nil from the method lookup block will result in a method not found error.

If the request JSON is not already parsed, you can use the handle_json method:

request_json = '{"jsonrpc": "2.0","id":1,"method":"add","params":{"a":1,"b":2}}'

result_json = JsonRpcHandler.handle_json(request_json) do |method_name|
  case method_name
  when 'add'
    ->(params) { params[:a] + params[:b] }
  end
end

puts result_json
# {"jsonrpc":"2.0","id":1,"result":3}

A nil from handle or handle_json means "no content" - either the method returned nil, or all method calls of a batch request returned nil. It is up to the integration to apply the appropriate transport-layer semantics (e.g. returning a 204 No Content).

Error handling

Unexpected exceptions from method lookup or a registered method return code -32603 and message Internal error, with no data member.

Pass on_error: to handle or handle_json to report these exceptions. The hook receives the exception and a context hash with id and method keys. It runs before the internal error response is built, once for each failing batch item, including notifications. The notification's id is nil.

methods = { 'add' => ->(params) { params[:a] + params[:b] } }
on_error = ->(exception, context) {
  warn "#{context[:method]} #{context[:id].inspect}: #{exception.message}"
}

JsonRpcHandler.handle(request, on_error:) { |name| methods[name] }
JsonRpcHandler.handle_json(request_json, on_error:) { |name| methods[name] }

The hook's return value is ignored. If it raises a StandardError, that error is ignored without calling the hook again. The original request still gets the internal error response, or no response for a notification.

For an explicit error response, raise JsonRpcHandler::Error, a StandardError subclass. Its constructor takes required code: and message: keywords and an optional data: keyword:

response = JsonRpcHandler.handle(request) do |name|
  if name == 'add'
    ->(_params) {
      raise JsonRpcHandler::Error.new(
        code: -32001,
        message: 'Try later',
        data: { retry: true },
      )
    }
  end
end

puts response.to_json
# {"jsonrpc":"2.0","id":1,"error":{"code":-32001,"message":"Try later","data":{"retry":true}}}

The supplied code, message, and data become the response's error fields. A nil data value is omitted; values such as false and 0 are kept. There is no fallback from data to the exception message. These explicit errors do not call on_error: and still produce no response for notifications. Other response errors, such as Method not found and Parse error, keep their existing fields and do not call the hook.

ID Validation

By default, string request IDs are validated to contain only alphanumeric characters, dashes, and underscores.

Note: The JSON-RPC 2.0 specification does not specify a default ID validation pattern, but this default validation is recommended to protect against XSS vulnerabilities when IDs are reflected in responses.

# Default behavior - accepts alphanumerics, dashes, underscores
request = { jsonrpc: '2.0', id: 'request-123_abc', method: 'add', params: {a: 1, b: 2} }
JsonRpcHandler.handle(request) { |method_name| ... }
# => {"jsonrpc":"2.0","id":"request-123_abc","result":3}

# Rejects potentially dangerous characters
request = { jsonrpc: '2.0', id: '<script>alert("xss")</script>', method: 'add', params: {a: 1, b: 2} }
JsonRpcHandler.handle(request) { |method_name| ... }
# => {"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"Invalid Request","data":"Request ID must match validation pattern, or be an integer or null"}}

You can customize the validation pattern by passing the id_validation_pattern parameter:

# Allow email-like IDs with a custom pattern
custom_pattern = /\A[a-zA-Z0-9_.\-@]+\z/
request = { jsonrpc: '2.0', id: 'user@example.com', method: 'add', params: {a: 1, b: 2} }

JsonRpcHandler.handle(request, id_validation_pattern: custom_pattern) do |method_name|
  # ...
end

# Also works with handle_json
JsonRpcHandler.handle_json(request_json, id_validation_pattern: custom_pattern) do |method_name|
  # ...
end

To disable ID validation entirely (not recommended), pass nil as the pattern:

# Accepts any string
JsonRpcHandler.handle(request, id_validation_pattern: nil) do |method_name|
  # ...
end

Development

After checking out the repo:

  1. Run bundle to install dependencies.
  2. Run rake test to run the tests, including code coverage.

To install this gem onto your local machine, run bundle exec rake install.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/json-rpc-handler. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct. Read more about contributing here.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in this repository is expected to follow the Code of Conduct.

About

A lightweight, fully spec-compliant handler for JSON-RPC 2.0 requests

Resources

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

56 watching

Forks

Releases

Packages

Used by

Contributors

Languages