/**
   * Writes an array of bytes on the stream. This method may not be used until this stream has been
   * passed to one of the methods in HTTPConnection (i.e. until it has been associated with a
   * request).
   *
   * @param buf an array containing the data to write
   * @param off the offset of the data whithin the buffer
   * @param len the number bytes (starting at <var>off</var>) to write
   * @exception IOException if any exception is thrown by the socket, or if writing <var>len</var>
   *     bytes would cause more bytes to be written than this stream is willing to accept.
   * @exception IllegalAccessError if this stream has not been associated with a request yet
   */
  public synchronized void write(byte[] buf, int off, int len)
      throws IOException, IllegalAccessError {
    if (req == null) throw new IllegalAccessError("Stream not associated with a request");

    if (ignore) return;

    if (length != -1 && rcvd + len > length) {
      IOException ioe =
          new IOException("Tried to write too many bytes (" + (rcvd + len) + " > " + length + ")");
      req.getConnection().closeDemux(ioe, false);
      req.getConnection().outputFinished();
      throw ioe;
    }

    try {
      if (bos != null) bos.write(buf, off, len);
      else if (length != -1) os.write(buf, off, len);
      else os.write(Codecs.chunkedEncode(buf, off, len, null, false));
    } catch (IOException ioe) {
      req.getConnection().closeDemux(ioe, true);
      req.getConnection().outputFinished();
      throw ioe;
    }

    rcvd += len;
  }
  /**
   * Closes the stream and causes the data to be sent if it has not already been done so. This
   * method <strong>must</strong> be invoked when all data has been written.
   *
   * @exception IOException if any exception is thrown by the underlying socket, or if too few bytes
   *     were written.
   * @exception IllegalAccessError if this stream has not been associated with a request yet.
   */
  public synchronized void close() throws IOException, IllegalAccessError {
    if (req == null) throw new IllegalAccessError("Stream not associated with a request");

    if (ignore) return;

    if (bos != null) {
      req.setData(bos.toByteArray());
      req.setStream(null);

      if (trailers.length > 0) {
        NVPair[] hdrs = req.getHeaders();

        // remove any Trailer header field

        int len = hdrs.length;
        for (int idx = 0; idx < len; idx++) {
          if (hdrs[idx].getName().equalsIgnoreCase("Trailer")) {
            System.arraycopy(hdrs, idx + 1, hdrs, idx, len - idx - 1);
            len--;
          }
        }

        // add the trailers to the headers

        hdrs = Util.resizeArray(hdrs, len + trailers.length);
        System.arraycopy(trailers, 0, hdrs, len, trailers.length);

        req.setHeaders(hdrs);
      }

      if (DebugConn) System.err.println("OutS:  Sending request");

      try {
        resp = req.getConnection().sendRequest(req, con_to);
      } catch (ModuleException me) {
        throw new IOException(me.toString());
      }
      notify();
    } else {
      if (rcvd < length) {
        IOException ioe =
            new IOException(
                "Premature close: only "
                    + rcvd
                    + " bytes written instead of the "
                    + "expected "
                    + length);
        req.getConnection().closeDemux(ioe, false);
        req.getConnection().outputFinished();
        throw ioe;
      }

      try {
        if (length == -1) {
          if (DebugConn && trailers.length > 0) {
            System.err.println("OutS:  Sending trailers:");
            for (int idx = 0; idx < trailers.length; idx++)
              System.err.println(
                  "       " + trailers[idx].getName() + ": " + trailers[idx].getValue());
          }

          os.write(Codecs.chunkedEncode(null, 0, 0, trailers, true));
        }

        os.flush();

        if (DebugConn) System.err.println("OutS:  All data sent");
      } catch (IOException ioe) {
        req.getConnection().closeDemux(ioe, true);
        throw ioe;
      } finally {
        req.getConnection().outputFinished();
      }
    }
  }