View Javadoc

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