/**
  * Writes response metadata to the channel if not already written previously and channel is
  * active.
  *
  * @param responseMetadata the {@link HttpResponse} that needs to be written.
  * @param listener the {@link GenericFutureListener} that needs to be attached to the write.
  * @return {@code true} if response metadata was written to the channel in this call. {@code
  *     false} otherwise.
  */
 private boolean maybeWriteResponseMetadata(
     HttpResponse responseMetadata, GenericFutureListener<ChannelFuture> listener) {
   long writeProcessingStartTime = System.currentTimeMillis();
   boolean writtenThisTime = false;
   if (responseMetadataWritten.compareAndSet(false, true)) {
     // we do some manipulation here for chunking. According to the HTTP spec, we can have either a
     // Content-Length
     // or Transfer-Encoding:chunked, never both. So we check for Content-Length - if it is not
     // there, we add
     // Transfer-Encoding:chunked. Note that sending HttpContent chunks data anyway - we are just
     // explicitly specifying
     // this in the header.
     if (!HttpHeaders.isContentLengthSet(responseMetadata)) {
       // This makes sure that we don't stomp on any existing transfer-encoding.
       HttpHeaders.setTransferEncodingChunked(responseMetadata);
     }
     logger.trace(
         "Sending response with status {} on channel {}",
         responseMetadata.getStatus(),
         ctx.channel());
     ChannelPromise writePromise = ctx.newPromise().addListener(listener);
     ctx.writeAndFlush(responseMetadata, writePromise);
     writtenThisTime = true;
     long writeProcessingTime = System.currentTimeMillis() - writeProcessingStartTime;
     nettyMetrics.responseMetadataProcessingTimeInMs.update(writeProcessingTime);
   }
   return writtenThisTime;
 }
 /**
  * Handler for a connection error. Sends a GO_AWAY frame to the remote endpoint. Once all streams
  * are closed, the connection is shut down.
  *
  * @param ctx the channel context
  * @param cause the exception that was caught
  * @param http2Ex the {@link Http2Exception} that is embedded in the causality chain. This may be
  *     {@code null} if it's an unknown exception.
  */
 protected void onConnectionError(
     ChannelHandlerContext ctx, Throwable cause, Http2Exception http2Ex) {
   if (http2Ex == null) {
     http2Ex = new Http2Exception(INTERNAL_ERROR, cause.getMessage(), cause);
   }
   goAway(ctx, http2Ex).addListener(new ClosingChannelFutureListener(ctx, ctx.newPromise()));
 }
  /*
   * SPDY Stream Error Handling:
   *
   * Upon a stream error, the endpoint must send a RST_STREAM frame which contains
   * the Stream-ID for the stream where the error occurred and the error getStatus which
   * caused the error.
   *
   * After sending the RST_STREAM, the stream is closed to the sending endpoint.
   *
   * Note: this is only called by the worker thread
   */
  private void issueStreamError(ChannelHandlerContext ctx, int streamId, SpdyStreamStatus status) {
    boolean fireChannelRead = !spdySession.isRemoteSideClosed(streamId);
    ChannelPromise promise = ctx.newPromise();
    removeStream(streamId, promise);

    SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, status);
    ctx.writeAndFlush(spdyRstStreamFrame, promise);
    if (fireChannelRead) {
      ctx.fireChannelRead(spdyRstStreamFrame);
    }
  }
    /**
     * Sends the HTTP/2 connection preface upon establishment of the connection, if not already
     * sent.
     */
    private void sendPreface(ChannelHandlerContext ctx) {
      if (prefaceSent || !ctx.channel().isActive()) {
        return;
      }

      prefaceSent = true;

      if (!connection().isServer()) {
        // Clients must send the preface string as the first bytes on the connection.
        ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
      }

      // Both client and server must send their initial settings.
      encoder
          .writeSettings(ctx, initialSettings(), ctx.newPromise())
          .addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
    }
Beispiel #5
0
  private void closeOutboundAndChannel(
      final ChannelHandlerContext ctx, final ChannelPromise promise, boolean disconnect)
      throws Exception {
    if (!ctx.channel().isActive()) {
      if (disconnect) {
        ctx.disconnect(promise);
      } else {
        ctx.close(promise);
      }
      return;
    }

    engine.closeOutbound();

    ChannelPromise closeNotifyFuture = ctx.newPromise();
    write(ctx, Unpooled.EMPTY_BUFFER, closeNotifyFuture);
    flush(ctx);
    safeClose(ctx, closeNotifyFuture, promise);
  }
