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.io.EOFException;
22  import java.nio.ByteBuffer;
23  
24  import org.eclipse.jetty.client.HttpClient;
25  import org.eclipse.jetty.client.HttpExchange;
26  import org.eclipse.jetty.client.HttpReceiver;
27  import org.eclipse.jetty.client.HttpResponse;
28  import org.eclipse.jetty.client.HttpResponseException;
29  import org.eclipse.jetty.http.HttpField;
30  import org.eclipse.jetty.http.HttpMethod;
31  import org.eclipse.jetty.http.HttpParser;
32  import org.eclipse.jetty.http.HttpVersion;
33  import org.eclipse.jetty.io.ByteBufferPool;
34  import org.eclipse.jetty.io.EndPoint;
35  import org.eclipse.jetty.io.EofException;
36  import org.eclipse.jetty.util.BufferUtil;
37  
38  public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
39  {
40      private final HttpParser parser = new HttpParser(this);
41  
42      public HttpReceiverOverHTTP(HttpChannelOverHTTP channel)
43      {
44          super(channel);
45      }
46  
47      @Override
48      public HttpChannelOverHTTP getHttpChannel()
49      {
50          return (HttpChannelOverHTTP)super.getHttpChannel();
51      }
52  
53      private HttpConnectionOverHTTP getHttpConnection()
54      {
55          return getHttpChannel().getHttpConnection();
56      }
57  
58      public void receive()
59      {
60          HttpConnectionOverHTTP connection = getHttpConnection();
61          EndPoint endPoint = connection.getEndPoint();
62          HttpClient client = getHttpDestination().getHttpClient();
63          ByteBufferPool bufferPool = client.getByteBufferPool();
64          ByteBuffer buffer = bufferPool.acquire(client.getResponseBufferSize(), true);
65          try
66          {
67              while (true)
68              {
69                  // Connection may be closed in a parser callback
70                  if (connection.isClosed())
71                  {
72                      LOG.debug("{} closed", connection);
73                      break;
74                  }
75                  else
76                  {
77                      int read = endPoint.fill(buffer);
78                      if (LOG.isDebugEnabled()) // Avoid boxing of variable 'read'
79                          LOG.debug("Read {} bytes from {}", read, endPoint);
80                      if (read > 0)
81                      {
82                          parse(buffer);
83                      }
84                      else if (read == 0)
85                      {
86                          fillInterested();
87                          break;
88                      }
89                      else
90                      {
91                          shutdown();
92                          break;
93                      }
94                  }
95              }
96          }
97          catch (EofException x)
98          {
99              LOG.ignore(x);
100             failAndClose(x);
101         }
102         catch (Exception x)
103         {
104             LOG.debug(x);
105             failAndClose(x);
106         }
107         finally
108         {
109             bufferPool.release(buffer);
110         }
111     }
112 
113     private void parse(ByteBuffer buffer)
114     {
115         while (buffer.hasRemaining())
116             parser.parseNext(buffer);
117     }
118 
119     private void fillInterested()
120     {
121         // TODO: do we need to call fillInterested() only if we are not failed (or we have an exchange) ?
122         getHttpChannel().getHttpConnection().fillInterested();
123     }
124 
125     private void shutdown()
126     {
127         // Shutting down the parser may invoke messageComplete() or earlyEOF()
128         parser.atEOF();
129         parser.parseNext(BufferUtil.EMPTY_BUFFER);
130         if (!responseFailure(new EOFException()))
131             getHttpChannel().getHttpConnection().close();
132     }
133 
134     @Override
135     public int getHeaderCacheSize()
136     {
137         // TODO get from configuration
138         return 256;
139     }
140 
141     @Override
142     public boolean startResponse(HttpVersion version, int status, String reason)
143     {
144         HttpExchange exchange = getHttpExchange();
145         if (exchange == null)
146             return false;
147 
148         String method = exchange.getRequest().getMethod();
149         parser.setHeadResponse(HttpMethod.HEAD.is(method) || HttpMethod.CONNECT.is(method));
150         exchange.getResponse().version(version).status(status).reason(reason);
151 
152         responseBegin(exchange);
153         return false;
154     }
155 
156     @Override
157     public boolean parsedHeader(HttpField field)
158     {
159         HttpExchange exchange = getHttpExchange();
160         if (exchange == null)
161             return false;
162 
163         responseHeader(exchange, field);
164         return false;
165     }
166 
167     @Override
168     public boolean headerComplete()
169     {
170         HttpExchange exchange = getHttpExchange();
171         if (exchange == null)
172             return false;
173 
174         responseHeaders(exchange);
175         return false;
176     }
177 
178     @Override
179     public boolean content(ByteBuffer buffer)
180     {
181         HttpExchange exchange = getHttpExchange();
182         if (exchange == null)
183             return false;
184 
185         responseContent(exchange, buffer);
186         return false;
187     }
188 
189     @Override
190     public boolean messageComplete()
191     {
192         HttpExchange exchange = getHttpExchange();
193         if (exchange == null)
194             return false;
195 
196         responseSuccess(exchange);
197         return true;
198     }
199 
200     @Override
201     public void earlyEOF()
202     {
203         failAndClose(new EOFException());
204     }
205 
206     @Override
207     public void badMessage(int status, String reason)
208     {
209         HttpExchange exchange = getHttpExchange();
210         if (exchange != null)
211         {
212             HttpResponse response = exchange.getResponse();
213             response.status(status).reason(reason);
214             failAndClose(new HttpResponseException("HTTP protocol violation: bad response", response));
215         }
216     }
217 
218     @Override
219     protected void reset()
220     {
221         super.reset();
222         parser.reset();
223     }
224 
225     @Override
226     protected void dispose()
227     {
228         super.dispose();
229         parser.close();
230     }
231 
232     private void failAndClose(Throwable failure)
233     {
234         if (responseFailure(failure))
235             getHttpChannel().getHttpConnection().close();
236     }
237 
238     @Override
239     public String toString()
240     {
241         return String.format("%s@%x on %s", getClass().getSimpleName(), hashCode(), getHttpConnection());
242     }
243 }