HTTPメッセージハンドラチュートリアル

HttpBroker

HttpBroker クラスは、mpchat_server で HttpMessageHandler を利用している部分である。 定義は次のようになっている:

class HttpBroker: public nine::HttpMessageHandler
{
   public:
      HttpBroker();
      virtual ~HttpBroker();

      bool initialize(ChatService*, const char* path);

      virtual bool handleMessage(nine::Message* pMsg);

   private:
      void responseLog(int sid, int from);

      ChatService *m_pService;
};

HttpServer への登録

HttpBroker は HttpMessageHandler であり、HttpHandler である。 HttpHandler は、HttpServer の authorizeHttp() 関数の戻り値としてフレームワークに渡すことで利用されるようになる。

この処理は Server の initialize() 内で実装されている:
bool Server::initialize(const ServerConfig* pCfg)
{
      ...
      if (! m_httpBroker.initialize(&m_service, "/http")) {
         return false;
      }
      m_httpServer.setHandler("/http", &m_httpBroker);

m_httpServer は PathHttpServer である。 上の初期化コードにより、HttpBroker は "/http" というリクエストパスに対応するよう設定される。

initialize

initialize() では、親クラスである HttpMessageHandler の初期化を行っている。
bool HttpBroker::initialize(ChatService* pService, const char* path)
{
   {
      nine::HttpMessageHandlerConfig cfg;
      cfg.nSessions = 10;
      cfg.sessionCookie = "sid";
      cfg.sessionCookiePath = path;
      cfg.jsonp = "jsonp";
      cfg.enableQueryStringMarshal = true;
      cfg.enablePostMarshal = true;
      typedef nine::HttpMessageHandler super;
      if (! super::initialize(&cfg)) { return false; }
   }
   m_pService = pService;
   return true;
}
上のコードは具体的には次のような設定を行っている:
  • HTTPセッションの同時接続数は 10
  • HTTPセッションは cookie を用いる。cookie のパスは外部から与えられる。
  • 要求のメッセージは QueryString と HTTP POST の両方の形式を受け付ける。
  • 応答は jsonp 形式で行い、その関数名は "jsonp" である。
Config の詳細は HttpMessageHandler#initialize() で説明している。

responseLog()

responseLog() は private 関数であり、他の関数から呼び出される。 処理内容は、ChatService からログを取得し、LogNotify メッセージに入れて post することである。

post() は HTTP 要求処理中にしか使えない。サンプルでは、responseLog() は handleMessage() から呼ばれるだけになっているので問題はない。

void HttpBroker::responseLog(int sid, int from)
{
   LogNotify msg;
   m_pService->get(from, &msg);
   if (! this->post(sid, &msg)) {
      printf("post failed in HttpMessageBroker::responseLog");
   }
}

HTTP の制約により、post() は要求の処理中にしか行えない。 このため、responseLog() は受信ハンドラの中から呼ばれるようになっている。

handleMessage()

handleMessage() は、要求に含まれていたメッセージを処理するハンドラである。 HttpMessageHandler においては、HTTP応答を返す前に呼びだされるので、post() や flow() を使うことができる。

プロトコル設計により、サーバーは ChatRequest と LogRequest の二種類のリクエストを受け付ける。 サンプルでは、この二つのメッセージを処理している:
   int sid = pMsg->getCommunicatorId();
   uint16_t msgid = pMsg->getMessageId();
   if (msgid == ChatRequest::_MESSAGE_ID) {
      ...
   } else if (msgid == LogRequest::_MESSAGE_ID) {
      ...
   }

ChatRequest は、クライアントが新規発言をした際に送られるメッセージである。 ChatRequest は同時に発言ログの要求も行う。

サーバー側では、
  • 発言を発言ログへ追加する
  • 要求された発言IDより新しいログを返す
という処理を行う。コードは次のようになる:
   if (msgid == ChatRequest::_MESSAGE_ID) {
      ChatRequest* p = static_cast< ChatRequest* >(pMsg);
      m_pService->add(p->get_chat());
      responseLog(sid, p->get_from());
      return true;

LogRequest は、発言ログを要求するメッセージである。

サーバーは、要求された発言IDより新しいログを返す。コードは次のようになる:
   } else if (msgid == LogRequest::_MESSAGE_ID) {
      LogRequest* p = static_cast< LogRequest* >(pMsg);
      responseLog(sid, p->get_from());
      return true;