Error parsing HTTP request header Note: further occurrences of HTTP header parsing errors

tomcat进行http request解析的时候报错,并将错误返回给客户端了,具体的错误如下:

 org.apache.coyote.http11.AbstractHttp11Processor.process Error parsing HTTP request header
 Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.

查看对应的类代码,其中涉及的方法如下:

public SocketState process(SocketWrapper socketWrapper)
    throws IOException {
    RequestInfo rp = request.getRequestProcessor();
    rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

    // Setting up the I/O
    setSocketWrapper(socketWrapper);
    getInputBuffer().init(socketWrapper, endpoint);
    getOutputBuffer().init(socketWrapper, endpoint);

    // Flags
    keepAlive = true;
    comet = false;
    openSocket = false;
    sendfileInProgress = false;
    readComplete = true;
    if (endpoint.getUsePolling()) {
      keptAlive = false;
    } else {
      keptAlive = socketWrapper.isKeptAlive();
    }

    if (disableKeepAlive()) {
      socketWrapper.setKeepAliveLeft(0);
    }

    while (!getErrorState().isError() && keepAlive && !comet && !isAsync() &&
        httpUpgradeHandler == null && !endpoint.isPaused()) {

      // Parsing the request header
      try {
        setRequestLineReadTimeout();

        if (!getInputBuffer().parseRequestLine(keptAlive)) {
          if (handleIncompleteRequestLineRead()) {
            break;
          }
        }

        if (endpoint.isPaused()) {
          // 503 - Service unavailable
          response.setStatus(503);
          setErrorState(ErrorState.CLOSE_CLEAN, null);
        } else {
          keptAlive = true;
          // Set this every time in case limit has been changed via JMX
          request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
          // Currently only NIO will ever return false here
          if (!getInputBuffer().parseHeaders()) {
            // We've read part of the request, don't recycle it
            // instead associate it with the socket
            openSocket = true;
            readComplete = false;
            break;
          }
          if (!disableUploadTimeout) {
            setSocketTimeout(connectionUploadTimeout);
          }
        }
      } catch (IOException e) {
        if (getLog().isDebugEnabled()) {
          getLog().debug(
              sm.getString("http11processor.header.parse"), e);
        }
        setErrorState(ErrorState.CLOSE_NOW, e);
        break;
      } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        UserDataHelper.Mode logMode = userDataHelper.getNextMode();
        if (logMode != null) {
          String message = sm.getString(
              "http11processor.header.parse");
          switch (logMode) {
            case INFO_THEN_DEBUG:
              message += sm.getString(
                  "http11processor.fallToDebug");
              //$FALL-THROUGH$
            case INFO:
              getLog().info(message);
              break;
            case DEBUG:
              getLog().debug(message);
          }
        }
        // 400 - Bad Request
        response.setStatus(400);
        setErrorState(ErrorState.CLOSE_CLEAN, t);
        getAdapter().log(request, response, 0);
      }

      if (!getErrorState().isError()) {
        // Setting up filters, and parse some request headers
        rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
        try {
          prepareRequest();
        } catch (Throwable t) {
          ExceptionUtils.handleThrowable(t);
          if (getLog().isDebugEnabled()) {
            getLog().debug(sm.getString(
                "http11processor.request.prepare"), t);
          }
          // 500 - Internal Server Error
          response.setStatus(500);
          setErrorState(ErrorState.CLOSE_CLEAN, t);
          getAdapter().log(request, response, 0);
        }
      }

      if (maxKeepAliveRequests == 1) {
        keepAlive = false;
      } else if (maxKeepAliveRequests > 0 &&
          socketWrapper.decrementKeepAlive() <= 0) {
        keepAlive = false;
      }

      // Process the request in the adapter
      if (!getErrorState().isError()) {
        try {
          rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
          getAdapter().service(request, response);
          // Handle when the response was committed before a serious
          // error occurred.  Throwing a ServletException should both
          // set the status to 500 and set the errorException.
          // If we fail here, then the response is likely already
          // committed, so we can't try and set headers.
          if(keepAlive && !getErrorState().isError() && (
              response.getErrorException() != null ||
                  (!isAsync() &&
                  statusDropsConnection(response.getStatus())))) {
            setErrorState(ErrorState.CLOSE_CLEAN, null);
          }
          setCometTimeouts(socketWrapper);
        } catch (InterruptedIOException e) {
          setErrorState(ErrorState.CLOSE_NOW, e);
        } catch (HeadersTooLargeException e) {
          // The response should not have been committed but check it
          // anyway to be safe
          if (response.isCommitted()) {
            setErrorState(ErrorState.CLOSE_NOW, e);
          } else {
            response.reset();
            response.setStatus(500);
            setErrorState(ErrorState.CLOSE_CLEAN, e);
            response.setHeader("Connection", "close"); // TODO: Remove
          }
        } catch (Throwable t) {
          ExceptionUtils.handleThrowable(t);
          getLog().error(sm.getString(
              "http11processor.request.process"), t);
          // 500 - Internal Server Error
          response.setStatus(500);
          setErrorState(ErrorState.CLOSE_CLEAN, t);
          getAdapter().log(request, response, 0);
        }
      }

      // Finish the handling of the request
      rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);

      if (!isAsync() && !comet) {
        if (getErrorState().isError()) {
          // If we know we are closing the connection, don't drain
          // input. This way uploading a 100GB file doesn't tie up the
          // thread if the servlet has rejected it.
          getInputBuffer().setSwallowInput(false);
        } else if (expectation &&
            (response.getStatus() < 200 || response.getStatus() > 299)) {
          // Client sent Expect: 100-continue but received a
          // non-2xx final response. Disable keep-alive (if enabled)
          // to ensure that the connection is closed. Some clients may
          // still send the body, some may send the next request.
          // No way to differentiate, so close the connection to
          // force the client to send the next request.
          getInputBuffer().setSwallowInput(false);
          keepAlive = false;
        }
        endRequest();
      }

      rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);

      // If there was an error, make sure the request is counted as
      // and error, and update the statistics counter
      if (getErrorState().isError()) {
        response.setStatus(500);
      }
      request.updateCounters();

      if (!isAsync() && !comet || getErrorState().isError()) {
        if (getErrorState().isIoAllowed()) {
          getInputBuffer().nextRequest();
          getOutputBuffer().nextRequest();
        }
      }

      if (!disableUploadTimeout) {
        if(endpoint.getSoTimeout() > 0) {
          setSocketTimeout(endpoint.getSoTimeout());
        } else {
          setSocketTimeout(0);
        }
      }

      rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);

      if (breakKeepAliveLoop(socketWrapper)) {
        break;
      }
    }

    rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

    if (getErrorState().isError() || endpoint.isPaused()) {
      return SocketState.CLOSED;
    } else if (isAsync() || comet) {
      return SocketState.LONG;
    } else if (isUpgrade()) {
      return SocketState.UPGRADING;
    } else {
      if (sendfileInProgress) {
        return SocketState.SENDFILE;
      } else {
        if (openSocket) {
          if (readComplete) {
            return SocketState.OPEN;
          } else {
            return SocketState.LONG;
          }
        } else {
          return SocketState.CLOSED;
        }
      }
    }
  }

