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.TimeoutException;
27 import java.util.concurrent.atomic.AtomicInteger;
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.HttpMethod;
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 AtomicInteger idleTimeoutState = new AtomicInteger();
49 private final HttpDestination destination;
50
51 protected HttpConnection(HttpDestination destination)
52 {
53 this.destination = destination;
54 }
55
56 public HttpClient getHttpClient()
57 {
58 return destination.getHttpClient();
59 }
60
61 public HttpDestination getHttpDestination()
62 {
63 return destination;
64 }
65
66 @Override
67 public void send(Request request, Response.CompleteListener listener)
68 {
69 ArrayList<Response.ResponseListener> listeners = new ArrayList<>(2);
70 if (request.getTimeout() > 0)
71 {
72 TimeoutCompleteListener timeoutListener = new TimeoutCompleteListener(request);
73 timeoutListener.schedule(getHttpClient().getScheduler());
74 listeners.add(timeoutListener);
75 }
76 if (listener != null)
77 listeners.add(listener);
78
79 HttpExchange exchange = new HttpExchange(getHttpDestination(), (HttpRequest)request, listeners);
80
81 SendFailure result = send(exchange);
82 if (result != null)
83 request.abort(result.failure);
84 }
85
86 protected abstract SendFailure send(HttpExchange exchange);
87
88 protected void normalizeRequest(Request request)
89 {
90 String method = request.getMethod();
91 HttpVersion version = request.getVersion();
92 HttpFields headers = request.getHeaders();
93 ContentProvider content = request.getContent();
94 ProxyConfiguration.Proxy proxy = destination.getProxy();
95
96
97 String path = request.getPath();
98 if (path.trim().length() == 0)
99 {
100 path = "/";
101 request.path(path);
102 }
103 if (proxy != null && !HttpMethod.CONNECT.is(method))
104 {
105 path = request.getURI().toString();
106 request.path(path);
107 }
108
109
110 if (version.getVersion() > 10)
111 {
112 if (!headers.containsKey(HttpHeader.HOST.asString()))
113 headers.put(getHttpDestination().getHostField());
114 }
115
116
117 if (content != null)
118 {
119 if (content instanceof ContentProvider.Typed)
120 {
121 if (!headers.containsKey(HttpHeader.CONTENT_TYPE.asString()))
122 {
123 String contentType = ((ContentProvider.Typed)content).getContentType();
124 if (contentType != null)
125 headers.put(HttpHeader.CONTENT_TYPE, contentType);
126 }
127 }
128 long contentLength = content.getLength();
129 if (contentLength >= 0)
130 {
131 if (!headers.containsKey(HttpHeader.CONTENT_LENGTH.asString()))
132 headers.put(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength));
133 }
134 else
135 {
136 if (!headers.containsKey(HttpHeader.TRANSFER_ENCODING.asString()))
137 headers.put(CHUNKED_FIELD);
138 }
139 }
140
141
142 CookieStore cookieStore = getHttpClient().getCookieStore();
143 if (cookieStore != null)
144 {
145 URI uri = request.getURI();
146 StringBuilder cookies = null;
147 if (uri != null)
148 cookies = convertCookies(cookieStore.get(uri), null);
149 cookies = convertCookies(request.getCookies(), cookies);
150 if (cookies != null)
151 request.header(HttpHeader.COOKIE.asString(), cookies.toString());
152 }
153
154
155 URI authenticationURI = proxy != null ? proxy.getURI() : request.getURI();
156 if (authenticationURI != null)
157 {
158 Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI);
159 if (authnResult != null)
160 authnResult.apply(request);
161 }
162 }
163
164 private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)
165 {
166 for (int i = 0; i < cookies.size(); ++i)
167 {
168 if (builder == null)
169 builder = new StringBuilder();
170 if (builder.length() > 0)
171 builder.append("; ");
172 HttpCookie cookie = cookies.get(i);
173 builder.append(cookie.getName()).append("=").append(cookie.getValue());
174 }
175 return builder;
176 }
177
178 protected SendFailure send(HttpChannel channel, HttpExchange exchange)
179 {
180
181
182
183 boolean send = false;
184 while (true)
185 {
186 int current = idleTimeoutState.get();
187 if (current < 0)
188 break;
189 if (idleTimeoutState.compareAndSet(current, current + 1))
190 {
191 send = true;
192 break;
193 }
194 }
195
196 if (send)
197 {
198 HttpRequest request = exchange.getRequest();
199 SendFailure result;
200 if (channel.associate(exchange))
201 {
202 channel.send();
203 result = null;
204 }
205 else
206 {
207 channel.release();
208 result = new SendFailure(new HttpRequestException("Could not associate request to connection", request), false);
209 }
210 idleTimeoutState.decrementAndGet();
211 return result;
212 }
213 else
214 {
215 return new SendFailure(new TimeoutException(), true);
216 }
217 }
218
219 public boolean onIdleTimeout()
220 {
221 if (LOG.isDebugEnabled())
222 LOG.debug("Idle timeout state {} - {}", idleTimeoutState, this);
223 return idleTimeoutState.compareAndSet(0, -1);
224 }
225
226 @Override
227 public String toString()
228 {
229 return String.format("%s@%h", getClass().getSimpleName(), this);
230 }
231 }