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