我们最近从Jetty v 6_1_26切换到9_4_11。
并在我们的代码中进行了必要的更改,以将遵从模式设置为RFC2616,因为我们需要产品中的多行标头支持。
这是我们的设置方式:
JasperFillManager.fillReport(JasperReport jReport, Map<String,Object> params, Connection connection)
并且我们已使用以下代码成功验证了合规性模式正在更改。
public class JettyPsServerConnector extends ServerConnector {
public JettyServerConnector(Server server, PsSelectorProvider psProvider,Map<String,String> configMap, boolean useSSL) throws Exception{
super(server,0,-1, new SslConnectionFactory( getSSLContextFactory(configMap),HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory( getHTTPConfiguration(), HttpCompliance.RFC2616 ));
}
这将合规模式打印为“ RFC2616”。
但是即使将遵从模式设置为RFC2616,我们仍然会看到此问题。
Connector[] connectorArray = server.getConnectors();
for(Connector conn: connectorArray){
Collection col = conn.getConnectionFactories();
for(ConnectionFactory con: col){
if (con instanceof HttpConnectionFactory)
System.out.println("HTTP Compliance Mode:"+((HttpConnectionFactory)con).getHttpCompliance());
}
}
我们正在通过两者之间的代理服务器访问服务器代码。
我们无法弄清楚是什么原因造成的。
答案 0 :(得分:0)
首先,除非您完全(我是说100%)完全了解Jetty 9.x中的整个ServerConnector
和HttpConnectionFactory
行为,否则请不要从Endpoint
开始扩展。一个小错误,您会打破很多事情。这并不是一个可扩展的公共API,在Jetty的未来版本中可能会标记为final。
如果需要自定义行为,请先查看HttpConfiguration.Customizer
,然后如果仍然需要其他自定义,则改用自定义HttpConnectionFactory
。
接下来,知道HttpCompliance
只是HttpComplianceSection
设置的集合/集合的持有者。您可能要确保所选的HttpCompliance
设置中没有包含HttpComplianceSection.NO_FIELD_FOLDING
。
最后,请确保您已确定并修复了那些有问题的客户端,由于放宽的行为(例如,行折叠)造成/造成的众多安全问题,近年来使用HTTP的趋势越来越严格。有一天,即使您的负载均衡器,代理,路由器等也将拒绝此类请求。
过时的RFC2616出于许多原因而进行了更新,其中很大一部分是使用诸如MUST NOT
(在RFC2119 Section 2中明确定义的短语)专门指出某些危险行为(例如行折叠) ),使行为对于更新的规范而言不是可选的。
IETF在2013年弃用Header Field行折叠的原因是由于与Header注入漏洞的变化相关的许多安全问题。启用标头字段行折叠后,您将无法防止响应拆分,会话固定,跨站点脚本,安全来源检查和恶意重定向。
许多现代的防火墙/网关/路由器/负载平衡器不支持标题折叠。
还要注意,HTTP / 2不支持标头折叠。
如果(由于某些原因)无法纠正那些有问题的客户端,那么您唯一的选择就是不要从现在开始升级任何服务器软件,以延迟这一天的发生。 (您无法控制的其他中介人甚至可能在您到达之前使这些请求失败!)
您将必须解决客户端中标头折叠使用过时的问题,因为超出您控制范围的许多事情已经拒绝了该概念,您会您剩下的唯一选择就是修复客户端行为。
无论如何,这是使用RFC2616遵从模式和行折叠的这种行为的独立演示。
package jetty;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.URI;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.IO;
public class HttpComplianceDemo
{
public static void main(String[] args) throws Exception
{
Server server = new Server();
HttpConfiguration http_config = new HttpConfiguration();
http_config.setSendServerVersion(true);
HttpCompliance compliance = HttpCompliance.RFC2616;
ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(http_config, compliance));
connector.setPort(9090);
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
context.addServlet(DumpServlet.class, "/dump");
context.addServlet(DefaultServlet.class, "/");
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
try
{
server.start();
testLineFolding(server.getURI().resolve("/"));
}
finally
{
server.stop();
}
}
private static void testLineFolding(URI serverUri) throws IOException
{
String host = serverUri.getHost();
int port = serverUri.getPort();
try (Socket socket = new Socket(host, port);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream())
{
StringBuilder rawRequest = new StringBuilder();
rawRequest.append("GET /dump HTTP/1.1\r\n");
rawRequest.append("Host: ").append(serverUri.getRawAuthority()).append("\r\n");
rawRequest.append("Connection: close\r\n");
rawRequest.append("X-Foo: name\r\n"); // the header with line folding
rawRequest.append(" extra\r\n");
rawRequest.append("\r\n");
byte bufRequest[] = rawRequest.toString().getBytes(UTF_8);
System.out.println("--request--");
System.out.println(new String(bufRequest, UTF_8));
out.write(bufRequest);
out.flush();
ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
IO.copy(in, outBuf);
String response = new String(outBuf.toByteArray(), UTF_8);
System.out.println("--Response--");
System.out.println(response);
}
}
public static class DumpServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/plain");
PrintWriter out = resp.getWriter();
Enumeration<String> enNames = req.getHeaderNames();
while (enNames.hasMoreElements())
{
String name = enNames.nextElement();
String value = req.getHeader(name);
out.printf("- [HEADER:%s=%s]\n", name, value);
}
}
}
}
输出...
2018-08-08 11:21:27.811:INFO::main: Logging initialized @338ms to org.eclipse.jetty.util.log.StdErrLog
2018-08-08 11:21:27.946:INFO:oejs.Server:main: jetty-9.4.11.v20180605; built: 2018-06-05T18:24:03.829Z; git: d5fc0523cfa96bfebfbda19606cad384d772f04c; jvm 9.0.4+11
2018-08-08 11:21:28.004:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@78aab498{/,null,AVAILABLE}
2018-08-08 11:21:28.204:INFO:oejs.AbstractConnector:main: Started ServerConnector@15ff3e9e{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
2018-08-08 11:21:28.205:INFO:oejs.Server:main: Started @739ms
--request--
GET /dump HTTP/1.1
Host: 192.168.0.119:9090
Connection: close
X-Foo: name
extra
--Response--
HTTP/1.1 200 OK
Connection: close
Date: Wed, 08 Aug 2018 16:21:28 GMT
Content-Type: text/plain;charset=iso-8859-1
Content-Length: 91
Server: Jetty(9.4.11.v20180605)
- [HEADER:Connection=close]
- [HEADER:X-Foo=name extra]
- [HEADER:Host=192.168.0.119:9090]
2018-08-08 11:21:28.307:INFO:oejs.AbstractConnector:main: Stopped ServerConnector@15ff3e9e{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
2018-08-08 11:21:28.310:INFO:oejsh.ContextHandler:main: Stopped o.e.j.s.ServletContextHandler@78aab498{/,null,UNAVAILABLE}
如果将HttpCompliance
模式更改为RFC7230,则会得到不同的结果。
--request--
GET /dump HTTP/1.1
Host: 192.168.0.119:9090
Connection: close
X-Foo: name
extra
--Response--
HTTP/1.1 400 Header Folding
Content-Type: text/html;charset=iso-8859-1
Content-Length: 57
Connection: close
Server: Jetty(9.4.11.v20180605)
<h1>Bad Message 400</h1><pre>reason: Header Folding</pre>