Skip to content

Commit d7ced73

Browse files
committed
Merge pull request #104939 from HolonProduction/json-rpc-notification-return-fix
JSONRPC: Fix notification return behavior
2 parents 4c02239 + a18d6e4 commit d7ced73

File tree

3 files changed

+95
-13
lines changed

3 files changed

+95
-13
lines changed

modules/jsonrpc/jsonrpc.cpp

+23-13
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,6 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem
100100
if (p_action.get_type() == Variant::DICTIONARY) {
101101
Dictionary dict = p_action;
102102
String method = dict.get("method", "");
103-
if (method.begins_with("$/")) {
104-
return ret;
105-
}
106103

107104
Array args;
108105
if (dict.has("params")) {
@@ -114,8 +111,11 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem
114111
}
115112
}
116113

114+
/// A Notification is a Request object without an "id" member.
115+
bool is_notification = !dict.has("id");
116+
117117
Variant id;
118-
if (dict.has("id")) {
118+
if (!is_notification) {
119119
id = dict["id"];
120120

121121
// Account for implementations that discern between int and float on the json serialization level, by using an int if there is a .0 fraction. See #100914
@@ -126,23 +126,33 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem
126126

127127
if (methods.has(method)) {
128128
Variant call_ret = methods[method].callv(args);
129-
if (id.get_type() != Variant::NIL) {
130-
ret = make_response(call_ret, id);
131-
}
129+
ret = make_response(call_ret, id);
132130
} else {
133131
ret = make_response_error(JSONRPC::METHOD_NOT_FOUND, "Method not found: " + method, id);
134132
}
133+
134+
/// The Server MUST NOT reply to a Notification
135+
if (is_notification) {
136+
ret = Variant();
137+
}
135138
} else if (p_action.get_type() == Variant::ARRAY && p_process_arr_elements) {
136139
Array arr = p_action;
137-
int size = arr.size();
138-
if (size) {
140+
if (!arr.is_empty()) {
139141
Array arr_ret;
140-
for (int i = 0; i < size; i++) {
141-
const Variant &var = arr.get(i);
142-
arr_ret.push_back(process_action(var));
142+
for (const Variant &var : arr) {
143+
Variant res = process_action(var);
144+
145+
if (res.get_type() != Variant::NIL) {
146+
arr_ret.push_back(res);
147+
}
148+
}
149+
150+
/// If there are no Response objects contained within the Response array as it is to be sent to the client, the server MUST NOT return an empty Array and should return nothing at all.
151+
if (!arr_ret.is_empty()) {
152+
ret = arr_ret;
143153
}
144-
ret = arr_ret;
145154
} else {
155+
/// If the batch rpc call itself fails to be recognized ... as an Array with at least one value, the response from the Server MUST be a single Response object.
146156
ret = make_response_error(JSONRPC::INVALID_REQUEST, "Invalid Request");
147157
}
148158
} else {

modules/jsonrpc/tests/test_jsonrpc.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,10 @@ void test_process_action_bad_method(const Dictionary &p_in) {
7979
check_error_no_method(out_dict);
8080
}
8181

82+
void test_no_response(const Variant &p_in) {
83+
TestClassJSONRPC json_rpc = TestClassJSONRPC();
84+
const Variant &res = json_rpc.process_action(p_in, true);
85+
CHECK(res.get_type() == Variant::NIL);
86+
}
87+
8288
} // namespace TestJSONRPC

modules/jsonrpc/tests/test_jsonrpc.h

+66
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ TEST_CASE("[JSONRPC] process_action invalid") {
4646
check_invalid(json_rpc.process_action(1234));
4747
check_invalid(json_rpc.process_action(false));
4848
check_invalid(json_rpc.process_action(3.14159));
49+
check_invalid(json_rpc.process_action(Array()));
4950
}
5051

5152
void check_invalid_string(const String &p_str);
@@ -57,6 +58,7 @@ TEST_CASE("[JSONRPC] process_string invalid") {
5758
check_invalid_string(json_rpc.process_string("1234"));
5859
check_invalid_string(json_rpc.process_string("false"));
5960
check_invalid_string(json_rpc.process_string("3.14159"));
61+
check_invalid_string(json_rpc.process_string("[]"));
6062
}
6163

6264
class TestClassJSONRPC : public JSONRPC {
@@ -125,9 +127,73 @@ void test_process_action_bad_method(const Dictionary &p_in);
125127

126128
TEST_CASE("[JSONRPC] process_action bad method") {
127129
Dictionary in_dict;
130+
in_dict["id"] = 1;
128131
in_dict["method"] = "nothing";
129132

130133
test_process_action_bad_method(in_dict);
131134
}
132135

136+
void test_no_response(const Variant &p_in);
137+
138+
TEST_CASE("[JSONRPC] process_action notification") {
139+
Dictionary in_dict = Dictionary();
140+
in_dict["method"] = "something";
141+
in_dict["params"] = "yes";
142+
143+
test_no_response(in_dict);
144+
}
145+
146+
TEST_CASE("[JSONRPC] process_action notification bad method") {
147+
Dictionary in_dict;
148+
in_dict["method"] = "nothing";
149+
150+
test_no_response(in_dict);
151+
}
152+
153+
TEST_CASE("[JSONRPC] process_action notification batch") {
154+
Array in;
155+
Dictionary in_1;
156+
in_1["method"] = "something";
157+
in_1["params"] = "more";
158+
in.push_back(in_1);
159+
Dictionary in_2;
160+
in_2["method"] = "something";
161+
in_2["params"] = "yes";
162+
in.push_back(in_2);
163+
164+
test_no_response(in);
165+
}
166+
167+
TEST_CASE("[JSONRPC] mixed batch") {
168+
Array in;
169+
Dictionary in_1;
170+
in_1["method"] = "something";
171+
in_1["id"] = 1;
172+
in_1["params"] = "more";
173+
in.push_back(in_1);
174+
Dictionary in_2;
175+
in_2["method"] = "something";
176+
in_2["id"] = 2;
177+
in_2["params"] = "yes";
178+
in.push_back(in_2);
179+
Dictionary in_3;
180+
in_3["method"] = "something";
181+
in_3["params"] = "yes";
182+
in.push_back(in_3);
183+
184+
Array expected;
185+
Dictionary expected_1;
186+
expected_1["jsonrpc"] = "2.0";
187+
expected_1["id"] = 1;
188+
expected_1["result"] = "more, please";
189+
expected.push_back(expected_1);
190+
Dictionary expected_2;
191+
expected_2["jsonrpc"] = "2.0";
192+
expected_2["id"] = 2;
193+
expected_2["result"] = "yes, please";
194+
expected.push_back(expected_2);
195+
196+
test_process_action(in, expected, true);
197+
}
198+
133199
} // namespace TestJSONRPC

0 commit comments

Comments
 (0)