ApacheConnector не обрабатывает заголовки запросов, которые были установлены в WriterInterceptor.

У меня возникают проблемы при настройке моего Джерси Client с помощью ApacheConnector. Кажется, он игнорирует все заголовки запросов, которые я определяю в файле WriterInterceptor. Я могу сказать, что WriterInterceptor вызывается, когда я устанавливаю точку останова в WriterInterceptor#aroundWriteTo(WriterInterceptorContext). Наоборот, я вижу, что модификация InputStream сохраняется.

Вот работающий пример, демонстрирующий мою проблему:

public class ApacheConnectorProblemDemonstration extends JerseyTest {

  private static final Logger LOGGER = Logger.getLogger(JerseyTest.class.getName());
  private static final String QUESTION = "baz", ANSWER = "qux";
  private static final String REQUEST_HEADER_NAME_CLIENT = "foo-cl", REQUEST_HEADER_VALUE_CLIENT = "bar-cl";
  private static final String REQUEST_HEADER_NAME_INTERCEPTOR = "foo-ic", REQUEST_HEADER_VALUE_INTERCEPTOR = "bar-ic";
  private static final int MAX_CONNECTIONS = 100;
  private static final String PATH = "/";

  @Path(PATH)
  public static class TestResource {
    @POST
    public String handle(InputStream questionStream,
                         @HeaderParam(REQUEST_HEADER_NAME_CLIENT) String client,
                         @HeaderParam(REQUEST_HEADER_NAME_INTERCEPTOR) String interceptor) 
        throws IOException {
      assertEquals(REQUEST_HEADER_VALUE_CLIENT, client);
      // Here, the header that was set in the client's writer interceptor is lost.
      assertEquals(REQUEST_HEADER_VALUE_INTERCEPTOR, interceptor);
      // However, the input stream got gzipped so the WriterInterceptor has been partly applied.
      assertEquals(QUESTION, new Scanner(new GZIPInputStream(questionStream)).nextLine());
      return ANSWER;
    }
  }

  @Provider
  @Priority(Priorities.ENTITY_CODER)
  public static class ClientInterceptor implements WriterInterceptor {
    @Override
    public void aroundWriteTo(WriterInterceptorContext context) 
        throws IOException, WebApplicationException {
      context.getHeaders().add(REQUEST_HEADER_NAME_INTERCEPTOR, REQUEST_HEADER_VALUE_INTERCEPTOR);
      context.setOutputStream(new GZIPOutputStream(context.getOutputStream()));
      context.proceed();
    }
  }

  @Override
  protected Application configure() {
    enable(TestProperties.LOG_TRAFFIC);
    enable(TestProperties.DUMP_ENTITY);
    return new ResourceConfig(TestResource.class);
  }

  @Override
  protected Client getClient(TestContainer tc, ApplicationHandler applicationHandler) {
    ClientConfig clientConfig = tc.getClientConfig() == null ? new ClientConfig() : tc.getClientConfig();
    clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, makeConnectionManager(MAX_CONNECTIONS));
    clientConfig.register(ClientInterceptor.class);
    // If I do not use the Apache connector, I avoid this problem.
    clientConfig.connector(new ApacheConnector(clientConfig));
    if (isEnabled(TestProperties.LOG_TRAFFIC)) {
      clientConfig.register(new LoggingFilter(LOGGER, isEnabled(TestProperties.DUMP_ENTITY)));
    }
    configureClient(clientConfig);
    return ClientBuilder.newClient(clientConfig);
  }

  private static ClientConnectionManager makeConnectionManager(int maxConnections) {
    PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
    connectionManager.setMaxTotal(maxConnections);
    connectionManager.setDefaultMaxPerRoute(maxConnections);
    return connectionManager;
  }

  @Test
  public void testInterceptors() throws Exception {
    Response response = target(PATH)
        .request()
        .header(REQUEST_HEADER_NAME_CLIENT, REQUEST_HEADER_VALUE_CLIENT)
        .post(Entity.text(QUESTION));
    assertEquals(200, response.getStatus());
    assertEquals(ANSWER, response.readEntity(String.class));
  }
}

Я хочу использовать ApacheConnector для оптимизации одновременных запросов через PoolingClientConnectionManager. Я перепутал конфигурацию?

PS: Точно такая же проблема возникает при использовании GrizzlyConnector.


person Rafael Winterhalter    schedule 06.11.2013    source источник


Ответы (1)


После дальнейших исследований я предполагаю, что это скорее неправильное поведение по умолчанию Connector, которое использует HttpURLConnection. Как я объяснил в этом других ответил на мой вопрос, состояния документации :

В то время как фильтры в первую очередь предназначены для манипулирования параметрами запросов и ответов, такими как заголовки HTTP, URI и/или методы HTTP, перехватчики предназначены для манипулирования сущностями посредством манипулирования входными/выходными потоками сущностей.

Предполагается, что WriterInterceptor не манипулирует значениями заголовка, а {Client,Server}RequestFilter не должен манипулировать потоком сущностей. Если вам нужно использовать оба, оба компонента должны быть объединены в javax.ws.rs.core.Feature или в один и тот же класс, который реализует два интерфейса. (Это может быть проблематично, если вам нужно установить два разных Priority.)

Все это очень неудачно, поскольку JerseyTest использует Connector, который использует HttpURLConnection, так что все мои модульные тесты прошли успешно, в то время как реальное приложение вело себя неправильно, поскольку оно было настроено с ApacheConnector. Кроме того, вместо того, чтобы подавлять изменения, я хотел, чтобы Джерси сделал для меня некоторые исключения. (Это общая проблема, с которой я столкнулся в Джерси. Когда я, например, использовал слишком новую версию ClientConnectionManager, где интерфейс был переименован в HttpClientConnectionManager, мне просто сообщили в однострочном операторе журнала, что все мои усилия по настройке были проигнорированы.Я обнаружил этот оператор журнала только на очень позднем этапе разработки.)

person Rafael Winterhalter    schedule 07.11.2013