Skip to content

Commit 7a43e1c

Browse files
committed
Copy-paste of files from original gem
1 parent c152f15 commit 7a43e1c

File tree

2 files changed

+816
-0
lines changed

2 files changed

+816
-0
lines changed

lib/json_rpc_handler.rb

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# frozen_string_literal: true
2+
3+
require "json_rpc_handler/version"
4+
require "json"
5+
6+
module JsonRpcHandler
7+
class Version
8+
V1_0 = "1.0"
9+
V2_0 = "2.0"
10+
end
11+
12+
class ErrorCode
13+
InvalidRequest = -32600
14+
MethodNotFound = -32601
15+
InvalidParams = -32602
16+
InternalError = -32603
17+
ParseError = -32700
18+
end
19+
20+
DEFAULT_ALLOWED_ID_CHARACTERS = /\A[a-zA-Z0-9_-]+\z/
21+
22+
module_function
23+
24+
def handle(request, id_validation_pattern: DEFAULT_ALLOWED_ID_CHARACTERS, &method_finder)
25+
if request.is_a?(Array)
26+
return error_response(id: :unknown_id, id_validation_pattern:, error: {
27+
code: ErrorCode::InvalidRequest,
28+
message: "Invalid Request",
29+
data: "Request is an empty array",
30+
}) if request.empty?
31+
32+
# Handle batch requests
33+
responses = request.map { |req| process_request(req, id_validation_pattern:, &method_finder) }.compact
34+
35+
# A single item is hoisted out of the array
36+
return responses.first if responses.one?
37+
38+
# An empty array yields nil
39+
responses if responses.any?
40+
elsif request.is_a?(Hash)
41+
# Handle single request
42+
process_request(request, id_validation_pattern:, &method_finder)
43+
else
44+
error_response(id: :unknown_id, id_validation_pattern:, error: {
45+
code: ErrorCode::InvalidRequest,
46+
message: "Invalid Request",
47+
data: "Request must be an array or a hash",
48+
})
49+
end
50+
end
51+
52+
def handle_json(request_json, id_validation_pattern: DEFAULT_ALLOWED_ID_CHARACTERS, &method_finder)
53+
begin
54+
request = JSON.parse(request_json, symbolize_names: true)
55+
response = handle(request, id_validation_pattern:, &method_finder)
56+
rescue JSON::ParserError
57+
response = error_response(id: :unknown_id, id_validation_pattern:, error: {
58+
code: ErrorCode::ParseError,
59+
message: "Parse error",
60+
data: "Invalid JSON",
61+
})
62+
end
63+
64+
response.to_json if response
65+
end
66+
67+
def process_request(request, id_validation_pattern:, &method_finder)
68+
id = request[:id]
69+
70+
error = if !valid_version?(request[:jsonrpc])
71+
"JSON-RPC version must be 2.0"
72+
elsif !valid_id?(request[:id], id_validation_pattern)
73+
"Request ID must match validation pattern, or be an integer or null"
74+
elsif !valid_method_name?(request[:method])
75+
'Method name must be a string and not start with "rpc."'
76+
end
77+
78+
return error_response(id: :unknown_id, id_validation_pattern:, error: {
79+
code: ErrorCode::InvalidRequest,
80+
message: "Invalid Request",
81+
data: error,
82+
}) if error
83+
84+
method_name = request[:method]
85+
params = request[:params]
86+
87+
unless valid_params?(params)
88+
return error_response(id:, id_validation_pattern:, error: {
89+
code: ErrorCode::InvalidParams,
90+
message: "Invalid params",
91+
data: "Method parameters must be an array or an object or null",
92+
})
93+
end
94+
95+
begin
96+
method = method_finder.call(method_name)
97+
98+
if method.nil?
99+
return error_response(id:, id_validation_pattern:, error: {
100+
code: ErrorCode::MethodNotFound,
101+
message: "Method not found",
102+
data: method_name,
103+
})
104+
end
105+
106+
result = method.call(params)
107+
108+
success_response(id:, result:)
109+
rescue StandardError => e
110+
error_response(id:, id_validation_pattern:, error: {
111+
code: ErrorCode::InternalError,
112+
message: "Internal error",
113+
data: e.message,
114+
})
115+
end
116+
end
117+
118+
def valid_version?(version)
119+
version == Version::V2_0
120+
end
121+
122+
def valid_id?(id, pattern = nil)
123+
return true if id.nil? || id.is_a?(Integer)
124+
return false unless id.is_a?(String)
125+
126+
pattern ? id.match?(pattern) : true
127+
end
128+
129+
def valid_method_name?(method)
130+
method.is_a?(String) && !method.start_with?("rpc.")
131+
end
132+
133+
def valid_params?(params)
134+
params.nil? || params.is_a?(Array) || params.is_a?(Hash)
135+
end
136+
137+
def success_response(id:, result:)
138+
{
139+
jsonrpc: Version::V2_0,
140+
id:,
141+
result:,
142+
} unless id.nil?
143+
end
144+
145+
def error_response(id:, id_validation_pattern:, error:)
146+
{
147+
jsonrpc: Version::V2_0,
148+
id: valid_id?(id, id_validation_pattern) ? id : nil,
149+
error: error.compact,
150+
} unless id.nil?
151+
end
152+
end

0 commit comments

Comments
 (0)