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