@Override
        public HttpResponse execute(ClientRequestContext ctx, HttpRequest req) {
            final AggregationOptions aggregationOptions =
                    AggregationOptions.usePooledObjects(ctx.alloc(), ctx.eventLoop());
            return HttpResponse.of(
                    req.aggregate(aggregationOptions)
                       .thenCompose(
                               msg -> {
                                   try (HttpData content = msg.content()) {
                                       final ByteBuf buf = content.byteBuf();
                                       final HttpData framed;
                                       try (ArmeriaMessageFramer framer = new ArmeriaMessageFramer(
                                               ctx.alloc(), Integer.MAX_VALUE, isGrpcWebText)) {
                                           framed = framer.writePayload(buf);
                                       }

                                       try {
                                           final HttpRequest framedReq = HttpRequest.of(req.headers(), framed);
                                           ctx.updateRequest(framedReq);
                                           return unwrap().execute(ctx, framedReq)
                                                          .aggregate(aggregationOptions);
                                       } catch (Exception e) {
                                           throw new ArmeriaStatusException(StatusCodes.INTERNAL,
                                                                            "Error executing request.");
                                       }
                                   }
                               })
                       .thenCompose(msg -> {
                           if (msg.status() != HttpStatus.OK || msg.content().isEmpty()) {
                               // Status can either be in the headers or trailers depending on error.
                               if (msg.headers().get(GrpcHeaderNames.GRPC_STATUS) != null) {
                                   GrpcWebTrailers.set(ctx, msg.headers());
                               } else {
                                   GrpcWebTrailers.set(ctx, msg.trailers());
                               }
                               // Nothing to deframe.
                               return UnmodifiableFuture.completedFuture(msg.toHttpResponse());
                           }

                           final CompletableFuture<HttpResponse> responseFuture = new CompletableFuture<>();
                           final ArmeriaMessageDeframer deframer =
                                   new ArmeriaMessageDeframer(Integer.MAX_VALUE, ctx.alloc(), isGrpcWebText);
                           msg.toHttpResponse()
                              .decode(deframer, ctx.alloc())
                              .subscribe(new DeframedMessageSubscriber(
                                                 ctx, msg, serializationFormat, responseFuture),
                                         ctx.eventLoop(), SubscriptionOption.WITH_POOLED_OBJECTS);
                           return responseFuture;
                       }), ctx.eventLoop());
        }
Executes a unary gRPC client request. The given {@code payload} will be framed and sent to the path at
{@code uri}. {@code uri} should be the method's URI, which is always of the format
{@code /:package-name.:service-name/:method}. For example, for the proto package
{@code armeria.protocol}, the service name {@code CoolService} and the method name
{@code RunWithoutStubs}, the {@code uri} would be {@code /armeria.protocol.CoolService/RunWithoutStubs}.
If you aren't sure what the package, service name, and method name are for your method, you should
probably use normal gRPC stubs instead of this class.
grpc-protocol/src/main/java/com/linecorp/armeria/client/grpc/protocol/UnaryGrpcClient.java