1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mortbay.jetty;
17
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.PrintWriter;
21
22 import javax.servlet.ServletInputStream;
23 import javax.servlet.ServletOutputStream;
24 import javax.servlet.http.HttpServletResponse;
25
26 import org.mortbay.io.Buffer;
27 import org.mortbay.io.Connection;
28 import org.mortbay.io.EndPoint;
29 import org.mortbay.io.BufferCache.CachedBuffer;
30 import org.mortbay.io.nio.SelectChannelEndPoint;
31 import org.mortbay.log.Log;
32 import org.mortbay.resource.Resource;
33 import org.mortbay.util.StringUtil;
34 import org.mortbay.util.URIUtil;
35 import org.mortbay.util.ajax.Continuation;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public class HttpConnection implements Connection
57 {
58 private static int UNKNOWN = -2;
59 private static ThreadLocal __currentConnection = new ThreadLocal();
60
61 private long _timeStamp = System.currentTimeMillis();
62 private int _requests;
63 private boolean _handling;
64 private boolean _destroy;
65
66 protected final Connector _connector;
67 protected final EndPoint _endp;
68 protected final Server _server;
69 protected final HttpURI _uri;
70
71 protected final Parser _parser;
72 protected final HttpFields _requestFields;
73 protected final Request _request;
74 protected ServletInputStream _in;
75
76 protected final Generator _generator;
77 protected final HttpFields _responseFields;
78 protected final Response _response;
79 protected Output _out;
80 protected OutputWriter _writer;
81 protected PrintWriter _printWriter;
82
83 int _include;
84
85 private Object _associatedObject;
86
87 private transient int _expect = UNKNOWN;
88 private transient int _version = UNKNOWN;
89 private transient boolean _head = false;
90 private transient boolean _host = false;
91 private transient boolean _delayedHandling = false;
92
93
94 public static HttpConnection getCurrentConnection()
95 {
96 return (HttpConnection)__currentConnection.get();
97 }
98
99
100 protected static void setCurrentConnection(HttpConnection connection)
101 {
102 __currentConnection.set(connection);
103 }
104
105
106
107
108
109
110 public HttpConnection(Connector connector, EndPoint endpoint, Server server)
111 {
112 _uri = URIUtil.__CHARSET==StringUtil.__UTF8?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
113 _connector = connector;
114 _endp = endpoint;
115 _parser = new HttpParser(_connector,endpoint,new RequestHandler(),_connector.getHeaderBufferSize(),_connector.getRequestBufferSize());
116 _requestFields = new HttpFields();
117 _responseFields = new HttpFields();
118 _request = new Request(this);
119 _response = new Response(this);
120 _generator = new HttpGenerator(_connector,_endp,_connector.getHeaderBufferSize(),_connector.getResponseBufferSize());
121 _generator.setSendServerVersion(server.getSendServerVersion());
122 _server = server;
123 }
124
125 protected HttpConnection(Connector connector, EndPoint endpoint, Server server,
126 Parser parser, Generator generator, Request request)
127 {
128 _uri = URIUtil.__CHARSET==StringUtil.__UTF8?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
129 _connector = connector;
130 _endp = endpoint;
131 _parser = parser;
132 _requestFields = new HttpFields();
133 _responseFields = new HttpFields();
134 _request = request;
135 _response = new Response(this);
136 _generator = generator;
137 _generator.setSendServerVersion(server.getSendServerVersion());
138 _server = server;
139 }
140
141
142 public void destroy()
143 {
144 synchronized (this)
145 {
146 _destroy = true;
147 if (!_handling)
148 {
149 if (_parser != null)
150 _parser.reset(true);
151
152 if (_generator != null)
153 _generator.reset(true);
154
155 if (_requestFields != null)
156 _requestFields.destroy();
157
158 if (_responseFields != null)
159 _responseFields.destroy();
160
161 }
162 }
163 }
164
165
166
167
168
169 public Parser getParser()
170 {
171 return _parser;
172 }
173
174
175
176
177
178 public int getRequests()
179 {
180 return _requests;
181 }
182
183
184
185
186
187 public long getTimeStamp()
188 {
189 return _timeStamp;
190 }
191
192
193
194
195
196 public Object getAssociatedObject()
197 {
198 return _associatedObject;
199 }
200
201
202
203
204
205
206 public void setAssociatedObject(Object associatedObject)
207 {
208 _associatedObject = associatedObject;
209 }
210
211
212
213
214
215 public Connector getConnector()
216 {
217 return _connector;
218 }
219
220
221
222
223
224 public HttpFields getRequestFields()
225 {
226 return _requestFields;
227 }
228
229
230
231
232
233 public HttpFields getResponseFields()
234 {
235 return _responseFields;
236 }
237
238
239
240
241
242
243
244 public boolean isConfidential(Request request)
245 {
246 if (_connector != null)
247 return _connector.isConfidential(request);
248 return false;
249 }
250
251
252
253
254
255
256
257
258
259
260 public boolean isIntegral(Request request)
261 {
262 if (_connector != null)
263 return _connector.isIntegral(request);
264 return false;
265 }
266
267
268
269
270
271 public EndPoint getEndPoint()
272 {
273 return _endp;
274 }
275
276
277
278
279
280 public boolean getResolveNames()
281 {
282 return _connector.getResolveNames();
283 }
284
285
286
287
288
289 public Request getRequest()
290 {
291 return _request;
292 }
293
294
295
296
297
298 public Response getResponse()
299 {
300 return _response;
301 }
302
303
304
305
306
307
308 public ServletInputStream getInputStream()
309 {
310 if (_in == null)
311 _in = new HttpParser.Input(((HttpParser)_parser),_connector.getMaxIdleTime());
312 return _in;
313 }
314
315
316
317
318
319
320 public ServletOutputStream getOutputStream()
321 {
322 if (_out == null)
323 _out = new Output();
324 return _out;
325 }
326
327
328
329
330
331
332 public PrintWriter getPrintWriter(String encoding)
333 {
334 getOutputStream();
335 if (_writer == null)
336 {
337 _writer = new OutputWriter();
338 _printWriter = new PrintWriter(_writer)
339 {
340
341
342
343
344 public void close()
345 {
346 try
347 {
348 out.close();
349 }
350 catch (IOException e)
351 {
352 Log.debug(e);
353 setError();
354 }
355 }
356
357 };
358 }
359 _writer.setCharacterEncoding(encoding);
360 return _printWriter;
361 }
362
363
364 public boolean isResponseCommitted()
365 {
366 return _generator.isCommitted();
367 }
368
369
370 public void handle() throws IOException
371 {
372
373 boolean more_in_buffer = true;
374 int no_progress = 0;
375
376 while (more_in_buffer)
377 {
378 try
379 {
380 synchronized (this)
381 {
382 if (_handling)
383 throw new IllegalStateException();
384
385 _handling = true;
386 }
387
388 setCurrentConnection(this);
389 long io = 0;
390
391 Continuation continuation = _request.getContinuation();
392 if (continuation != null && continuation.isPending())
393 {
394 Log.debug("resume continuation {}",continuation);
395 if (_request.getMethod() == null)
396 throw new IllegalStateException();
397 handleRequest();
398 }
399 else
400 {
401
402 if (!_parser.isComplete())
403 io = _parser.parseAvailable();
404
405
406
407
408
409
410 while (_generator.isCommitted() && !_generator.isComplete())
411 {
412 long written = _generator.flush();
413 io += written;
414 if (written <= 0)
415 break;
416 if (_endp.isBufferingOutput())
417 _endp.flush();
418 }
419
420
421 if (_endp.isBufferingOutput())
422 {
423 _endp.flush();
424 if (!_endp.isBufferingOutput())
425 no_progress = 0;
426 }
427
428 if (io > 0)
429 no_progress = 0;
430 else if (no_progress++ >= 2)
431 return;
432 }
433 }
434 catch (HttpException e)
435 {
436 if (Log.isDebugEnabled())
437 {
438 Log.debug("uri=" + _uri);
439 Log.debug("fields=" + _requestFields);
440 Log.debug(e);
441 }
442 _generator.sendError(e.getStatus(),e.getReason(),null,true);
443
444 _parser.reset(true);
445 _endp.close();
446 throw e;
447 }
448 finally
449 {
450 setCurrentConnection(null);
451
452 more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
453
454 synchronized (this)
455 {
456 _handling = false;
457
458 if (_destroy)
459 {
460 destroy();
461 return;
462 }
463 }
464
465 if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput())
466 {
467 if (!_generator.isPersistent())
468 {
469 _parser.reset(true);
470 more_in_buffer = false;
471 }
472
473 reset(!more_in_buffer);
474 no_progress = 0;
475 }
476
477 Continuation continuation = _request.getContinuation();
478 if (continuation != null && continuation.isPending())
479 {
480 break;
481 }
482 else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof SelectChannelEndPoint)
483
484
485
486 ((SelectChannelEndPoint)_endp).setWritable(false);
487 }
488 }
489 }
490
491
492 public void reset(boolean returnBuffers)
493 {
494 _parser.reset(returnBuffers);
495
496 _requestFields.clear();
497 _request.recycle();
498
499 _generator.reset(returnBuffers);
500
501 _responseFields.clear();
502 _response.recycle();
503
504 _uri.clear();
505 }
506
507
508 protected void handleRequest() throws IOException
509 {
510 if (_server.isRunning())
511 {
512 boolean retrying = false;
513 boolean error = false;
514 String threadName = null;
515 try
516 {
517
518 String info = URIUtil.canonicalPath(_uri.getDecodedPath());
519 if (info == null)
520 throw new HttpException(400);
521 _request.setPathInfo(info);
522
523 if (_out != null)
524 _out.reopen();
525
526 if (Log.isDebugEnabled())
527 {
528 threadName = Thread.currentThread().getName();
529 Thread.currentThread().setName(threadName + " - " + _uri);
530 }
531
532 _connector.customize(_endp,_request);
533
534 _server.handle(this);
535 }
536 catch (RetryRequest r)
537 {
538 if (Log.isDebugEnabled())
539 Log.ignore(r);
540 retrying = true;
541 }
542 catch (EofException e)
543 {
544 Log.ignore(e);
545 error = true;
546 }
547 catch (HttpException e)
548 {
549 Log.debug(e);
550 _request.setHandled(true);
551 _response.sendError(e.getStatus(),e.getReason());
552 error = true;
553 }
554 catch (Exception e)
555 {
556 Log.warn(e);
557 _request.setHandled(true);
558 _generator.sendError(500,null,null,true);
559 error = true;
560 }
561 catch (Error e)
562 {
563 Log.warn(e);
564 _request.setHandled(true);
565 _generator.sendError(500,null,null,true);
566 error = true;
567 }
568 finally
569 {
570 if (threadName != null)
571 Thread.currentThread().setName(threadName);
572
573 if (!retrying)
574 {
575 if (_request.getContinuation() != null)
576 {
577 Log.debug("continuation still pending {}");
578 _request.getContinuation().reset();
579 }
580
581 if (_endp.isOpen())
582 {
583 if (_generator.isPersistent())
584 _connector.persist(_endp);
585
586 if (error)
587 _endp.close();
588 else
589 {
590 if (!_response.isCommitted() && !_request.isHandled())
591 _response.sendError(HttpServletResponse.SC_NOT_FOUND);
592 _response.complete();
593 }
594 }
595 else
596 {
597 _response.complete();
598 }
599 }
600 }
601 }
602 }
603
604
605 public void commitResponse(boolean last) throws IOException
606 {
607 if (!_generator.isCommitted())
608 {
609 _generator.setResponse(_response.getStatus(),_response.getReason());
610 _generator.completeHeader(_responseFields,last);
611 }
612 if (last)
613 _generator.complete();
614 }
615
616
617 public void completeResponse() throws IOException
618 {
619 if (!_generator.isCommitted())
620 {
621 _generator.setResponse(_response.getStatus(),_response.getReason());
622 _generator.completeHeader(_responseFields,HttpGenerator.LAST);
623 }
624
625 _generator.complete();
626 }
627
628
629 public void flushResponse() throws IOException
630 {
631 try
632 {
633 commitResponse(HttpGenerator.MORE);
634 _generator.flush();
635 }
636 catch (IOException e)
637 {
638 throw (e instanceof EofException)?e:new EofException(e);
639 }
640 }
641
642
643 public Generator getGenerator()
644 {
645 return _generator;
646 }
647
648
649 public boolean isIncluding()
650 {
651 return _include > 0;
652 }
653
654
655 public void include()
656 {
657 _include++;
658 }
659
660
661 public void included()
662 {
663 _include--;
664 if (_out != null)
665 _out.reopen();
666 }
667
668
669 public boolean isIdle()
670 {
671 return _generator.isIdle() && (_parser.isIdle() || _delayedHandling);
672 }
673
674
675
676
677 private class RequestHandler extends HttpParser.EventHandler
678 {
679 private String _charset;
680
681
682
683
684
685
686
687 public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
688 {
689 _host = false;
690 _expect = UNKNOWN;
691 _delayedHandling = false;
692 _charset = null;
693
694 if (_request.getTimeStamp() == 0)
695 _request.setTimeStamp(System.currentTimeMillis());
696 _request.setMethod(method.toString());
697
698 try
699 {
700 _uri.parse(uri.array(),uri.getIndex(),uri.length());
701 _request.setUri(_uri);
702
703 if (version == null)
704 {
705 _request.setProtocol(HttpVersions.HTTP_0_9);
706 _version = HttpVersions.HTTP_0_9_ORDINAL;
707 }
708 else
709 {
710 version = HttpVersions.CACHE.get(version);
711 _version = HttpVersions.CACHE.getOrdinal(version);
712 if (_version <= 0)
713 _version = HttpVersions.HTTP_1_0_ORDINAL;
714 _request.setProtocol(version.toString());
715 }
716
717 _head = method == HttpMethods.HEAD_BUFFER;
718
719 }
720 catch (Exception e)
721 {
722 throw new HttpException(HttpStatus.ORDINAL_400_Bad_Request,null,e);
723 }
724 }
725
726
727
728
729
730
731 public void parsedHeader(Buffer name, Buffer value)
732 {
733 int ho = HttpHeaders.CACHE.getOrdinal(name);
734 switch (ho)
735 {
736 case HttpHeaders.HOST_ORDINAL:
737
738 _host = true;
739 break;
740
741 case HttpHeaders.EXPECT_ORDINAL:
742 value = HttpHeaderValues.CACHE.lookup(value);
743 _expect = HttpHeaderValues.CACHE.getOrdinal(value);
744 break;
745
746 case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
747 case HttpHeaders.USER_AGENT_ORDINAL:
748 value = HttpHeaderValues.CACHE.lookup(value);
749 break;
750
751 case HttpHeaders.CONTENT_TYPE_ORDINAL:
752 value = MimeTypes.CACHE.lookup(value);
753 _charset = MimeTypes.getCharsetFromContentType(value);
754 break;
755
756 case HttpHeaders.CONNECTION_ORDINAL:
757
758
759 int ordinal = HttpHeaderValues.CACHE.getOrdinal(value);
760 switch (ordinal)
761 {
762 case -1:
763 {
764 String[] values = value.toString().split(",");
765 for (int i = 0; values != null && i < values.length; i++)
766 {
767 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
768
769 if (cb != null)
770 {
771 switch (cb.getOrdinal())
772 {
773 case HttpHeaderValues.CLOSE_ORDINAL:
774 _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
775 _generator.setPersistent(false);
776 break;
777
778 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
779 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
780 _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
781 break;
782 }
783 }
784 }
785 break;
786 }
787 case HttpHeaderValues.CLOSE_ORDINAL:
788 _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
789 _generator.setPersistent(false);
790 break;
791
792 case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
793 if (_version == HttpVersions.HTTP_1_0_ORDINAL)
794 _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
795 break;
796 }
797 }
798
799 _requestFields.add(name,value);
800 }
801
802
803
804
805 public void headerComplete() throws IOException
806 {
807 _requests++;
808 _generator.setVersion(_version);
809 switch (_version)
810 {
811 case HttpVersions.HTTP_0_9_ORDINAL:
812 break;
813 case HttpVersions.HTTP_1_0_ORDINAL:
814 _generator.setHead(_head);
815 break;
816 case HttpVersions.HTTP_1_1_ORDINAL:
817 _generator.setHead(_head);
818
819 if (_server.getSendDateHeader())
820 _responseFields.put(HttpHeaders.DATE_BUFFER,_request.getTimeStampBuffer(),_request.getTimeStamp());
821
822 if (!_host)
823 {
824 _generator.setResponse(HttpStatus.ORDINAL_400_Bad_Request,null);
825 _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
826 _generator.completeHeader(_responseFields,true);
827 _generator.complete();
828 return;
829 }
830
831 if (_expect != UNKNOWN)
832 {
833 if (_expect == HttpHeaderValues.CONTINUE_ORDINAL)
834 {
835
836
837 if (((HttpParser)_parser).getHeaderBuffer() == null || ((HttpParser)_parser).getHeaderBuffer().length() < 2)
838 {
839 _generator.setResponse(HttpStatus.ORDINAL_100_Continue,null);
840 _generator.completeHeader(null,true);
841 _generator.complete();
842 _generator.reset(false);
843 }
844 }
845 else if (_expect == HttpHeaderValues.PROCESSING_ORDINAL)
846 {
847 }
848 else
849 {
850 _generator.sendError(HttpStatus.ORDINAL_417_Expectation_Failed,null,null,true);
851 return;
852 }
853 }
854
855 break;
856 default:
857 }
858
859 if (_charset != null)
860 _request.setCharacterEncodingUnchecked(_charset);
861
862
863 if (((HttpParser)_parser).getContentLength() <= 0 && !((HttpParser)_parser).isChunking())
864 handleRequest();
865 else
866 _delayedHandling = true;
867 }
868
869
870
871
872
873
874 public void content(Buffer ref) throws IOException
875 {
876 if (_delayedHandling)
877 {
878 _delayedHandling = false;
879 handleRequest();
880 }
881 }
882
883
884
885
886
887
888 public void messageComplete(long contextLength) throws IOException
889 {
890 if (_delayedHandling)
891 {
892 _delayedHandling = false;
893 handleRequest();
894 }
895 }
896
897
898
899
900
901
902
903
904 public void startResponse(Buffer version, int status, Buffer reason)
905 {
906 Log.debug("Bad request!: " + version + " " + status + " " + reason);
907 }
908
909 }
910
911
912
913
914 public class Output extends AbstractGenerator.Output
915 {
916 Output()
917 {
918 super((AbstractGenerator)HttpConnection.this._generator,_connector.getMaxIdleTime());
919 }
920
921
922
923
924
925 public void close() throws IOException
926 {
927 if (_closed)
928 return;
929
930 if (!isIncluding() && !_generator.isCommitted())
931 commitResponse(HttpGenerator.LAST);
932 else
933 flushResponse();
934
935 super.close();
936 }
937
938
939
940
941
942 public void flush() throws IOException
943 {
944 if (!_generator.isCommitted())
945 commitResponse(HttpGenerator.MORE);
946 super.flush();
947 }
948
949
950
951
952
953 public void print(String s) throws IOException
954 {
955 if (_closed)
956 throw new IOException("Closed");
957 PrintWriter writer = getPrintWriter(null);
958 writer.print(s);
959 }
960
961
962 public void sendResponse(Buffer response) throws IOException
963 {
964 ((HttpGenerator)_generator).sendResponse(response);
965 }
966
967
968 public void sendContent(Object content) throws IOException
969 {
970 Resource resource = null;
971
972 if (_closed)
973 throw new IOException("Closed");
974
975 if (_generator.getContentWritten() > 0)
976 throw new IllegalStateException("!empty");
977
978 if (content instanceof HttpContent)
979 {
980 HttpContent c = (HttpContent)content;
981 if (c.getContentType() != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
982 _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER,c.getContentType());
983 if (c.getContentLength() > 0)
984 _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER,c.getContentLength());
985 Buffer lm = c.getLastModified();
986 long lml = c.getResource().lastModified();
987 if (lm != null)
988 _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER,lm,lml);
989 else if (c.getResource() != null)
990 {
991 if (lml != -1)
992 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,lml);
993 }
994
995 content = c.getBuffer();
996 if (content == null)
997 content = c.getInputStream();
998 }
999 else if (content instanceof Resource)
1000 {
1001 resource = (Resource)content;
1002 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,resource.lastModified());
1003 content = resource.getInputStream();
1004 }
1005
1006 if (content instanceof Buffer)
1007 {
1008 _generator.addContent((Buffer)content,HttpGenerator.LAST);
1009 commitResponse(HttpGenerator.LAST);
1010 }
1011 else if (content instanceof InputStream)
1012 {
1013 InputStream in = (InputStream)content;
1014
1015 try
1016 {
1017 int max = _generator.prepareUncheckedAddContent();
1018 Buffer buffer = _generator.getUncheckedBuffer();
1019
1020 int len = buffer.readFrom(in,max);
1021
1022 while (len >= 0)
1023 {
1024 _generator.completeUncheckedAddContent();
1025 _out.flush();
1026
1027 max = _generator.prepareUncheckedAddContent();
1028 buffer = _generator.getUncheckedBuffer();
1029 len = buffer.readFrom(in,max);
1030 }
1031 _generator.completeUncheckedAddContent();
1032 _out.flush();
1033 }
1034 finally
1035 {
1036 if (resource != null)
1037 resource.release();
1038 else
1039 in.close();
1040
1041 }
1042 }
1043 else
1044 throw new IllegalArgumentException("unknown content type?");
1045
1046 }
1047 }
1048
1049
1050
1051
1052 public class OutputWriter extends AbstractGenerator.OutputWriter
1053 {
1054 OutputWriter()
1055 {
1056 super(HttpConnection.this._out);
1057 }
1058 }
1059
1060 }