而报错的地方则是在:

// Currently only NIO will ever return false here
          if (!getInputBuffer().parseHeaders()) {
            // We've read part of the request, don't recycle it
            // instead associate it with the socket
            openSocket = true;
            readComplete = false;
            break;
          }
          if (!disableUploadTimeout) {
            setSocketTimeout(connectionUploadTimeout);
          }
的时候抛出了异常,对应的catch块代码如下:
 catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        UserDataHelper.Mode logMode = userDataHelper.getNextMode();
        if (logMode != null) {
          String message = sm.getString(
              "http11processor.header.parse");
          switch (logMode) {
            case INFO_THEN_DEBUG:
              message += sm.getString(
                  "http11processor.fallToDebug");
              //$FALL-THROUGH$
            case INFO:
              getLog().info(message);
              break;
            case DEBUG:
              getLog().debug(message);
          }
        }
        // 400 - Bad Request
        response.setStatus(400);
        setErrorState(ErrorState.CLOSE_CLEAN, t);
        getAdapter().log(request, response, 0);
      }

错误日志的输出,是配置在LocalStrings.properties中的,相关的两个属性:

http11processor.fallToDebug=\n Note\: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
http11processor.header.parse=Error parsing HTTP request header

找到对应的 getInputBuffer().parseHeaders()

public boolean parseHeaders()
    throws IOException {
    if (!parsingHeader) {
      throw new IllegalStateException(
          sm.getString("iib.parseheaders.ise.error"));
    }

    HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;

    do {
      status = parseHeader();
      // Checking that
      // (1) Headers plus request line size does not exceed its limit
      // (2) There are enough bytes to avoid expanding the buffer when
      // reading body
      // Technically, (2) is technical limitation, (1) is logical
      // limitation to enforce the meaning of headerBufferSize
      // From the way how buf is allocated and how blank lines are being
      // read, it should be enough to check (1) only.
      if (pos > headerBufferSize
          || buf.length - pos < socketReadBufferSize) {
        throw new IllegalArgumentException(
            sm.getString("iib.requestheadertoolarge.error"));
      }
    } while ( status == HeaderParseStatus.HAVE_MORE_HEADERS );
    if (status == HeaderParseStatus.DONE) {
      parsingHeader = false;
      end = pos;
      return true;
    } else {
      return false;
    }
  }
可知此处涉及了两个缓冲区大小,headerBufferSize和socketReadBufferSize,如果读取时数据的长度大于这两个值,就会报iib.requestheadertoolarge.error即Request header is too large,在网上搜索时,有的是因为这个设置导致的400,解决方法就是修改Tomcat的server.xml,
在<Connector port="8080" protocol="HTTP/1.1"
 connectionTimeout="20000"    redirectPort="8443" />的配置中增加maxHttpHeaderSize的配置

在org.apache.coyote.http11.AbstractHttp11Protocol类中定义了其默认值:

/**
 Maximum size of the HTTP message header.  */ 
private int maxHttpHeaderSize = 8 * 1024;
/**
Maximum size of the post which will be saved when processing certain  requests, such as a POST. */ 
private int maxSavePostSize = 4 * 1024;
/**
* Specifies a different (usually  longer) connection timeout during data  upload. */ 
private int connectionUploadTimeout = 300000;
 /**
Maximum size of trailing headers in bytes */ 
private int maxTrailerSize = 8192;
/**
 Maximum size of extension information in chunked encoding */ 
private int maxExtensionSize = 8192;
/**Maximum amount of request body to swallow.*/
private int maxSwallowSize = 2 * 1024 * 1024;

其他参数设置介绍: http://tomcat.apache.org/tomcat-8.0-doc/config/ajp.html

你可能感兴趣的:(http)