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