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