HttpTunnelServer,從源碼看Springboot如何實現雙向數據交互

Tunnel是什麼?

Tunnel存在的意義,就是使用http協議來傳輸非http協議的內容,在使用fiddler4抓包的時候,經常會看見Tunnel這樣的包,打開以後會發現裏面沒有標準http協議的模式,沒有http頭,包體也和http協議包體不相同。java

Tunnel能夠用來作服務器和客戶端進行雙向交流,這就解決了http1.1中沒法實現服務器主動給客戶端發送信息的問題。web

Springboot中如何實現Tunnel?

Springboot中提供了HttpTunnelServer來提供Tunnel這樣的功能,咱們能夠先來看一看它的源碼服務器

public HttpTunnelServer(TargetServerConnection serverConnection) {
    Assert.notNull(serverConnection, "ServerConnection must not be null");
    this.serverConnection = serverConnection;
}

從構造方法中咱們能夠看出,傳入了一個TargetServerConnection實例,而TargetServerConnection是一個函數式接口。app

@FunctionalInterface
public interface TargetServerConnection {
	ByteChannel open(int timeout) throws IOException;
}

在源碼中,有一個該接口的實現類SocketTargetServerConnectionsocket

public class SocketTargetServerConnection implements TargetServerConnection {
    public SocketTargetServerConnection(PortProvider portProvider) {
		Assert.notNull(portProvider, "PortProvider must not be null");
		this.portProvider = portProvider;
	}

	@Override
	public ByteChannel open(int socketTimeout) throws IOException {
		SocketAddress address = new InetSocketAddress(this.portProvider.getPort());
		logger.trace(LogMessage.format("Opening tunnel connection to target server on %s", address));
		SocketChannel channel = SocketChannel.open(address);
		channel.socket().setSoTimeout(socketTimeout);
		return new TimeoutAwareChannel(channel);
	}
    ... ...
}

// SocketTargetServerConnection的構造函數傳入的是函數式接口PortProvider,
// 是一個獲取port的函數式接口
@FunctionalInterface
public interface PortProvider {
	/**
	 * Return the port number.
	 * @return the port number
	 */
	int getPort();
}

很明顯,這個方法是提供的該服務於其餘服務進行Tunnel交互的,SocketTargetServerConnection的open函數是打開一個端口與遠程端口進行交互。在Springboot本身的測試代碼中ide

@Bean
DispatcherFilter filter(AnnotationConfigServletWebServerApplicationContext context) {
    TargetServerConnection connection = new SocketTargetServerConnection(
        () -> context.getWebServer().getPort());
    HttpTunnelServer server = new HttpTunnelServer(connection); // 在webServer的對等端口,定義了一個HttpTunnelServer
    HandlerMapper mapper = new UrlHandlerMapper("/httptunnel", new HttpTunnelServerHandler(server)); // 建立一個處理器
    Collection<HandlerMapper> mappers = Collections.singleton(mapper); // 建立單例
    Dispatcher dispatcher = new Dispatcher(AccessManager.PERMIT_ALL, mappers); // 建立處理任務分發
    return new DispatcherFilter(dispatcher); // 監聽並分發處理任務
}
// HttpTunnelServerHandler.java
public class HttpTunnelServerHandler implements Handler {

	private HttpTunnelServer server;

	public HttpTunnelServerHandler(HttpTunnelServer server) {
		Assert.notNull(server, "Server must not be null");
		this.server = server;
	}
    
	@Override
	public void handle(ServerHttpRequest request, ServerHttpResponse response) throws IOException {
		this.server.handle(request, response);
	}
}

// 其中Handler也是一個函數式接口
@FunctionalInterface
public interface Handler {
	/**
	 * Handle the request.
	 * @param request the request
	 * @param response the response
	 * @throws IOException in case of I/O errors
	 */
	void handle(ServerHttpRequest request, ServerHttpResponse response) throws IOException;
}

也就是說,在須要與遠程進行Tunnel隧道傳輸的時候,HttpTunnelServer經過將數據包封裝進Servlet中的方法,首先與遠端服務器創建connet鏈接,鏈接創建成功之後,將數據包再經過Http的方法傳出去。函數

有了HttpTunnelServer,咱們就能夠和遠端集成服務器雙向交流測試

炒雞辣雞原創文章,轉載請註明來源this

相關文章
相關標籤/搜索