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.channels.AsynchronousCloseException;
22  import java.util.concurrent.TimeoutException;
23  import java.util.concurrent.atomic.AtomicBoolean;
24  
25  import org.eclipse.jetty.client.HttpConnection;
26  import org.eclipse.jetty.client.HttpDestination;
27  import org.eclipse.jetty.client.HttpExchange;
28  import org.eclipse.jetty.client.api.Connection;
29  import org.eclipse.jetty.client.api.Request;
30  import org.eclipse.jetty.client.api.Response;
31  import org.eclipse.jetty.io.AbstractConnection;
32  import org.eclipse.jetty.io.EndPoint;
33  import org.eclipse.jetty.util.log.Log;
34  import org.eclipse.jetty.util.log.Logger;
35  
36  public class HttpConnectionOverHTTP extends AbstractConnection implements Connection
37  {
38      private static final Logger LOG = Log.getLogger(HttpConnectionOverHTTP.class);
39  
40      private final AtomicBoolean closed = new AtomicBoolean();
41      private final Delegate delegate;
42      private final HttpChannelOverHTTP channel;
43      private long idleTimeout;
44  
45      public HttpConnectionOverHTTP(EndPoint endPoint, HttpDestination destination)
46      {
47          super(endPoint, destination.getHttpClient().getExecutor(), destination.getHttpClient().isDispatchIO());
48          this.delegate = new Delegate(destination);
49          this.channel = new HttpChannelOverHTTP(this);
50      }
51  
52      public HttpChannelOverHTTP getHttpChannel()
53      {
54          return channel;
55      }
56  
57      public HttpDestinationOverHTTP getHttpDestination()
58      {
59          return (HttpDestinationOverHTTP)delegate.getHttpDestination();
60      }
61  
62      @Override
63      public void send(Request request, Response.CompleteListener listener)
64      {
65          delegate.send(request, listener);
66      }
67  
68      protected void send(HttpExchange exchange)
69      {
70          delegate.send(exchange);
71      }
72  
73      @Override
74      public void onOpen()
75      {
76          super.onOpen();
77          fillInterested();
78      }
79  
80      public boolean isClosed()
81      {
82          return closed.get();
83      }
84  
85      @Override
86      protected boolean onReadTimeout()
87      {
88          if (LOG.isDebugEnabled())
89              LOG.debug("{} idle timeout", this);
90          close(new TimeoutException());
91          return false;
92      }
93  
94      @Override
95      public void onFillable()
96      {
97          HttpExchange exchange = channel.getHttpExchange();
98          if (exchange != null)
99          {
100             channel.receive();
101         }
102         else
103         {
104             // If there is no exchange, then could be either a remote close,
105             // or garbage bytes; in both cases we close the connection
106             close();
107         }
108     }
109 
110     public void release()
111     {
112         // Restore idle timeout
113         getEndPoint().setIdleTimeout(idleTimeout);
114         getHttpDestination().release(this);
115     }
116 
117     @Override
118     public void close()
119     {
120         close(new AsynchronousCloseException());
121     }
122 
123     protected void close(Throwable failure)
124     {
125         if (softClose())
126         {
127             // First close then abort, to be sure that the connection cannot be reused
128             // from an onFailure() handler or by blocking code waiting for completion.
129             getHttpDestination().close(this);
130             getEndPoint().shutdownOutput();
131             if (LOG.isDebugEnabled())
132                 LOG.debug("{} oshut", this);
133             getEndPoint().close();
134             if (LOG.isDebugEnabled())
135                 LOG.debug("{} closed", this);
136 
137             abort(failure);
138         }
139     }
140 
141     public boolean softClose()
142     {
143         return closed.compareAndSet(false, true);
144     }
145 
146     private boolean abort(Throwable failure)
147     {
148         HttpExchange exchange = channel.getHttpExchange();
149         return exchange != null && exchange.getRequest().abort(failure);
150     }
151 
152     @Override
153     public String toString()
154     {
155         return String.format("%s@%h(l:%s <-> r:%s)",
156                 getClass().getSimpleName(),
157                 this,
158                 getEndPoint().getLocalAddress(),
159                 getEndPoint().getRemoteAddress());
160     }
161 
162     private class Delegate extends HttpConnection
163     {
164         private Delegate(HttpDestination destination)
165         {
166             super(destination);
167         }
168 
169         @Override
170         protected void send(HttpExchange exchange)
171         {
172             Request request = exchange.getRequest();
173             normalizeRequest(request);
174 
175             // Save the old idle timeout to restore it
176             EndPoint endPoint = getEndPoint();
177             idleTimeout = endPoint.getIdleTimeout();
178             endPoint.setIdleTimeout(request.getIdleTimeout());
179 
180             // One channel per connection, just delegate the send
181             channel.associate(exchange);
182             channel.send();
183         }
184 
185         @Override
186         public void close()
187         {
188             HttpConnectionOverHTTP.this.close();
189         }
190 
191         @Override
192         public String toString()
193         {
194             return HttpConnectionOverHTTP.this.toString();
195         }
196     }
197 }