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