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