content: /*
   * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
   *
   * The Apache Software License, Version 1.1
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Caucho Technology (http://www.caucho.com/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "Hessian", "Resin", and "Caucho" must not be used to
   *    endorse or promote products derived from this software without prior
   *    written permission. For written permission, please contact
   *    info@caucho.com.
   *
   * 5. Products derived from this software may not be called "Resin"
   *    nor may "Resin" appear in their names without prior written
   *    permission of Caucho Technology.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
   * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
   * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
   * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * @author Scott Ferguson
   */
  
  package com.caucho.hessian.mux;
  
  import java.io.IOException;
  import java.io.InputStream;
  
  /**
   * Input stream to a specific channel.
   */
  public class MuxInputStream extends InputStream {
    private MuxServer server;
    protected InputStream is;
    private int channel;
    
    private String url;
    
    private int chunkLength;
  
    /**
     * Null argument constructor.
     */
    public MuxInputStream()
    {
    }
  
    /**
     * Initialize the multiplexor with input and output streams.
     */
    protected void init(MuxServer server, int channel)
      throws IOException
    {
      this.server = server;
      this.channel = channel;
  
      this.url = null;
  
      chunkLength = 0;
    }
  
    /**
     * Gets the raw input stream.  Clients will normally not call
     * this.
     */
    protected InputStream getInputStream()
      throws IOException
    {
      if (is == null && server != null)
        is = server.readChannel(channel);
      
      return is;
    }
  
    void setInputStream(InputStream is)
    {
      this.is = is;
    }
  
    /**
     * Gets the channel of the connection.
     */
    public int getChannel()
    {
      return channel;
    }
  
    /**
     * Returns the request's URL
     */
    public String getURL()
    {
      return url;
    }
  
    /**
     * Writes a data byte to the output stream.
     */
    public int read()
      throws IOException
    {
      if (chunkLength <= 0) {
        readToData(false);
  
        if (chunkLength <= 0)
          return -1;
      }
      
      chunkLength--;
      return is.read();
    }
  
    /**
     * Complete writing to the stream, closing the channel.
     */
    public void close()
      throws IOException
    {
      skipToEnd();
    }
  
    /**
     * Skips data until the end of the channel.
     */
    private void skipToEnd()
      throws IOException
    {
      InputStream is = getInputStream();
      
      if (is == null)
        return;
  
      if (chunkLength > 0)
        is.skip(chunkLength);
  
      for (int tag = is.read(); tag >= 0; tag = is.read()) {
        switch (tag) {
        case 'Y':
          server.freeReadLock();
          this.is = is = server.readChannel(channel);
          if (is == null) {
            this.server = null;
            return;
          }
          break;
          
        case 'Q':
          server.freeReadLock();
          this.is = null;
          this.server = null;
          return;
  
        case -1:
          server.freeReadLock();
          this.is = null;
          this.server = null;
          return;
          
        default:
          int length = (is.read() << 8) + is.read();
          is.skip(length);
          break;
        }
      }
    }
  
    /**
     * Reads tags, until getting data.
     */
    void readToData(boolean returnOnYield)
      throws IOException
    {
      InputStream is = getInputStream();
      
      if (is == null)
        return;
      
      for (int tag = is.read(); tag >= 0; tag = is.read()) {
        switch (tag) {
        case 'Y':
          server.freeReadLock();
          if (returnOnYield)
            return;
          server.readChannel(channel);
          break;
          
        case 'Q':
          server.freeReadLock();
          this.is = null;
          this.server = null;
          return;
          
        case 'U':
          this.url = readUTF();
          break;
  
        case 'D':
          chunkLength = (is.read() << 8) + is.read();
          return;
  
        default:
          readTag(tag);
          break;
        }
      }
    }
  
    /**
     * Subclasses will extend this to read values.
     */
    protected void readTag(int tag)
      throws IOException
    {
      int length = (is.read() << 8) + is.read();
      is.skip(length);
    }
  
    /**
     * Reads a UTF-8 string.
     *
     * @return the utf-8 encoded string
     */
    protected String readUTF()
      throws IOException
    {
      int len = (is.read() << 8) + is.read();
  
      StringBuffer sb = new StringBuffer();
  
      while (len > 0) {
        int d1 = is.read();
  
        if (d1 < 0)
          return sb.toString();
        else if (d1 < 0x80) {
          len--;
          sb.append((char) d1);
        }
        else if ((d1 & 0xe0) == 0xc0) {
          len -= 2;
          sb.append(((d1 & 0x1f) << 6) + (is.read() & 0x3f));
        }
        else if ((d1 & 0xf0) == 0xe0) {
          len -= 3;
          sb.append(((d1 & 0x0f) << 12) +
                    ((is.read() & 0x3f) << 6) +
                    (is.read() & 0x3f));
        }
        else
          throw new IOException("utf-8 encoding error");
      }
  
      return sb.toString();
    }
  }
  