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