1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.client;
20
21 import java.net.CookieStore;
22 import java.net.HttpCookie;
23 import java.net.URI;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.concurrent.TimeUnit;
27 import java.util.concurrent.TimeoutException;
28
29 import org.eclipse.jetty.client.api.Authentication;
30 import org.eclipse.jetty.client.api.Connection;
31 import org.eclipse.jetty.client.api.ContentProvider;
32 import org.eclipse.jetty.client.api.Request;
33 import org.eclipse.jetty.client.api.Response;
34 import org.eclipse.jetty.http.HttpField;
35 import org.eclipse.jetty.http.HttpFields;
36 import org.eclipse.jetty.http.HttpHeader;
37 import org.eclipse.jetty.http.HttpHeaderValue;
38 import org.eclipse.jetty.http.HttpScheme;
39 import org.eclipse.jetty.http.HttpVersion;
40 import org.eclipse.jetty.util.log.Log;
41 import org.eclipse.jetty.util.log.Logger;
42
43 public abstract class HttpConnection implements Connection
44 {
45 private static final Logger LOG = Log.getLogger(HttpConnection.class);
46 private static final HttpField CHUNKED_FIELD = new HttpField(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED);
47
48 private final HttpDestination destination;
49 private int idleTimeoutGuard;
50 private long idleTimeoutStamp;
51
52 protected HttpConnection(HttpDestination destination)
53 {
54 this.destination = destination;
55 this.idleTimeoutStamp = System.nanoTime();
56 }
57
58 public HttpClient getHttpClient()
59 {
60 return destination.getHttpClient();
61 }
62
63 public HttpDestination getHttpDestination()
64 {
65 return destination;
66 }
67
68 @Override
69 public void send(Request request, Response.CompleteListener listener)
70 {
71 HttpRequest httpRequest = (HttpRequest)request;
72
73 ArrayList<Response.ResponseListener> listeners = new ArrayList<>(httpRequest.getResponseListeners());
74 if (httpRequest.getTimeout() > 0)
75 {
76 TimeoutCompleteListener timeoutListener = new TimeoutCompleteListener(httpRequest);
77 timeoutListener.schedule(getHttpClient().getScheduler());
78 listeners.add(timeoutListener);
79 }
80 if (listener != null)
81 listeners.add(listener);
82
83 HttpExchange exchange = new HttpExchange(getHttpDestination(), httpRequest, listeners);
84
85 SendFailure result = send(exchange);
86 if (result != null)
87 httpRequest.abort(result.failure);
88 }
89
90 protected abstract SendFailure send(HttpExchange exchange);
91
92 protected void normalizeRequest(Request request)
93 {
94 HttpVersion version = request.getVersion();
95 HttpFields headers = request.getHeaders();
96 ContentProvider content = request.getContent();
97 ProxyConfiguration.Proxy proxy = destination.getProxy();
98
99
100 String path = request.getPath();
101 if (path.trim().length() == 0)
102 {
103 path = "/";
104 request.path(path);
105 }
106
107 URI uri = request.getURI();
108
109 if (proxy instanceof HttpProxy && !HttpScheme.HTTPS.is(request.getScheme()) && uri != null)
110 {
111 path = uri.toString();
112 request.path(path);
113 }
114
115
116 if (version.getVersion() <= 11)
117 {
118 if (!headers.containsKey(HttpHeader.HOST.asString()))
119 headers.put(getHttpDestination().getHostField());
120 }
121
122
123 if (content != null)
124 {
125 if (!headers.containsKey(HttpHeader.CONTENT_TYPE.asString()))
126 {
127 String contentType = null;
128 if (content instanceof ContentProvider.Typed)
129 contentType = ((ContentProvider.Typed)content).getContentType();
130 if (contentType != null)
131 headers.put(HttpHeader.CONTENT_TYPE, contentType);
132 else
133 headers.put(HttpHeader.CONTENT_TYPE, "application/octet-stream");
134 }
135 long contentLength = content.getLength();
136 if (contentLength >= 0)
137 {
138 if (!headers.containsKey(HttpHeader.CONTENT_LENGTH.asString()))
139 headers.put(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength));
140 }
141 }
142
143
144 CookieStore cookieStore = getHttpClient().getCookieStore();
145 if (cookieStore != null)
146 {
147 StringBuilder cookies = null;
148 if (uri != null)
149 cookies = convertCookies(cookieStore.get(uri), null);
150 cookies = convertCookies(request.getCookies(), cookies);
151 if (cookies != null)
152 request.header(HttpHeader.COOKIE.asString(), cookies.toString());
153 }
154
155
156 applyAuthentication(request, proxy != null ? proxy.getURI() : null);
157 applyAuthentication(request, uri);
158 }
159
160 private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)
161 {
162 for (int i = 0; i < cookies.size(); ++i)
163 {
164 if (builder == null)
165 builder = new StringBuilder();
166 if (builder.length() > 0)
167 builder.append("; ");
168 HttpCookie cookie = cookies.get(i);
169 builder.append(cookie.getName()).append("=").append(cookie.getValue());
170 }
171 return builder;
172 }
173
174 private void applyAuthentication(Request request, URI uri)
175 {
176 if (uri != null)
177 {
178 Authentication.Result result = getHttpClient().getAuthenticationStore().findAuthenticationResult(uri);
179 if (result != null)
180 result.apply(request);
181 }
182 }
183
184 protected SendFailure send(HttpChannel channel, HttpExchange exchange)
185 {
186
187
188
189 boolean send;
190 synchronized (this)
191 {
192 send = idleTimeoutGuard >= 0;
193 if (send)
194 ++idleTimeoutGuard;
195 }
196
197 if (send)
198 {
199 HttpRequest request = exchange.getRequest();
200 SendFailure result;
201 if (channel.associate(exchange))
202 {
203 channel.send();
204 result = null;
205 }
206 else
207 {
208 channel.release();
209 result = new SendFailure(new HttpRequestException("Could not associate request to connection", request), false);
210 }
211
212 synchronized (this)
213 {
214 --idleTimeoutGuard;
215 idleTimeoutStamp = System.nanoTime();
216 }
217
218 return result;
219 }
220 else
221 {
222 return new SendFailure(new TimeoutException(), true);
223 }
224 }
225
226 public boolean onIdleTimeout(long idleTimeout)
227 {
228 synchronized (this)
229 {
230 if (idleTimeoutGuard == 0)
231 {
232 long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTimeoutStamp);
233 boolean idle = elapsed > idleTimeout / 2;
234 if (idle)
235 idleTimeoutGuard = -1;
236 if (LOG.isDebugEnabled())
237 LOG.debug("Idle timeout {}/{}ms - {}", elapsed, idleTimeout, this);
238 return idle;
239 }
240 else
241 {
242 if (LOG.isDebugEnabled())
243 LOG.debug("Idle timeout skipped - {}", this);
244 return false;
245 }
246 }
247 }
248
249 @Override
250 public String toString()
251 {
252 return String.format("%s@%h", getClass().getSimpleName(), this);
253 }
254 }