Package com.sun.jini.jeri.internal.http

Source Code of com.sun.jini.jeri.internal.http.HttpServerConnection

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.sun.jini.jeri.internal.http;

import com.sun.jini.thread.Executor;
import com.sun.jini.thread.GetThreadPoolAction;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.StringTokenizer;
import net.jini.core.constraint.InvocationConstraints;
import net.jini.io.UnsupportedConstraintException;
import net.jini.io.context.AcknowledgmentSource;
import net.jini.jeri.InboundRequest;
import net.jini.jeri.RequestDispatcher;

/**
* Class representing a server-side HTTP connection used to receive and
* dispatch incoming HTTP requests.
*
* @author Sun Microsystems, Inc.
*
*/
public abstract class HttpServerConnection implements TimedConnection {
   
    private static final int HTTP_MAJOR = 1;
    private static final int HTTP_MINOR = 1;
   
    private static final int UNSTARTED = 0;
    private static final int IDLE      = 1;
    private static final int BUSY      = 2;
    private static final int CLOSED    = 3;

    private static final String serverString = (String)
  AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
    return "Java/" + System.getProperty("java.version", "???") +
           " " + HttpServerConnection.class.getName();
      }
  });

    private static final Executor userThreadPool = (Executor)
  java.security.AccessController.doPrivileged(
      new GetThreadPoolAction(true));

    private final Socket sock;
    private final InputStream in;
    private final OutputStream out;
    private final RequestDispatcher dispatcher;
    private final HttpServerManager manager;
    private final Object stateLock = new Object();
    private int state = UNSTARTED;

    /**
     * Creates new HttpServerConnection on top of given socket.
     */
    public HttpServerConnection(Socket sock,
        RequestDispatcher dispatcher,
        HttpServerManager manager)
  throws IOException
    {
  if (dispatcher == null) {
      throw new NullPointerException();
  }
  this.sock = sock;
  this.dispatcher = dispatcher;
  this.manager = manager;
  in = new BufferedInputStream(sock.getInputStream());
  out = new BufferedOutputStream(sock.getOutputStream());
    }

    /**
     * Starts request dispatch thread.  Throws IllegalStateException if
     * connection has already been started, or is closed.
     */
    protected void start() {
  synchronized (stateLock) {
      if (state != UNSTARTED) {
    throw new IllegalStateException();
      }
      state = IDLE;
      userThreadPool.execute(new Dispatcher(), "HTTP dispatcher");
  }
    }

    /**
     * Verifies that calling context has sufficient security permissions to
     * receive a request on this connection.
     */
    protected void checkPermissions() {
    }
   
    /**
     * Checks that the specified requirements are either fully or
     * partially satisfied by the constraints actually in force for
     * this connection, and returns any constraints that must be fully
     * or partially implemented by higher layers in order to fully
     * satisfy all of the specified requirements.
     **/
    protected InvocationConstraints
  checkConstraints(InvocationConstraints constraints)
  throws UnsupportedConstraintException
    {
  if (constraints.requirements().isEmpty()) {
      return InvocationConstraints.EMPTY;
  }
  throw new UnsupportedConstraintException(
      "cannot satisfy constraints: " + constraints);
    }

    /**
     * Populates the given context collection with context information
     * representing this connection.
     */
    protected abstract void populateContext(Collection context);
   
    /**
     * Upcall indicating that connection has become idle.  Subclasses may
     * override this method to perform an appropriate action, such as
     * scheduling an idle timeout.
     */
    protected void idle() {
    }
   
    /**
     * Upcall indicating that connection is about to become busy.  Subclasses
     * may override this method to perform an appropriate action, such as
     * cancelling an idle timeout.
     */
    protected void busy() {
    }
   
    /**
     * Attempts to shut down connection, returning true if connection is
     * closed.  If force is true, connection is always shut down; if force is
     * false, connection is only shut down if idle.
     */
    public boolean shutdown(boolean force) {
  synchronized (stateLock) {
      if (state == CLOSED) {
    return true;
      }
      if (!force && state == BUSY) {
    return false;
      }
      state = CLOSED;
  }
  try { sock.close(); } catch (IOException ex) {}
  return true;
    }
   
    /**
     * Incoming request dispatcher.
     */
    private class Dispatcher implements Runnable {

  /**
   * Dispatch loop.
   */
  public void run() {
      try {
    for (;;) {
        idle();

        MessageReader reader = new MessageReader(in, false);
        StartLine sline = reader.readStartLine();
       
        busy();
        synchronized (stateLock) {
      if (state == CLOSED) {
          return;
      }
      state = BUSY;
        }

        Header header = reader.readHeader();
        String reqType = header.getField("RMI-Request-Type");
        if (!"POST".equals(sline.method)) {
      handleBadRequest(sline, header, reader);
        } else if ("standard".equalsIgnoreCase(reqType)) {
      handleRequest(sline, header, reader);
        } else if ("ping".equalsIgnoreCase(reqType)) {
      handlePing(sline, header, reader);
        } else {
      handleBadRequest(sline, header, reader);
        }
       
        synchronized (stateLock) {
      if (state == CLOSED) {
          return;
      }
      state = IDLE;
        }
    }
      } catch (IOException ex) {
      } finally {
    shutdown(true);
      }
  }

  /**
   * Handles unacceptable HTTP request.
   */
  private void handleBadRequest(StartLine inLine,
              Header inHeader,
              MessageReader reader)
      throws IOException
  {
      inHeader.merge(reader.readTrailer());
      registerAcks(inHeader.getField("RMI-Response-Ack"));
      boolean persist = supportsPersist(inLine, inHeader);
     
      MessageWriter writer = new MessageWriter(out, false);
      writer.writeStartLine(new StartLine(HTTP_MAJOR, HTTP_MINOR,
              HttpURLConnection.HTTP_BAD_REQUEST,
              "Bad Request"));
      writer.writeHeader(createResponseHeader(persist));
      writer.writeTrailer(null);
     
      if (!persist) {
    shutdown(true);
      }
  }

  /**
   * Handles ping request.
   */
  private void handlePing(StartLine inLine, Header inHeader,
        MessageReader reader)
      throws IOException
  {
      inHeader.merge(reader.readTrailer());
      registerAcks(inHeader.getField("RMI-Response-Ack"));
      boolean persist = supportsPersist(inLine, inHeader);

      MessageWriter writer = new MessageWriter(out, false);
      writer.writeStartLine(new StartLine(HTTP_MAJOR, HTTP_MINOR,
              HttpURLConnection.HTTP_OK, "OK"));
      writer.writeHeader(createResponseHeader(persist));
      writer.writeTrailer(null);
     
      if (!persist) {
    shutdown(true);
      }
  }
 
  /**
   * Handles "standard" (i.e., dispatchable) request.
   */
  private void handleRequest(StartLine inLine, Header inHeader,
           MessageReader reader)
      throws IOException
  {
      registerAcks(inHeader.getField("RMI-Response-Ack"));
      boolean persist = supportsPersist(inLine, inHeader);
      boolean chunk = supportsChunking(inLine, inHeader);

      MessageWriter writer = new MessageWriter(out, chunk);
      writer.writeStartLine(new StartLine(HTTP_MAJOR, HTTP_MINOR,
              HttpURLConnection.HTTP_OK, "OK"));
      writer.writeHeader(createResponseHeader(persist));
     
      InboundRequestImpl req = new InboundRequestImpl(reader, writer);
      try { dispatcher.dispatch(req); } catch (Throwable th) {}
      req.finish();
     
      if (!persist || req.streamCorrupt()) {
    shutdown(true);
      }
  }
    }

    /**
     * HTTP-based implementation of InboundRequest abstraction.
     */
    private class InboundRequestImpl
  extends Request implements InboundRequest
    {
  private final MessageReader reader;
  private final MessageWriter writer;
  private String cookie;
  private boolean corrupt = false;

  /**
   * Creates new InboundRequestImpl which uses given MessageReader and
   * MessageWriter instances to read/write request content.
   */
  InboundRequestImpl(MessageReader reader, MessageWriter writer) {
      this.reader = reader;
      this.writer = writer;
  }
 
  public void checkPermissions() {
      HttpServerConnection.this.checkPermissions();
  }
 
  public InvocationConstraints
      checkConstraints(InvocationConstraints constraints)
      throws UnsupportedConstraintException
  {
      return HttpServerConnection.this.checkConstraints(constraints);
  }

  public void populateContext(Collection context) {
      context.add(new AcknowledgmentSource() {
    public boolean addAcknowledgmentListener(
        AcknowledgmentSource.Listener listener)
    {
        if (listener == null) {
      throw new NullPointerException();
        }
        return InboundRequestImpl.this.addAcknowledgmentListener(
      listener);
    }
      });
      HttpServerConnection.this.populateContext(context);
  }
 
  public InputStream getRequestInputStream() {
      return getInputStream();
  }
 
  public OutputStream getResponseOutputStream() {
      return getOutputStream();
  }
 
  /**
   * Returns true if stream corrupted, false if stream ok.
   */
  boolean streamCorrupt() {
      return corrupt;
  }

  void startOutput() throws IOException {
      // start line, header already written
  }

  void write(byte[] b, int off, int len) throws IOException {
      writer.writeContent(b, off, len);
  }

  void endOutput() throws IOException {
      if (cookie != null) {
    Header trailer = new Header();
    trailer.setField("RMI-Response-Cookie", cookie);
    writer.writeTrailer(trailer);
      } else {
    writer.writeTrailer(null);
      }
  }

  boolean startInput() throws IOException {
      return true// header already read
  }

  int read(byte[] b, int off, int len) throws IOException {
      return reader.readContent(b, off, len);
  }

  int available() throws IOException {
      return reader.availableContent();
  }

  void endInput() throws IOException {
      Header trailer = reader.readTrailer();
      if (trailer != null) {
    registerAcks(trailer.getField("RMI-Response-Ack"));
      }
  }

  void addAckListener(AcknowledgmentSource.Listener listener) {
      if (cookie == null) {
    cookie = manager.newCookie();
      }
      manager.addAckListener(cookie, listener);
  }

  void done(boolean corrupt) {
      this.corrupt = corrupt;
  }
    }
   
    /**
     * Notifies listeners for response ack cookies parsed from (possibly null)
     * comma-separated cookie list string.
     */
    private void registerAcks(String ackList) {
  if (ackList != null) {
      StringTokenizer tok = new StringTokenizer(ackList, ",");
      while (tok.hasMoreTokens()) {
    manager.notifyAckListeners(tok.nextToken().trim());
      }
  }
    }

    /**
     * Returns true if the received message start line and header indicate that
     * the connection can be persisted.
     */
    private static boolean supportsPersist(StartLine sline, Header header) {
  if (header.containsValue("Connection", "close", true)) {
      return false;
  } else if (header.containsValue("Connection", "Keep-Alive", true)) {
      return true;
  } else {
      int c = StartLine.compareVersions(sline.major, sline.minor, 1, 1);
      return c >= 0;
  }
    }

    /**
     * Returns true if the received message start line indicates that the
     * sender understands chunked transfer coding.
     */
    private static boolean supportsChunking(StartLine sline, Header header) {
  int c = StartLine.compareVersions(sline.major, sline.minor, 1, 1);
  // REMIND: is requiring "TE: trailers" too strict?
  return c >= 0 && header.containsValue("TE", "trailers", true);
    }
   
    /**
     * Creates base header to be used in response message.  If persist is true,
     * adds fields indicating a persistent connection.
     */
    private static Header createResponseHeader(boolean persist) {
  Header header = new Header();
  long now = System.currentTimeMillis();
  header.setField("Date", Header.getDateString(now));
  header.setField("Server", serverString);
  header.setField("Connection", persist ? "Keep-Alive" : "close");
  return header;
    }
}
TOP

Related Classes of com.sun.jini.jeri.internal.http.HttpServerConnection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.