/**
   * Setup the reponse output stream. Use the current state of the request and response, to set
   * tranfer parameters such as chunking and content length.
   */
  protected void firstWrite() throws IOException {
    if (_response.isCommitted()) return;

    // Nobble the OutputStream for HEAD requests
    if (HttpRequest.__HEAD.equals(_request.getMethod())) _outputStream.nullOutput();

    int length = _response.getIntField(HttpFields.__ContentLength);
    if (length >= 0) _outputStream.setContentLength(length);
  }
  /* Verify HTTP/1.1 request
   * @exception HttpException problem with the request.
   * @exception IOException problem with the connection.
   */
  private void verifyHTTP_1_1() throws HttpException, IOException {
    // Check Host Field exists
    String host = _request.getField(HttpFields.__Host);
    if (host == null) throw new HttpException(HttpResponse.__400_Bad_Request);

    // check and enable requests transfer encodings.
    String transfer_coding = _request.getField(HttpFields.__TransferEncoding);

    if (transfer_coding != null && transfer_coding.length() > 0) {
      // Handling of codings other than chunking is now
      // the responsibility of handlers, filters or servlets.
      // Thanks to the compression filter, we now don't know if
      // what we can handle here.
      if (transfer_coding.equalsIgnoreCase(HttpFields.__Chunked)
          || StringUtil.endsWithIgnoreCase(transfer_coding, HttpFields.__Chunked))
        _inputStream.setChunking();
      else if (StringUtil.asciiToLowerCase(transfer_coding).indexOf(HttpFields.__Chunked) >= 0)
        throw new HttpException(HttpResponse.__400_Bad_Request);
    }

    // Check input content length can be determined
    int content_length = _request.getIntField(HttpFields.__ContentLength);
    String content_type = _request.getField(HttpFields.__ContentType);
    if (!_inputStream.isChunking()) {
      // If we have a content length, use it
      if (content_length >= 0) _inputStream.setContentLength(content_length);
      // else if we have no content
      else if (content_type == null || content_type.length() == 0) _inputStream.setContentLength(0);
      // else we need a content length
      else {
        // TODO - can't do this check as IE stuff up on
        // a redirect.
        // throw new HttpException(HttpResponse.__411_Length_Required);
        _inputStream.setContentLength(0);
      }
    }

    // Handle Continue Expectations
    String expect = _request.getField(HttpFields.__Expect);
    if (expect != null && expect.length() > 0) {
      if (StringUtil.asciiToLowerCase(expect).equals(HttpFields.__ExpectContinue)) {
        _inputStream.setExpectContinues(_outputStream.getOutputStream());
      } else throw new HttpException(HttpResponse.__417_Expectation_Failed);
    } else if (__2068_Continues
        && _inputStream.available() <= 0
        && (HttpRequest.__PUT.equals(_request.getMethod())
            || HttpRequest.__POST.equals(_request.getMethod()))) {
      // Send continue for RFC 2068 exception
      OutputStream real_out = _outputStream.getOutputStream();
      real_out.write(HttpResponse.__Continue);
      real_out.flush();
    }

    // Persistent unless requested otherwise
    _persistent = !_close;
  }
Beispiel #3
0
 protected void commit() throws HttpException {
   if (headersProvider != null) {
     headersProvider.writeHeaders(this);
   }
   try {
     request.writeHeader(HttpConstants.Headers.CONTENT_LENGTH, "0");
     request.writeLine("");
     request.flush();
     status = Status.COMPLETE;
   } catch (IOException ex) {
     throw new HttpConnectionException(ex);
   }
 }
Beispiel #4
0
 public void send(String contentType, byte[] content) throws HttpException {
   if (headersProvider != null) {
     headersProvider.writeHeaders(this);
   }
   try {
     request.writeHeader(HttpConstants.Headers.CONTENT_TYPE, contentType);
     request.writeIntHeader(HttpConstants.Headers.CONTENT_LENGTH, content.length);
     request.writeLine("");
     request.write(content);
     request.flush();
     status = Status.COMPLETE;
   } catch (IOException ex) {
     throw new HttpConnectionException(ex);
   }
 }
