docs: public HttpServerExchange endExchange() {
          final int state = this.state;
          if (allAreSet(state, FLAG_REQUEST_TERMINATED | FLAG_RESPONSE_TERMINATED)) {
              if(blockingHttpExchange != null) {
                  //we still have to close the blocking exchange in this case,
                  IoUtils.safeClose(blockingHttpExchange);
              }
              return this;
          }
          if(defaultResponseListeners != null) {
              int i = defaultResponseListeners.length - 1;
              while (i >= 0) {
                  DefaultResponseListener listener = defaultResponseListeners[i];
                  if (listener != null) {
                      defaultResponseListeners[i] = null;
                      try {
                          if (listener.handleDefaultResponse(this)) {
                              return this;
                          }
                      } catch (Throwable e) {
                          UndertowLogger.REQUEST_LOGGER.debug("Exception running default response listener", e);
                      }
                  }
                  i--;
              }
          }
  
          if (anyAreClear(state, FLAG_REQUEST_TERMINATED)) {
              connection.terminateRequestChannel(this);
          }
  
          if (blockingHttpExchange != null) {
              try {
                  //TODO: can we end up in this situation in a IO thread?
                  blockingHttpExchange.close();
              } catch (IOException e) {
                  UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
                  IoUtils.safeClose(connection);
              } catch (Throwable t) {
                  UndertowLogger.REQUEST_IO_LOGGER.handleUnexpectedFailure(t);
                  IoUtils.safeClose(connection);
              }
          }
  
          //417 means that we are rejecting the request
          //so the client should not actually send any data
          if (anyAreClear(state, FLAG_REQUEST_TERMINATED)) {
  
              //not really sure what the best thing to do here is
              //for now we are just going to drain the channel
              if (requestChannel == null) {
                  getRequestChannel();
              }
              int totalRead = 0;
              for (; ; ) {
                  try {
                      long read = Channels.drain(requestChannel, Long.MAX_VALUE);
                      totalRead += read;
                      if (read == 0) {
                          //if the response code is 417 this is a rejected continuation request.
                          //however there is a chance the client could have sent the data anyway
                          //so we attempt to drain, and if we have not drained anything then we
                          //assume the server has not sent any data
  
                          if (getStatusCode() != StatusCodes.EXPECTATION_FAILED || totalRead > 0) {
                              requestChannel.getReadSetter().set(ChannelListeners.drainListener(Long.MAX_VALUE,
                                      new ChannelListener<StreamSourceChannel>() {
                                          @Override
                                          public void handleEvent(final StreamSourceChannel channel) {
                                              if (anyAreClear(state, FLAG_RESPONSE_TERMINATED)) {
                                                  closeAndFlushResponse();
                                              }
                                          }
                                      }, new ChannelExceptionHandler<StreamSourceChannel>() {
                                          @Override
                                          public void handleException(final StreamSourceChannel channel, final IOException e) {
  
                                              //make sure the listeners have been invoked
                                              //unless the connection has been killed this is a no-op
                                              invokeExchangeCompleteListeners();
                                              UndertowLogger.REQUEST_LOGGER.debug("Exception draining request stream", e);
                                              IoUtils.safeClose(connection);
                                          }
                                      }
                              ));
                              requestChannel.resumeReads();
                              return this;
                          } else {
                              break;
                          }
                      } else if (read == -1) {
                          break;
                      }
                  } catch (Throwable t) {
                      if (t instanceof IOException) {
                          UndertowLogger.REQUEST_IO_LOGGER.ioException((IOException) t);
                      } else {
                          UndertowLogger.REQUEST_IO_LOGGER.handleUnexpectedFailure(t);
                      }
                      invokeExchangeCompleteListeners();
                      IoUtils.safeClose(connection);
                      return this;
                  }
  
              }
          }
          if (anyAreClear(state, FLAG_RESPONSE_TERMINATED)) {
              closeAndFlushResponse();
          }
          return this;
      }