Http2Handler extends Http2EventAdapter { @Override public void onHeadersRead( ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, boolean endOfStream) { Channel channel = ctx.channel(); ServerContext xctx = channel.attr(ServerContext.ATTRIBUTE_KEY).get(); XrpcRequest request = requests.get(streamId); if (request == null) { xctx.requestMeter().mark(); } if (channel.hasAttr(XrpcConstants.XRPC_SOFT_RATE_LIMITED)) { writeResponse( ctx, streamId, HttpResponseStatus.TOO_MANY_REQUESTS, Unpooled.wrappedBuffer(XrpcConstants.RATE_LIMIT_RESPONSE)); return; } long contentLength = 0; try { contentLength = headers.getLong(CONTENT_LENGTH, 0); } catch (NumberFormatException nfe) { } if (contentLength > maxPayloadBytes) { writeResponse( ctx, streamId, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.wrappedBuffer(XrpcConstants.PAYLOAD_EXCEEDED_RESPONSE)); ctx.flush(); ctx.close(); return; } Handler handler; if (request == null) { String path = getPathFromHeaders(headers); CompiledRoutes.Match match = xctx.routes().match(path, headers.method().toString()); request = new XrpcRequest(headers, xctx, match.getGroups(), channel); handler = match.getHandler(); } else { request.h2Headers().add(headers); handler = handlers.get(streamId); } if (endOfStream) { Optional<HttpResponse> corsResponse = corsHandler.inbound(headers, streamId); if (corsResponse.isPresent()) { writeResponse(ctx, streamId, corsResponse.get()); return; } try { HttpResponse response = handler.handle(request); writeResponse(ctx, streamId, response); } catch (IOException e) { log.error("Error in handling Route", e); ByteBuf buf = channel.alloc().directBuffer(); buf.writeBytes("Error executing endpoint".getBytes(XrpcConstants.DEFAULT_CHARSET)); writeResponse(ctx, streamId, HttpResponseStatus.INTERNAL_SERVER_ERROR, buf); } } else { requests.put(streamId, request); handlers.put(streamId, handler); } } Http2Handler(Http2ConnectionEncoder encoder, int maxPayloadBytes, Http2CorsHandler corsHandler); @Override int onDataRead(
      ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream); @Override void onHeadersRead(
      ChannelHandlerContext ctx,
      int streamId,
      Http2Headers headers,
      int padding,
      boolean endOfStream); @Override void onHeadersRead(
      ChannelHandlerContext ctx,
      int streamId,
      Http2Headers headers,
      int streamDependency,
      short weight,
      boolean exclusive,
      int padding,
      boolean endOfStream); @Override void onStreamRemoved(Http2Stream stream);  }
