<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed 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.
 */

#include <wangle/acceptor/Acceptor.h>
#include <folly/io/async/EventBase.h>
#include <folly/io/async/test/AsyncSSLSocketTest.h>
#include <folly/portability/GMock.h>
#include <folly/portability/GTest.h>
#include <glog/logging.h>
#include <wangle/acceptor/AcceptObserver.h>

using namespace folly;
using namespace wangle;
using namespace testing;

class TestConnection : public wangle::ManagedConnection {
 public:
  void timeoutExpired() noexcept override {}
  void describe(std::ostream& /*os*/) const override {}
  bool isBusy() const override {
    return false;
  }
  void notifyPendingShutdown() override {}
  void closeWhenIdle() override {}
  void dropConnection(const std::string& /* errorMsg */ = "") override {
    delete this;
  }
  void dumpConnectionState(uint8_t /*loglevel*/) override {}
};

class TestAcceptor : public Acceptor {
 public:
  explicit TestAcceptor(const ServerSocketConfig& accConfig)
      : Acceptor(accConfig) {}

  void onNewConnection(
      folly::AsyncTransportWrapper::UniquePtr /*sock*/,
      const folly::SocketAddress* /*address*/,
      const std::string& /*nextProtocolName*/,
      SecureTransportType /*secureTransportType*/,
      const TransportInfo& /*tinfo*/) override {
    addConnection(new TestConnection);
    getEventBase()->terminateLoopSoon();
  }

  DefaultToFizzPeekingCallback* getFizzPeeker() override {
    return Acceptor::getFizzPeeker();
  }
};

enum class TestSSLConfig { NO_SSL, SSL };

class AcceptorTest : public ::testing::TestWithParam<TestSSLConfig> {
 public:
  AcceptorTest() = default;

  std::shared_ptr<AsyncSocket> connectClientSocket(
      const SocketAddress& serverAddress) {
    TestSSLConfig testConfig = GetParam();
    if (testConfig == TestSSLConfig::SSL) {
      auto clientSocket = AsyncSSLSocket::newSocket(getTestSslContext(), &evb_);
      clientSocket->connect(nullptr, serverAddress);
      return clientSocket;
    } else {
      return AsyncSocket::newSocket(&evb_, serverAddress);
    }
  }

  std::tuple<std::shared_ptr<TestAcceptor>, std::shared_ptr<AsyncServerSocket>>
  initTestAcceptorAndSocket() {
    TestSSLConfig testConfig = GetParam();
    ServerSocketConfig config;
    if (testConfig == TestSSLConfig::SSL) {
      config.sslContextConfigs.emplace_back(getTestSslContextConfig());
    }
    return initTestAcceptorAndSocket(config);
  }

  std::tuple<std::shared_ptr<TestAcceptor>, std::shared_ptr<AsyncServerSocket>>
  initTestAcceptorAndSocket(ServerSocketConfig config) {
    auto acceptor = std::make_shared<TestAcceptor>(config);
    auto socket = AsyncServerSocket::newSocket(&evb_);
    socket->addAcceptCallback(acceptor.get(), &evb_);
    acceptor->init(socket.get(), &evb_);
    socket->bind(0);
    socket->listen(100);
    socket->startAccepting();
    return std::make_tuple(acceptor, socket);
  }

  static std::shared_ptr<folly::SSLContext> getTestSslContext() {
    auto sslContext = std::make_shared<folly::SSLContext>();
    sslContext->setOptions(SSL_OP_NO_TICKET);
    sslContext->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
    return sslContext;
  }

  static wangle::SSLContextConfig getTestSslContextConfig() {
    wangle::SSLContextConfig sslCtxConfig;
    sslCtxConfig.setCertificate(folly::kTestCert, folly::kTestKey, "");
    sslCtxConfig.clientCAFile = folly::kTestCA;
    sslCtxConfig.sessionContext = "AcceptorTest";
    sslCtxConfig.isDefault = true;
    sslCtxConfig.clientVerification =
        folly::SSLContext::SSLVerifyPeerEnum::NO_VERIFY;
    sslCtxConfig.sessionCacheEnabled = false;
    return sslCtxConfig;
  }

 protected:
  EventBase evb_;
};

INSTANTIATE_TEST_CASE_P(
    NoSSLAndSSLTests,
    AcceptorTest,
    ::testing::Values(TestSSLConfig::NO_SSL, TestSSLConfig::SSL));