Beispiel #5
0
 public void addIntHeader(String name, int value) throws HttpException {
   try {
     request.writeIntHeader(name, value);
   } catch (IOException ex) {
     throw new HttpConnectionException(ex);
   }
 }
Beispiel #6
0
 protected void writeLine(String s) throws HttpException {
   try {
     request.writeLine(s);
   } catch (IOException ex) {
     throw new HttpConnectionException(ex);
   }
 }
  /**
   * Constructor.
   *
   * @param listener The listener that created this connection.
   * @param remoteAddr The address of the remote end or null.
   * @param in InputStream to read request(s) from.
   * @param out OutputputStream to write response(s) to.
   * @param connection The underlying connection object, most likely a socket. This is not used by
   *     HttpConnection other than to make it available via getConnection().
   */
  public HttpConnection(
      HttpListener listener,
      InetAddress remoteAddr,
      InputStream in,
      OutputStream out,
      Object connection) {
    if (log.isDebugEnabled()) log.debug("new HttpConnection: " + connection);
    _listener = listener;
    _remoteInetAddress = remoteAddr;
    int bufferSize = listener == null ? 4096 : listener.getBufferSize();
    int reserveSize = listener == null ? 512 : listener.getBufferReserve();
    _inputStream = new HttpInputStream(in, bufferSize);
    _outputStream = new HttpOutputStream(out, bufferSize, reserveSize);
    _outputStream.addObserver(this);
    _firstWrite = false;
    if (_listener != null) _httpServer = _listener.getHttpServer();
    _connection = connection;

    _statsOn = _httpServer != null && _httpServer.getStatsOn();
    if (_statsOn) {
      _openTime = System.currentTimeMillis();
      _httpServer.statsOpenConnection();
    }
    _reqTime = 0;
    _requests = 0;

    _request = new HttpRequest(this);
    _response = new HttpResponse(this);

    _resolveRemoteHost =
        _listener != null
            && _listener.getHttpServer() != null
            && _listener.getHttpServer().getResolveRemoteHost();
  }
  /** Destroy the connection. called by handle when handleNext returns false. */
  protected void destroy() {
    try {
      close();
    } catch (IOException e) {
      LogSupport.ignore(log, e);
    } catch (Exception e) {
      log.warn(LogSupport.EXCEPTION, e);
    }

    // Destroy request and response
    if (_request != null) _request.destroy();
    if (_response != null) _response.destroy();
    if (_inputStream != null) _inputStream.destroy();
    if (_outputStream != null) _outputStream.destroy();
    _inputStream = null;
    _outputStream = null;
    _request = null;
    _response = null;
    _handlingThread = null;

    if (_statsOn) {
      _tmpTime = System.currentTimeMillis();
      if (_reqTime > 0) _httpServer.statsEndRequest(_tmpTime - _reqTime, false);
      _httpServer.statsCloseConnection(_tmpTime - _openTime, _requests);
    }
  }
