Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ Changes log
their case (`Realm=`, `Charset=`), as required by RFC 9110. Issue #1455.
- `ChallengeScheme.valueOf()` returned `HTTP_OAUTH` for the names of the Bearer and MAC schemes
(`HTTP_Bearer`, `HTTP_MAC`) instead of `HTTP_OAUTH_BEARER` and `HTTP_OAUTH_MAC`. Issue #1618.
- The gRPC client could reject a call with `RESOURCE_EXHAUSTED` ("gRPC call limit exceeded")
right after the end of the previous one: the call slot was released after the caller was
notified. Issue #1622.
- Misc
- Fixed JDK HTTP client accessibility in core and WebSocket tests in Eclipse and VS Code,
without adding a production module dependency.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,14 +380,13 @@ protected void http2ProcessingFailed(

@Override
public void deframerClosed(boolean partial) {
try {
super.deframerClosed(partial);
} finally {
if (!done) {
done = true;
finished.accept(RestletClientStream.this);
}
// Release the call slot first: the SDK notifies the caller from super.deframerClosed(),
// and a caller that starts its next call right away must not find the slot still taken.
if (!done) {
done = true;
finished.accept(RestletClientStream.this);
Comment thread
jlouvel marked this conversation as resolved.
Resolved
}
super.deframerClosed(partial);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.restlet.ext.grpc;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand Down Expand Up @@ -189,6 +190,41 @@ void malformedBinaryMetadataFailsExplicitly() throws Exception {
assertRecovered();
}

/**
* The call slot must be free as soon as the caller is told that a call failed, otherwise the
* next call of the same thread is rejected with RESOURCE_EXHAUSTED.
*/
@Test
void failedCallReleasesItsSlotBeforeTheCallerIsNotified() throws Exception {
adapter.setMaxConcurrentCalls(1);
AtomicInteger requests = new AtomicInteger();
start(
new Restlet() {
@Override
public void handle(Request request, Response response) {
if (requests.incrementAndGet() % 2 == 1) {
response.setEntity("", MediaType.valueOf("application/grpc"));
} else {
response.setEntity(
new ByteArrayRepresentation(
frame("recovered"),
MediaType.valueOf("application/grpc")));
response.getTrailers().add("grpc-status", "0");
}
}
});
for (int i = 0; i < 200; i++) {
int call = i;
Status status = failure();
// Only the slot matters here: depending on timing the failure is UNKNOWN or
// UNAVAILABLE.
assertNotEquals(Status.Code.OK, status.getCode(), () -> call + ": " + status);
assertNotEquals(
Status.Code.RESOURCE_EXHAUSTED, status.getCode(), () -> call + ": " + status);
assertRecovered();
}
}

@Test
void wrongContentTypeFailsAndRecovers() throws Exception {
startMalformedThenHealthy(response -> response.setEntity("not grpc", MediaType.TEXT_PLAIN));
Expand Down
Loading