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