Netty:透明地使用SPDY和HTTP

大多數人已經從谷歌那里聽說過SPDY,該協議被提議作為老化的HTTP協議的替代品。 Web服務器是瀏覽器正在緩慢地實現該協議,并且支持正在增長。 在最近的文章中,我已經寫過SPDY的工作方式以及如何在Jetty中啟用SPDY支持。 由于Netty(最初來自JBoss)幾個月以來也支持SPDY 。由于Netty通常用于高性能協議服務器,因此SPDY是合乎邏輯的。 在本文中,我將向您展示如何創建一個基于Netty的基本服務器,該服務器在SPDY和HTTP之間進行協議協商。 它使用Netty snoop示例中的示例HTTPRequestHandler來消耗并產生一些HTTP內容。
為了使一切正常,我們需要做以下事情:
  • 在Java中啟用NPN以確定要使用的協議。
  • 根據協商的協議,確定使用HTTP還是SPDY。
  • 確保使用HTTP發送回正確的SPDY標頭。
SPDY使用TLS擴展來確定要在通信中使用的協議。 這稱為NPN。 我寫了一個更完整的說明,并顯示了文章中有關如何在Jetty上使用SPDY的消息,因此,有關該文章的更多信息,請參見。 基本上,此擴展的作用是在TLS交換期間,服務器和客戶端也會交換它們支持的傳輸級別協議。 對于SPDY,服務器可以同時支持SPDY協議和HTTP協議。 然后,客戶端實現可以確定要使用的協議。
由于這不是標準Java實現中可用的功能,因此我們需要使用NPN擴展Java TLS功能。
在Java中啟用NPN支持
到目前為止,我發現了兩個可以用來在Java中添加NPN支持的選項。 其中一個來自https://github.com/benmmurphy/ssl_npn ,他的回購中也有一個基本的SPDY / Netty示例,他使用自己的實現。 我將要使用的另一個選項是Jetty提供的NPN支持。 Jetty提供了一個易于使用的API,可用于將NPN支持添加到Java SSL上下文中。 再次,在關于碼頭的文章中,您可以找到關于此的更多信息。 要為Netty設置NPN,我們需要執行以下操作:
  1. 將NPN lib添加到引導路徑
  2. 將SSL上下文連接到NPN Api
