我正在使用嵌入式Jetty構建沙盒RESTful API。我的概念驗證設計:一個簡單的嵌入式碼頭服務器,它(1)接受SSL端口上的連接,(2)使用ContextHandlerCollection根據URI前綴調用適當的Handler。Embedded Jetty:在安全的https服務器中,ContextHandler重定向到http URI
我原來的測試,使用簡單的非SSL連接,似乎完美工作(注意,代碼爲導入和助手HelloHandler類在附錄中)。
public static void main(String[] args) throws Exception {
Server server = new Server(12000);
ContextHandler test1Context = new ContextHandler();
test1Context.setContextPath("/test1");
test1Context.setHandler(new HelloHandler("Hello1"));
ContextHandler test2Context = new ContextHandler();
test2Context.setContextPath("/test2");
test2Context.setHandler(new HelloHandler("Hello2"));
ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
contextHandlers.setHandlers(new Handler[] { test1Context, test2Context });
server.setHandler(contextHandlers);
server.start();
server.join();
}
然而,在測試這一點,它逃過了我的注意,當我省略了結尾斜線,所以http://localhost:12000/test1
是越來越重定向到http://localhost:12000/test1/
瀏覽器重定向正在發生。 (FWIW,這個oversite會稍後轉化爲4個小時以上的故障排除)。
當我切換到HTTPS SSL連接時,一切都出錯了。下面的代碼:
public static void main(String[] args) throws Exception {
Server server = new Server();
// Setups
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath("C:/keystore.jks");
sslContextFactory.setKeyStorePassword("password");
sslContextFactory.setKeyManagerPassword("password");
ContextHandler test1Context = new ContextHandler();
test1Context.setContextPath("/test1");
test1Context.setHandler(new HelloHandler("Hello1"));
ContextHandler test2Context = new ContextHandler();
test2Context.setContextPath("/test2");
test2Context.setHandler(new HelloHandler("Hello2"));
ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
contextHandlers.setHandlers(new Handler[] { test1Context, test2Context });
ServerConnector serverConnector = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory, "http/1.1"),
new HttpConnectionFactory());
serverConnector.setPort(12000);
server.setConnectors(new Connector[] { serverConnector });
server.setHandler(contextHandlers);
server.start();
server.join();
}
症狀:
嘗試使用https://localhost:12000/test1
(沒有尾隨斜線)會導致瀏覽器報告,服務器必須重置連接。另外,我最初做的不是,URI被重定向到http://localhost:12000/test1/
(不是https!)。有趣的是(以某種虐待感的方式),在某些情況下,我會改變代碼中無關緊要的內容,然後不經意地用https://localhost:12000/test1/
進行測試,它會起作用。對於這種誤報帶來的挫折,言語並不公平。
除了瀏覽器重定向和報告的連接復位錯誤,我會得到下面的異常在我的服務器日誌:
2013-11-23 13:57:48 DEBUG org.eclipse.jetty.server.HttpConnection:282 -
org.eclipse.jetty.io.EofException
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:653)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:240)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:601)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:532)
at java.lang.Thread.run(Thread.java:722)
Caused by: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
at sun.security.ssl.EngineInputRecord.bytesInCompletePacket(EngineInputRecord.java:171)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:845)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:758)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:518)
... 5 more
不幸的是,我花了我所有的時間試圖此異常直接排除故障,當時關鍵線索在瀏覽器重定向的URL中。事實證明,ContextHandler代碼有一個默認行爲,當不存在結尾的正斜槓時,會導致它重定向到一個具有尾部斜槓的URI。不幸的是,這個重定向到一個HTTP URI--所以HTTPS方案被丟棄,導致服務器抱怨純文本。
解決方法:
一旦這種重定向行爲,我就明白了,一個快速谷歌搜索,實際的問題,導致我ContextHandler.setAllowNullPathInfo(true)方法 - 這將關閉此重定向行爲。無法識別的SSL - :
test1Context.setAllowNullPathInfo(true);
test2Context.setAllowNullPathInfo(true);
要點這個帖子:4小時試圖解決「javax.net.ssl.SSLException
我花了3在上面我的代碼,這是與2號線實現消息,明文連接?「例外情況以及網絡上的任何地方,我都發現與上述解決方案/解決方法相關的異常。如果我從其中一位開發人員那裏獲得經驗的挫折,那麼任務就完成了。
揮之不去的問題(另一個原因張貼本): 好了,所以我得到這個代碼的工作,但不得不承認:我極其不安的事實,我非常簡單的證明了概念測試,做事情我認爲這是相當普遍的,遇到了這種似乎前所未有的局面。這告訴我,我可能做了超出「最佳實踐」領域的事情;或者更糟糕的是,我在設計這個方面做了一些完全錯誤的事情。所以,問題:
1)AM我做錯了嗎?
2)爲什麼ContextHandler的默認行爲會重定向缺少尾部空格的URI?通過使用setAllowNullPathInfo(true)來覆蓋該默認行爲會帶來什麼風險?
附錄(幫助類和進口代碼) 進口: import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.ssl.SslContextFactory;
助手類:
static class HelloHandler extends AbstractHandler {
final String _greeting;
public HelloHandler(String greeting) {
_greeting = greeting;
}
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
response.getWriter().println("<h1>" + _greeting + "</h1>");
}
}
釘它!補充它,我甚至嘗試過,但省略了httpConf.addCustomizer(新的SecureRequestCustomizer())步驟;所以添加沒有影響,看起來多餘。 – KevinKirkpatrick