TEST_P(AcceptorTest, Basic) {
  auto [acceptor, serverSocket] = initTestAcceptorAndSocket();
  SocketAddress serverAddress;
  serverSocket->getAddress(&serverAddress);
  auto clientSocket = connectClientSocket(serverAddress);

  evb_.loopForever();

  CHECK_EQ(acceptor->getNumConnections(), 1);
  CHECK(acceptor->getState() == Acceptor::State::kRunning);
  acceptor->forceStop();
  serverSocket->stopAccepting();
  evb_.loop();
}

class MockAcceptObserver : public AcceptObserver {
 public:
  GMOCK_METHOD1_(, noexcept, , accept, void(folly::AsyncTransport* const));
  GMOCK_METHOD1_(, noexcept, , ready, void(folly::AsyncTransport* const));
  GMOCK_METHOD1_(, noexcept, , acceptorDestroy, void(Acceptor* const));
  GMOCK_METHOD1_(, noexcept, , observerAttach, void(Acceptor* const));
  GMOCK_METHOD1_(, noexcept, , observerDetach, void(Acceptor* const));
};

class MockAsyncSocketLifecycleObserver : public AsyncSocket::LifecycleObserver {
 public:
  GMOCK_METHOD1_(, noexcept, , observerAttach, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , observerDetach, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , destroy, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , close, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , connect, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , fdDetach, void(AsyncSocket*));
  GMOCK_METHOD2_(, noexcept, , move, void(AsyncSocket*, AsyncSocket*));
};