@Test void testOnHeadersRead_softRateLimited() { testHandler = new Http2Handler(mockEncoder, MAX_PAYLOAD, NO_CORS); channel.attr(XrpcConstants.XRPC_SOFT_RATE_LIMITED).set(Boolean.TRUE); testHandler.onHeadersRead(mockContext, STREAM_ID, headers, 1, false); assertEquals(1L, requestMeter.getCount()); verifyResponse( HttpResponseStatus.TOO_MANY_REQUESTS, ImmutableMap.of(), Optional.of(Unpooled.wrappedBuffer(XrpcConstants.RATE_LIMIT_RESPONSE)), STREAM_ID); }
@Test void testOnHeadersRead_fullRequestGoodPath() { testHandler = new Http2Handler(mockEncoder, MAX_PAYLOAD, NO_CORS); headers.method("GET").path(OK_PATH); testHandler.onHeadersRead(mockContext, STREAM_ID, headers, 1, true); assertEquals(1L, requestMeter.getCount()); verifyResponse(HttpResponseStatus.OK, ImmutableMap.of(), Optional.empty(), STREAM_ID); }
@Test void testOnHeadersRead_headersStreamContinuing() throws Exception { testHandler = new Http2Handler(mockEncoder, MAX_PAYLOAD, NO_CORS); headers.method("GET").path(PARAM_PATH_PREFIX + "/group"); testHandler.onHeadersRead(mockContext, STREAM_ID, headers, 1, false); assertEquals(1L, requestMeter.getCount()); verifyNoResponse(); XrpcRequest xrpcRequest = testHandler.requests.get(STREAM_ID); assertNotNull(xrpcRequest); assertEquals(headers, xrpcRequest.h2Headers()); assertEquals("group", xrpcRequest.variable(PARAM_NAME)); Handler handler = testHandler.handlers.get(STREAM_ID); assertNotNull(handler); assertEquals(HttpResponseStatus.OK, handler.handle(xrpcRequest).status()); }
@Test void testOnHeadersRead_contentLengthTooLarge() { testHandler = new Http2Handler(mockEncoder, MAX_PAYLOAD, NO_CORS); headers.method("GET").path(OK_PATH).addLong(HttpHeaderNames.CONTENT_LENGTH, MAX_PAYLOAD + 10L); testHandler.onHeadersRead(mockContext, STREAM_ID, headers, 1, false); assertEquals(1L, requestMeter.getCount()); verifyResponse( HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, ImmutableMap.of(), Optional.of(Unpooled.wrappedBuffer(XrpcConstants.PAYLOAD_EXCEEDED_RESPONSE)), STREAM_ID); }
@Test void testOnHeadersRead_contentLengthMalformed() { testHandler = new Http2Handler(mockEncoder, MAX_PAYLOAD, NO_CORS); headers.method("GET").path(OK_PATH).add(HttpHeaderNames.CONTENT_LENGTH, "abc"); testHandler.onHeadersRead(mockContext, STREAM_ID, headers, 1, true); assertEquals(1L, requestMeter.getCount()); verifyResponse(HttpResponseStatus.OK, ImmutableMap.of(), Optional.empty(), STREAM_ID); }
@Test void testOnHeadersRead_trailerPart() { testHandler = new Http2Handler(mockEncoder, MAX_PAYLOAD, NO_CORS); Http2Headers initialHeaders = new DefaultHttp2Headers().method("GET").path(OK_PATH); XrpcRequest fakeRequest = new XrpcRequest(initialHeaders, null, null, channel); testHandler.requests.put(STREAM_ID, fakeRequest); testHandler.handlers.put(STREAM_ID, OK_HANDLER); headers.add("some-header", "some-value"); testHandler.onHeadersRead(mockContext, STREAM_ID, headers, 1, true); assertEquals(0L, requestMeter.getCount()); verifyResponse(HttpResponseStatus.OK, ImmutableMap.of(), Optional.empty(), STREAM_ID); assertEquals("some-value", fakeRequest.h2Headers().get("some-header")); }
@Test void testOnHeadersRead_preflightOptionsRequest() { CorsConfig corsConfig = CorsConfigBuilder.forOrigin("test.domain") .allowCredentials() .allowedRequestMethods(HttpMethod.GET) .build(); Http2CorsHandler corsHandler = new Http2CorsHandler(corsConfig); testHandler = new Http2Handler(mockEncoder, MAX_PAYLOAD, corsHandler); headers .method("OPTIONS") .add("origin", "test.domain") .add("access-control-request-method", "GET") .path(OK_PATH); testHandler.onHeadersRead(mockContext, STREAM_ID, headers, 1, true); assertEquals(1L, requestMeter.getCount()); verifyResponse( HttpResponseStatus.OK, ImmutableMap.of( "access-control-allow-methods", "GET", "access-control-allow-origin", corsConfig.origin(), "access-control-allow-credentials", "true"), Optional.empty(), STREAM_ID); }
@Test void testOnHeadersRead_noOrigin() { CorsConfig corsConfig = CorsConfigBuilder.forOrigin("test.domain") .allowCredentials() .allowedRequestMethods(HttpMethod.GET) .build(); Http2CorsHandler corsHandler = new Http2CorsHandler(corsConfig); testHandler = new Http2Handler(mockEncoder, MAX_PAYLOAD, corsHandler); headers.method("GET").path(OK_PATH); testHandler.onHeadersRead(mockContext, STREAM_ID, headers, 1, true); assertEquals(1L, requestMeter.getCount()); verifyResponse(HttpResponseStatus.OK, ImmutableMap.of(), Optional.empty(), STREAM_ID); }
@Test void testOnHeadersRead_corsShortCircuit() { CorsConfig corsConfig = CorsConfigBuilder.forOrigin("test.domain").shortCircuit().build(); Http2CorsHandler corsHandler = new Http2CorsHandler(corsConfig); testHandler = new Http2Handler(mockEncoder, MAX_PAYLOAD, corsHandler); headers.method("GET").add("origin", "illegal.domain").path(OK_PATH); testHandler.onHeadersRead(mockContext, STREAM_ID, headers, 1, true); assertEquals(1L, requestMeter.getCount()); verifyResponse(HttpResponseStatus.FORBIDDEN, ImmutableMap.of(), Optional.empty(), STREAM_ID); }