Beispiel #6
0
 @Override
 public void flush(ChannelHandlerContext ctx) throws Exception {
   // Do not encrypt the first write request if this handler is
   // created with startTLS flag turned on.
   if (startTls && !sentFirstMessage) {
     sentFirstMessage = true;
     pendingUnencryptedWrites.removeAndWriteAll();
     ctx.flush();
     return;
   }
   if (pendingUnencryptedWrites.isEmpty()) {
     // It's important to NOT use a voidPromise here as the user
     // may want to add a ChannelFutureListener to the ChannelPromise later.
     //
     // See https://github.com/netty/netty/issues/3364
     pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER, ctx.newPromise());
   }
   if (!handshakePromise.isDone()) {
     flushedBeforeHandshake = true;
   }
   wrap(ctx, false);
   ctx.flush();
 }
  /*
   * SPDY Session Error Handling:
   *
   * When a session error occurs, the endpoint encountering the error must first
   * send a GOAWAY frame with the Stream-ID of the most recently received stream
   * from the remote endpoint, and the error code for why the session is terminating.
   *
   * After sending the GOAWAY frame, the endpoint must close the TCP connection.
   */
  private void issueSessionError(ChannelHandlerContext ctx, SpdySessionStatus status) {

    sendGoAwayFrame(ctx, status)
        .addListener(new ClosingChannelFutureListener(ctx, ctx.newPromise()));
  }
Beispiel #8
0
 /**
  * Sends an SSL {@code close_notify} message to the specified channel and destroys the underlying
  * {@link SSLEngine}.
  */
 public ChannelFuture close() {
   return close(ctx.newPromise());
 }
 /**
  * Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush
  * immediately, this is the responsibility of the caller.
  */
 private ChannelFuture goAway(ChannelHandlerContext ctx, Http2Exception cause) {
   long errorCode = cause != null ? cause.error().code() : NO_ERROR.code();
   ByteBuf debugData = Http2CodecUtil.toByteBuf(ctx, cause);
   int lastKnownStream = connection().remote().lastStreamCreated();
   return goAway(ctx, lastKnownStream, errorCode, debugData, ctx.newPromise());
 }
 /**
  * Handler for a stream error. Sends a {@code RST_STREAM} frame to the remote endpoint and closes
  * the stream.
  *
  * @param ctx the channel context
  * @param cause the exception that was caught
  * @param http2Ex the {@link StreamException} that is embedded in the causality chain.
  */
 protected void onStreamError(
     ChannelHandlerContext ctx, Throwable cause, StreamException http2Ex) {
   resetStream(ctx, http2Ex.streamId(), http2Ex.error().code(), ctx.newPromise());
 }
  @Override
  protected ChannelFuture doOnContent(ChannelHandlerContext context, HttpContent content) {
    if (_writeChannel != null) {
      try {
        ByteBuf data = content.content();
        while (data.isReadable()) {
          data.readBytes(_writeChannel, data.readableBytes());
        }
        if (content instanceof LastHttpContent) {
          checkContentHeader(
              ((LastHttpContent) content).trailingHeaders().names(),
              Collections.singletonList(CONTENT_LENGTH));

          context.channel().config().setAutoRead(false);

          NettyTransferService<HttpProtocolInfo>.NettyMoverChannel writeChannel = _writeChannel;
          _writeChannel = null;
          _files.remove(writeChannel);

          long size = writeChannel.size();
          URI location = writeChannel.getProtocolInfo().getLocation();

          ChannelPromise promise = context.newPromise();
          Futures.addCallback(
              writeChannel.release(),
              new FutureCallback<Void>() {
                @Override
                public void onSuccess(Void result) {
                  try {
                    context.writeAndFlush(new HttpPutResponse(size, location), promise);
                  } catch (IOException e) {
                    context.writeAndFlush(
                        createErrorResponse(INTERNAL_SERVER_ERROR, e.getMessage()), promise);
                  }
                  context.channel().config().setAutoRead(true);
                }

                @Override
                public void onFailure(Throwable t) {
                  if (t instanceof FileCorruptedCacheException) {
                    context.writeAndFlush(
                        createErrorResponse(BAD_REQUEST, t.getMessage()), promise);
                  } else if (t instanceof CacheException) {
                    context.writeAndFlush(
                        createErrorResponse(INTERNAL_SERVER_ERROR, t.getMessage()), promise);
                  } else {
                    context.writeAndFlush(
                        createErrorResponse(INTERNAL_SERVER_ERROR, t.toString()), promise);
                  }
                  context.channel().config().setAutoRead(true);
                }
              },
              MoreExecutors.directExecutor());
          return promise;
        }
      } catch (IOException e) {
        _writeChannel.release(e);
        _files.remove(_writeChannel);
        _writeChannel = null;
        return context.writeAndFlush(createErrorResponse(INTERNAL_SERVER_ERROR, e.getMessage()));
      } catch (HttpException e) {
        _writeChannel.release(e);
        _files.remove(_writeChannel);
        _writeChannel = null;
        return context.writeAndFlush(
            createErrorResponse(HttpResponseStatus.valueOf(e.getErrorCode()), e.getMessage()));
      }
    }
    return null;
  }
 @Override
 public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
   handshakeFuture = ctx.newPromise();
 }