Beispiel #9
0
 public OutputStream getOutputStream(String contentType) throws HttpException {
   try {
     if (headersProvider != null) {
       headersProvider.writeHeaders(this);
     }
     request.writeHeader(HttpConstants.Headers.CONTENT_TYPE, contentType);
     request.writeHeader(HttpConstants.Headers.TRANSFER_ENCODING, "chunked");
     request.flushHeaders();
     request.flush(); // TODO: remove this when no longer necessary
     status = Status.STREAMING;
     return new CloseListenerOutputStream(new ChunkedOutputStream(request)) {
       @Override
       protected void onClosed() {
         status = Status.COMPLETE;
       }
     };
   } catch (IOException ex) {
     throw new HttpConnectionException(ex);
   }
 }
 /**
  * Close the connection. This method calls close on the input and output streams and interrupts
  * any thread in the handle method. may be specialized to close sockets etc.
  *
  * @exception IOException
  */
 public void close() throws IOException {
   try {
     _completing = true;
     if (_connection instanceof Socket && !(_connection instanceof SSLSocket))
       ((Socket) _connection).shutdownOutput();
     _outputStream.close();
     _inputStream.close();
   } finally {
     if (_handlingThread != null && Thread.currentThread() != _handlingThread)
       _handlingThread.interrupt();
   }
 }
 /** Handles an HTTP request along with its redirects and authentication */
 protected void doRequest() throws IOException {
   // do nothing if we've already sent the request
   if (sentRequest) {
     // If necessary, finish the request by
     // closing the uncached output stream.
     if (resHeader == null && os != null) {
       os.close();
       readServerResponse();
       getContentStream();
     }
     return;
   }
   doRequestInternal();
 }
  /**
   * Sends the request header to the remote HTTP server Not all of them are guaranteed to have any
   * effect on the content the server will return, depending on if the server supports that field.
   *
   * <p>Examples : Accept: text/*, text/html, text/html;level=1, Accept-Charset: iso-8859-5,
   * unicode-1-1;q=0.8
   */
  private boolean sendRequest() throws IOException {
    byte[] request = createRequest();

    // make sure we have a connection
    if (!connected) {
      connect();
    }
    if (null != cacheResponse) {
      // does not send if already has a response cache
      return true;
    }
    // send out the HTTP request
    socketOut.write(request);
    sentRequest = true;
    // send any output to the socket (i.e. POST data)
    if (os != null && os.isCached()) {
      socketOut.write(os.toByteArray());
    }
    if (os == null || os.isCached()) {
      readServerResponse();
      return true;
    }
    return false;
  }
 protected void endRequest() throws IOException {
   if (os != null) {
     os.close();
   }
   sentRequest = false;
 }
  private byte[] createRequest() throws IOException {
    StringBuilder output = new StringBuilder(256);
    output.append(method);
    output.append(' ');
    output.append(requestString());
    output.append(' ');
    output.append("HTTP/1."); // $NON-NLS-1$
    if (httpVersion == 0) {
      output.append("0\r\n"); // $NON-NLS-1$
    } else {
      output.append("1\r\n"); // $NON-NLS-1$
    }
    // add user-specified request headers if any
    boolean hasContentLength = false;
    for (int i = 0; i < reqHeader.length(); i++) {
      String key = reqHeader.getKey(i);
      if (key != null) {
        String lKey = key.toLowerCase();
        if ((os != null && !os.isChunked())
            || (!lKey.equals("transfer-encoding")
                && !lKey //$NON-NLS-1$
                    .equals("content-length"))) { // $NON-NLS-1$
          output.append(key);
          String value = reqHeader.get(i);
          /*
           * duplicates are allowed under certain conditions see
           * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
           */
          if (lKey.equals("content-length")) { // $NON-NLS-1$
            hasContentLength = true;
            /*
             * if both setFixedLengthStreamingMode and
             * content-length are set, use fixedContentLength first
             */
            if (fixedContentLength >= 0) {
              value = String.valueOf(fixedContentLength);
            }
          }
          if (value != null) {
            output.append(": "); // $NON-NLS-1$
            output.append(value);
          }
          output.append("\r\n"); // $NON-NLS-1$
        }
      }
    }
    if (fixedContentLength >= 0 && !hasContentLength) {
      output.append("content-length: "); // $NON-NLS-1$
      output.append(String.valueOf(fixedContentLength));
      output.append("\r\n"); // $NON-NLS-1$
    }

    if (reqHeader.get("User-Agent") == null) { // $NON-NLS-1$
      output.append("User-Agent: "); // $NON-NLS-1$
      String agent = getSystemProperty("http.agent"); // $NON-NLS-1$
      if (agent == null) {
        output.append("Java"); // $NON-NLS-1$
        output.append(getSystemProperty("java.version")); // $NON-NLS-1$
      } else {
        output.append(agent);
      }
      output.append("\r\n"); // $NON-NLS-1$
    }
    if (reqHeader.get("Host") == null) { // $NON-NLS-1$
      output.append("Host: "); // $NON-NLS-1$
      output.append(url.getHost());
      int port = url.getPort();
      if (port > 0 && port != defaultPort) {
        output.append(':');
        output.append(Integer.toString(port));
      }
      output.append("\r\n"); // $NON-NLS-1$
    }
    if (reqHeader.get("Accept") == null) { // $NON-NLS-1$
      output.append("Accept: *; */*\r\n"); // $NON-NLS-1$
    }
    if (httpVersion > 0 && reqHeader.get("Connection") == null) { // $NON-NLS-1$
      output.append("Connection: Keep-Alive\r\n"); // $NON-NLS-1$
    }

    // if we are doing output make sure the appropriate headers are sent
    if (os != null) {
      if (reqHeader.get("Content-Type") == null) { // $NON-NLS-1$
        output.append("Content-Type: application/x-www-form-urlencoded\r\n"); // $NON-NLS-1$
      }
      if (os.isCached()) {
        if (reqHeader.get("Content-Length") == null) { // $NON-NLS-1$
          output.append("Content-Length: "); // $NON-NLS-1$
          output.append(Integer.toString(os.size()));
          output.append("\r\n"); // $NON-NLS-1$
        }
      } else if (os.isChunked()) {
        output.append("Transfer-Encoding: chunked\r\n"); // $NON-NLS-1$
      }
    }
    // end the headers
    output.append("\r\n"); // $NON-NLS-1$
    return output.toString().getBytes("ISO8859_1"); // $NON-NLS-1$
  }
  /**
   * Handle next request off the connection. The service(request,response) method is called by
   * handle to service each request received on the connection. If the thread is a PoolThread, the
   * thread is set as inactive when waiting for a request.
   *
   * <p>If a HttpTunnel has been set on this connection, it's handle method is called and when that
   * completes, false is return from this method.
   *
   * <p>The Connection is set as a ThreadLocal of the calling thread and is available via the
   * getHttpConnection() method.
   *
   * @return true if the connection is still open and may provide more requests.
   */
  public boolean handleNext() {
    // Handle a HTTP tunnel
    if (_tunnel != null) {
      if (log.isDebugEnabled()) log.debug("Tunnel: " + _tunnel);
      _outputStream.resetObservers();
      _tunnel.handle(_inputStream.getInputStream(), _outputStream.getOutputStream());
      return false;
    }

    // Normal handling.
    HttpContext context = null;
    boolean stats = false;
    try {
      // Assume the connection is not persistent,
      // unless told otherwise.
      _persistent = false;
      _close = false;
      _keepAlive = false;
      _firstWrite = false;
      _completing = false;
      _dotVersion = 0;

      // Read requests
      readRequest();
      if (_listener == null || !_listener.isStarted()) {
        // dead connection
        _response.destroy();
        _response = null;
        _persistent = false;
        return false;
      }

      _listener.customizeRequest(this, _request);
      if (_request.getState() != HttpMessage.__MSG_RECEIVED)
        throw new HttpException(HttpResponse.__400_Bad_Request);

      // We have a valid request!
      statsRequestStart();
      stats = true;

      // Pick response version, we assume that _request.getVersion() == 1
      _dotVersion = _request.getDotVersion();

      if (_dotVersion > 1) {
        _dotVersion = 1;
      }

      // Common fields on the response
      _response.setVersion(HttpMessage.__HTTP_1_1);
      _response.setField(HttpFields.__Date, _request.getTimeStampStr());
      if (!Version.isParanoid()) _response.setField(HttpFields.__Server, Version.getDetail());

      // Handle Connection header field
      Enumeration connectionValues =
          _request.getFieldValues(HttpFields.__Connection, HttpFields.__separators);
      if (connectionValues != null) {
        while (connectionValues.hasMoreElements()) {
          String token = connectionValues.nextElement().toString();
          // handle close token
          if (token.equalsIgnoreCase(HttpFields.__Close)) {
            _close = true;
            _response.setField(HttpFields.__Connection, HttpFields.__Close);
          } else if (token.equalsIgnoreCase(HttpFields.__KeepAlive) && _dotVersion == 0)
            _keepAlive = true;

          // Remove headers for HTTP/1.0 requests
          if (_dotVersion == 0) _request.forceRemoveField(token);
        }
      }

      // Handle version specifics
      if (_dotVersion == 1) verifyHTTP_1_1();
      else if (_dotVersion == 0) verifyHTTP_1_0();
      else if (_dotVersion != -1)
        throw new HttpException(HttpResponse.__505_HTTP_Version_Not_Supported);

      if (log.isDebugEnabled()) log.debug("REQUEST from " + _listener + ":\n" + _request);

      // handle HttpListener handlers
      if (!_request.isHandled() && _listener.getHttpHandler() != null)
        _listener.getHttpHandler().handle("", null, _request, _response);

      // service the request
      if (!_request.isHandled()) context = service(_request, _response);
    } catch (HttpException e) {
      exception(e);
    } catch (IOException e) {
      if (_request.getState() != HttpMessage.__MSG_RECEIVED) {
        if (log.isDebugEnabled()) {
          if (log.isTraceEnabled()) log.trace(LogSupport.EXCEPTION, e);
          else if (log.isDebugEnabled()) log.debug(e.toString());
        }
        _response.destroy();
        _response = null;
      } else exception(e);
    } catch (Exception e) {
      exception(e);
    } catch (Error e) {
      exception(e);
    } finally {
      int bytes_written = 0;
      int content_length =
          _response == null ? -1 : _response.getIntField(HttpFields.__ContentLength);

      // Complete the request
      if (_persistent) {
        boolean no_continue_sent = false;
        try {
          if (_inputStream.getExpectContinues() != null) {
            _inputStream.setExpectContinues(null);
            no_continue_sent = true;
          } else {
            int remaining = _inputStream.getContentLength();
            if (remaining != 0)
              // Read remaining input
              while (_inputStream.skip(4096) > 0 || _inputStream.read() >= 0) ;
          }
        } catch (IOException e) {
          if (_inputStream.getContentLength() > 0) _inputStream.setContentLength(0);
          _persistent = false;
          LogSupport.ignore(log, e);
          exception(new HttpException(HttpResponse.__400_Bad_Request, "Missing Content"));
        }

        // Check for no more content
        if (!no_continue_sent && _inputStream.getContentLength() > 0) {
          _inputStream.setContentLength(0);
          _persistent = false;
          exception(new HttpException(HttpResponse.__400_Bad_Request, "Missing Content"));
        }

        // Commit the response
        try {
          _outputStream.close();
          bytes_written = _outputStream.getBytesWritten();
          _outputStream.resetStream();
          _outputStream.addObserver(this);
          _inputStream.resetStream();
        } catch (IOException e) {
          exception(e);
        }
      } else if (_response != null) // There was a request
      {
        // half hearted attempt to eat any remaining input
        try {
          if (_inputStream.getContentLength() > 0)
            while (_inputStream.skip(4096) > 0 || _inputStream.read() >= 0) ;
          _inputStream.resetStream();
        } catch (IOException e) {
          LogSupport.ignore(log, e);
        }

        // commit non persistent
        try {
          _outputStream.flush();
          _response.commit();
          bytes_written = _outputStream.getBytesWritten();
          _outputStream.close();
          _outputStream.resetStream();
        } catch (IOException e) {
          exception(e);
        }
      }

      // Check response length
      if (_response != null) {
        if (log.isDebugEnabled()) log.debug("RESPONSE:\n" + _response);
        if (_persistent
            && content_length >= 0
            && bytes_written > 0
            && content_length != bytes_written) {
          log.warn(
              "Invalid length: Content-Length="
                  + content_length
                  + " written="
                  + bytes_written
                  + " for "
                  + _request.getRequestURL());
          _persistent = false;
          try {
            _outputStream.close();
          } catch (IOException e) {
            log.warn(LogSupport.EXCEPTION, e);
          }
        }
      }

      // stats & logging
      if (stats) statsRequestEnd();
      if (context != null) context.log(_request, _response, bytes_written);
    }

    return (_tunnel != null) || _persistent;
  }
  /* ------------------------------------------------------------ */
  protected void commit() throws IOException {
    if (_response.isCommitted()) return;

    int status = _response.getStatus();
    int length = -1;

    // Check if there is missing content expectations
    if (_inputStream.getExpectContinues() != null) {
      // No input read yet - so assume it never will be
      _inputStream.setExpectContinues(null);
      _inputStream.unsafeSetContentLength(0);
    }

    // Handler forced close, listener stopped or no idle threads left.
    boolean has_close = HttpFields.__Close.equals(_response.getField(HttpFields.__Connection));
    if (!_persistent
        || _close
        || _listener != null && (!_listener.isStarted() || _listener.isOutOfResources())) {
      _close = true;
      if (!has_close) _response.setField(HttpFields.__Connection, HttpFields.__Close);
      has_close = true;
    }
    if (_close) _persistent = false;

    // Determine how to limit content length
    if (_persistent) {
      switch (_dotVersion) {
        case 1:
          {
            String transfer_coding = _response.getField(HttpFields.__TransferEncoding);
            if (transfer_coding == null
                || transfer_coding.length() == 0
                || HttpFields.__Identity.equalsIgnoreCase(transfer_coding)) {
              // if (can have content and no content length)
              if (status != HttpResponse.__304_Not_Modified
                  && status != HttpResponse.__204_No_Content
                  && _response.getField(HttpFields.__ContentLength) == null) {
                if (_completing) {
                  length = _outputStream.getBytesWritten();
                  _response.setContentLength(length);
                } else {
                  // Chunk it!
                  _response.setField(HttpFields.__TransferEncoding, HttpFields.__Chunked);
                  _outputStream.setChunking();
                }
              }
            } else {
              // Use transfer encodings to determine length
              _response.removeField(HttpFields.__ContentLength);
              _outputStream.setChunking();

              if (!HttpFields.__Chunked.equalsIgnoreCase(transfer_coding)) {
                // Check against any TE field
                List te = _request.getAcceptableTransferCodings();
                Enumeration enm =
                    _response.getFieldValues(
                        HttpFields.__TransferEncoding, HttpFields.__separators);
                while (enm.hasMoreElements()) {
                  String coding = (String) enm.nextElement();
                  if (HttpFields.__Identity.equalsIgnoreCase(coding)
                      || HttpFields.__Chunked.equalsIgnoreCase(coding)) continue;
                  if (te == null || !te.contains(coding))
                    throw new HttpException(HttpResponse.__501_Not_Implemented, coding);
                }
              }
            }
          }
          break;

        case 0:
          {
            // if (can have content and no content length)
            _response.removeField(HttpFields.__TransferEncoding);
            if (_keepAlive) {
              if (status != HttpResponse.__304_Not_Modified
                  && status != HttpResponse.__204_No_Content
                  && _response.getField(HttpFields.__ContentLength) == null) {
                if (_completing) {
                  length = _outputStream.getBytesWritten();
                  _response.setContentLength(length);
                  _response.setField(HttpFields.__Connection, HttpFields.__KeepAlive);
                } else {
                  _response.setField(HttpFields.__Connection, HttpFields.__Close);
                  has_close = _close = true;
                  _persistent = false;
                }
              } else _response.setField(HttpFields.__Connection, HttpFields.__KeepAlive);
            } else if (!has_close) _response.setField(HttpFields.__Connection, HttpFields.__Close);

            break;
          }
        default:
          {
            _close = true;
            _persistent = false;
            _keepAlive = false;
          }
      }
    }

    // Mark request as handled.
    _request.setHandled(true);

    _outputStream.writeHeader(_response);
    _outputStream.flush();
  }