View Javadoc

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