1 module hunt.http.client.Http2ClientHandler;
2 
3 import hunt.http.client.Http1ClientConnection;
4 import hunt.http.client.Http2ClientContext;
5 import hunt.http.client.Http2ClientConnection;
6 
7 import hunt.http.codec.http.model.HttpVersion;
8 import hunt.http.codec.http.stream.AbstractHttpHandler;
9 import hunt.http.codec.http.stream.Http2Configuration;
10 import hunt.net.secure.SecureSession;
11 import hunt.net.secure.SecureSessionFactory;
12 import hunt.net.Session;
13 
14 import hunt.util.concurrent.Promise;
15 import hunt.lang.exception;
16 import hunt.string;
17 
18 import hunt.container.Map;
19 
20 import hunt.logging;
21 import std.array;
22 
23 class Http2ClientHandler : AbstractHttpHandler {
24 
25     private Map!(int, Http2ClientContext) http2ClientContext;
26 
27     this(Http2Configuration config, Map!(int, Http2ClientContext) http2ClientContext) {
28         super(config);
29         this.http2ClientContext = http2ClientContext;
30     }
31 
32     override
33     void sessionOpened(Session session) {
34         Http2ClientContext context = http2ClientContext.get(session.getSessionId());
35 
36         if (context is null) {
37             errorf("http2 client can not get the client context of session %s", session.getSessionId());
38             session.closeNow();
39             return;
40         }
41 
42         if (config.isSecureConnectionEnabled()) {
43             SecureSessionFactory factory = config.getSecureSessionFactory();
44             SecureSession secureSession = factory.create(session, true, delegate void (SecureSession sslSession) {
45 
46                 string protocol = "http/1.1";
47                 string p = sslSession.getApplicationProtocol();
48                 if(p.empty)
49                     warningf("The selected application protocol is empty. now use default: %s", protocol);
50                 else
51                     protocol = p;
52 
53                 infof("Client session %s SSL handshake finished. The app protocol is %s", session.getSessionId(), protocol);
54                 switch (protocol) {
55                     case "http/1.1":
56                         initializeHttp1ClientConnection(session, context, sslSession);
57                         break;
58                     case "h2":
59                         initializeHttp2ClientConnection(session, context, sslSession);
60                         break;
61                     default:
62                         throw new IllegalStateException("SSL application protocol negotiates failure. The protocol " ~ protocol ~ " is not supported");
63                 }
64             });
65 
66             session.attachObject(cast(Object)secureSession);
67         } else {
68             if (config.getProtocol().empty) {
69                 initializeHttp1ClientConnection(session, context, null);
70             } else {
71                 HttpVersion httpVersion = HttpVersion.fromString(config.getProtocol());
72                 if (httpVersion == HttpVersion.Null) {
73                     throw new IllegalArgumentException("the protocol " ~ config.getProtocol() ~ " is not support.");
74                 }
75                 if(httpVersion == HttpVersion.HTTP_1_1) {
76                         initializeHttp1ClientConnection(session, context, null);
77                 } else if(httpVersion == HttpVersion.HTTP_2) {
78                         initializeHttp2ClientConnection(session, context, null);
79                 } else {
80                         throw new IllegalArgumentException("the protocol " ~ config.getProtocol() ~ " is not support.");
81                 }
82             }
83 
84         }
85     }
86 
87     private void initializeHttp1ClientConnection(Session session, Http2ClientContext context,
88                                                  SecureSession sslSession) {
89         try {
90             Http1ClientConnection http1ClientConnection = new Http1ClientConnection(config, session, sslSession);
91             session.attachObject(http1ClientConnection);
92             // context.getPromise().succeeded(http1ClientConnection);
93             import hunt.http.client.HttpClientConnection;
94             Promise!(HttpClientConnection) promise  = context.getPromise();
95             infof("Promise id = %s", promise.id);
96             promise.succeeded(http1ClientConnection);
97 
98         } catch (Exception t) {
99             context.getPromise().failed(t);
100         } finally {
101             http2ClientContext.remove(session.getSessionId());
102         }
103     }
104 
105     private void initializeHttp2ClientConnection(Session session, Http2ClientContext context,
106                                                  SecureSession sslSession) {
107         try {
108             Http2ClientConnection connection = new Http2ClientConnection(config, session, sslSession, context.getListener());
109             session.attachObject(connection);
110             context.getListener().setConnection(connection);            
111             // connection.initialize(config, cast(Promise!(Http2ClientConnection))context.getPromise(), context.getListener());
112             connection.initialize(config, context.getPromise(), context.getListener());
113         } finally {
114             http2ClientContext.remove(session.getSessionId());
115         }
116     }
117 
118     override
119     void sessionClosed(Session session) {
120         try {
121             super.sessionClosed(session);
122         } finally {
123             http2ClientContext.remove(session.getSessionId());
124         }
125     }
126 
127     override
128     void failedOpeningSession(int sessionId, Exception t) {
129 
130         auto c = http2ClientContext.remove(sessionId);
131         if(c !is null)
132         {
133             auto promise = c.getPromise();
134             if(promise !is null)
135                 promise.failed(t);
136         }
137         
138         // Optional.ofNullable(http2ClientContext.remove(sessionId))
139         //         .map(Http2ClientContext::getPromise)
140         //         .ifPresent(promise => promise.failed(t));
141     }
142 
143     override
144     void exceptionCaught(Session session, Exception t) {
145         try {
146             super.exceptionCaught(session, t);
147         } finally {
148             http2ClientContext.remove(session.getSessionId());
149         }
150     }
151 
152 }