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