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