Package tkuri.jxy.server

Source Code of tkuri.jxy.server.IHasMessageBodyState

/**
* (c) 2012 tkuri
*/
package tkuri.jxy.server;

import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;

import tkuri.jxy.Const;
import tkuri.jxy.Util;
import tkuri.jxy.arch.ChannelReader;
import tkuri.jxy.arch.MessageBodyHandler;
import tkuri.jxy.arch.MessageBodyHandler.MbhState;
import tkuri.uty.Bs;


/**
*
*/
public class TaskHttpTransfer implements ITransfer, IHasMessageBodyState {


  private ClientPeer client_ = null;
  //private ClientRequest request_ = null;
  private ByteBuffer requestHeaderBuf_;

  private OriginPeer origin_ = null;
  private OriginResponse response_ = null;
  private ByteBuffer responseHeaderBuf_ = null;

  private MessageBodyHandler msgBodyHandler_ = null;

  private Msg ascentMsg_ = null;
  private Msg descent_ = null;
//  private TaskHttpDescent descent_ = null;

  private String stringified_ = "";


  /**
   *
   * @param aClientPeer
   * @param aRequest
   */
  public TaskHttpTransfer(ClientPeer aClientPeer) {

    client_ = aClientPeer;
    ascentMsg_ = new Msg();
    ascentMsg_.reader = client_.reader;
    ascentMsg_.headerBuffer = TaskUtil.makeRequestHeaderBuffer(client_.request);
//    requestHeaderBuf_ = TaskUtil.makeRequestHeaderBuffer(aRequest);
//    msgBodyHandler_ = new MessageBodyHandler(client_.reader, request_.headerInfo);

    stringified_ = client_.request.toString();
  }


  @Override
  public void onOriginConnect(OriginPeer aOrigin, boolean aConnectFinished) {

    if (origin_ != null) {
      throw new RuntimeException("wtf?");
    }
    origin_ = aOrigin;

    if (aConnectFinished) {
      // ok

    } else if (TaskUtil.finishConnection(origin_.socket)) {
      // ok

    } else {
      Util.say("httptask: no connect. client.invalidate()");
      client_.invalidate();
      origin_.invalidate(true);
    }
  }


  @Override
  public void onClientReadable() {

    ascentMsg_.reader.fill();
    _receive(ascentMsg_);
  }


  @Override
  public void onOriginWritable() {
    //Util.say("httptask: onOriginW");

    try {
      _transfer(ascentMsg_, origin_.socket);

    } catch (Exception x) {
      client_.invalidate();
      origin_.invalidate(true);
    }
  }


  @Override
  public void onOriginReadable() {
    //Util.say("httptask: onOriginR");

    if (response_ == null) {
      boolean isRequestHead = Const.S_HEAD.equals(client_.request.method);
      response_ = new OriginResponse(origin_, isRequestHead, client_.request.toString());
      client_.notifyResponseStart();
    }

    if (responseHeaderBuf_ == null) {
      if (! response_.getReady()) {
        Util.say("no response yeth");
        if (origin_.reader.available() < 0) {
          Util.say("httptask: origin.reader.avail < 0");
          client_.notifyResponseEnd();
          origin_.notifyResponseEnd();
        }
        return;
      }
      responseHeaderBuf_ = TaskUtil.makeResponseHeaderBuffer(response_, client_.request.headerInfo.connectionClose);
      //Util.say(new String(responseHeaderBuf_.array()));
      msgBodyHandler_ = new MessageBodyHandler(origin_.reader, response_.headerInfo);
    }

    if (! client_.isValid()) { // クライアントの接続が切れた.独自に消化しなければ
      Util.say("httptask: onOriginRead: fake transfer.");
      _transferResponse();
    }
  }


  @Override
  public void onClientWritable() {
    //Util.say("httptask: onClientW");

    if (response_ == null) {
      Util.say("httptask: no resp obj.");
      if (! origin_.isValid()) {
        Util.say("httptask: onClientW: invalid origin. go next");
        client_.notifyResponseEnd();
      }
      return;
    }

    _transferResponse();
  }


