ALPN

作者: YDDMAX_Y | 来源:发表于2018-05-29 23:15 被阅读0次

    ALPN介绍

    ALPN(Application Layer Protocol Negotiation)是TLS的扩展协议,用于应用层协议的协商。
    chrome浏览器和curl是支持ALPN的,在发起HTTPS请求时通过ALPN发起请求的协议为h2, http/1.1。为了支持HTTP2的升级和兼容ALPN的才诞生的。

    ALPN环境支持

    JDK9才原生在API和库上支持ALPN,JDK9以下可以通过其他途径支持,比如使用netty的库和jetty。jetty实现ALPN参见:http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn

    通过ALPN协商使用MQTT或者HTTP1.1

    下面方案只是低于JDK9实现ALPN的一种方式。通过jetty实现ALPN的方案参见
    http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn,通过netty也能支持ALPN,在此不再描述。

    1. 使低于JDK9的JVM支持ALPN
      CATALINA_OPTS="${CATALINA_OPTS} -Xbootclasspath/p:/opt/java/lib/alpn-boot-8.1.11.v20170118.jar"
      jar根据JVM版本的不同而不同
    2. 添加依赖
                        <dependency>
                    <groupId>org.eclipse.jetty.alpn</groupId>
                    <artifactId>alpn-api</artifactId>
                    <version>1.0.0</version>
                    <scope>provided</scope>
                </dependency>
                <dependency>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-alpn-openjdk8-server</artifactId>
                    <version>9.4.8.v20171121</version>
                </dependency>
    

    MySslHandler.java

    package alpn.server;
    
    import io.netty.handler.ssl.SslHandler;
    
    import javax.net.ssl.SSLEngine;
    
    /**
     * 一种方法是通过Netty的ApplicationProtocolNegotiationHandler获得ALPN协商之后的数据,但是使用jetty-alpn,并不能获得协商之后的协议(sslHandler.applicationProtocol返回的为空)
     * 所以使用该方式实现。
     */
    public class MySslHandler extends SslHandler {
        /**
         * 选择的协议
         */
        private volatile String selectedProtocol;
    
        public MySslHandler(SSLEngine sslEngine){
            super(sslEngine);
        }
    
        public String getSelectedProtocol() {
            return selectedProtocol;
        }
    
        public void setSelectedProtocol(String selectedProtocol) {
            this.selectedProtocol = selectedProtocol;
        }
    }
    
    

    ServerPipelineInitializer.java

    package alpn.server;
    
    import io.netty.channel.*;
    import io.netty.handler.codec.http.HttpObjectAggregator;
    import io.netty.handler.codec.http.HttpRequestDecoder;
    import io.netty.handler.codec.http.HttpResponseEncoder;
    import io.netty.handler.codec.mqtt.MqttDecoder;
    import io.netty.handler.codec.mqtt.MqttEncoder;
    import io.netty.handler.ssl.SslHandshakeCompletionEvent;
    import io.netty.handler.timeout.IdleStateHandler;
    import org.eclipse.jetty.alpn.ALPN;
    
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLEngine;
    import java.util.List;
    
    /**
     */
    public class ServerPipelineInitializer extends ChannelInitializer {
    
        private static final String HTTP_1_1="http/1.1";
    
        private static final String HTTP_2="h2";
    
        @Override
        protected void initChannel(Channel ch) throws Exception {
            SSLContext sslContext=null;//先设置sslContext
            ChannelPipeline pipeline=ch.pipeline();
            pipeline.addLast("sslHandler",getMySSlHandler(sslContext));
            //ALPN
            pipeline.addLast("alpnHandler" ,new ChannelDuplexHandler() {
                @Override
                public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                    if (evt instanceof SslHandshakeCompletionEvent) {
                        ctx.pipeline().remove(this);
                        //sslHandler握手结束会触发SslHandshakeCompletionEvent事件
                        SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
                        if (handshakeEvent.isSuccess()) {
                            MySslHandler mySslHandler = (MySslHandler)ctx.pipeline().get("sslHandler");
                            String protocol = mySslHandler.getSelectedProtocol();
                            //HTTP1.1
                            if (HTTP_1_1.equals(protocol)) {
                                pipeline.addLast(new HttpResponseEncoder());
                                // 客户端接收到的是httpResponse响应,所以要使用HttpResponseDecoder进行解码
                                pipeline.addLast(new HttpRequestDecoder());
                                pipeline.addLast(new HttpObjectAggregator(65536));
                                pipeline.addLast(new IdleStateHandler(5000, 5000, 0));
                                //业务处理器
                                pipeline.addLast(new HttpServiceHandler());
    
                            } else {
                                //MQTT的编码和解码器
                                pipeline.addLast(MqttEncoder.INSTANCE);
                                pipeline.addLast(new MqttDecoder());
                                //业务处理器
                            }
                        }
                    }else{
                        ctx.channel().close();
                    }
                }
            });
    
        }
        public MySslHandler getMySSlHandler(SSLContext sslContext){
            SSLEngine sslEngine = sslContext.createSSLEngine();
            sslEngine.setUseClientMode(false);
            sslEngine.setNeedClientAuth(false);
            final MySslHandler mySslHandler = new MySslHandler(sslEngine);
            //JDK9才原生支持ALPN,JDK7和JDK8通过依赖其他库实现。这里使用jetty实现
            
            ALPN.put(sslEngine, new ALPN.ServerProvider() {
    
                @Override
                public void unsupported() {
                    ALPN.remove(sslEngine);
                }
    
                @Override
                public String select(List<String> protocols) {
                    ALPN.remove(sslEngine);
                    if(protocols.size()==2&&protocols.contains(HTTP_1_1)&&protocols.contains(HTTP_2)){
                        mySslHandler.setSelectedProtocol(HTTP_1_1);
                        return HTTP_1_1;
                    }else if(protocols.size()==1&&protocols.contains(HTTP_1_1)){
                        mySslHandler.setSelectedProtocol(HTTP_1_1);
                        return HTTP_1_1;
                    }else{
                        throw new RuntimeException("alpn error");
                    }
                }
            });
            return mySslHandler;
        }
    }
    
    

    HttpServiceHandler.java

    package alpn.server;
    
    import io.netty.buffer.Unpooled;
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelDuplexHandler;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.handler.codec.http.*;
    import io.netty.handler.codec.http.multipart.Attribute;
    import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
    import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
    import io.netty.handler.codec.http.multipart.InterfaceHttpData;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     */
    public class HttpServiceHandler extends ChannelDuplexHandler {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            FullHttpRequest request = (FullHttpRequest) msg;
            String uri = request.getUri();
            if (!HttpMethod.POST.equals(request.getMethod())) {
                send(null, ctx.channel(), HttpResponseStatus.METHOD_NOT_ALLOWED);
                return;
            }
            HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
            Map<String, String> params = new HashMap<>();
            List<InterfaceHttpData> parmList = decoder.getBodyHttpDatas();
            for (InterfaceHttpData parm : parmList) {
                if (parm.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                    Attribute attribute = (Attribute) parm;
                    params.put(attribute.getName(), attribute.getValue());
                }
            }
            //设备初始化通道服务
            if ("/service/init.do".equals(uri)) {
                send("hello world", ctx.channel(), HttpResponseStatus.OK);
    
            } else {
                //404
                send(null, ctx.channel(), HttpResponseStatus.NOT_FOUND);
            }
            //释放buffer
            ctx.fireChannelRead(msg);
        }
    
        private void send(String content, Channel channel, HttpResponseStatus status) throws Exception {
            FullHttpResponse fullHttpResponse = null;
            if (content==null) {
                fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
            } else {
                fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(content.getBytes("utf-8")));
            }
            fullHttpResponse.headers().set(HttpHeaders.Names.CONTENT_TYPE, "application/json");
            fullHttpResponse.headers().set(HttpHeaders.Names.CONTENT_LENGTH, fullHttpResponse.content().readableBytes());
            fullHttpResponse.headers().set(HttpHeaders.Names.CONNECTION, false);
            channel.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
        }
    }
    
    

    相关文章

      网友评论

          本文标题:ALPN

          本文链接:https://www.haomeiwen.com/subject/kpxzjftx.html