View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
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                         // The headers have already been generated, perhaps by a concurrent abort.
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 }