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