package com.firefly.client.http2; import com.firefly.codec.http2.decode.Parser; import com.firefly.codec.http2.encode.Generator; import com.firefly.codec.http2.frame.HeadersFrame; import com.firefly.codec.http2.frame.PrefaceFrame; import com.firefly.codec.http2.frame.SettingsFrame; import com.firefly.codec.http2.frame.WindowUpdateFrame; import com.firefly.codec.http2.model.HttpHeader; import com.firefly.codec.http2.model.HttpHeaderValue; import com.firefly.codec.http2.model.MetaData; import com.firefly.codec.http2.model.MetaData.Request; import com.firefly.codec.http2.stream.*; import com.firefly.codec.http2.stream.Session.Listener; import com.firefly.net.Session; import com.firefly.net.tcp.ssl.SSLSession; import com.firefly.utils.concurrent.Callback; import com.firefly.utils.concurrent.FuturePromise; import com.firefly.utils.concurrent.Promise; import com.firefly.utils.exception.CommonRuntimeException; import com.firefly.utils.io.BufferUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; public class HTTP2ClientConnection extends AbstractHTTP2Connection implements HTTPClientConnection { private static Logger log = LoggerFactory.getLogger("firefly-system"); public void initialize(HTTP2Configuration config, final Promise<HTTPClientConnection> promise, final Listener listener) { Map<Integer, Integer> settings = listener.onPreface(getHttp2Session()); if (settings == null) { settings = Collections.emptyMap(); } PrefaceFrame prefaceFrame = new PrefaceFrame(); SettingsFrame settingsFrame = new SettingsFrame(settings, false); SessionSPI sessionSPI = getSessionSPI(); int windowDelta = config.getInitialSessionRecvWindow() - FlowControlStrategy.DEFAULT_WINDOW_SIZE; Callback callback = new Callback() { @Override public void succeeded() { promise.succeeded(HTTP2ClientConnection.this); } @Override public void failed(Throwable x) { try { HTTP2ClientConnection.this.close(); } catch (IOException e) { log.error("http2 client connection initialization error", e); } promise.failed(x); } }; if (windowDelta > 0) { sessionSPI.updateRecvWindow(windowDelta); sessionSPI.frames(null, callback, prefaceFrame, settingsFrame, new WindowUpdateFrame(0, windowDelta)); } else { sessionSPI.frames(null, callback, prefaceFrame, settingsFrame); } } public HTTP2ClientConnection(HTTP2Configuration config, Session tcpSession, SSLSession sslSession, Listener listener) { super(config, tcpSession, sslSession, listener); } @Override protected HTTP2Session initHTTP2Session(HTTP2Configuration config, FlowControlStrategy flowControl, Listener listener) { HTTP2ClientSession http2ClientSession = new HTTP2ClientSession(scheduler, this.tcpSession, this.generator, listener, flowControl, config.getStreamIdleTimeout()); return http2ClientSession; } @Override protected Parser initParser(HTTP2Configuration config) { return new Parser((HTTP2ClientSession) http2Session, config.getMaxDynamicTableSize(), config.getMaxRequestHeadLength()); } Parser getParser() { return parser; } Generator getGenerator() { return generator; } SSLSession getSSLSession() { return sslSession; } SessionSPI getSessionSPI() { return http2Session; } @Override public void send(Request request, ClientHTTPHandler handler) { Promise<HTTPOutputStream> promise = new Promise<HTTPOutputStream>() { @Override public void succeeded(HTTPOutputStream output) { try { output.close(); } catch (IOException e) { log.error("write data unsuccessfully", e); } } @Override public void failed(Throwable x) { log.error("write data unsuccessfully", x); } }; request(request, true, promise, handler); } @Override public void send(Request request, final ByteBuffer buffer, ClientHTTPHandler handler) { send(request, Collections.singleton(buffer), handler); } @Override public void send(Request request, final ByteBuffer[] buffers, ClientHTTPHandler handler) { send(request, Arrays.asList(buffers), handler); } @Override public void send(MetaData.Request request, Collection<ByteBuffer> buffers, ClientHTTPHandler handler) { long contentLength = BufferUtils.remaining(buffers); request.getFields().put(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength)); Promise<HTTPOutputStream> promise = new Promise<HTTPOutputStream>() { @Override public void succeeded(HTTPOutputStream output) { try { output.writeWithContentLength(buffers); } catch (IOException e) { log.error("write data unsuccessfully", e); } } @Override public void failed(Throwable x) { log.error("write data unsuccessfully", x); } }; send(request, promise, handler); } @Override public HTTPOutputStream sendRequestWithContinuation(MetaData.Request request, ClientHTTPHandler handler) { request.getFields().put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE); return getHTTPOutputStream(request, handler); } @Override public HTTPOutputStream getHTTPOutputStream(final Request request, final ClientHTTPHandler handler) { FuturePromise<HTTPOutputStream> promise = new FuturePromise<>(); send(request, promise, handler); try { return promise.get(); } catch (Throwable e) { log.error("get http output stream unsuccessfully", e); return null; } } @Override public void send(final Request request, final Promise<HTTPOutputStream> promise, final ClientHTTPHandler handler) { request(request, false, promise, handler); } public void request(final Request request, boolean endStream, final Promise<HTTPOutputStream> promise, final ClientHTTPHandler handler) { http2Session.newStream(new HeadersFrame(request, null, endStream), new HTTP2ClientResponseHandler.ClientStreamPromise(request, promise, endStream), new HTTP2ClientResponseHandler(request, handler, this)); } @Override public void upgradeHTTP2(Request request, SettingsFrame settings, Promise<HTTPClientConnection> promise, ClientHTTPHandler handler) { throw new CommonRuntimeException("current connection version is http2, it does not need to upgrading"); } }