1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.client.http;
20
21 import java.nio.ByteBuffer;
22
23 import org.eclipse.jetty.client.HttpClient;
24 import org.eclipse.jetty.client.HttpContent;
25 import org.eclipse.jetty.client.HttpExchange;
26 import org.eclipse.jetty.client.HttpRequestException;
27 import org.eclipse.jetty.client.HttpSender;
28 import org.eclipse.jetty.client.api.ContentProvider;
29 import org.eclipse.jetty.client.api.Request;
30 import org.eclipse.jetty.http.HttpGenerator;
31 import org.eclipse.jetty.http.HttpURI;
32 import org.eclipse.jetty.http.MetaData;
33 import org.eclipse.jetty.io.ByteBufferPool;
34 import org.eclipse.jetty.io.EndPoint;
35 import org.eclipse.jetty.util.Callback;
36
37 public class HttpSenderOverHTTP extends HttpSender
38 {
39 private final HttpGenerator generator = new HttpGenerator();
40
41 public HttpSenderOverHTTP(HttpChannelOverHTTP channel)
42 {
43 super(channel);
44 }
45
46 @Override
47 public HttpChannelOverHTTP getHttpChannel()
48 {
49 return (HttpChannelOverHTTP)super.getHttpChannel();
50 }
51
52 @Override
53 protected void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback)
54 {
55 Request request = exchange.getRequest();
56 ContentProvider requestContent = request.getContent();
57 long contentLength = requestContent == null ? -1 : requestContent.getLength();
58 String path = request.getPath();
59 String query = request.getQuery();
60 if (query != null)
61 path += "?" + query;
62 MetaData.Request requestInfo = new MetaData.Request(request.getMethod(), new HttpURI(path), request.getVersion(), request.getHeaders(), contentLength);
63
64 try
65 {
66 HttpClient client = getHttpChannel().getHttpDestination().getHttpClient();
67 ByteBufferPool bufferPool = client.getByteBufferPool();
68 ByteBuffer header = bufferPool.acquire(client.getRequestBufferSize(), false);
69 ByteBuffer chunk = null;
70
71 ByteBuffer contentBuffer = null;
72 boolean lastContent = false;
73 if (!expects100Continue(request))
74 {
75 content.advance();
76 contentBuffer = content.getByteBuffer();
77 lastContent = content.isLast();
78 }
79 while (true)
80 {
81 HttpGenerator.Result result = generator.generateRequest(requestInfo, header, chunk, contentBuffer, lastContent);
82 switch (result)
83 {
84 case NEED_CHUNK:
85 {
86 chunk = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
87 break;
88 }
89 case FLUSH:
90 {
91 int size = 1;
92 boolean hasChunk = chunk != null;
93 if (hasChunk)
94 ++size;
95 boolean hasContent = contentBuffer != null;
96 if (hasContent)
97 ++size;
98 ByteBuffer[] toWrite = new ByteBuffer[size];
99 ByteBuffer[] toRecycle = new ByteBuffer[hasChunk ? 2 : 1];
100 toWrite[0] = header;
101 toRecycle[0] = header;
102 if (hasChunk)
103 {
104 toWrite[1] = chunk;
105 toRecycle[1] = chunk;
106 }
107 if (hasContent)
108 toWrite[toWrite.length - 1] = contentBuffer;
109 EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint();
110 endPoint.write(new ByteBufferRecyclerCallback(callback, bufferPool, toRecycle), toWrite);
111 return;
112 }
113 case DONE:
114 {
115
116 callback.failed(new HttpRequestException("Could not generate headers", request));
117 return;
118 }
119 default:
120 {
121 callback.failed(new IllegalStateException(result.toString()));
122 return;
123 }
124 }
125 }
126 }
127 catch (Throwable x)
128 {
129 if (LOG.isDebugEnabled())
130 LOG.debug(x);
131 callback.failed(x);
132 }
133 }
134
135 @Override
136 protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
137 {
138 try
139 {
140 HttpClient client = getHttpChannel().getHttpDestination().getHttpClient();
141 ByteBufferPool bufferPool = client.getByteBufferPool();
142 ByteBuffer chunk = null;
143 while (true)
144 {
145 ByteBuffer contentBuffer = content.getByteBuffer();
146 boolean lastContent = content.isLast();
147 HttpGenerator.Result result = generator.generateRequest(null, null, chunk, contentBuffer, lastContent);
148 switch (result)
149 {
150 case NEED_CHUNK:
151 {
152 chunk = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
153 break;
154 }
155 case FLUSH:
156 {
157 EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint();
158 if (chunk != null)
159 endPoint.write(new ByteBufferRecyclerCallback(callback, bufferPool, chunk), chunk, contentBuffer);
160 else
161 endPoint.write(callback, contentBuffer);
162 return;
163 }
164 case SHUTDOWN_OUT:
165 {
166 shutdownOutput();
167 break;
168 }
169 case CONTINUE:
170 {
171 break;
172 }
173 case DONE:
174 {
175 assert generator.isEnd();
176 callback.succeeded();
177 return;
178 }
179 default:
180 {
181 throw new IllegalStateException();
182 }
183 }
184 }
185 }
186 catch (Throwable x)
187 {
188 if (LOG.isDebugEnabled())
189 LOG.debug(x);
190 callback.failed(x);
191 }
192 }
193
194 @Override
195 protected void reset()
196 {
197 generator.reset();
198 super.reset();
199 }
200
201 @Override
202 protected void dispose()
203 {
204 generator.abort();
205 super.dispose();
206 shutdownOutput();
207 }
208
209 private void shutdownOutput()
210 {
211 getHttpChannel().getHttpConnection().getEndPoint().shutdownOutput();
212 }
213
214 @Override
215 public String toString()
216 {
217 return String.format("%s[%s]", super.toString(), generator);
218 }
219
220 private class ByteBufferRecyclerCallback implements Callback
221 {
222 private final Callback callback;
223 private final ByteBufferPool pool;
224 private final ByteBuffer[] buffers;
225
226 private ByteBufferRecyclerCallback(Callback callback, ByteBufferPool pool, ByteBuffer... buffers)
227 {
228 this.callback = callback;
229 this.pool = pool;
230 this.buffers = buffers;
231 }
232
233 @Override
234 public boolean isNonBlocking()
235 {
236 return callback.isNonBlocking();
237 }
238
239 @Override
240 public void succeeded()
241 {
242 for (ByteBuffer buffer : buffers)
243 {
244 assert !buffer.hasRemaining();
245 pool.release(buffer);
246 }
247 callback.succeeded();
248 }
249
250 @Override
251 public void failed(Throwable x)
252 {
253 for (ByteBuffer buffer : buffers)
254 pool.release(buffer);
255 callback.failed(x);
256 }
257 }
258 }