1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.http2.client;
20
21 import java.io.IOException;
22 import java.util.Collections;
23 import java.util.Map;
24 import java.util.concurrent.Executor;
25
26 import org.eclipse.jetty.http2.FlowControlStrategy;
27 import org.eclipse.jetty.http2.HTTP2Connection;
28 import org.eclipse.jetty.http2.ISession;
29 import org.eclipse.jetty.http2.api.Session;
30 import org.eclipse.jetty.http2.frames.PrefaceFrame;
31 import org.eclipse.jetty.http2.frames.SettingsFrame;
32 import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
33 import org.eclipse.jetty.http2.generator.Generator;
34 import org.eclipse.jetty.http2.parser.Parser;
35 import org.eclipse.jetty.io.ByteBufferPool;
36 import org.eclipse.jetty.io.ClientConnectionFactory;
37 import org.eclipse.jetty.io.Connection;
38 import org.eclipse.jetty.io.EndPoint;
39 import org.eclipse.jetty.util.Callback;
40 import org.eclipse.jetty.util.Promise;
41 import org.eclipse.jetty.util.component.LifeCycle;
42 import org.eclipse.jetty.util.thread.Scheduler;
43
44 public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
45 {
46 public static final String CLIENT_CONTEXT_KEY = "http2.client";
47 public static final String BYTE_BUFFER_POOL_CONTEXT_KEY = "http2.client.byteBufferPool";
48 public static final String EXECUTOR_CONTEXT_KEY = "http2.client.executor";
49 public static final String SCHEDULER_CONTEXT_KEY = "http2.client.scheduler";
50 public static final String SESSION_LISTENER_CONTEXT_KEY = "http2.client.sessionListener";
51 public static final String SESSION_PROMISE_CONTEXT_KEY = "http2.client.sessionPromise";
52
53 private final Connection.Listener connectionListener = new ConnectionListener();
54 private int initialSessionRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
55
56 @Override
57 public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
58 {
59 HTTP2Client client = (HTTP2Client)context.get(CLIENT_CONTEXT_KEY);
60 ByteBufferPool byteBufferPool = (ByteBufferPool)context.get(BYTE_BUFFER_POOL_CONTEXT_KEY);
61 Executor executor = (Executor)context.get(EXECUTOR_CONTEXT_KEY);
62 Scheduler scheduler = (Scheduler)context.get(SCHEDULER_CONTEXT_KEY);
63 Session.Listener listener = (Session.Listener)context.get(SESSION_LISTENER_CONTEXT_KEY);
64 @SuppressWarnings("unchecked")
65 Promise<Session> promise = (Promise<Session>)context.get(SESSION_PROMISE_CONTEXT_KEY);
66
67 Generator generator = new Generator(byteBufferPool);
68 FlowControlStrategy flowControl = newFlowControlStrategy();
69 if (flowControl == null)
70 flowControl = client.getFlowControlStrategyFactory().newFlowControlStrategy();
71 HTTP2ClientSession session = new HTTP2ClientSession(scheduler, endPoint, generator, listener, flowControl);
72 Parser parser = new Parser(byteBufferPool, session, 4096, 8192);
73 HTTP2ClientConnection connection = new HTTP2ClientConnection(client, byteBufferPool, executor, endPoint, parser, session, client.getInputBufferSize(), promise, listener);
74 connection.addListener(connectionListener);
75 return connection;
76 }
77
78
79
80
81 @Deprecated
82 protected FlowControlStrategy newFlowControlStrategy()
83 {
84 return null;
85 }
86
87
88
89
90 @Deprecated
91 public int getInitialSessionRecvWindow()
92 {
93 return initialSessionRecvWindow;
94 }
95
96
97
98
99 @Deprecated
100 public void setInitialSessionRecvWindow(int initialSessionRecvWindow)
101 {
102 this.initialSessionRecvWindow = initialSessionRecvWindow;
103 }
104
105 private class HTTP2ClientConnection extends HTTP2Connection implements Callback
106 {
107 private final HTTP2Client client;
108 private final Promise<Session> promise;
109 private final Session.Listener listener;
110
111 public HTTP2ClientConnection(HTTP2Client client, ByteBufferPool byteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise<Session> promise, Session.Listener listener)
112 {
113 super(byteBufferPool, executor, endpoint, parser, session, bufferSize);
114 this.client = client;
115 this.promise = promise;
116 this.listener = listener;
117 }
118
119 @Override
120 public void onOpen()
121 {
122 Map<Integer, Integer> settings = listener.onPreface(getSession());
123 if (settings == null)
124 settings = Collections.emptyMap();
125
126 PrefaceFrame prefaceFrame = new PrefaceFrame();
127 SettingsFrame settingsFrame = new SettingsFrame(settings, false);
128
129 ISession session = getSession();
130
131 int sessionRecv = client.getInitialSessionRecvWindow();
132 if (sessionRecv == FlowControlStrategy.DEFAULT_WINDOW_SIZE)
133 sessionRecv = initialSessionRecvWindow;
134
135 int windowDelta = sessionRecv - FlowControlStrategy.DEFAULT_WINDOW_SIZE;
136 if (windowDelta > 0)
137 {
138 session.updateRecvWindow(windowDelta);
139 session.frames(null, this, prefaceFrame, settingsFrame, new WindowUpdateFrame(0, windowDelta));
140 }
141 else
142 {
143 session.frames(null, this, prefaceFrame, settingsFrame);
144 }
145
146
147
148 super.onOpen();
149 }
150
151 @Override
152 public void succeeded()
153 {
154 promise.succeeded(getSession());
155 }
156
157 @Override
158 public void failed(Throwable x)
159 {
160 close();
161 promise.failed(x);
162 }
163 }
164
165 private class ConnectionListener implements Connection.Listener
166 {
167 @Override
168 public void onOpened(Connection connection)
169 {
170 HTTP2ClientConnection http2Connection = (HTTP2ClientConnection)connection;
171 http2Connection.client.addManaged((LifeCycle)http2Connection.getSession());
172 }
173
174 @Override
175 public void onClosed(Connection connection)
176 {
177 HTTP2ClientConnection http2Connection = (HTTP2ClientConnection)connection;
178 http2Connection.client.removeBean(http2Connection.getSession());
179 }
180 }
181 }