Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unframed grpc service support richer error model #4231

Merged
merged 39 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
171a58c
add rich error mode
natsumehu Apr 26, 2022
618cf00
Merge branch 'master' of https://github.com/line/armeria into rich_error
natsumehu Apr 26, 2022
665741a
some conflict
natsumehu Apr 26, 2022
c1993e3
add status to response
natsumehu Apr 27, 2022
6866921
change license header
natsumehu Apr 27, 2022
659aa8d
some minor change
natsumehu Apr 27, 2022
4ab4a28
some comment
natsumehu Apr 27, 2022
69980c1
tmp
natsumehu May 16, 2022
a67121d
some code cleanup
natsumehu May 16, 2022
80cb34b
change class name
natsumehu May 16, 2022
05af491
add not null annotation
natsumehu May 16, 2022
ce42076
Update grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultUnf…
natsumehu Jun 13, 2022
4649b2b
Update grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultUnf…
natsumehu Jun 13, 2022
a72032f
Update grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultUnf…
natsumehu Jun 13, 2022
48b28a7
Update grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultUnf…
natsumehu Jun 14, 2022
953e057
Update grpc/src/main/java/com/linecorp/armeria/server/grpc/UnframedGr…
natsumehu Jun 14, 2022
117d243
some change
natsumehu Jun 14, 2022
f68bce8
minor change
natsumehu Jun 14, 2022
6ffe02e
minor change
natsumehu Jun 14, 2022
576dc5e
minor change
natsumehu Jun 14, 2022
cccd0e1
resolve some comment
natsumehu Jun 16, 2022
ae09d9d
resolve some comment
natsumehu Jun 16, 2022
a49e3d9
resolve some comment
natsumehu Jun 16, 2022
16697f6
resolve some comment
natsumehu Jun 16, 2022
1a1298c
change exception location
natsumehu Jun 17, 2022
e82d1f3
change exception location
natsumehu Jun 17, 2022
abb80a9
change response code to grpc.code
natsumehu Jun 22, 2022
4b0c93e
to make the error message compatible with other platform
natsumehu Jun 22, 2022
b182829
move utility method to the bottom of the class
natsumehu Jun 23, 2022
bff944c
checkstyle
natsumehu Jun 23, 2022
a9fce68
address the comments
natsumehu Jul 25, 2022
7e6f35b
resolve checkstyle error
natsumehu Jul 25, 2022
fbc4a01
Update grpc/src/test/java/com/linecorp/armeria/server/grpc/DefaultUnf…
ikhoon Jul 27, 2022
542ca41
Update grpc/src/test/java/com/linecorp/armeria/server/grpc/DefaultUnf…
ikhoon Jul 27, 2022
6a4e32c
Update grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultUnf…
natsumehu Jul 29, 2022
cf5f98a
Update grpc/src/main/java/com/linecorp/armeria/server/grpc/UnframedGr…
natsumehu Jul 30, 2022
8b3bc31
Address comments
ikhoon Aug 4, 2022
824890a
Merge branch 'master' into pr-4231@natsumehu+rich_error
ikhoon Aug 4, 2022
21ce155
Address comments by @minwoox
ikhoon Aug 4, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,16 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.armeria.server.grpc;

import static java.util.Objects.requireNonNull;

import com.google.common.collect.ImmutableMap;
package com.linecorp.armeria.server.grpc;

import com.linecorp.armeria.common.AggregatedHttpResponse;
import com.linecorp.armeria.common.HttpData;
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.common.ResponseHeaders;
import com.linecorp.armeria.common.annotation.UnstableApi;
import com.linecorp.armeria.common.grpc.protocol.GrpcHeaderNames;
import com.linecorp.armeria.common.logging.RequestLogAccess;
import com.linecorp.armeria.common.logging.RequestLogProperty;
import com.linecorp.armeria.common.util.Exceptions;
import com.linecorp.armeria.internal.common.util.TemporaryThreadLocals;
import com.linecorp.armeria.server.ServiceRequestContext;

import io.grpc.Status;
import io.grpc.Status.Code;

/**
* Error handler which maps a gRPC response to an {@link HttpResponse}.
Expand All @@ -47,7 +35,7 @@ public interface UnframedGrpcErrorHandler {
* Returns a plain text or json response based on the content type.
*/
static UnframedGrpcErrorHandler of() {
return of(UnframedGrpcStatusMappingFunction.of());
return UnframedGrpcErrorHandlers.of(UnframedGrpcStatusMappingFunction.of());
}

/**
Expand All @@ -57,26 +45,14 @@ static UnframedGrpcErrorHandler of() {
* to an {@link HttpStatus} code.
*/
static UnframedGrpcErrorHandler of(UnframedGrpcStatusMappingFunction statusMappingFunction) {
// Ensure that unframedGrpcStatusMappingFunction never returns null
// by falling back to the default.
final UnframedGrpcStatusMappingFunction mappingFunction =
requireNonNull(statusMappingFunction, "statusMappingFunction")
.orElse(UnframedGrpcStatusMappingFunction.of());
return (ctx, status, response) -> {
final MediaType grpcMediaType = response.contentType();
if (grpcMediaType != null && grpcMediaType.isJson()) {
return ofJson(mappingFunction).handle(ctx, status, response);
} else {
return ofPlainText(mappingFunction).handle(ctx, status, response);
}
};
return UnframedGrpcErrorHandlers.of(statusMappingFunction);
}

