View Javadoc

1   //========================================================================
2   //Copyright 2006 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //Licensed under the Apache License, Version 2.0 (the "License");
5   //you may not use this file except in compliance with the License.
6   //You may obtain a copy of the License at
7   //http://www.apache.org/licenses/LICENSE-2.0
8   //Unless required by applicable law or agreed to in writing, software
9   //distributed under the License is distributed on an "AS IS" BASIS,
10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  //See the License for the specific language governing permissions and
12  //limitations under the License.
13  //========================================================================
14  
15  package org.mortbay.jetty.ajp;
16  
17  import java.io.IOException;
18  import java.io.UnsupportedEncodingException;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  
22  import org.mortbay.io.Buffer;
23  import org.mortbay.io.Buffers;
24  import org.mortbay.io.ByteArrayBuffer;
25  import org.mortbay.io.EndPoint;
26  import org.mortbay.jetty.*;
27  import org.mortbay.jetty.HttpFields.Field;
28  import org.mortbay.log.Log;
29  import org.mortbay.util.StringUtil;
30  import org.mortbay.util.TypeUtil;
31  
32  /**
33   * @author lagdeppa (at) exist.com
34   * @author Greg Wilkins
35   */                                                                                                       
36  public class Ajp13Generator extends AbstractGenerator
37  {
38      private static HashMap __headerHash = new HashMap();
39  
40      static
41      {
42          byte[] xA001 =
43          { (byte) 0xA0, (byte) 0x01 };
44          byte[] xA002 =
45          { (byte) 0xA0, (byte) 0x02 };
46          byte[] xA003 =
47          { (byte) 0xA0, (byte) 0x03 };
48          byte[] xA004 =
49          { (byte) 0xA0, (byte) 0x04 };
50          byte[] xA005 =
51          { (byte) 0xA0, (byte) 0x05 };
52          byte[] xA006 =
53          { (byte) 0xA0, (byte) 0x06 };
54          byte[] xA007 =
55          { (byte) 0xA0, (byte) 0x07 };
56          byte[] xA008 =
57          { (byte) 0xA0, (byte) 0x08 };
58          byte[] xA009 =
59          { (byte) 0xA0, (byte) 0x09 };
60          byte[] xA00A =
61          { (byte) 0xA0, (byte) 0x0A };
62          byte[] xA00B =
63          { (byte) 0xA0, (byte) 0x0B };
64          __headerHash.put("Content-Type", xA001);
65          __headerHash.put("Content-Language", xA002);
66          __headerHash.put("Content-Length", xA003);
67          __headerHash.put("Date", xA004);
68          __headerHash.put("Last-Modified", xA005);
69          __headerHash.put("Location", xA006);
70          __headerHash.put("Set-Cookie", xA007);
71          __headerHash.put("Set-Cookie2", xA008);
72          __headerHash.put("Servlet-Engine", xA009);
73          __headerHash.put("Status", xA00A);
74          __headerHash.put("WWW-Authenticate", xA00B);
75  
76      }
77  
78      // A, B ajp response header
79      // 0, 1 ajp int 1 packet length
80      // 9 CPONG response Code
81      private static final byte[] AJP13_CPONG_RESPONSE =
82      { 'A', 'B', 0, 1, 9};
83  
84      private static final byte[] AJP13_END_RESPONSE =
85      { 'A', 'B', 0, 2, 5, 1 };
86  
87      // AB ajp respose
88      // 0, 3 int = 3 packets in length
89      // 6, send signal to get more data
90      // 31, -7 byte values for int 8185 = (8 * 1024) - 7 MAX_DATA
91      private static final byte[] AJP13_MORE_CONTENT =
92      { 'A', 'B', 0, 3, 6, 31, -7 };
93  
94      private static String SERVER = "Server: Jetty(6.0.x)";
95  
96      public static void setServerVersion(String version)
97      {
98          SERVER = "Jetty(" + version + ")";
99      }
100 
101     /* ------------------------------------------------------------ */
102     private boolean _expectMore = false;
103 
104     private boolean _needMore = false;
105 
106     private boolean _needEOC = false;
107 
108     private boolean _bufferPrepared = false;
109 
110     /* ------------------------------------------------------------ */
111     public Ajp13Generator(Buffers buffers, EndPoint io, int headerBufferSize, int contentBufferSize)
112     {
113         super(buffers, io, headerBufferSize, contentBufferSize);
114     }
115 
116     /* ------------------------------------------------------------ */
117     public void reset(boolean returnBuffers)
118     {
119         super.reset(returnBuffers);
120 
121         _needEOC = false;
122         _needMore = false;
123         _expectMore = false;
124         _bufferPrepared = false;
125         _last=false;
126 
127 
128 
129         _state = STATE_HEADER;
130 
131         _status = 0;
132         _version = HttpVersions.HTTP_1_1_ORDINAL;
133         _reason = null;
134         _method = null;
135         _uri = null;
136 
137         _contentWritten = 0;
138         _contentLength = HttpTokens.UNKNOWN_CONTENT;
139         _last = false;
140         _head = false;
141         _noContent = false;
142         _close = false;
143 
144 
145        
146 
147        _header = null; // Buffer for HTTP header (and maybe small _content)
148        _buffer = null; // Buffer for copy of passed _content
149        _content = null; // Buffer passed to addContent
150 
151 
152     }
153 
154     /* ------------------------------------------------------------ */
155     /**
156      * Add content.
157      * 
158      * @param content
159      * @param last
160      * @throws IllegalArgumentException
161      *             if <code>content</code> is
162      *             {@link Buffer#isImmutable immutable}.
163      * @throws IllegalStateException
164      *             If the request is not expecting any more content, or if the
165      *             buffers are full and cannot be flushed.
166      * @throws IOException
167      *             if there is a problem flushing the buffers.
168      */
169     public void addContent(Buffer content, boolean last) throws IOException
170     {
171         if (_noContent)
172         {
173             content.clear();
174             return;
175         }
176 
177         if (content.isImmutable())
178             throw new IllegalArgumentException("immutable");
179 
180         if (_last || _state == STATE_END)
181         {
182             Log.debug("Ignoring extra content {}", content);
183             content.clear();
184             return;
185         }
186         _last = last;
187 
188         if(!_endp.isOpen())
189         {
190             _state = STATE_END;
191             return;
192         }
193 
194         // Handle any unfinished business?
195         if (_content != null && _content.length() > 0)
196         {
197 
198             flush();
199             if (_content != null && _content.length() > 0)
200                 throw new IllegalStateException("FULL");
201         }
202 
203         _content = content;
204 
205         _contentWritten += content.length();
206 
207         // Handle the _content
208         if (_head)
209         {
210 
211             content.clear();
212             _content = null;
213         }
214         else
215         {
216             // Yes - so we better check we have a buffer
217             initContent();
218             // Copy _content to buffer;
219             int len = 0;
220             len = _buffer.put(_content);
221 
222             // make sure there is space for a trailing null
223             if (len > 0 && _buffer.space() == 0)
224             {
225                 len--;
226                 _buffer.setPutIndex(_buffer.putIndex() - 1);
227             }
228 
229             _content.skip(len);
230 
231             if (_content.length() == 0)
232                 _content = null;
233         }
234     }
235 
236     /* ------------------------------------------------------------ */
237     /**
238      * Add content.
239      * 
240      * @param b
241      *            byte
242      * @return true if the buffers are full
243      * @throws IOException
244      */
245     public boolean addContent(byte b) throws IOException
246     {
247 
248         if (_noContent)
249             return false;
250 
251         if (_last || _state == STATE_END)
252             throw new IllegalStateException("Closed");
253 
254 
255         if(!_endp.isOpen())
256         {
257             _state = STATE_END;
258             return false;
259         }
260 
261         // Handle any unfinished business?
262         if (_content != null && _content.length() > 0)
263         {
264             flush();
265             if (_content != null && _content.length() > 0)
266                 throw new IllegalStateException("FULL");
267         }
268 
269         _contentWritten++;
270 
271         // Handle the _content
272         if (_head)
273             return false;
274 
275         // we better check we have a buffer
276         initContent();
277 
278         // Copy _content to buffer;
279 
280         _buffer.put(b);
281 
282         return _buffer.space() <= 1;
283     }
284 
285     /* ------------------------------------------------------------ */
286     /**
287      * Prepare buffer for unchecked writes. Prepare the generator buffer to
288      * receive unchecked writes
289      * 
290      * @return the available space in the buffer.
291      * @throws IOException
292      */
293     protected int prepareUncheckedAddContent() throws IOException
294     {
295         if (_noContent)
296             return -1;
297 
298         if (_last || _state == STATE_END)
299             throw new IllegalStateException("Closed");
300 
301 
302         if(!_endp.isOpen())
303         {
304             _state = STATE_END;
305             return -1;
306         }
307 
308         // Handle any unfinished business?
309         Buffer content = _content;
310         if (content != null && content.length() > 0)
311         {
312             flush();
313             if (content != null && content.length() > 0)
314                 throw new IllegalStateException("FULL");
315         }
316 
317         // we better check we have a buffer
318         initContent();
319 
320         _contentWritten -= _buffer.length();
321 
322         // Handle the _content
323         if (_head)
324             return Integer.MAX_VALUE;
325 
326         return _buffer.space() - 1;
327     }
328 
329     /* ------------------------------------------------------------ */
330     public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException
331     {
332         if (_state != STATE_HEADER)
333             return;
334 
335         if (_last && !allContentAdded)
336             throw new IllegalStateException("last?");
337         _last = _last | allContentAdded;
338 
339         boolean has_server = false;
340         if (_version == HttpVersions.HTTP_1_0_ORDINAL)
341             _close = true;
342 
343         // get a header buffer
344         if (_header == null)
345             _header = _buffers.getBuffer(_headerBufferSize);
346 
347         Buffer tmpbuf = _buffer;
348         _buffer = _header;
349 
350         try
351         {
352             // start the header
353             _buffer.put((byte) 'A');
354             _buffer.put((byte) 'B');
355             addInt(0);
356             _buffer.put((byte) 0x4);
357             addInt(_status);
358             if (_reason == null)
359                 _reason = getReasonBuffer(_status);
360             if (_reason == null)
361                 _reason = new ByteArrayBuffer(TypeUtil.toString(_status));
362             addBuffer(_reason);
363 
364             if (_status == 100 || _status == 204 || _status == 304)
365             {
366                 _noContent = true;
367                 _content = null;
368             }
369 
370 
371             // allocate 2 bytes for number of headers
372             int field_index = _buffer.putIndex();
373             addInt(0);
374 
375             int num_fields = 0;
376 
377             if (fields != null)
378             { 
379                 // Add headers
380                 Iterator i = fields.getFields();
381 
382                 while (i.hasNext())
383                 {
384                     num_fields++;
385                     Field f = (Field) i.next();
386 
387                     byte[] codes = (byte[]) __headerHash.get(f.getName());
388                     if (codes != null)
389                     {
390                         _buffer.put(codes);
391                     }
392                     else
393                     {
394                         addString(f.getName());
395                     }
396                     addString(f.getValue());
397                 }
398             }
399 
400             if (!has_server && _status > 100 && getSendServerVersion())
401             {
402                 num_fields++;
403                 addString("Server");
404                 addString(SERVER);
405             }
406 
407             // TODO Add content length if last content known.
408 
409             // insert the number of headers
410             int tmp = _buffer.putIndex();
411             _buffer.setPutIndex(field_index);
412             addInt(num_fields);
413             _buffer.setPutIndex(tmp);
414 
415             // get the payload size ( - 4 bytes for the ajp header)
416             // excluding the
417             // ajp header
418             int payloadSize = _buffer.length() - 4;
419             // insert the total packet size on 2nd and 3rd byte that
420             // was previously
421             // allocated
422             addInt(2, payloadSize);
423         }
424         finally
425         {
426             _buffer = tmpbuf;
427         }
428 
429 
430         _state = STATE_CONTENT;
431 
432     }
433 
434     /* ------------------------------------------------------------ */
435     /**
436      * Complete the message.
437      * 
438      * @throws IOException
439      */
440     public void complete() throws IOException
441     {
442         if (_state == STATE_END)
443             return;
444 
445         super.complete();
446 
447         if (_state < STATE_FLUSHING)
448         {
449             _state = STATE_FLUSHING;
450             _needEOC = true;
451         }
452 
453         flush();
454     }
455 
456     /* ------------------------------------------------------------ */
457     public long flush() throws IOException
458     {
459         try
460         {
461             if (_state == STATE_HEADER  && !_expectMore)
462                 throw new IllegalStateException("State==HEADER");
463             prepareBuffers();
464 
465             if (_endp == null)
466             {
467                 // TODO - probably still needed!
468                 // if (_rneedMore && _buffe != null)
469                 // {
470                 // if(!_hasSentEOC)
471                 // _buffer.put(AJP13_MORE_CONTENT);
472                 // }
473                 if (!_expectMore && _needEOC && _buffer != null)
474                 {
475                     _buffer.put(AJP13_END_RESPONSE);
476                 }
477                 _needEOC = false;
478                 return 0;
479             }
480 
481             // Keep flushing while there is something to flush
482             // (except break below)
483             int total = 0;
484             long last_len = -1;
485             Flushing: while (true)
486             {
487                 int len = -1;
488                 int to_flush = ((_header != null && _header.length() > 0) ? 4 : 0) | ((_buffer != null && _buffer.length() > 0) ? 2 : 0);
489                 
490 
491                 switch (to_flush)
492                 {
493                 case 7:
494                     throw new IllegalStateException(); // should
495                     // never
496                     // happen!
497                 case 6:
498                     len = _endp.flush(_header, _buffer, null);
499 
500                     break;
501                 case 5:
502                     throw new IllegalStateException(); // should
503                     // never
504                     // happen!
505                 case 4:
506                     len = _endp.flush(_header);
507                     break;
508                 case 3:
509                     throw new IllegalStateException(); // should
510                     // never
511                     // happen!
512                 case 2:
513                     len = _endp.flush(_buffer);
514 
515                     break;
516                 case 1:
517                     throw new IllegalStateException(); // should
518                     // never
519                     // happen!
520                 case 0:
521                 {
522                     // Nothing more we can write now.
523                     if (_header != null)
524                         _header.clear();
525 
526                     _bufferPrepared = false;
527 
528                     if (_buffer != null)
529                     {
530                         _buffer.clear();
531 
532                         // reserve some space for the
533                         // header
534                         _buffer.setPutIndex(7);
535                         _buffer.setGetIndex(7);
536 
537                         // Special case handling for
538                         // small left over buffer from
539                         // an addContent that caused a
540                         // buffer flush.
541                         if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING)
542                         {
543 
544                             _buffer.put(_content);
545                             _content.clear();
546                             _content = null;
547                             break Flushing;
548                         }
549 
550                     }
551 
552 
553 
554                     // Are we completely finished for now?
555                     if (!_expectMore && !_needEOC && (_content == null || _content.length() == 0))
556                     {
557                         if (_state == STATE_FLUSHING)
558                             _state = STATE_END;
559 
560 //                        if (_state == STATE_END)
561 //                        {
562 //                            _endp.close();
563 //                        }
564 //
565 
566                         break Flushing;
567                     }
568 
569                     // Try to prepare more to write.
570                     prepareBuffers();
571                 }
572                 }
573 
574                 // If we failed to flush anything twice in a row
575                 // break
576                 if (len <= 0)
577                 {
578                     if (last_len <= 0)
579                         break Flushing;
580                     break;
581                 }
582                 last_len = len;
583                 total += len;
584             }
585 
586 
587 
588             return total;
589         }
590         catch (IOException e)
591         {
592             Log.ignore(e);
593             throw (e instanceof EofException) ? e : new EofException(e);
594         }
595 
596     }
597 
598     /* ------------------------------------------------------------ */
599     private void prepareBuffers()
600     {
601         if (!_bufferPrepared)
602         {
603 
604             // Refill buffer if possible
605             if (_content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0)
606             {
607 
608                 int len = _buffer.put(_content);
609 
610                 // Make sure there is space for a trailing null
611                 if (len > 0 && _buffer.space() == 0)
612                 {
613                     len--;
614                     _buffer.setPutIndex(_buffer.putIndex() - 1);
615                 }
616                 _content.skip(len);
617 
618                 if (_content.length() == 0)
619                     _content = null;
620 
621                 if (_buffer.length() == 0)
622                 {
623                     _content = null;
624                 }
625             }
626 
627             // add header if needed
628             if (_buffer != null)
629             {
630 
631                 int payloadSize = _buffer.length();
632 
633                 // 4 bytes for the ajp header
634                 // 1 byte for response type
635                 // 2 bytes for the response size
636                 // 1 byte because we count from zero??
637 
638                 if (payloadSize > 0)
639                 {
640                     _bufferPrepared = true;
641 
642                     _buffer.put((byte) 0);
643                     int put = _buffer.putIndex();
644                     _buffer.setGetIndex(0);
645                     _buffer.setPutIndex(0);
646                     _buffer.put((byte) 'A');
647                     _buffer.put((byte) 'B');
648                     addInt(payloadSize + 4);
649                     _buffer.put((byte) 3);
650                     addInt(payloadSize);
651                     _buffer.setPutIndex(put);
652                 }
653             }
654 
655             if (_needMore)
656             {
657 
658                 if (_header == null)
659                 {
660                     _header = _buffers.getBuffer(_headerBufferSize);
661                 }
662 
663                 if (_buffer == null && _header != null && _header.space() >= AJP13_MORE_CONTENT.length)
664                 {
665                     _header.put(AJP13_MORE_CONTENT);
666                     _needMore = false;
667                 }
668                 else if (_buffer != null && _buffer.space() >= AJP13_MORE_CONTENT.length)
669                 {
670                     // send closing packet if all contents
671                     // are added
672                     _buffer.put(AJP13_MORE_CONTENT);
673                     _needMore = false;
674                     _bufferPrepared = true;
675                 }
676 
677             }
678 
679             if (!_expectMore && _needEOC)
680             {
681                 if (_buffer == null && _header.space() >= AJP13_END_RESPONSE.length)
682                 {
683 
684                     _header.put(AJP13_END_RESPONSE);
685                     _needEOC = false;
686                 }
687                 else if (_buffer != null && _buffer.space() >= AJP13_END_RESPONSE.length)
688                 {
689                     // send closing packet if all contents
690                     // are added
691 
692                     _buffer.put(AJP13_END_RESPONSE);
693                     _needEOC = false;
694                     _bufferPrepared = true;
695                 }
696             }
697         }
698     }
699 
700     /* ------------------------------------------------------------ */
701     public boolean isComplete()
702     {
703         return !_expectMore && _state == STATE_END;
704     }
705 
706     /* ------------------------------------------------------------ */
707     private void initContent() throws IOException
708     {
709         if (_buffer == null)
710         {
711             _buffer = _buffers.getBuffer(_contentBufferSize);
712             _buffer.setPutIndex(7);
713             _buffer.setGetIndex(7);
714         }
715     }
716 
717     /* ------------------------------------------------------------ */
718     private void addInt(int i)
719     {
720         _buffer.put((byte) ((i >> 8) & 0xFF));
721         _buffer.put((byte) (i & 0xFF));
722     }
723 
724     /* ------------------------------------------------------------ */
725     private void addInt(int startIndex, int i)
726     {
727         _buffer.poke(startIndex, (byte) ((i >> 8) & 0xFF));
728         _buffer.poke((startIndex + 1), (byte) (i & 0xFF));
729     }
730 
731     /* ------------------------------------------------------------ */
732     private void addString(String str) throws UnsupportedEncodingException
733     {
734         if (str == null)
735         {
736             addInt(0xFFFF);
737             return;
738         }
739 
740         // TODO - need to use a writer to convert, to avoid this hacky
741         // conversion and temp buffer
742         byte[] b = str.getBytes(StringUtil.__ISO_8859_1);
743 
744         addInt(b.length);
745 
746         _buffer.put(b);
747         _buffer.put((byte) 0);
748     }
749 
750     /* ------------------------------------------------------------ */
751     private void addBuffer(Buffer b)
752     {
753         if (b == null)
754         {
755             addInt(0xFFFF);
756             return;
757         }
758 
759         addInt(b.length());
760         _buffer.put(b);
761         _buffer.put((byte) 0);
762     }
763 
764     /* ------------------------------------------------------------ */
765     public void getBodyChunk() throws IOException
766     {
767         _needMore = true;
768         _expectMore = true;
769         flush();
770     }
771 
772     /* ------------------------------------------------------------ */
773     public void gotBody()
774     {
775         _needMore = false;
776         _expectMore = false;
777     }
778 
779 
780     /* ------------------------------------------------------------ */
781     public void sendCPong() throws IOException
782     {
783 
784         Buffer buff = _buffers.getBuffer(AJP13_CPONG_RESPONSE.length);
785         buff.put(AJP13_CPONG_RESPONSE);
786 
787         // flushing cpong response
788         do
789         {
790             _endp.flush(buff);
791 
792         }
793         while(buff.length() >0);
794         _buffers.returnBuffer(buff);
795 
796         reset(true);
797 
798     }
799 
800 
801 
802 }