static GrpcService of(GrpcService delegate, HttpJsonTranscodingOptions httpJsonTranscodingOptions) {
        requireNonNull(delegate, "delegate");
        requireNonNull(httpJsonTranscodingOptions, "httpJsonTranscodingOptions");

        final Map<Route, TranscodingSpec> specs = new HashMap<>();

        final List<ServerServiceDefinition> serviceDefinitions = delegate.services();
        for (ServerServiceDefinition serviceDefinition : serviceDefinitions) {
            final Descriptors.ServiceDescriptor serviceDesc = serviceDescriptor(serviceDefinition);
            if (serviceDesc == null) {
                continue;
            }

            for (ServerMethodDefinition<?, ?> methodDefinition : serviceDefinition.getMethods()) {
                final Descriptors.MethodDescriptor methodDesc = methodDescriptor(methodDefinition);
                if (methodDesc == null) {
                    continue;
                }

                final MethodOptions methodOptions = methodDesc.getOptions();
                if (!methodOptions.hasExtension((ExtensionLite<MethodOptions, ?>) AnnotationsProto.http)) {
                    continue;
                }

                final HttpRule httpRule = methodOptions.getExtension(
                        (ExtensionLite<MethodOptions, HttpRule>)AnnotationsProto.http
                );

                if (methodDefinition.getMethodDescriptor().getType() != MethodType.UNARY) {
                    logger.warn("Only unary methods can be configured with an HTTP/JSON endpoint: " +
                                "method={}, httpRule={}",
                                methodDefinition.getMethodDescriptor().getFullMethodName(), httpRule);
                    continue;
                }

                @Nullable
                final HttpJsonTranscodingRouteAndPathVariables routeAndVariables =
                        HttpJsonTranscodingRouteAndPathVariables.of(httpRule);
                if (routeAndVariables == null) {
                    continue;
                }

                // TODO(ikhoon): Extract the build-time code into a separate class such as
                //               HttpJsonTranscodingServiceBuilder or HttpJsonTranscodingSpecGenerator
                final Set<HttpJsonTranscodingQueryParamMatchRule> queryParamMatchRules =
                        httpJsonTranscodingOptions.queryParamMatchRules();
                final Route route = routeAndVariables.route();
                final Map<String, Field> originalFields = buildFields(methodDesc.getInputType(),
                                                                      ImmutableList.of(),
                                                                      "",
                                                                      ImmutableSet.of(),
                                                                      ORIGINAL_FIELD,
                                                                      ImmutableSet.of(ORIGINAL_FIELD));
                final List<Map<String, Field>> queryMappingFields =
                        queryParamMatchRules.stream().map(matchRule -> {
                            return buildFields(methodDesc.getInputType(),
                                               ImmutableList.of(),
                                               "",
                                               ImmutableSet.of(),
                                               matchRule, queryParamMatchRules);
                        }).collect(toImmutableList());

                if (specs.containsKey(route)) {
                    logger.warn("{} is not added because the route is duplicate: {}", httpRule, route);
                    continue;
                }
                final List<FieldDescriptor> topLevelFields = methodDesc.getOutputType().getFields();
                final String responseBody = getResponseBody(topLevelFields, httpRule.getResponseBody());
                int order = 0;
                specs.put(route, new TranscodingSpec(order++, httpRule, methodDefinition,
                                                     serviceDesc, methodDesc, originalFields,
                                                     queryMappingFields,
                                                     routeAndVariables.pathVariables(),
                                                     routeAndVariables.hasVerb(),
                                                     responseBody));
                for (HttpRule additionalHttpRule : httpRule.getAdditionalBindingsList()) {
                    @Nullable
                    final HttpJsonTranscodingRouteAndPathVariables additionalRouteAndVariables
                            = HttpJsonTranscodingRouteAndPathVariables.of(additionalHttpRule);
                    if (additionalRouteAndVariables != null) {
                        specs.put(additionalRouteAndVariables.route(),
                                  new TranscodingSpec(order++, additionalHttpRule, methodDefinition,
                                                      serviceDesc, methodDesc, originalFields,
                                                      queryMappingFields,
                                                      additionalRouteAndVariables.pathVariables(),
                                                      routeAndVariables.hasVerb(), responseBody));
                    }
                }
            }
        }

        if (specs.isEmpty()) {
            // We don't need to create a new HttpJsonTranscodingService instance in this case.
            return delegate;
        }
        return new HttpJsonTranscodingService(delegate, ImmutableMap.copyOf(specs), httpJsonTranscodingOptions);
    }
Creates a new {@link GrpcService} instance from the given {@code delegate}. If it is possible
to support HTTP/JSON to gRPC transcoding, a new {@link HttpJsonTranscodingService} instance
would be returned. Otherwise, the {@code delegate} would be returned.
grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java