基于netty的wsproxy 访问xenserver的vm console

当我们需要通过网页直接访问XenServer的vm console时,我们可以直接使用websocket建立和xenserver的连接,这种方式有个问题是xenserver的主机为了支持这种WebSocket的访问方式,会在Xenserver的主机上面起很多wsproxy,作为xenserver vm console和websocket的代理。若我们不希望Xenserver加大负担,我们希望实现自己的webproxy,然后访问Xenserver Vm console,下面就是这种方法的介绍。

思路很简单,两个步骤:

1、创建一个websocketproxy服务

2、基于这个服务访问Xenserver vm console

这个websocketproxy的实现基于netty(netty是一个高性能的异步事件IO框架),下面是关键代码:

收到消息时先进行判断,若是建立websocket的连接,则消息类型肯定是HttpRequest,因为websocket协议是基于Http协议的。简单来说就是将http协议进行升级,从而变为websocket协议。进而调用ensureTargetConnect函数,连接具体的VM Console。

在ensureTargetConnect函数中,我们先是建立和Xenserver的连接,当连接建立之后,再发送消息给Xenserver,这个消息采用类似HTTP格式的非标准HTTP协议,具体参见makeHeaders函数,若和具体的VM Console建立好连接,则Xenserver会返回一个HTTP 200的状态码,表示我们已经和VM Console 成功建立连接了,这时Xenserver会立马发送这个VM Console的协议版本号。

    @Override
    public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e)
            throws Exception {
        Object msg = e.getMessage();
        // An HttpRequest means either an initial websocket connection
        // or a web server request
        if (msg instanceof HttpRequest) {
            handleHttpRequest(ctx, (HttpRequest) msg, e);
        // A WebSocketFrame means a continuation of an established websocket connection
        } else if (msg instanceof WebSocketFrame) {
            handleWebSocketFrame(ctx, (WebSocketFrame) msg, e);
            // A channel buffer we treat as a VNC protocol request
        } else if (msg instanceof ChannelBuffer) {
            handleVncDirect(ctx, (ChannelBuffer) msg, e, null);
        }
    }

    private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req, final MessageEvent e) throws Exception {
        // Allow only GET methods.
        if (req.getMethod() != GET) {
        	Logger.getLogger(WebsockifyProxyHandler.class.getName()).info("Just support GET Method.");
            return;
        }

        String upgradeHeader = req.getHeader("Upgrade");
        if(upgradeHeader != null && upgradeHeader.toUpperCase().equals("WEBSOCKET")){
			Logger.getLogger(WebsockifyProxyHandler.class.getName()).fine("Websocket request from " + e.getRemoteAddress() + ".");
	        // Handshake
	        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
	                this.getWebSocketLocation(req), "base64", false);
	        this.handshaker = wsFactory.newHandshaker(req);
	        if (this.handshaker == null) {
	            wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel());
	        } else {
	        	// deal with a bug in the flash websocket emulation
	        	// it specifies WebSocket-Protocol when it seems it should specify Sec-WebSocket-Protocol
	        	String protocol = req.getHeader("WebSocket-Protocol");
	        	String secProtocol = req.getHeader("Sec-WebSocket-Protocol");
	        	if(protocol != null && secProtocol == null )
	        	{
	        		req.addHeader("Sec-WebSocket-Protocol", protocol);
	        	}
	            this.handshaker.handshake(ctx.getChannel(), req);
	        }
	    	ensureTargetConnection (e, true, null, req.getUri());
        }
    }

    private void ensureTargetConnection(ChannelEvent e, boolean websocket, final Object sendMsg, String para)
            throws Exception {
    	if(outboundChannel == null) {
    		String[] paras = para.split("&");
    		String[] uuids   = paras[0].split("=");
    		String[] authids = paras[1].split("=");

	        // Suspend incoming traffic until connected to the remote host.
	        final Channel inboundChannel = e.getChannel();
	        inboundChannel.setReadable(false);
			Logger.getLogger(WebsockifyProxyHandler.class.getName()).info("Inbound proxy connection from " + inboundChannel.getRemoteAddress() 
					+ " uuid=" + uuids[1] + " authid=" + authids[1] + ".");
	        
	        // resolve the target
			Console console = Websockify.ConsoleMap.get(uuids[1]);
			final String location = console.getLocation(Websockify.Conn);
	        InetSocketAddress target = new InetSocketAddress(new URL(location).getHost(), 80);
	        
	        // Start the connection attempt.
	        ClientBootstrap cb = new ClientBootstrap(cf);
	        cb.getPipeline().addLast("aggregator", new HttpChunkAggregator(65536));
	        if ( websocket ) {
	        	cb.getPipeline().addLast("handler", new OutboundWebsocketHandler(e.getChannel(), trafficLock));
	        }
	        else {
	        	cb.getPipeline().addLast("handler", new OutboundHandler(e.getChannel(), trafficLock));	        	
	        }

	        ChannelFuture f = cb.connect(target);
	        outboundChannel = f.getChannel();
	        
	        if ( sendMsg != null ) outboundChannel.write(sendMsg);
	        f.addListener(new ChannelFutureListener() {
	            public void operationComplete(ChannelFuture future) throws Exception {
	                if (future.isSuccess()) {
	                    // Connection attempt succeeded:
	                    // Begin to accept incoming traffic.
	                	
	                	connectVmConsole(location);
	                	
                    	inboundChannel.setReadable(true);
	    				Logger.getLogger(WebsockifyProxyHandler.class.getName()).info("Created outbound connection to " + location + ".");
	                    
	                } else {
	    				Logger.getLogger(WebsockifyProxyHandler.class.getName()).severe("Failed to create outbound connection to " + location + ".");
	                    inboundChannel.close();
	                }
	            }
	        });   
    	} else {
	        if ( sendMsg != null ) outboundChannel.write(sendMsg);
    	}
    }

    private void connectVmConsole(String location) throws BadServerResponse, XenAPIException, XmlRpcException, MalformedURLException {
		URL uri = new URL(location);
		
		String headers[] = makeHeaders(uri.getPath().concat("?").concat(uri.getQuery()), uri.getHost(), Websockify.Conn.getSessionReference());
    	for (String header : headers) {
    		ChannelBuffer msgdata = new BigEndianHeapChannelBuffer(header.getBytes());
        	outboundChannel.write(msgdata);
        	outboundChannel.write(new BigEndianHeapChannelBuffer("\r\n".getBytes())); 
        } 
    }

    private String[] makeHeaders(String path, String host, String session) {
    	
        String[] headers = { String.format("CONNECT %s HTTP/1.1", path),
                String.format("Host: %s", host),
                String.format("Cookie: session_id=%s", session), "" };
        return headers;
    }


参考资料:

http://docs.vmd.citrix.com/XenServer/5.6.0fp1/1.0/en_gb/sdk.html#retrieving_vnc_consoles_with_api

http://blogs.citrix.com/2011/02/11/xenserverconsole-examples/

转自:http://blog.csdn.net/pingnanlee/article/details/13627573

你可能感兴趣的:(云计算,java网络编程,Netty相关)