// Copyright 2007 Google Inc.
// All Rights Reserved.

#include "talk/base/gunit.h"
#include "talk/base/httpserver.h"
#include "talk/base/testutils.h"

using namespace testing;

namespace talk_base {

namespace {
  const char* const kRequest =
    "GET /index.html HTTP/1.1\r\n"
    "Host: localhost\r\n"
    "\r\n";

  const char* const kResponse =
    "HTTP/1.1 200\r\n"
    "Connection: Close\r\n"
    "Content-Length: 0\r\n"
    "\r\n";

  struct HttpServerMonitor : public sigslot::has_slots<> {
    HttpServerTransaction* transaction;
    bool server_closed, connection_closed;

    HttpServerMonitor(HttpServer* server)
    : transaction(NULL), server_closed(false), connection_closed(false) {
      server->SignalCloseAllComplete.connect(this,
        &HttpServerMonitor::OnClosed);
      server->SignalHttpRequest.connect(this, &HttpServerMonitor::OnRequest);
      server->SignalHttpRequestComplete.connect(this,
        &HttpServerMonitor::OnRequestComplete);
      server->SignalConnectionClosed.connect(this,
        &HttpServerMonitor::OnConnectionClosed);
    }
    void OnRequest(HttpServer*, HttpServerTransaction* t) {
      ASSERT_FALSE(transaction);
      transaction = t;
      transaction->response.set_success();
      transaction->response.setHeader(HH_CONNECTION, "Close");
    }
    void OnRequestComplete(HttpServer*, HttpServerTransaction* t, int) {
      ASSERT_EQ(transaction, t);
      transaction = NULL;
    }
    void OnClosed(HttpServer*) {
      server_closed = true;
    }
    void OnConnectionClosed(HttpServer*, int, StreamInterface* stream) {
      connection_closed = true;
      delete stream;
    }
  };

  void CreateClientConnection(HttpServer& server,
                              HttpServerMonitor& monitor,
                              bool send_request) {
    StreamSource* client = new StreamSource;
    client->SetState(SS_OPEN);
    server.HandleConnection(client);
    EXPECT_FALSE(monitor.server_closed);
    EXPECT_FALSE(monitor.transaction);

    if (send_request) {
      // Simulate a request
      client->QueueString(kRequest);
      EXPECT_FALSE(monitor.server_closed);
    }
  }
}  // anonymous namespace

TEST(HttpServer, DoesNotSignalCloseUnlessCloseAllIsCalled) {
  HttpServer server;
  HttpServerMonitor monitor(&server);
  // Add an active client connection
  CreateClientConnection(server, monitor, true);
  // Simulate a response
  ASSERT_TRUE(NULL != monitor.transaction);
  server.Respond(monitor.transaction);
  EXPECT_FALSE(monitor.transaction);
  // Connection has closed, but no server close signal
  EXPECT_FALSE(monitor.server_closed);
  EXPECT_TRUE(monitor.connection_closed);
}

TEST(HttpServer, SignalsCloseWhenNoConnectionsAreActive) {
  HttpServer server;
  HttpServerMonitor monitor(&server);
  // Add an idle client connection
  CreateClientConnection(server, monitor, false);
  // Perform graceful close
  server.CloseAll(false);
  // Connections have all closed
  EXPECT_TRUE(monitor.server_closed);
  EXPECT_TRUE(monitor.connection_closed);
}

TEST(HttpServer, SignalsCloseAfterGracefulCloseAll) {
  HttpServer server;
  HttpServerMonitor monitor(&server);
  // Add an active client connection
  CreateClientConnection(server, monitor, true);
  // Initiate a graceful close
  server.CloseAll(false);
  EXPECT_FALSE(monitor.server_closed);
  // Simulate a response
  ASSERT_TRUE(NULL != monitor.transaction);
  server.Respond(monitor.transaction);
  EXPECT_FALSE(monitor.transaction);
  // Connections have all closed
  EXPECT_TRUE(monitor.server_closed);
  EXPECT_TRUE(monitor.connection_closed);
}

TEST(HttpServer, SignalsCloseAfterForcedCloseAll) {
  HttpServer server;
  HttpServerMonitor monitor(&server);
  // Add an active client connection
  CreateClientConnection(server, monitor, true);
  // Initiate a forceful close
  server.CloseAll(true);
  // Connections have all closed
  EXPECT_TRUE(monitor.server_closed);
  EXPECT_TRUE(monitor.connection_closed);
}

} // namespace talk_base