class MockFizzLoggingCallback
    : public FizzAcceptorHandshakeHelper::LoggingCallback {
 public:
  MOCK_METHOD2(
      logFizzHandshakeSuccess,
      void(const fizz::server::AsyncFizzServer&, const wangle::TransportInfo*));
  MOCK_METHOD2(
      logFizzHandshakeFallback,
      void(const fizz::server::AsyncFizzServer&, const wangle::TransportInfo*));
  MOCK_METHOD2(
      logFizzHandshakeError,
      void(
          const fizz::server::AsyncFizzServer&,
          const folly::exception_wrapper&));<commit_msg>Invoke life cycle observer callbacks during event base attach and<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed 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.
 */

#include <wangle/acceptor/Acceptor.h>
#include <folly/io/async/EventBase.h>
#include <folly/io/async/test/AsyncSSLSocketTest.h>
#include <folly/portability/GMock.h>
#include <folly/portability/GTest.h>
#include <glog/logging.h>
#include <wangle/acceptor/AcceptObserver.h>

using namespace folly;
using namespace wangle;
using namespace testing;

class TestConnection : public wangle::ManagedConnection {
 public:
  void timeoutExpired() noexcept override {}
  void describe(std::ostream& /*os*/) const override {}
  bool isBusy() const override {
    return false;
  }
  void notifyPendingShutdown() override {}
  void closeWhenIdle() override {}
  void dropConnection(const std::string& /* errorMsg */ = "") override {
    delete this;
  }
  void dumpConnectionState(uint8_t /*loglevel*/) override {}
};

class TestAcceptor : public Acceptor {
 public:
  explicit TestAcceptor(const ServerSocketConfig& accConfig)
      : Acceptor(accConfig) {}

  void onNewConnection(
      folly::AsyncTransportWrapper::UniquePtr /*sock*/,
      const folly::SocketAddress* /*address*/,
      const std::string& /*nextProtocolName*/,
      SecureTransportType /*secureTransportType*/,
      const TransportInfo& /*tinfo*/) override {
    addConnection(new TestConnection);
    getEventBase()->terminateLoopSoon();
  }

  DefaultToFizzPeekingCallback* getFizzPeeker() override {
    return Acceptor::getFizzPeeker();
  }
};

enum class TestSSLConfig { NO_SSL, SSL };

class AcceptorTest : public ::testing::TestWithParam<TestSSLConfig> {
 public:
  AcceptorTest() = default;

  std::shared_ptr<AsyncSocket> connectClientSocket(
      const SocketAddress& serverAddress) {
    TestSSLConfig testConfig = GetParam();
    if (testConfig == TestSSLConfig::SSL) {
      auto clientSocket = AsyncSSLSocket::newSocket(getTestSslContext(), &evb_);
      clientSocket->connect(nullptr, serverAddress);
      return clientSocket;
    } else {
      return AsyncSocket::newSocket(&evb_, serverAddress);
    }
  }

  std::tuple<std::shared_ptr<TestAcceptor>, std::shared_ptr<AsyncServerSocket>>
  initTestAcceptorAndSocket() {
    TestSSLConfig testConfig = GetParam();
    ServerSocketConfig config;
    if (testConfig == TestSSLConfig::SSL) {
      config.sslContextConfigs.emplace_back(getTestSslContextConfig());
    }
    return initTestAcceptorAndSocket(config);
  }

  std::tuple<std::shared_ptr<TestAcceptor>, std::shared_ptr<AsyncServerSocket>>
  initTestAcceptorAndSocket(ServerSocketConfig config) {
    auto acceptor = std::make_shared<TestAcceptor>(config);
    auto socket = AsyncServerSocket::newSocket(&evb_);
    socket->addAcceptCallback(acceptor.get(), &evb_);
    acceptor->init(socket.get(), &evb_);
    socket->bind(0);
    socket->listen(100);
    socket->startAccepting();
    return std::make_tuple(acceptor, socket);
  }

  static std::shared_ptr<folly::SSLContext> getTestSslContext() {
    auto sslContext = std::make_shared<folly::SSLContext>();
    sslContext->setOptions(SSL_OP_NO_TICKET);
    sslContext->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
    return sslContext;
  }

  static wangle::SSLContextConfig getTestSslContextConfig() {
    wangle::SSLContextConfig sslCtxConfig;
    sslCtxConfig.setCertificate(folly::kTestCert, folly::kTestKey, "");
    sslCtxConfig.clientCAFile = folly::kTestCA;
    sslCtxConfig.sessionContext = "AcceptorTest";
    sslCtxConfig.isDefault = true;
    sslCtxConfig.clientVerification =
        folly::SSLContext::SSLVerifyPeerEnum::NO_VERIFY;
    sslCtxConfig.sessionCacheEnabled = false;
    return sslCtxConfig;
  }

 protected:
  EventBase evb_;
};

INSTANTIATE_TEST_CASE_P(
    NoSSLAndSSLTests,
    AcceptorTest,
    ::testing::Values(TestSSLConfig::NO_SSL, TestSSLConfig::SSL));

TEST_P(AcceptorTest, Basic) {
  auto [acceptor, serverSocket] = initTestAcceptorAndSocket();
  SocketAddress serverAddress;
  serverSocket->getAddress(&serverAddress);
  auto clientSocket = connectClientSocket(serverAddress);

  evb_.loopForever();

  CHECK_EQ(acceptor->getNumConnections(), 1);
  CHECK(acceptor->getState() == Acceptor::State::kRunning);
  acceptor->forceStop();
  serverSocket->stopAccepting();
  evb_.loop();
}

class MockAcceptObserver : public AcceptObserver {
 public:
  GMOCK_METHOD1_(, noexcept, , accept, void(folly::AsyncTransport* const));
  GMOCK_METHOD1_(, noexcept, , ready, void(folly::AsyncTransport* const));
  GMOCK_METHOD1_(, noexcept, , acceptorDestroy, void(Acceptor* const));
  GMOCK_METHOD1_(, noexcept, , observerAttach, void(Acceptor* const));
  GMOCK_METHOD1_(, noexcept, , observerDetach, void(Acceptor* const));
};

class MockAsyncSocketLifecycleObserver : public AsyncSocket::LifecycleObserver {
 public:
  GMOCK_METHOD1_(, noexcept, , observerAttach, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , observerDetach, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , destroy, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , close, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , connect, void(AsyncTransport*));
  GMOCK_METHOD1_(, noexcept, , fdDetach, void(AsyncSocket*));
  GMOCK_METHOD2_(, noexcept, , move, void(AsyncSocket*, AsyncSocket*));
  GMOCK_METHOD2_(, noexcept, , evbAttach, void(AsyncTransport*, EventBase*));
  GMOCK_METHOD2_(, noexcept, , evbDetach, void(AsyncTransport*, EventBase*));
};

class MockFizzLoggingCallback
    : public FizzAcceptorHandshakeHelper::LoggingCallback {
 public:
  MOCK_METHOD2(
      logFizzHandshakeSuccess,
      void(const fizz::server::AsyncFizzServer&, const wangle::TransportInfo*));
  MOCK_METHOD2(
      logFizzHandshakeFallback,
      void(const fizz::server::AsyncFizzServer&, const wangle::TransportInfo*));
  MOCK_METHOD2(
      logFizzHandshakeError,
      void(
          const fizz::server::AsyncFizzServer&,
          const folly::exception_wrapper&));
{'fraction_non_alphanumeric': 0.12094257178526842, 'fraction_numerical': 0.004213483146067416, 'mean_word_length': 5.240019474196689, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4996122', 'n_tokens_mistral': 3969, 'n_tokens_neox': 3908, 'n_words': 806}