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.http;
20
21 import java.io.IOException;
22 import java.nio.ByteBuffer;
23 import java.util.ArrayDeque;
24 import java.util.Locale;
25 import java.util.Queue;
26
27 import org.eclipse.jetty.client.HttpChannel;
28 import org.eclipse.jetty.client.HttpExchange;
29 import org.eclipse.jetty.client.HttpReceiver;
30 import org.eclipse.jetty.client.HttpResponse;
31 import org.eclipse.jetty.http.HttpField;
32 import org.eclipse.jetty.http.HttpFields;
33 import org.eclipse.jetty.http.HttpStatus;
34 import org.eclipse.jetty.http.MetaData;
35 import org.eclipse.jetty.http2.ErrorCode;
36 import org.eclipse.jetty.http2.api.Stream;
37 import org.eclipse.jetty.http2.frames.DataFrame;
38 import org.eclipse.jetty.http2.frames.HeadersFrame;
39 import org.eclipse.jetty.http2.frames.PushPromiseFrame;
40 import org.eclipse.jetty.http2.frames.ResetFrame;
41 import org.eclipse.jetty.io.ByteBufferPool;
42 import org.eclipse.jetty.util.BufferUtil;
43 import org.eclipse.jetty.util.Callback;
44 import org.eclipse.jetty.util.IteratingCallback;
45
46 public class HttpReceiverOverHTTP2 extends HttpReceiver implements Stream.Listener
47 {
48 private final ContentNotifier contentNotifier = new ContentNotifier();
49
50 public HttpReceiverOverHTTP2(HttpChannel channel)
51 {
52 super(channel);
53 }
54
55 @Override
56 protected HttpChannelOverHTTP2 getHttpChannel()
57 {
58 return (HttpChannelOverHTTP2)super.getHttpChannel();
59 }
60
61 @Override
62 public void onHeaders(Stream stream, HeadersFrame frame)
63 {
64 HttpExchange exchange = getHttpExchange();
65 if (exchange == null)
66 return;
67
68 HttpResponse response = exchange.getResponse();
69 MetaData.Response metaData = (MetaData.Response)frame.getMetaData();
70 response.version(metaData.getVersion()).status(metaData.getStatus()).reason(metaData.getReason());
71
72 if (responseBegin(exchange))
73 {
74 HttpFields headers = metaData.getFields();
75 for (HttpField header : headers)
76 {
77 if (!responseHeader(exchange, header))
78 return;
79 }
80
81 if (responseHeaders(exchange))
82 {
83 int status = metaData.getStatus();
84 boolean informational = HttpStatus.isInformational(status) && status != HttpStatus.SWITCHING_PROTOCOLS_101;
85 if (frame.isEndStream() || informational)
86 responseSuccess(exchange);
87 }
88 }
89 }
90
91 @Override
92 public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
93 {
94
95 stream.reset(new ResetFrame(stream.getId(), ErrorCode.REFUSED_STREAM_ERROR.code), Callback.NOOP);
96 return null;
97 }
98
99 @Override
100 public void onData(Stream stream, DataFrame frame, Callback callback)
101 {
102 HttpExchange exchange = getHttpExchange();
103 if (exchange == null)
104 {
105 callback.failed(new IOException("terminated"));
106 return;
107 }
108
109
110
111
112
113 ByteBufferPool byteBufferPool = getHttpDestination().getHttpClient().getByteBufferPool();
114 ByteBuffer original = frame.getData();
115 int length = original.remaining();
116 final ByteBuffer copy = byteBufferPool.acquire(length, original.isDirect());
117 BufferUtil.clearToFill(copy);
118 copy.put(original);
119 BufferUtil.flipToFlush(copy, 0);
120
121 contentNotifier.offer(new DataInfo(exchange, copy, callback, frame.isEndStream()));
122 contentNotifier.iterate();
123 }
124
125 @Override
126 public void onReset(Stream stream, ResetFrame frame)
127 {
128 HttpExchange exchange = getHttpExchange();
129 if (exchange == null)
130 return;
131
132 ErrorCode error = ErrorCode.from(frame.getError());
133 String reason = error == null ? "reset" : error.name().toLowerCase(Locale.ENGLISH);
134 exchange.getRequest().abort(new IOException(reason));
135 }
136
137 @Override
138 public boolean onIdleTimeout(Stream stream, Throwable x)
139 {
140 responseFailure(x);
141 return true;
142 }
143
144 private class ContentNotifier extends IteratingCallback
145 {
146 private final Queue<DataInfo> queue = new ArrayDeque<>();
147 private DataInfo dataInfo;
148
149 private boolean offer(DataInfo dataInfo)
150 {
151 synchronized (this)
152 {
153 return queue.offer(dataInfo);
154 }
155 }
156
157 @Override
158 protected Action process() throws Exception
159 {
160 DataInfo dataInfo;
161 synchronized (this)
162 {
163 dataInfo = queue.poll();
164 }
165
166 if (dataInfo == null)
167 {
168 DataInfo prevDataInfo = this.dataInfo;
169 if (prevDataInfo != null && prevDataInfo.last)
170 return Action.SUCCEEDED;
171 return Action.IDLE;
172 }
173
174 this.dataInfo = dataInfo;
175 responseContent(dataInfo.exchange, dataInfo.buffer, this);
176 return Action.SCHEDULED;
177 }
178
179 @Override
180 public void succeeded()
181 {
182 ByteBufferPool byteBufferPool = getHttpDestination().getHttpClient().getByteBufferPool();
183 byteBufferPool.release(dataInfo.buffer);
184 dataInfo.callback.succeeded();
185 super.succeeded();
186 }
187
188 @Override
189 protected void onCompleteSuccess()
190 {
191 responseSuccess(dataInfo.exchange);
192 }
193
194 @Override
195 protected void onCompleteFailure(Throwable failure)
196 {
197 ByteBufferPool byteBufferPool = getHttpDestination().getHttpClient().getByteBufferPool();
198 byteBufferPool.release(dataInfo.buffer);
199 dataInfo.callback.failed(failure);
200 responseFailure(failure);
201 }
202 }
203
204 private static class DataInfo
205 {
206 private final HttpExchange exchange;
207 private final ByteBuffer buffer;
208 private final Callback callback;
209 private final boolean last;
210
211 private DataInfo(HttpExchange exchange, ByteBuffer buffer, Callback callback, boolean last)
212 {
213 this.exchange = exchange;
214 this.buffer = buffer;
215 this.callback = callback;
216 this.last = last;
217 }
218 }
219 }