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