void NetworkInterfaceMock::startCommand(const TaskExecutor::CallbackHandle& cbHandle,
                                        const RemoteCommandRequest& request,
                                        const RemoteCommandCompletionFn& onFinish) {
    stdx::lock_guard<stdx::mutex> lk(_mutex);
    invariant(!_inShutdown);
    const Date_t now = _now_inlock();
    NetworkOperationIterator insertBefore = _unscheduled.begin();
    while ((insertBefore != _unscheduled.end()) &&
           (insertBefore->getNextConsiderationDate() <= now)) {
        ++insertBefore;
    }
    _unscheduled.insert(insertBefore, NetworkOperation(cbHandle, request, now, onFinish));
}
Status NetworkInterfaceMock::startCommand(const CallbackHandle& cbHandle,
                                          RemoteCommandRequest& request,
                                          const RemoteCommandCompletionFn& onFinish) {
    if (inShutdown()) {
        return {ErrorCodes::ShutdownInProgress, "NetworkInterfaceMock shutdown in progress"};
    }

    stdx::lock_guard<stdx::mutex> lk(_mutex);

    const Date_t now = _now_inlock();
    auto op = NetworkOperation(cbHandle, request, now, onFinish);

    // If we don't have a hook, or we have already 'connected' to this host, enqueue the op.
    if (!_hook || _connections.count(request.target)) {
        _enqueueOperation_inlock(std::move(op));
    } else {
        _connectThenEnqueueOperation_inlock(request.target, std::move(op));
    }

    return Status::OK();
}
void NetworkInterfaceMock::_connectThenEnqueueOperation_inlock(const HostAndPort& target,
                                                               NetworkOperation&& op) {
    invariant(_hook);  // if there is no hook, we shouldn't even hit this codepath
    invariant(!_connections.count(target));

    auto handshakeReplyIter = _handshakeReplies.find(target);

    auto handshakeReply = (handshakeReplyIter != std::end(_handshakeReplies))
        ? handshakeReplyIter->second
        : RemoteCommandResponse(BSONObj(), BSONObj(), Milliseconds(0));

    auto valid = _hook->validateHost(target, handshakeReply);
    if (!valid.isOK()) {
        op.setResponse(_now_inlock(), valid);
        op.finishResponse();
        return;
    }

    auto swHookPostconnectCommand = _hook->makeRequest(target);

    if (!swHookPostconnectCommand.isOK()) {
        op.setResponse(_now_inlock(), swHookPostconnectCommand.getStatus());
        op.finishResponse();
        return;
    }

    boost::optional<RemoteCommandRequest> hookPostconnectCommand =
        std::move(swHookPostconnectCommand.getValue());

    if (!hookPostconnectCommand) {
        // If we don't have a post connect command, enqueue the actual command.
        _enqueueOperation_inlock(std::move(op));
        _connections.emplace(op.getRequest().target);
        return;
    }

    // The completion handler for the postconnect command schedules the original command.
    auto postconnectCompletionHandler = [this, op](ResponseStatus rs) mutable {
        stdx::lock_guard<stdx::mutex> lk(_mutex);
        if (!rs.isOK()) {
            op.setResponse(_now_inlock(), rs);
            op.finishResponse();
            return;
        }

        auto handleStatus = _hook->handleReply(op.getRequest().target, std::move(rs));

        if (!handleStatus.isOK()) {
            op.setResponse(_now_inlock(), handleStatus);
            op.finishResponse();
            return;
        }

        _enqueueOperation_inlock(std::move(op));
        _connections.emplace(op.getRequest().target);
    };

    auto postconnectOp = NetworkOperation(op.getCallbackHandle(),
                                          std::move(*hookPostconnectCommand),
                                          _now_inlock(),
                                          std::move(postconnectCompletionHandler));

    _enqueueOperation_inlock(std::move(postconnectOp));
}