/**
* Returns a json response.
* Returns a JSON response.
*/
static UnframedGrpcErrorHandler ofJson() {
return ofJson(UnframedGrpcStatusMappingFunction.of());
return UnframedGrpcErrorHandlers.ofJson(UnframedGrpcStatusMappingFunction.of());
}

/**
Expand All @@ -86,44 +62,14 @@ static UnframedGrpcErrorHandler ofJson() {
* to an {@link HttpStatus} code.
*/
static UnframedGrpcErrorHandler ofJson(UnframedGrpcStatusMappingFunction statusMappingFunction) {
// Ensure that unframedGrpcStatusMappingFunction never returns null
// by falling back to the default.
final UnframedGrpcStatusMappingFunction mappingFunction =
requireNonNull(statusMappingFunction, "statusMappingFunction")
.orElse(UnframedGrpcStatusMappingFunction.of());
return (ctx, status, response) -> {
final Code grpcCode = status.getCode();
final String grpcMessage = status.getDescription();
final RequestLogAccess log = ctx.log();
final Throwable cause;
if (log.isAvailable(RequestLogProperty.RESPONSE_CAUSE)) {
cause = log.partial().responseCause();
} else {
cause = null;
}
final HttpStatus httpStatus = mappingFunction.apply(ctx, status, cause);
final ResponseHeaders responseHeaders = ResponseHeaders.builder(httpStatus)
.contentType(MediaType.JSON_UTF_8)
.addInt(GrpcHeaderNames.GRPC_STATUS,
grpcCode.value())
.build();
final ImmutableMap.Builder<String, String> messageBuilder = ImmutableMap.builder();
messageBuilder.put("grpc-code", grpcCode.name());
if (grpcMessage != null) {
messageBuilder.put("message", grpcMessage);
}
if (cause != null && ctx.config().verboseResponses()) {
messageBuilder.put("stack-trace", Exceptions.traceText(cause));
}
return HttpResponse.ofJson(responseHeaders, messageBuilder.build());
};
return UnframedGrpcErrorHandlers.ofJson(statusMappingFunction);
}

/**
* Returns a plain text response.
natsumehu marked this conversation as resolved.
Show resolved Hide resolved
*/
static UnframedGrpcErrorHandler ofPlainText() {
natsumehu marked this conversation as resolved.
Show resolved Hide resolved
return ofPlainText(UnframedGrpcStatusMappingFunction.of());
return UnframedGrpcErrorHandlers.ofPlaintext(UnframedGrpcStatusMappingFunction.of());
}

/**
Expand All @@ -133,41 +79,28 @@ static UnframedGrpcErrorHandler ofPlainText() {
* to an {@link HttpStatus} code.
*/
static UnframedGrpcErrorHandler ofPlainText(UnframedGrpcStatusMappingFunction statusMappingFunction) {
// Ensure that unframedGrpcStatusMappingFunction never returns null
// by falling back to the default.
final UnframedGrpcStatusMappingFunction mappingFunction =
requireNonNull(statusMappingFunction, "statusMappingFunction")
.orElse(UnframedGrpcStatusMappingFunction.of());
return (ctx, status, response) -> {
final Code grpcCode = status.getCode();
final RequestLogAccess log = ctx.log();
final Throwable cause;
if (log.isAvailable(RequestLogProperty.RESPONSE_CAUSE)) {
cause = log.partial().responseCause();
} else {
cause = null;
}
final HttpStatus httpStatus = mappingFunction.apply(ctx, status, cause);
final ResponseHeaders responseHeaders = ResponseHeaders.builder(httpStatus)
.contentType(MediaType.PLAIN_TEXT_UTF_8)
.addInt(GrpcHeaderNames.GRPC_STATUS,
grpcCode.value())
.build();
final HttpData content;
try (TemporaryThreadLocals ttl = TemporaryThreadLocals.acquire()) {
final StringBuilder msg = ttl.stringBuilder();
msg.append("grpc-code: ").append(grpcCode.name());
final String grpcMessage = status.getDescription();
if (grpcMessage != null) {
msg.append(", ").append(grpcMessage);
}
if (cause != null && ctx.config().verboseResponses()) {
msg.append("\nstack-trace:\n").append(Exceptions.traceText(cause));
}
content = HttpData.ofUtf8(msg);
}
return HttpResponse.of(responseHeaders, content);
};
return UnframedGrpcErrorHandlers.ofPlaintext(statusMappingFunction);
}

/**
* Returns a rich error JSON response based on Google APIs.
* See <a href="https://cloud.google.com/apis/design/errors#error_model">Google error model</a>
* for more information.
*/
static UnframedGrpcErrorHandler ofRichJson() {
ikhoon marked this conversation as resolved.
Show resolved Hide resolved
return ofRichJson(UnframedGrpcStatusMappingFunction.of());
}

/**
* Returns a rich error JSON response based on Google APIs.
* See <a href="https://cloud.google.com/apis/design/errors#error_model">Google error model</a>
* for more information.
*
* @param statusMappingFunction The function which maps the {@link Throwable} or gRPC {@link Status} code
* to an {@link HttpStatus} code.
*/
static UnframedGrpcErrorHandler ofRichJson(UnframedGrpcStatusMappingFunction statusMappingFunction) {
return UnframedGrpcErrorHandlers.ofRichJson(statusMappingFunction);
}

/**
Expand Down
Loading