  private void _transferResponse() {

    try {
      if (responseHeaderBuf_.hasRemaining()) {
        if (client_.isValid()) {
          client_.socket.write(responseHeaderBuf_);
        } else {
          responseHeaderBuf_.position(responseHeaderBuf_.limit());
        }
        if (responseHeaderBuf_.hasRemaining()) {
          return;
        }
        //Util.say("httptask: res header end.");
      }

      int rv = msgBodyHandler_.transferTo(
          client_.isValid() ? client_.socket : null);
      Util.say("httptask: response body rv = ", rv);
      if (rv <= 0) {
        Util.say("httptask: res body end.");

        if (client_.isValid()) {
          if (client_.request.headerInfo.connectionClose) {
            Util.say("httptask: client req conn close");
            client_.invalidate(); // Connection: close の場合は,こちらから切断してあげないとクライアントは分からないからね
          } else {
            client_.notifyResponseEnd();
          }
        }

        origin_.notifyResponseEnd();
      }

    } catch (Exception x) {
      Util.say(x);
      Util.say("httptask: go fake transfer mode.");
      client_.invalidate(); // おそらくクライアントが勝手に接続を切った.
      // 後は onOriginReadable 時に独自に消化しなきゃ
    }
  }


  public ITaskAscent ascent() {

    return null;//ascent_;
  }


  public ITaskDescent descent() {

    return null;//descent_;
  }


  @Override
  public String toString() {

    return stringified_;
  }


  private void _receive(Msg aMsgData) {

    while (aMsgData.state == MSG_CHUNK_SIZE
        || aMsgData.state == MSG_CHUNK_CRLF
        || aMsgData.state == MSG_CHUNK_TRAILER) {

      Bs line = _findNextLF(aMsgData);
      if (line == null) {
        break; // 次のバッファを待つ
      }
      aMsgData.rest += line.length();

      if (aMsgData.state == MSG_CHUNK_SIZE) {
        int size = (int) line.toLong(16);
        aMsgData.rest += size;
        aMsgData.state = (size == 0)
            ? MSG_CHUNK_TRAILER
            : MSG_CHUNK_CRLF;

      } else if (aMsgData.state == MSG_CHUNK_CRLF) {
        aMsgData.state = MSG_CHUNK_SIZE;

      } else { // TRAILER & CRLF
        aMsgData.state = MSG_SIZE_FIXED;
        return; // chunk 正常終了
      }
    }

    if (aMsgData.reader.isClosed()) {
      if (aMsgData.state == MSG_UNTIL_CLOSED) {
        aMsgData.rest = aMsgData.reader.length();
        aMsgData.state = MSG_SIZE_FIXED;

      } else {
        aMsgData.state = MSG_RECV_ERR;
      }
    }
  }


  private Bs _findNextLF(Msg aMsgData) {

    int lf = aMsgData.reader.indexOf('\n', aMsgData.rest);
    if (lf < 0) {
      return null;
    }

    int len = lf + 1;

    Bs rv = aMsgData.reader.sub(0, len);
    aMsgData.rest += len;

    return rv;
  }


  void _transfer(Msg aMsg, WritableByteChannel aOut) throws Exception {

    try {
      if (aMsg.headerBuffer == null) {
        return;
      }

      if (aMsg.headerBuffer.hasRemaining()) {
        aOut.write(aMsg.headerBuffer);
        if (aMsg.headerBuffer.hasRemaining()) {
          return;
        }
      }

      if (0 < aMsg.rest) {
        int wrote = aMsg.reader.through(aOut, aMsg.rest);
        aMsg.rest -= wrote;
      }

      if (aMsg.rest == 0 && aMsg.state == MSG_SIZE_FIXED) {
        aMsg.state = MSG_TRANS_END;
      }

    } catch (Exception x) {
    }
  }


  class Msg {

    ChannelReader reader;
    ByteBuffer headerBuffer;
    int rest = 0;
    int state = 0;
  }
}

interface IHasMessageBodyState {

  static final int MSG_CHUNK_SIZE = 1;
  static final int MSG_CHUNK_CRLF = 2;
  static final int MSG_CHUNK_TRAILER = 3;
  static final int MSG_UNTIL_CLOSED = 4;
  static final int MSG_SIZE_FIXED = 5;
  static final int MSG_RECV_ERR = 6;
  static final int MSG_TRANS_END = 7;
}
TOP

Related Classes of tkuri.jxy.server.IHasMessageBodyState

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.