View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.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 }