1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.jetty.client;
16
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.InterruptedIOException;
20
21 import org.mortbay.io.Buffer;
22 import org.mortbay.io.Buffers;
23 import org.mortbay.io.ByteArrayBuffer;
24 import org.mortbay.io.Connection;
25 import org.mortbay.io.EndPoint;
26 import org.mortbay.io.nio.SelectChannelEndPoint;
27 import org.mortbay.jetty.HttpGenerator;
28 import org.mortbay.jetty.HttpHeaderValues;
29 import org.mortbay.jetty.HttpHeaders;
30 import org.mortbay.jetty.HttpParser;
31 import org.mortbay.jetty.HttpSchemes;
32 import org.mortbay.jetty.HttpVersions;
33 import org.mortbay.jetty.client.security.Authorization;
34 import org.mortbay.jetty.security.SslHttpChannelEndPoint;
35 import org.mortbay.log.Log;
36 import org.mortbay.thread.Timeout;
37
38
39
40
41
42
43 public class HttpConnection implements Connection
44 {
45 HttpDestination _destination;
46 EndPoint _endp;
47 HttpGenerator _generator;
48 HttpParser _parser;
49 boolean _http11 = true;
50 Buffer _connectionHeader;
51 Buffer _requestContentChunk;
52 long _last;
53 boolean _requestComplete;
54 public String _message;
55 public Throwable _throwable;
56
57
58 HttpExchange _exchange;
59 HttpExchange _pipeline;
60
61 public void dump() throws IOException
62 {
63 System.err.println("endp=" + _endp + " " + _endp.isBufferingInput() + " " + _endp.isBufferingOutput());
64 System.err.println("generator=" + _generator);
65 System.err.println("parser=" + _parser.getState() + " " + _parser.isMoreInBuffer());
66 System.err.println("exchange=" + _exchange);
67 if (_endp instanceof SslHttpChannelEndPoint)
68 ((SslHttpChannelEndPoint)_endp).dump();
69 }
70
71 Timeout.Task _timeout = new Timeout.Task()
72 {
73 public void expire()
74 {
75 HttpExchange ex = null;
76 try
77 {
78 synchronized (HttpConnection.this)
79 {
80 ex = _exchange;
81 _exchange = null;
82 if (ex != null)
83 _destination.returnConnection(HttpConnection.this,true);
84 }
85 }
86 catch (Exception e)
87 {
88 Log.debug(e);
89 }
90 finally
91 {
92 try
93 {
94 _endp.close();
95 }
96 catch (IOException e)
97 {
98 Log.ignore(e);
99 }
100
101 if (ex.getStatus() < HttpExchange.STATUS_COMPLETED)
102 {
103 ex.setStatus(HttpExchange.STATUS_EXPIRED);
104 }
105 }
106 }
107
108 };
109
110
111 HttpConnection(Buffers buffers, EndPoint endp, int hbs, int cbs)
112 {
113 _endp = endp;
114 _generator = new HttpGenerator(buffers,endp,hbs,cbs);
115 _parser = new HttpParser(buffers,endp,new Handler(),hbs,cbs);
116 }
117
118
119 public HttpDestination getDestination()
120 {
121 return _destination;
122 }
123
124
125 public void setDestination(HttpDestination destination)
126 {
127 _destination = destination;
128 }
129
130
131 public boolean send(HttpExchange ex) throws IOException
132 {
133
134
135
136 _throwable = new Throwable();
137 synchronized (this)
138 {
139 if (_exchange != null)
140 {
141 if (_pipeline != null)
142 throw new IllegalStateException(this + " PIPELINED!!! _exchange=" + _exchange);
143 _pipeline = ex;
144 return true;
145 }
146
147 if (!_endp.isOpen())
148 return false;
149
150 ex.setStatus(HttpExchange.STATUS_WAITING_FOR_COMMIT);
151 _exchange = ex;
152
153 if (_endp.isBlocking())
154 this.notify();
155 else
156 {
157 SelectChannelEndPoint scep = (SelectChannelEndPoint)_endp;
158 scep.scheduleWrite();
159 }
160
161 if (!_endp.isBlocking())
162 _destination.getHttpClient().schedule(_timeout);
163
164 return true;
165 }
166 }
167
168
169 public void handle() throws IOException
170 {
171 int no_progress = 0;
172 long flushed = 0;
173
174 boolean failed = false;
175 while (_endp.isBufferingInput() || _endp.isOpen())
176 {
177 synchronized (this)
178 {
179 while (_exchange == null)
180 {
181 if (_endp.isBlocking())
182 {
183 try
184 {
185 this.wait();
186 }
187 catch (InterruptedException e)
188 {
189 throw new InterruptedIOException();
190 }
191 }
192 else
193 {
194
195 _parser.fill();
196 _parser.skipCRLF();
197 if (_parser.isMoreInBuffer())
198 {
199 Log.warn("unexpected data");
200 _endp.close();
201 }
202
203 return;
204 }
205 }
206 }
207 if (_exchange.getStatus() == HttpExchange.STATUS_WAITING_FOR_COMMIT)
208 {
209 no_progress = 0;
210 commitRequest();
211 }
212
213 try
214 {
215 long io = 0;
216
217 if (_generator.isComplete())
218 {
219 if (!_requestComplete)
220 {
221 _requestComplete = true;
222 _exchange.getEventListener().onRequestComplete();
223 }
224 }
225 else
226 {
227
228 synchronized (this)
229 {
230 if (_exchange == null)
231 continue;
232 flushed = _generator.flush();
233 io += flushed;
234 }
235
236 if (!_generator.isComplete())
237 {
238 InputStream in = _exchange.getRequestContentSource();
239 if (in != null)
240 {
241 if (_requestContentChunk == null || _requestContentChunk.length() == 0)
242 {
243 _requestContentChunk = _exchange.getRequestContentChunk();
244 if (_requestContentChunk != null)
245 _generator.addContent(_requestContentChunk,false);
246 else
247 _generator.complete();
248 io += _generator.flush();
249 }
250 }
251 else
252 _generator.complete();
253 }
254 }
255
256
257 if (!_parser.isComplete() && _generator.isCommitted())
258 {
259 long filled = _parser.parseAvailable();
260 io += filled;
261 }
262
263 if (io > 0)
264 no_progress = 0;
265 else if (no_progress++ >= 2 && !_endp.isBlocking())
266 return;
267 }
268 catch (IOException e)
269 {
270 synchronized (this)
271 {
272 if (_exchange != null)
273 {
274 _exchange.getEventListener().onException(e);
275 _exchange.setStatus(HttpExchange.STATUS_EXCEPTED);
276 }
277 }
278 failed = true;
279 throw e;
280 }
281 finally
282 {
283 boolean complete = false;
284 boolean close = failed;
285 if (!failed)
286 {
287
288 if (_generator.isComplete())
289 {
290 if (!_requestComplete)
291 {
292 _requestComplete = true;
293 _exchange.getEventListener().onRequestComplete();
294 }
295
296
297
298 if (_parser.isComplete())
299 {
300 _destination.getHttpClient().cancel(_timeout);
301 complete = true;
302 }
303 }
304 }
305
306 if (complete || failed)
307 {
308 synchronized (this)
309 {
310 if (!close)
311 close = shouldClose();
312
313 reset(true);
314 no_progress = 0;
315 flushed = -1;
316 if (_exchange != null)
317 {
318 _exchange = null;
319
320 if (_pipeline == null)
321 {
322 _destination.returnConnection(this,close);
323 if (close)
324 return;
325 }
326 else
327 {
328 if (close)
329 {
330 _destination.returnConnection(this,close);
331 _destination.send(_pipeline);
332 _pipeline = null;
333 return;
334 }
335
336 HttpExchange ex = _pipeline;
337 _pipeline = null;
338
339 send(ex);
340 }
341 }
342 }
343 }
344 }
345 }
346 }
347
348
349 public boolean isIdle()
350 {
351 synchronized (this)
352 {
353 return _exchange == null;
354 }
355 }
356
357
358 public EndPoint getEndPoint()
359 {
360 return _endp;
361 }
362
363
364 private void commitRequest() throws IOException
365 {
366 synchronized (this)
367 {
368 if (_exchange.getStatus() != HttpExchange.STATUS_WAITING_FOR_COMMIT)
369 throw new IllegalStateException();
370
371 _exchange.setStatus(HttpExchange.STATUS_SENDING_REQUEST);
372 _generator.setVersion(_exchange._version);
373
374 String uri = _exchange._uri;
375 if (_destination.isProxied() && uri.startsWith("/"))
376 {
377
378 uri = (_destination.isSecure()?HttpSchemes.HTTPS:HttpSchemes.HTTP) + "://" + _destination.getAddress().getHost() + ":"
379 + _destination.getAddress().getPort() + uri;
380 Authorization auth = _destination.getProxyAuthentication();
381 if (auth != null)
382 auth.setCredentials(_exchange);
383 }
384
385 _generator.setRequest(_exchange._method,uri);
386
387 if (_exchange._version >= HttpVersions.HTTP_1_1_ORDINAL)
388 {
389 if (!_exchange._requestFields.containsKey(HttpHeaders.HOST_BUFFER))
390 _exchange._requestFields.add(HttpHeaders.HOST_BUFFER,_destination.getHostHeader());
391 }
392
393 if (_exchange._requestContent != null)
394 {
395 _exchange._requestFields.putLongField(HttpHeaders.CONTENT_LENGTH,_exchange._requestContent.length());
396 _generator.completeHeader(_exchange._requestFields,false);
397 _generator.addContent(_exchange._requestContent,true);
398 }
399 else if (_exchange._requestContentSource != null)
400 {
401 _generator.completeHeader(_exchange._requestFields,false);
402 int available = _exchange._requestContentSource.available();
403 if (available > 0)
404 {
405
406
407
408 byte[] buf = new byte[available];
409 int length = _exchange._requestContentSource.read(buf);
410 _generator.addContent(new ByteArrayBuffer(buf,0,length),false);
411 }
412 }
413 else
414 {
415 _exchange._requestFields.remove(HttpHeaders.CONTENT_LENGTH);
416
417
418
419
420
421 _generator.completeHeader(_exchange._requestFields,true);
422 }
423
424 _exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
425 }
426 }
427
428
429 protected void reset(boolean returnBuffers) throws IOException
430 {
431 _requestComplete = false;
432 _connectionHeader = null;
433 _parser.reset(returnBuffers);
434 _generator.reset(returnBuffers);
435 _http11 = true;
436 }
437
438
439 private boolean shouldClose()
440 {
441 if (_connectionHeader!=null)
442 {
443 if (HttpHeaderValues.CLOSE_BUFFER.equals(_connectionHeader))
444 return true;
445 if (HttpHeaderValues.KEEP_ALIVE_BUFFER.equals(_connectionHeader))
446 return false;
447 }
448 return !_http11;
449 }
450
451
452 private class Handler extends HttpParser.EventHandler
453 {
454 @Override
455 public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException
456 {
457
458
459
460
461
462 }
463
464 @Override
465 public void startResponse(Buffer version, int status, Buffer reason) throws IOException
466 {
467 _http11 = HttpVersions.HTTP_1_1_BUFFER.equals(version);
468 _exchange.getEventListener().onResponseStatus(version,status,reason);
469 _exchange.setStatus(HttpExchange.STATUS_PARSING_HEADERS);
470 }
471
472 @Override
473 public void parsedHeader(Buffer name, Buffer value) throws IOException
474 {
475 if (HttpHeaders.CACHE.getOrdinal(name) == HttpHeaders.CONNECTION_ORDINAL)
476 {
477 _connectionHeader = HttpHeaderValues.CACHE.lookup(value);
478 }
479 _exchange.getEventListener().onResponseHeader(name,value);
480 }
481
482 @Override
483 public void headerComplete() throws IOException
484 {
485 _exchange.setStatus(HttpExchange.STATUS_PARSING_CONTENT);
486 }
487
488 @Override
489 public void content(Buffer ref) throws IOException
490 {
491 _exchange.getEventListener().onResponseContent(ref);
492 }
493
494 @Override
495 public void messageComplete(long contextLength) throws IOException
496 {
497 _exchange.setStatus(HttpExchange.STATUS_COMPLETED);
498 }
499 }
500
501
502 public String toString()
503 {
504 return "HttpConnection@" + hashCode() + "//" + _destination.getAddress().getHost() + ":" + _destination.getAddress().getPort();
505 }
506
507
508 public String toDetailString()
509 {
510 return toString() + " ex=" + _exchange + " " + _timeout.getAge();
511 }
512
513
514
515
516
517 public long getLast()
518 {
519 return _last;
520 }
521
522
523
524
525
526
527 public void setLast(long last)
528 {
529 _last = last;
530 }
531
532
533 public void close() throws IOException
534 {
535 _endp.close();
536 }
537
538 }