View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 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.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                         // The headers have already been generated, perhaps by a concurrent abort.
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 }