-
Notifications
You must be signed in to change notification settings - Fork 266
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Add custom HttpStatusCodeProvider to adjust to JSON-RPC 2.0 conventions #1522
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package co.rsk.rpc.netty; | ||
|
||
import com.googlecode.jsonrpc4j.HttpStatusCodeProvider; | ||
import io.netty.handler.codec.http.HttpResponseStatus; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.*; | ||
|
||
public class Web3HttpStatusCodeProvider implements HttpStatusCodeProvider { | ||
|
||
private static final List<Integer> BAD_REQUEST_JSON_ERRORS = Stream.of(METHOD_PARAMS_INVALID, INVALID_REQUEST, PARSE_ERROR) | ||
.map(jsonError -> jsonError.code) | ||
.collect(Collectors.toList()); | ||
|
||
/** | ||
* Only invalid JSON-RPC requests (e.g. a malformed JSON) qualify for a Bad Request HTTP status. | ||
* In any other case, the HTTP protocol remains entirely independent from JSON-RPC (since its 2.0 version). | ||
* Reference: https://www.jsonrpc.org/ | ||
*/ | ||
@Override | ||
public int getHttpStatusCode(int resultCode) { | ||
return isBadRequestResultCode(resultCode) ? | ||
HttpResponseStatus.BAD_REQUEST.code() : | ||
HttpResponseStatus.OK.code(); | ||
} | ||
|
||
private static boolean isBadRequestResultCode(int resultCode) { | ||
return BAD_REQUEST_JSON_ERRORS.contains(resultCode); | ||
} | ||
|
||
/** | ||
* The default implementation wrongly assumes that an HTTP status code is mapped to a single JSON RPC error - this | ||
* is not always the case. Either way, this is not used in the current project. | ||
*/ | ||
@Override | ||
public Integer getJsonRpcCode(int httpStatusCode) { | ||
throw new UnsupportedOperationException("Cannot possibly determine a single JSON RPC from an HTTP status code"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. jsonrpc4j maps each supported HTTP status code to a single JSON-RPC code - this is not entirely correct in my opinion, since a single HTTP code could map to N JSON-RPC codes (this even happens in jsonrpc4j). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Take into account that a JSON RPC request could be an array of valid JSON RPC requests. Is this case covered in the process of responses/responses code? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since I just swapped the |
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,23 +17,30 @@ | |
*/ | ||
package co.rsk.rpc.netty; | ||
|
||
import com.googlecode.jsonrpc4j.DefaultHttpStatusCodeProvider; | ||
import com.googlecode.jsonrpc4j.HttpStatusCodeProvider; | ||
import io.netty.channel.ChannelFutureListener; | ||
import io.netty.channel.ChannelHandlerContext; | ||
import io.netty.channel.SimpleChannelInboundHandler; | ||
import io.netty.handler.codec.http.DefaultFullHttpResponse; | ||
import io.netty.handler.codec.http.HttpResponseStatus; | ||
import io.netty.handler.codec.http.HttpVersion; | ||
|
||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE; | ||
import static io.netty.handler.codec.http.HttpHeaders.Values.APPLICATION_JSON; | ||
|
||
public class Web3ResultHttpResponseHandler extends SimpleChannelInboundHandler<Web3Result> { | ||
|
||
private final HttpStatusCodeProvider httpStatusCodeProvider; | ||
|
||
public Web3ResultHttpResponseHandler(HttpStatusCodeProvider httpStatusCodeProvider) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess using this 3rd-party interface is perfectly fine here, but as an option it could also be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's an interesting suggestion, but considering that we already depend on many classes of com.googlecode.jsonrpc4 for JSON-RPC handling, I think it'd be overkill (besides, I guess rskj shouldn't have the responsibility of defining stuff related to this). Is it ok if I leave it like this? |
||
this.httpStatusCodeProvider = httpStatusCodeProvider; | ||
} | ||
|
||
@Override | ||
protected void channelRead0(ChannelHandlerContext ctx, Web3Result msg) { | ||
DefaultFullHttpResponse response = new DefaultFullHttpResponse( | ||
HttpVersion.HTTP_1_1, | ||
HttpResponseStatus.valueOf(DefaultHttpStatusCodeProvider.INSTANCE.getHttpStatusCode(msg.getCode())), | ||
HttpResponseStatus.valueOf(httpStatusCodeProvider.getHttpStatusCode(msg.getCode())), | ||
msg.getContent() | ||
); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package co.rsk.rpc.netty; | ||
|
||
import com.googlecode.jsonrpc4j.HttpStatusCodeProvider; | ||
import io.netty.handler.codec.http.HttpResponseStatus; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
import static com.googlecode.jsonrpc4j.ErrorResolver.JsonError.*; | ||
import static org.junit.Assert.assertEquals; | ||
|
||
public class Web3HttpStatusCodeProviderTest { | ||
|
||
private HttpStatusCodeProvider httpStatusCodeProvider; | ||
|
||
@Before | ||
public void setup() throws Exception { | ||
httpStatusCodeProvider = new Web3HttpStatusCodeProvider(); | ||
} | ||
|
||
@Test | ||
public void getHttpStatusCodeReturnsExpectedHttpStatusCodes() { | ||
assertThatOkHttpStatusIsReturnedWhenExpected(httpStatusCodeProvider); | ||
assertThatBadRequestHttpStatusIsReturnedWhenExpected(httpStatusCodeProvider); | ||
} | ||
|
||
private void assertThatBadRequestHttpStatusIsReturnedWhenExpected(HttpStatusCodeProvider httpStatusCodeProvider) { | ||
assertReturnedHttpStatusCodeIsExpected(httpStatusCodeProvider, OK.code, HttpResponseStatus.OK.code()); | ||
assertReturnedHttpStatusCodeIsExpected(httpStatusCodeProvider, METHOD_NOT_FOUND.code, HttpResponseStatus.OK.code()); | ||
assertReturnedHttpStatusCodeIsExpected(httpStatusCodeProvider, INTERNAL_ERROR.code, HttpResponseStatus.OK.code()); | ||
assertReturnedHttpStatusCodeIsExpected(httpStatusCodeProvider, ERROR_NOT_HANDLED.code, HttpResponseStatus.OK.code()); | ||
assertReturnedHttpStatusCodeIsExpected(httpStatusCodeProvider, BULK_ERROR.code, HttpResponseStatus.OK.code()); | ||
} | ||
|
||
private void assertThatOkHttpStatusIsReturnedWhenExpected(HttpStatusCodeProvider httpStatusCodeProvider) { | ||
assertReturnedHttpStatusCodeIsExpected(httpStatusCodeProvider, METHOD_PARAMS_INVALID.code, HttpResponseStatus.BAD_REQUEST.code()); | ||
assertReturnedHttpStatusCodeIsExpected(httpStatusCodeProvider, INVALID_REQUEST.code, HttpResponseStatus.BAD_REQUEST.code()); | ||
assertReturnedHttpStatusCodeIsExpected(httpStatusCodeProvider, PARSE_ERROR.code, HttpResponseStatus.BAD_REQUEST.code()); | ||
} | ||
|
||
private void assertReturnedHttpStatusCodeIsExpected(HttpStatusCodeProvider httpStatusCodeProvider, | ||
int jsonRpcResultCode, int expectedHttpStatusCode) { | ||
int httpStatusCode = httpStatusCodeProvider.getHttpStatusCode(jsonRpcResultCode); | ||
assertEquals(expectedHttpStatusCode, httpStatusCode); | ||
} | ||
|
||
@Test(expected = UnsupportedOperationException.class) | ||
public void getJsonRpcCodeThrowsUnsupportedOperationException() { | ||
httpStatusCodeProvider.getJsonRpcCode(INVALID_REQUEST.code); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on this class (which we used prior to this change).
FYI: version 1.6 of jsonrpc4j doesn't adjust error handling to the JSON-RPC guidelines, that's why I decided not to update it this PR.