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