-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Describe the bug
When attempting to force the response to be json (as a workaround to #5), I get in a situation where the connection between the client and the server is terminated server-side, and no error is written anywhere, leaving the user guessing as to what the problem is.
To Reproduce
(shortened for convenience, but if you think information from the ellided sections is required, I can provide more on demand).
anHttpMateConfiguredAs(USE_CASE_DRIVEN)
.get("/api/usecase", UseCase.class)
.mappingRequestsAndResponsesUsing(mapMate()
.matchingTheContentType(fromString("application/x-www-form-urlencoded"))
.toTheMarshallerType(...)
.assumingTheDefaultContentType(ContentType.json())
.usingTheSerializer(...)
.andTheDeserializer(...))
.configured(toCreateUseCaseInstancesUsing(injector::getInstance))
.configured(Configurator.configuratorForType(EventModule.class, eventModule -> {
eventModule.setDefaultRequestToEventMapper(byDirectlyMappingAllData());
}))
...
.configured(toLogUsing((message, metaData) ->
log.info("{} (MetaData: {})", message, metaData)))
.configured(toCustomizeResponsesUsing(metaData -> <<<<< These lines
metaData.set(CONTENT_TYPE, ContentType.json()))) <<<<< cause the trouble
.build();
- Breakpoint the
metaData.set
line -> B1 - Breakpoint the entry of the main use case invocation method (in my case
GetUserInfo.invoke()
) -> B2 - Breakpoint the
Serializer.serializeFromMap
method -> B3 - Issue a request to the use case endpoint (
/api/usecase
) - B1 is hit first, continue execution
- B2 is hit next, continue execution
- B3 is hit
- the marshallingType looks correct (
MarshallingType(type=json)
), - the method returns the correctly serialized json value
- the marshallingType looks correct (
- Add an Exception breakpoing (for any exception) -> B4
- Continue execution
- B4 is hit at line 61 in
com.envimate.httpmate.chains.MetaData
public <T> T get(final MetaDataKey<T> key) {
return getOptional(key).orElseThrow(() -> new RuntimeException(format(
"Could not find meta datum %s in %s", key.key(), map)));
}
- Evaluate the expression for the exception
java.lang.RuntimeException: Could not find meta datum RESPONSE_STATUS in {PATH=Path(path=/api/...}
- Step out in the debugger
- Assuming you are using the PureJavaEndpoint on top of the oracle jvm 12.0.1, you arrive in
sun.net.httpserver.ServerImpl
, which closes the connection:
catch (Exception e4) {
logger.log (Level.TRACE, "ServerImpl.Exchange (2)", e4);
closeConnection(connection);
}
- The HTTP client that issued the request gets an abrupt connection termination
[2019-07-08 14:38:14.003] NO_REQUEST_ID INFO o.a.h.i.c.DefaultHttpClient - I/O exception (org.apache.http.NoHttpResponseException) caught when processing request to {}->http://localhost:3000: The target server failed to respond
[2019-07-08 14:38:14.004] NO_REQUEST_ID INFO o.a.h.i.c.DefaultHttpClient - Retrying request to {}->http://localhost:3000
[2019-07-08 14:38:14.011] NO_REQUEST_ID INFO o.a.h.i.c.DefaultHttpClient - I/O exception (org.apache.http.NoHttpResponseException) caught when processing request to {}->http://localhost:3000: The target server failed to respond
[2019-07-08 14:38:14.011] NO_REQUEST_ID INFO o.a.h.i.c.DefaultHttpClient - Retrying request to {}->http://localhost:3000
[2019-07-08 14:38:14.014] NO_REQUEST_ID INFO o.a.h.i.c.DefaultHttpClient - I/O exception (org.apache.http.NoHttpResponseException) caught when processing request to {}->http://localhost:3000: The target server failed to respond
[2019-07-08 14:38:14.015] NO_REQUEST_ID INFO o.a.h.i.c.DefaultHttpClient - Retrying request to {}->http://localhost:3000
org.apache.http.NoHttpResponseException: localhost:3000 failed to respond
- No logging occurs server side
Expected behavior
- The internal RuntimeException should be reported server-side to enable troubleshooting, probably to the callback configured earlier:
.configured(toLogUsing((message, metaData) -> ...))
-
An HTTP 500 must be returned instead of forcibly closing the connection, as the last resort when such an error condition is encountered.
If you don't do this, as you can see, depending on the client, there
may be multiple retries, each time invoking the use case. If the invoked logic isn't idempotent, there be dragons. -
My initial expectation was that I only override in the MetaData the exact keys I need to override. And that the code would otherwise run as usual (first), and then my key modifications come after (as an overlay so to speak).
When I don't add the customizer, a http response of HTTP 200 occurs, even though I did not explicitly set the response status anywhere.
So, adding the customizer that does not touch the response status should result in a HTTP 200 as well?
-
If the customizer absolutely must configure the response status, then maybe try to make it harder or impossible to ignore (or rather, forget) it, by having ResponseTemplate.apply() return an HttpStatusCode.