用java写了一组接口,同时给web,ios,android调用,web端访问正常,但ios访问会报400,打断点进行调试,ios的请求根本都到不了controller的代码中,也就是说在tomcat进行http request解析的时候就报错,并将错误返回给客户端了,具体的错误如下:
01-Dec-2014 11:08:11.688 INFO [http-apr-8080-exec-3] 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<S> 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
----------------------------------------分隔线-------------------------------------------------
各种配置尝试后,最终发现问题是因为ios发送http请求时自己添加的User-Agent中的字符编码导致的tomcat在解析http header的时候出错导致的。并非是因为tomcat这边的配置导致的。
相关推荐
Apache Tomcat 8.0.11 是一个广泛使用的开源软件,它是一个符合Java Servlet和JavaServer Pages(JSP)规范的应用服务器,主要用于部署和运行Java Web应用程序。这个版本是针对Windows操作系统优化的,因此在Windows...
Apache Tomcat 8.0.11 是一个广泛使用的开源软件,它是一个实现了Java Servlet、JavaServer Pages(JSP)和Java EE的Web应用程序容器。这个版本是Tomcat服务器的一个重要分支,它提供了对Java EE 7规范的支持,使得...
tomcat_v8.0.11各版本绿色版及安装版,包含安装版apache-tomcat-8.0.11.exe,绿色版apache-tomcat-8.0.11.tar.gz、apache-tomcat-8.0.11.zip 、apache-tomcat-8.0.11-windows-i64.zip、apache-tomcat-8.0.11-...
Apache Tomcat 8.0.11 是一个广泛使用的开源软件,用以部署和运行Java Servlet和JavaServer Pages (JSP)。这个版本是Tomcat服务器的一个特定发行版,提供了对Java EE 7规范的支持。在了解这个版本之前,我们先来简述...
10. **版本更新**:Apache Tomcat 8.0.11是8.x系列的一个特定版本,可能包含了一些错误修复、安全更新和新特性。随着版本的升级,Tomcat不断改进其性能和稳定性。 在解压"apache-tomcat-8.0.11.zip"后,用户通常...
- 错误修复:对已知问题进行修复,提高稳定性。 在部署和使用Apache Tomcat 8.0.11时,需要注意以下几点: 1. 配置环境变量:确保`JAVA_HOME`指向有效的Java JDK安装路径,以便Tomcat能找到Java运行时环境。 2. ...
《深入理解Tomcat-8.0.11 Windows x64免安装版》 Tomcat作为一款广泛应用的Java Servlet容器,其免安装版在IT环境中扮演着重要角色,特别是对于那些需要多套运行环境的用户而言。免安装版Tomcat 8.0.11与免安装JDK...
这个“apache-tomcat-8.0.11-windows-x86.zip”压缩包是Tomcat 8.0.11版本的Windows 32位版本,适用于在Windows环境下搭建Java Web服务器。 1. **Apache Tomcat简介** Apache Tomcat是Apache软件基金会的项目之一...
该项目是一个基于Java技术的第三方考研信息管理系统,采用Tomcat9.0作为应用服务器,并结合MySQL8.0数据库来存储和管理数据。以下是该系统涉及的关键知识点: 1. **Java编程语言**:Java是一种广泛使用的面向对象的...
MySQL JDBC驱动8.0.11版本相较于早期版本,可能包含性能优化、新功能、对最新MySQL服务器特性的支持以及错误修复。例如,它可能支持更高级的SSL连接选项,提供更好的事务处理机制,或者改进了大数据量处理的效率。 ...
Tomcat JDBC池软件包 org.apache.tomcat/tomcat-jdbc/8.0.11/tomcat-jdbc-8.0.11.jar
mysql数据库版本从5.6.28升到8.0.11过程中部署项目时遇到的问题和解决方法,具体介绍如下所示: 首先这个项目用到了hibernate4.2.0,链接mysql5.6.28没问题,换到8.0.11,启动报错 1.Caused by: org.hibernate....
9. **改进的错误处理**:提供了更清晰的错误信息,帮助开发者更快定位和解决问题。 10. **Unicode和字符集支持**:全面支持Unicode,可以处理各种语言和字符集,适应全球化应用需求。 要使用MySQL Connector/J,...
本配置教程将详细介绍如何在Tomcat 8.5上部署Solr 7.4,并连接到MySQL 8.0.11数据库,同时集成IK分词器,提升中文搜索体验。 首先,确保你已安装Java Development Kit (JDK) 8或以上版本,因为Tomcat和Solr都需要...
1. **JDBC接口**:JDBC是Java中用于访问数据库的标准API,它提供了一套接口和类,使得开发者可以用统一的方式处理各种不同的数据库。MySQL Connector/J实现了这些接口,使得Java应用可以连接、查询、更新MySQL数据库...
标题“mysql-java-8.0.11.zip”暗示了这是一个与MySQL数据库和Java编程语言相关的压缩包。这个包很可能是MySQL的Java连接器(JDBC驱动),它允许Java应用程序与MySQL数据库进行交互。MySQL JDBC驱动是Java开发人员在...
4. **错误处理**:PHP8引入了错误类,使得错误处理更像异常处理,能够提供更详细的错误信息,帮助开发者更快定位问题。 5. **新特性**:比如`match`表达式,它提供了类似switch语句的功能,但语法更简洁且具有更好...
- **更好的错误处理**:提供了更详细的错误信息,便于排查问题。 总的来说,`mysql-connector-java-8.0.11.jar`是开发Java应用与MySQL数据库交互不可或缺的组件,它的正确使用能够帮助开发者高效地完成数据操作任务...
同时,还可以配置日志记录,以便于分析和解决运行时问题。 8. **兼容性**:该版本的连接器与MySQL Server 5.5及更高版本兼容,包括社区版和企业版。 9. **安装和配置**:"mysql-connector-net-8.0.11.msi"是...
总之,"ODBC驱动 v8.0.11 win x64位"是用于连接64位Windows系统上的MySQL数据库的重要工具,它简化了跨平台数据访问,增强了应用的可移植性,并且提供了与MySQL服务器的高效通信。通过正确安装和配置,用户可以充分...