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