All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fullcontact.rpc.jersey.HttpHeaderInterceptors Maven / Gradle / Ivy

The newest version!
package com.fullcontact.rpc.jersey;

import static com.fullcontact.rpc.jersey.HttpHeaderContext.REQUEST_HEADERS;
import static com.fullcontact.rpc.jersey.HttpHeaderContext.RESPONSE_HEADERS;

import com.fullcontact.rpc.Header;
import com.fullcontact.rpc.Headers;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.Context;
import io.grpc.Contexts;
import io.grpc.ForwardingClientCall;
import io.grpc.ForwardingClientCallListener;
import io.grpc.ForwardingServerCall;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
import io.grpc.protobuf.ProtoUtils;
import java.util.List;
import java.util.Map;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;

public class HttpHeaderInterceptors {
    private static final Metadata.Key HEADERS_KEY = ProtoUtils.keyForProto(Headers.getDefaultInstance());

    private HttpHeaderInterceptors() {} // Do not instantiate.

    /**
     * Returns the server interceptor necessary to make {@link HttpHeaderContext} work. It is recommended you use {@link
     * GrpcJerseyPlatformInterceptors#intercept} if possible.
     */
    public static HttpHeaderServerInterceptor serverInterceptor() {
        return HttpHeaderServerInterceptor.INSTANCE;
    }

    /**
     * Returns the client interceptor used to extract the HTTP headers from the RPC sidechannel. Public for use in
     * generated code, should not be used by the end user.
     */
    public static HttpHeaderClientInterceptor clientInterceptor(HttpHeaders httpHeaders) {
        return new HttpHeaderClientInterceptor(httpHeaders);
    }

    private static Headers headersFromMultimap(Multimap headers) {
        Headers.Builder builder = Headers.newBuilder();

        if (headers == null) {
            return builder.build();
        }

        for (Map.Entry header : headers.entries()) {
            builder.addHeader(Header.newBuilder()
                    .setName(header.getKey())
                    .setValue(header.getValue())
                    .build());
        }

        return builder.build();
    }

    private static ImmutableMultimap toMultimapFromHeaders(Headers headers) {
        if (headers == null) {
            return ImmutableMultimap.of();
        }

        ImmutableMultimap.Builder builder = ImmutableMultimap.builder();
        for (Header header : headers.getHeaderList()) {
            builder.put(header.getName(), header.getValue());
        }

        return builder.build();
    }

    private static ImmutableMultimap toMultimapFromJerseyHeaders(HttpHeaders headers) {
        ImmutableMultimap.Builder builder = ImmutableMultimap.builder();
        for (Map.Entry> header : headers.getRequestHeaders().entrySet()) {
            builder.putAll(header.getKey(), header.getValue());
        }

        return builder.build();
    }

    public static class HttpHeaderClientInterceptor implements ClientInterceptor {
        private final ImmutableMultimap httpRequestHeaders;

        private ImmutableMultimap httpResponseHeaders = ImmutableMultimap.of();
        private boolean receivedHeaders = false;

        HttpHeaderClientInterceptor(HttpHeaders httpRequestHeaders) {
            this.httpRequestHeaders = toMultimapFromJerseyHeaders(httpRequestHeaders);
        }

        @Override
        public  ClientCall interceptCall(MethodDescriptor method,
                CallOptions callOptions, Channel next) {
            return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) {
                @Override
                public void start(Listener responseListener, Metadata headers) {
                    // Bundle known request headers into RPC side-channel.
                    headers.put(HEADERS_KEY, headersFromMultimap(httpRequestHeaders));

                    delegate().start(
                            new ForwardingClientCallListener.SimpleForwardingClientCallListener(
                                    responseListener) {
                                @Override
                                public void onHeaders(Metadata headers) {
                                    processMetadata(headers);
                                    super.onHeaders(headers);
                                }

                                @Override
                                public void onClose(Status status, Metadata trailers) {
                                    processMetadata(trailers);
                                    super.onClose(status, trailers);
                                }

                                private void processMetadata(Metadata metadata) {
                                    if (receivedHeaders) {
                                        return;
                                    }

                                    // Set response headers if present on RPC.
                                    if (metadata.containsKey(HEADERS_KEY)) {
                                        receivedHeaders = true;
                                        httpResponseHeaders = toMultimapFromHeaders(metadata.get(HEADERS_KEY));
                                    }
                                }
                            }, headers);
                }
            };
        }

        ImmutableMultimap getHttpResponseHeaders() {
            return httpResponseHeaders;
        }

        Response.ResponseBuilder withResponseHeaders(Response.ResponseBuilder builder) {
            if (!httpResponseHeaders.isEmpty()) {
                for (Map.Entry header : httpResponseHeaders.entries()) {
                    builder.header(header.getKey(), header.getValue());
                }
            }

            return builder;
        }
    }

    private static class HttpHeaderServerInterceptor implements ServerInterceptor {
        private static final HttpHeaderServerInterceptor INSTANCE = new HttpHeaderServerInterceptor();

        private HttpHeaderServerInterceptor() {}

        @Override
        public  ServerCall.Listener interceptCall(
                ServerCall call,
                Metadata headers,
                ServerCallHandler next) {
            Context context = Context.current()
                    .withValues(
                            REQUEST_HEADERS, toMultimapFromHeaders(headers.get(HEADERS_KEY)),
                            RESPONSE_HEADERS, HashMultimap.create());

            boolean sideChannelOn = headers.containsKey(HEADERS_KEY);
            ForwardingServerCall.SimpleForwardingServerCall simpleForwardingServerCall =
                    new ForwardingServerCall.SimpleForwardingServerCall(call) {
                        private boolean sentHeaders = false;

                        @Override
                        public void sendHeaders(Metadata headers) {
                            if(sideChannelOn) {
                                headers.put(HEADERS_KEY, headersFromMultimap(RESPONSE_HEADERS.get()));
                            }
                            sentHeaders = true;
                            super.sendHeaders(headers);
                        }

                        @Override
                        public void close(Status status, Metadata trailers) {
                            if (!sentHeaders && sideChannelOn) {
                                trailers.put(HEADERS_KEY, headersFromMultimap(RESPONSE_HEADERS.get()));
                            }

                            super.close(status, trailers);
                        }
                    };

            return Contexts.interceptCall(context, simpleForwardingServerCall, headers, next);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy