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 throw new IllegalStateException(result.toString());
120 }
121 }
122 }
123 }
124 catch (Throwable x)
125 {
126 if (LOG.isDebugEnabled())
127 LOG.debug(x);
128 callback.failed(x);
129 }
130 }
131
132 @Override
133 protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
134 {
135 try
136 {
137 HttpClient client = getHttpChannel().getHttpDestination().getHttpClient();
138 ByteBufferPool bufferPool = client.getByteBufferPool();
139 ByteBuffer chunk = null;
140 while (true)
141 {
142 ByteBuffer contentBuffer = content.getByteBuffer();
143 boolean lastContent = content.isLast();
144 HttpGenerator.Result result = generator.generateRequest(null, null, chunk, contentBuffer, lastContent);
145 switch (result)
146 {
147 case NEED_CHUNK:
148 {
149 chunk = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
150 break;
151 }
152 case FLUSH:
153 {
154 EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint();
155 if (chunk != null)
156 endPoint.write(new ByteBufferRecyclerCallback(callback, bufferPool, chunk), chunk, contentBuffer);
157 else
158 endPoint.write(callback, contentBuffer);
159 return;
160 }
161 case SHUTDOWN_OUT:
162 {
163 shutdownOutput();
164 break;
165 }
166 case CONTINUE:
167 {
168 break;
169 }
170 case DONE:
171 {
172 assert generator.isEnd();
173 callback.succeeded();
174 return;
175 }
176 default:
177 {
178 throw new IllegalStateException();
179 }
180 }
181 }
182 }
183 catch (Exception x)
184 {
185 if (LOG.isDebugEnabled())
186 LOG.debug(x);
187 callback.failed(x);
188 }
189 }
190
191 @Override
192 protected void reset()
193 {
194 generator.reset();
195 super.reset();
196 }
197
198 @Override
199 protected void dispose()
200 {
201 generator.abort();
202 super.dispose();
203 shutdownOutput();
204 }
205
206 private void shutdownOutput()
207 {
208 getHttpChannel().getHttpConnection().getEndPoint().shutdownOutput();
209 }
210
211 private class ByteBufferRecyclerCallback implements Callback
212 {
213 private final Callback callback;
214 private final ByteBufferPool pool;
215 private final ByteBuffer[] buffers;
216
217 private ByteBufferRecyclerCallback(Callback callback, ByteBufferPool pool, ByteBuffer... buffers)
218 {
219 this.callback = callback;
220 this.pool = pool;
221 this.buffers = buffers;
222 }
223
224 @Override
225 public void succeeded()
226 {
227 for (ByteBuffer buffer : buffers)
228 {
229 assert !buffer.hasRemaining();
230 pool.release(buffer);
231 }
232 callback.succeeded();
233 }
234
235 @Override
236 public void failed(Throwable x)
237 {
238 for (ByteBuffer buffer : buffers)
239 pool.release(buffer);
240 callback.failed(x);
241 }
242 }
243 }