/**
  * Sets the value of response headers after making sure that the response metadata is not already
  * sent.
  *
  * @param headerName The name of the header.
  * @param headerValue The intended value of the header.
  * @throws IllegalArgumentException if any of {@code headerName} or {@code headerValue} is null.
  * @throws IllegalStateException if response metadata has already been written to the channel.
  */
 private void setResponseHeader(String headerName, Object headerValue) {
   if (headerName != null && headerValue != null) {
     long startTime = System.currentTimeMillis();
     if (headerValue instanceof Date) {
       HttpHeaders.setDateHeader(responseMetadata, headerName, (Date) headerValue);
     } else {
       HttpHeaders.setHeader(responseMetadata, headerName, headerValue);
     }
     if (responseMetadataWritten.get()) {
       nettyMetrics.deadResponseAccessError.inc();
       throw new IllegalStateException(
           "Response metadata changed after it has already been written to the channel");
     } else {
       logger.trace(
           "Header {} set to {} for channel {}",
           headerName,
           responseMetadata.headers().get(headerName),
           ctx.channel());
       nettyMetrics.headerSetTimeInMs.update(System.currentTimeMillis() - startTime);
     }
   } else {
     throw new IllegalArgumentException(
         "Header name [" + headerName + "] or header value [" + headerValue + "] null");
   }
 }
 /**
  * Provided a cause, returns an error response with the right status and error message.
  *
  * @param cause the cause of the error.
  * @return a {@link FullHttpResponse} with the error message that can be sent to the client.
  */
 private FullHttpResponse getErrorResponse(Throwable cause) {
   HttpResponseStatus status;
   StringBuilder errReason = new StringBuilder();
   if (cause instanceof RestServiceException) {
     RestServiceErrorCode restServiceErrorCode = ((RestServiceException) cause).getErrorCode();
     status = getHttpResponseStatus(ResponseStatus.getResponseStatus(restServiceErrorCode));
     if (status == HttpResponseStatus.BAD_REQUEST) {
       errReason.append(" [").append(Utils.getRootCause(cause).getMessage()).append("]");
     }
   } else {
     nettyMetrics.internalServerErrorCount.inc();
     status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
   }
   String fullMsg = "Failure: " + status + errReason;
   logger.trace("Constructed error response for the client - [{}]", fullMsg);
   FullHttpResponse response;
   if (request != null && !request.getRestMethod().equals(RestMethod.HEAD)) {
     response =
         new DefaultFullHttpResponse(
             HttpVersion.HTTP_1_1, status, Unpooled.wrappedBuffer(fullMsg.getBytes()));
   } else {
     // for HEAD, we cannot send the actual body but we need to return what the length would have
     // been if this was GET.
     // https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html (Section 9.4)
     response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
   }
   HttpHeaders.setDate(response, new GregorianCalendar().getTime());
   HttpHeaders.setContentLength(response, fullMsg.length());
   HttpHeaders.setHeader(response, HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
   boolean keepAlive =
       request != null
           && !request.getRestMethod().equals(RestMethod.POST)
           && !CLOSE_CONNECTION_ERROR_STATUSES.contains(status);
   HttpHeaders.setKeepAlive(response, keepAlive);
   return response;
 }
Example #3
0
 private static void sendHttpRedirect(
     ChannelHandlerContext ctx, FullHttpRequest req, String newUri) {
   final FullHttpResponse res = new DefaultFullHttpResponse(req.getProtocolVersion(), FOUND);
   HttpHeaders.setHeader(res, LOCATION, newUri);
   writeHttpResponse(ctx, req, res, true);
 }
Example #4
0
    @Override
    public final boolean trySend(HttpResponse message) {
      try {
        final HttpRequestWrapper nettyRequest = (HttpRequestWrapper) message.getRequest();
        final FullHttpRequest req = nettyRequest.req;
        final ChannelHandlerContext ctx = nettyRequest.ctx;

        final HttpResponseStatus status = HttpResponseStatus.valueOf(message.getStatus());

        if (message.getStatus() >= 400 && message.getStatus() < 600) {
          sendHttpResponse(
              ctx, req, new DefaultFullHttpResponse(req.getProtocolVersion(), status), false);
          close();
          return true;
        }

        if (message.getRedirectPath() != null) {
          sendHttpRedirect(ctx, req, message.getRedirectPath());
          close();
          return true;
        }

        FullHttpResponse res;
        if (message.getStringBody() != null)
          res =
              new DefaultFullHttpResponse(
                  req.getProtocolVersion(),
                  status,
                  Unpooled.wrappedBuffer(message.getStringBody().getBytes()));
        else if (message.getByteBufferBody() != null)
          res =
              new DefaultFullHttpResponse(
                  req.getProtocolVersion(),
                  status,
                  Unpooled.wrappedBuffer(message.getByteBufferBody()));
        else res = new DefaultFullHttpResponse(req.getProtocolVersion(), status);

        if (message.getCookies() != null) {
          final ServerCookieEncoder enc = ServerCookieEncoder.STRICT;
          for (final Cookie c : message.getCookies())
            HttpHeaders.setHeader(res, COOKIE, enc.encode(getNettyCookie(c)));
        }
        if (message.getHeaders() != null) {
          for (final Map.Entry<String, String> h : message.getHeaders().entries())
            HttpHeaders.setHeader(res, h.getKey(), h.getValue());
        }

        if (message.getContentType() != null) {
          String ct = message.getContentType();
          if (message.getCharacterEncoding() != null)
            ct = ct + "; charset=" + message.getCharacterEncoding().name();
          HttpHeaders.setHeader(res, CONTENT_TYPE, ct);
        }

        final boolean sseStarted = message.shouldStartActor();
        if (trackSession(sseStarted)) {
          final String sessionId = UUID.randomUUID().toString();
          res.headers()
              .add(SET_COOKIE, ServerCookieEncoder.STRICT.encode(SESSION_COOKIE_KEY, sessionId));
          startSession(sessionId, actorContext);
        }
        if (!sseStarted) {
          final String stringBody = message.getStringBody();
          long contentLength = 0L;
          if (stringBody != null) contentLength = stringBody.getBytes().length;
          else {
            final ByteBuffer byteBufferBody = message.getByteBufferBody();
            if (byteBufferBody != null) contentLength = byteBufferBody.remaining();
          }
          res.headers().add(CONTENT_LENGTH, contentLength);
        }

        final HttpStreamActorAdapter httpStreamActorAdapter;
        if (sseStarted)
          // This will copy the request content, which must still be referenceable, doing before the
          // request handler
          // unallocates it (unfortunately it is explicitly reference-counted in Netty)
          httpStreamActorAdapter = new HttpStreamActorAdapter(ctx, req);
        else httpStreamActorAdapter = null;

        sendHttpResponse(ctx, req, res, false);

        if (sseStarted) {
          if (httpResponseEncoderName != null) {
            ctx.pipeline().remove(httpResponseEncoderName);
          } else {
            final ChannelPipeline pl = ctx.pipeline();
            final List<String> handlerKeysToBeRemoved = new ArrayList<>();
            for (final Map.Entry<String, ChannelHandler> e : pl) {
              if (e.getValue() instanceof HttpResponseEncoder)
                handlerKeysToBeRemoved.add(e.getKey());
            }
            for (final String k : handlerKeysToBeRemoved) pl.remove(k);
          }

          try {
            message.getFrom().send(new HttpStreamOpened(httpStreamActorAdapter.ref(), message));
          } catch (SuspendExecution e) {
            throw new AssertionError(e);
          }
        }

        return true;
      } finally {
        if (actor != null) actor.unwatch();
      }
    }