將NPN庫添加到boothpath
首先是第一件事。 從http://repo2.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/8.1.2.v2012下載NPN引導jar,并確保在運行服務器時像這樣啟動它:
java -Xbootclasspath/p:<path_to_npn_boot_jar>
通過這段代碼,Java SSL支持NPN。 但是,我們仍然需要訪問此協商的結果。 我們需要知道我們使用的是HTTP還是SPDY,因為這決定了我們如何處理接收到的數據。 為此,Jetty提供了一個API。 為此和所需的Netty庫,我們將以下依賴項添加到pom中,因為我使用的是maven。
<dependency><groupId>io.netty</groupId><artifactId>netty</artifactId><version>3.4.1.Final</version></dependency><dependency><groupId>org.eclipse.jetty.npn</groupId><artifactId>npn-api</artifactId><version>8.1.2.v20120308</version></dependency>
將SSL上下文連接到NPN API
現在,我們已啟用NPN并將正確的API添加到項目中,我們可以配置Netty SSL處理程序。 在Netty中配置處理程序是在PipelineFactory中完成的。 對于我們的服務器,我創建了以下PipelineFactory:
package smartjava.netty.spdy; 
import static org.jboss.netty.channel.Channels.pipeline;import java.io.FileInputStream;
import java.security.KeyStore;import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;import org.eclipse.jetty.npn.NextProtoNego;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.ssl.SslHandler;public class SPDYPipelineFactory implements ChannelPipelineFactory {private SSLContext context;public SPDYPipelineFactory() {try {KeyStore keystore = KeyStore.getInstance("JKS");keystore.load(new FileInputStream("src/main/resources/server.jks"),"secret".toCharArray());KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");kmf.init(keystore, "secret".toCharArray());context = SSLContext.getInstance("TLS");context.init(kmf.getKeyManagers(), null, null);} catch (Exception e) {e.printStackTrace();}}public ChannelPipeline getPipeline() throws Exception {// Create a default pipeline implementation.ChannelPipeline pipeline = pipeline();// Uncomment the following line if you want HTTPSSSLEngine engine = context.createSSLEngine();engine.setUseClientMode(false);NextProtoNego.put(engine, new SimpleServerProvider());NextProtoNego.debug = true;pipeline.addLast("ssl", new SslHandler(engine));pipeline.addLast("pipeLineSelector", new HttpOrSpdyHandler());return pipeline;}
}
在此類的構造函數中,我們設置了基本的SSL上下文。 我們使用的密鑰庫和密鑰是我使用java keytool創建的,這是常規的SSL配置。 收到請求后,將調用getPipeline操作來確定如何處理該請求。 在這里,我們使用Jetty-NPN-API提供的NextProtoNego類將SSL連接連接到NPN實現。 在此操作中,我們傳遞一個提供程序,該提供程序用作服務器的回調和配置。 我們還將NextProtoNego.debug設置為true。 這會打印出一些調試信息,從而使調試更加容易。 SimpleServerProvider的代碼非常簡單:
public class SimpleServerProvider implements ServerProvider {private String selectedProtocol = null;public void unsupported() {//if unsupported, default to http/1.1selectedProtocol = "http/1.1";}public List<String> protocols() {return Arrays.asList("spdy/2","http/1.1");}public void protocolSelected(String protocol) {selectedProtocol = protocol;}public String getSelectedProtocol() { return selectedProtocol;}
}
此代碼幾乎是不言自明的。
  • 當客戶端不支持NPN時,將調用不受支持的操作。 在這種情況下,我們默認為HTTP。
  • protocol()操作返回服務器支持的協議
  • 服務器和客戶端協商協議后,將調用protocolSelected操作
getSelectedProtocol是一種用于從Netty管道中的其他處理程序獲取所選協議的方法。
根據協商的協議確定使用HTTP還是SPDY
現在,我們需要配置Netty,使其為HTTPS請求和SPDY請求運行特定的管道。 為此,讓我們回顧一下管道工廠的一小部分。
pipeline.addLast("ssl", new SslHandler(engine));
pipeline.addLast("pipeLineSelector", new HttpOrSpdyHandler());
該管道的第一部分是配置了NPN支持的SslHandler。 下一個將被調用的處理程序是HttpOrSpdyHandler。 該處理程序根據協議確定要使用的管道。 接下來列出此處理程序的代碼:
public class HttpOrSpdyHandler implements  ChannelUpstreamHandler {public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)throws Exception {// determine protocol typeSslHandler handler = ctx.getPipeline().get(SslHandler.class);SimpleServerProvider provider = (SimpleServerProvider) NextProtoNego.get(handler.getEngine());if ("spdy/2".equals(provider.getSelectedProtocol())) {ChannelPipeline pipeline = ctx.getPipeline();pipeline.addLast("decoder", new SpdyFrameDecoder());pipeline.addLast("spdy_encoder", new SpdyFrameEncoder());pipeline.addLast("spdy_session_handler", new SpdySessionHandler(true));pipeline.addLast("spdy_http_encoder", new SpdyHttpEncoder());// Max size of SPDY messages set to 1MBpipeline.addLast("spdy_http_decoder", new SpdyHttpDecoder(1024*1024)); pipeline.addLast("handler", new HttpRequestHandler());// remove this handler, and process the requests as spdypipeline.remove(this);ctx.sendUpstream(e);}  else if ("http/1.1".equals(provider.getSelectedProtocol())) {ChannelPipeline pipeline = ctx.getPipeline();pipeline.addLast("decoder", new HttpRequestDecoder());pipeline.addLast("http_encoder", new HttpResponseEncoder());pipeline.addLast("handler", new HttpRequestHandler());// remove this handler, and process the requests as httppipeline.remove(this);ctx.sendUpstream(e);} else {// we're still in protocol negotiation, no need for any handlers// at this point.}}
}
使用NPN API和當前的SSL上下文,我們可以檢索之前添加的SimpleServerProvider。 我們檢查是否已設置selectedProtocol,如果已設置,則設置一條鏈進行處理。 我們在此類中處理三個選項:
  1. 沒有協議 :可能尚未協商任何協議。 在那種情況下,我們沒有做任何特別的事情,只需正常處理即可。
  2. 有一個http協議 :我們建立了一個處理程序鏈來處理HTTP請求。
  3. 有一個spdy協議 :我們建立了一個處理程序鏈來處理SPDY請求。
有了這個鏈,我們最終由HttpRequestHandler接收到的所有消息都是HTTP請求。 我們可以正常處理此HTTP請求,然后返回HTTP響應。 各種管道配置將正確處理所有這些問題。
確保使用HTTP發送回正確的SPDY標頭
我們需要做的最后一步是測試。 我們將使用最新版本的Chrome進行測試,以測試SPDY是否正常運行,并使用wget測試正常的http請求。 我提到了鏈中最后一個處理程序HttpRequestHandler進行我們的HTTP處理。 我已經使用http://netty.io/docs/stable/xref/org/jboss/netty/example/http/snoop/Http…作為HTTPRequestHandler,因為那很好地返回了有關HTTP請求的信息,而我卻沒有做任何事。 如果不做任何更改就運行它,則會遇到問題。 為了將HTTP響應與正確的SPDY會話相關聯,我們需要將傳入請求中的標頭復制到響應中:“ X-SPDY-Stream-ID”標頭。 我將以下內容添加到HttpSnoopServerHandler中,以確保復制了這些標頭(實際上應該在單獨的處理程序中完成此操作)。
private final static String SPDY_STREAM_ID = = "X-SPDY-Stream-ID";private final static String SPDY_STREAM_PRIO = "X-SPDY-Stream-Priority";// in the writeResponse method addif (request.containsHeader(SPDY_STREAM_ID)) {response.addHeader(SPDY_STREAM_ID,request.getHeader(SPDY_STREAM_ID));// optional header for prioresponse.addHeader(SPDY_STREAM_PRIO,0);}
現在剩下的就是一臺具有啟動所有內容的主服務器的服務器,并且我們可以測試SPDY實現。
public class SPDYServer {public static void main(String[] args) {// bootstrap is used to configure and setup the serverServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool()));bootstrap.setPipelineFactory(new SPDYPipelineFactory());bootstrap.bind(new InetSocketAddress(8443));}
}
啟動服務器,啟動Chrome,然后查看是否一切正常。 打開https:// localhost:8443 / thisIsATest網址,您應該得到如下所示的結果:
在服務器的輸出中,您可以看到一些NPN調試日志記錄:
[S] NPN received for 68ce4f39[SSLEngine[hostname=null port=-1] SSL_NULL_WITH_NULL_NULL]
[S] NPN protocols [spdy/2, http/1.1] sent to client for 68ce4f39[SSLEngine[hostname=null port=-1] SSL_NULL_WITH_NULL_NULL]
[S] NPN received for 4b24e48f[SSLEngine[hostname=null port=-1] SSL_NULL_WITH_NULL_NULL]
[S] NPN protocols [spdy/2, http/1.1] sent to client for 4b24e48f[SSLEngine[hostname=null port=-1] SSL_NULL_WITH_NULL_NULL]
[S] NPN selected 'spdy/2' for 4b24e48f[SSLEngine[hostname=null port=-1] SSL_NULL_WITH_NULL_NULL]
額外的檢查是使用以下網址查看chrome瀏覽器中打開的SPDY會話:chrome:// net-internals /#spdy
現在讓我們檢查普通的舊HTTP是否仍在工作。 從命令行執行以下操作:
jos@Joss-MacBook-Pro.local:~$ wget --no-check-certificate https://localhost:8443/thisIsATest
--2012-04-27 16:29:09--  https://localhost:8443/thisIsATest
Resolving localhost... ::1, 127.0.0.1, fe80::1
Connecting to localhost|::1|:8443... connected.
WARNING: cannot verify localhost's certificate, issued by `/C=NL/ST=NB/L=Waalwijk/O=smartjava/OU=smartjava/CN=localhost':Self-signed certificate encountered.
HTTP request sent, awaiting response... 200 OK
Length: 285 
Saving to: `thisIsATest'100%[==================================================================================>] 285         --.-K/s   in 0s      2012-04-27 16:29:09 (136 MB/s) - `thisIsATest' saved [285/285]jos@Joss-MacBook-Pro.local:~$ cat thisIsATest 
WELCOME TO THE WILD WILD WEB SERVER
===================================
VERSION: HTTP/1.1
HOSTNAME: localhost:8443
REQUEST_URI: /thisIsATestHEADER: User-Agent = Wget/1.13.4 (darwin11.2.0)
HEADER: Accept = */*
HEADER: Host = localhost:8443
HEADER: Connection = Keep-Alivejos@Joss-MacBook-Pro.local:~$
而且有效! Wget使用標準的HTTPS,我們得到一個結果,而chrome使用SPDY,并從同一處理程序中呈現結果。 在最后的幾天里,我還將發布有關如何為Play Framework 2.0啟用SPDY的文章,因為它們的Web服務器也基于Netty。
參考:來自Smart Java博客的JCG合作伙伴 Jos Dirksen 透明地使用Netty使用SPDY和HTTP 。

翻譯自: https://www.javacodegeeks.com/2012/05/netty-using-spdy-and-http-transparently.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/373152.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373152.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373152.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

selenium 等待頁面加載完成

一、隱形加載等待&#xff1a; file:///C:/Users/leixiaoj/Desktop/test.html 該頁面負責創建一個div <html> <head><title>Set Timeout</title><style>.red_box {background-color: red; width 20%; height:100px; border: none;}</style&…

linux nfsnobody用戶,處理CentOS 5.5 x64 配置NFS服務過程中nfsnobody用戶造成的問題

4、我們編譯一下這個NFS的配置文件。[rootNFS /]# vi /etc/exports/share 192.168.60.0/24(rw,sync,all_squash,root_squash) (我們允許這個共享對192.168.60.0/24網段可讀可寫&#xff0c;且將所有訪問者包括root的身份都改為nfsnobody)[rootNFS /]# /etc/init.d/nfs resta…

計算機語言

軟件&#xff1a;是一系列按照特定順序組織的計算機數據和指令的集合。一般來講軟件被劃分為系統軟件、應用軟件和介于這兩者之間的中間件。 系統軟件 系統軟件是各類操作系統&#xff0c;如windows、Linux、UNIX等&#xff0c;還包括操作系統的補丁程序及硬件驅動程序&#xf…

Apache Shiro第2部分–領域,數據庫和PGP證書

這是致力于Apache Shiro的系列文章的第二部分。 我們從簡單的不安全Web應用程序開始了上一部分 。 完成后&#xff0c;該應用程序具有基本的身份驗證和授權。 用戶可以登錄和注銷。 所有網頁和按鈕均已分配和實施訪問權限。 授權和身份驗證數據都已存儲在靜態配置文件中。 正如…

js中變量作用域的小理解

一&#xff1a;變量作用域 在js代碼中每個變量都是有自己的作用域的&#xff0c;js中不像C語言有塊級作用域的概念&#xff0c;取而代之的是函數作用域&#xff0c;看如下代碼&#xff1a; var scope"global"; function init(){ alert(scope);var scope "local…

安卓linux開機畫面,Android系統的開機畫面顯示過程分析(1)

好幾個月都沒有更新過博客了&#xff0c;從今天開始&#xff0c;老羅將嘗試對Android系統的UI實現作一個系統的分析&#xff0c;也算是落實之前所作出的承諾。提到Android系統的UI&#xff0c;我們最先接觸到的便是系統在啟動過程中所出現的畫面了。Android系統在啟動的過程中&…

如果你的NavigationDrawer里面的Item沒有響應,Drawer不能左滑關閉

如果你的NavigationDrawer里面的Item沒有響應&#xff0c;Drawer不能左滑關閉&#xff0c;應該是因為你沒有把主要內容放在DrawerLayout標簽下的第一位。 The main content view (the FrameLayout above) must be the first child in the DrawerLayout because the XML order i…

JAXB和未映射的屬性

JAXB&#xff08;JSR-222&#xff09;是例外配置&#xff0c;這意味著存在默認映射應用于域對象。 這意味著有時您需要顯式排除字段/屬性。 在本文中&#xff0c;我將討論如何使用XmlTransient或XmlAccessorType&#xff08;XmlAccessType.NONE&#xff09;以及何時使用每個選項…

sublime text3 使用SVN插件

Simon在項目中經常使用SVN&#xff0c;每次都要切換提交&#xff0c;很麻煩&#xff0c;有了這個SVN插件就很方便了&#xff0c;使用快捷方式提交&#xff0c;更新。 安裝: Ctrl Shift P 調用出Sublime Text的包管理工具&#xff0c;輸入TortoiseSVN&#xff0c;回車進行安裝…

c語言空格有什么作用,空格在c語言中怎么表示 C語言中的空格字符怎么表示

c語言中表示空格的是什么代碼&#xff1f;分析如下&#xff1a; 不是所有字符都需要轉義的&#xff0c;空格直接就敲空格&#xff0c;或者使用ASCII碼值賦值為32。 空格沒有轉義字符。合法轉義字符如下&#xff1a;\a 響鈴(BEL) 、\b 退格(BS)、\f 換頁(FF)、\n 換行(LF)、\r 回…

二維數組實現八皇后問題

之前關八皇后的問題全部使用的是一維數組進行實現(http://www.cnblogs.com/SeaSky0606/p/4604955.html)&#xff0c;現改一種數據存儲方式&#xff0c;按照8x8的二維棋盤存儲皇后。基本邏輯不變&#xff0c;可參見如下代碼&#xff1a; #include<cstdio> #include<alg…

Java的深度:通過協方差暴露的API泄漏

Java有時可能非常棘手&#xff0c;特別是在API設計中。 讓我們看一個非常有趣的展示柜。 jOOQ強烈地將API與實現分開。 所有API都在org.jooq包中&#xff0c;并且是公共的。 大多數實現都在org.jooq.impl包和package-private中。 只有工廠和一些專用的基礎實現是公開的。 這允許…

StringMVC 中如何做數據校驗

步驟一&#xff1a;引入四個jar包 步驟二&#xff1a;注冊類型轉換器 <context:component-scan base-package"cn.happy.controller"></context:component-scan><!-- 配置驗證器 --><bean id"myvalidator" class"org.springframe…

ibm+x3650+m4+linux+raid驅動,IBM X3650M4陣列卡驅動下載

ibm X3650M4raid陣列卡驅動適合安裝windowsserver2008,windowsserver2008R2,系統問題&#xff0c;服務器問題&#xff0c;可以聯系我們也可以到5分享論壇發帖求助。IBM System x3650 M4服務器是一款應用最為廣泛的2U機架服務器&#xff0c;支持Xeon E5-2600機架服務器的所有產品…

為什么在Java 6上Math.round(0.499999999999999917)舍入為1

總覽 錯誤表示錯誤和算術舍入錯誤有兩種類型&#xff0c;它們在浮點計算中很常見。 在此簡單示例中&#xff0c;這兩個錯誤組合在一起&#xff0c;在Java 6中Math.round&#xff08;0.4999999999999999999917&#xff09;舍入為1。 表示錯誤 浮點數是以2為底的格式&#xff0c…

單利模式

class Singleton{ public:static Singleton* GetInstance(){if (m_pInstance nullptr){m_pInstance new Singleton;}return m_pInstance;} private:Singleton(){}//需要將構造和析構定義成私有的防止外界構造和析構~Singleton(){}static Singleton* m_pInstance;//static所有…

C語言switch中break的作用,C語言中switch...case語句中break的重要性

在C語言中switch...case語句是經常用到的&#xff0c;下面我介紹一下在使用該語句時候需要注意的一個細節問題。話不多說&#xff0c;直接舉例子&#xff1a;例子1&#xff1a;switch(fruit){case 1:printf("apple"); break;case 2:printf("banana"); brea…

BZOJ 1898: [Zjoi2005]Swamp 沼澤鱷魚 [矩陣乘法]

1898: [Zjoi2005]Swamp 沼澤鱷魚 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 1082 Solved: 602[Submit][Status][Discuss]Description 潘塔納爾沼澤地號稱世界上最大的一塊濕地&#xff0c;它地位于巴西中部馬托格羅索州的南部地區。每當雨季來臨&#xff0c;這里碧波蕩漾…

從Spring開始,Java EE 6必須具備哪些附加功能?

我是一名高級Java開發人員&#xff0c;必須研究應用程序架構師選擇的技術。 我最多只能表達對特定技術的看法&#xff0c;不能做出/影響技術選擇的決定。 因此&#xff0c;在我的正式項目中&#xff0c;我別無選擇從Spring遷移到JavaEE6或從JavaEE6遷移到Spring。 我堅信&#…

UML類圖與類的關系詳解

在畫類圖的時候&#xff0c;理清類和類之間的關系是重點。類的關系有泛化(Generalization)、實現&#xff08;Realization&#xff09;、依賴(Dependency)和關聯(Association)。其中關聯又分為一般關聯關系和聚合關系(Aggregation)&#xff0c;合成關系(Composition)。下面我們…