| // Copyright 2015 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "net/ssl/ssl_key_logger_impl.h" | |
| #include <stdio.h> | |
| #include <algorithm> | |
| #include <utility> | |
| #include "base/files/file_util.h" | |
| #include "base/files/scoped_file.h" | |
| #include "base/functional/bind.h" | |
| #include "base/location.h" | |
| #include "base/logging.h" | |
| #include "base/memory/ref_counted.h" | |
| #include "base/sequence_checker.h" | |
| #include "base/synchronization/lock.h" | |
| #include "base/task/sequenced_task_runner.h" | |
| #include "base/task/task_traits.h" | |
| #include "base/task/thread_pool.h" | |
| #include "base/thread_annotations.h" | |
| namespace net { | |
| namespace { | |
| // Bound the number of outstanding writes to bound memory usage. Some | |
| // antiviruses point this at a pipe and then read too slowly. See | |
| // https://crbug.com/566951 and https://crbug.com/914880. | |
| static constexpr size_t kMaxOutstandingLines = 512; | |
| } // namespace | |
| // An object which performs the blocking file operations on a background | |
| // SequencedTaskRunner. | |
| class SSLKeyLoggerImpl::Core | |
| : public base::RefCountedThreadSafe<SSLKeyLoggerImpl::Core> { | |
| public: | |
| Core() { | |
| DETACH_FROM_SEQUENCE(sequence_checker_); | |
| // That the user explicitly asked for debugging information would suggest | |
| // waiting to flush these to disk, but some buggy antiviruses point this at | |
| // a pipe and hang, so we avoid blocking shutdown. If writing to a real | |
| // file, writes should complete quickly enough that this does not matter. | |
| task_runner_ = base::ThreadPool::CreateSequencedTaskRunner( | |
| {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}); | |
| } | |
| Core(const Core&) = delete; | |
| Core& operator=(const Core&) = delete; | |
| void SetFile(base::File file) { | |
| #if defined(COBALT_PENDING_CLEAN_UP) | |
| NOTREACHED(); | |
| #else | |
| file_.reset(base::FileToFILE(std::move(file), "a")); | |
| #endif | |
| if (!file_) | |
| DVLOG(1) << "Could not adopt file"; | |
| } | |
| void OpenFile(const base::FilePath& path) { | |
| task_runner_->PostTask(FROM_HERE, | |
| base::BindOnce(&Core::OpenFileImpl, this, path)); | |
| } | |
| void WriteLine(const std::string& line) { | |
| bool was_empty; | |
| { | |
| base::AutoLock lock(lock_); | |
| was_empty = buffer_.empty(); | |
| if (buffer_.size() < kMaxOutstandingLines) { | |
| buffer_.push_back(line); | |
| } else { | |
| lines_dropped_ = true; | |
| } | |
| } | |
| if (was_empty) { | |
| task_runner_->PostTask(FROM_HERE, base::BindOnce(&Core::Flush, this)); | |
| } | |
| } | |
| private: | |
| friend class base::RefCountedThreadSafe<Core>; | |
| ~Core() = default; | |
| void OpenFileImpl(const base::FilePath& path) { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| DCHECK(!file_); | |
| #if defined(COBALT_PENDING_CLEAN_UP) | |
| NOTREACHED(); | |
| #else | |
| file_.reset(base::OpenFile(path, "a")); | |
| #endif | |
| if (!file_) | |
| DVLOG(1) << "Could not open " << path.value(); | |
| } | |
| void Flush() { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| bool lines_dropped = false; | |
| std::vector<std::string> buffer; | |
| { | |
| base::AutoLock lock(lock_); | |
| std::swap(lines_dropped, lines_dropped_); | |
| std::swap(buffer, buffer_); | |
| } | |
| if (file_) { | |
| #if defined(STARBOARD) | |
| for (const auto& line : buffer) { | |
| file_->WriteAll(line.c_str(), line.length()); | |
| } | |
| #else | |
| for (const auto& line : buffer) { | |
| fprintf(file_.get(), "%s\n", line.c_str()); | |
| } | |
| if (lines_dropped) { | |
| fprintf(file_.get(), "# Some lines were dropped due to slow writes.\n"); | |
| } | |
| fflush(file_.get()); | |
| #endif | |
| } | |
| } | |
| scoped_refptr<base::SequencedTaskRunner> task_runner_; | |
| base::ScopedFILE file_; | |
| SEQUENCE_CHECKER(sequence_checker_); | |
| base::Lock lock_; | |
| bool lines_dropped_ GUARDED_BY(lock_) = false; | |
| std::vector<std::string> buffer_ GUARDED_BY(lock_); | |
| }; | |
| SSLKeyLoggerImpl::SSLKeyLoggerImpl(const base::FilePath& path) | |
| : core_(base::MakeRefCounted<Core>()) { | |
| core_->OpenFile(path); | |
| } | |
| SSLKeyLoggerImpl::SSLKeyLoggerImpl(base::File file) | |
| : core_(base::MakeRefCounted<Core>()) { | |
| core_->SetFile(std::move(file)); | |
| } | |
| SSLKeyLoggerImpl::~SSLKeyLoggerImpl() = default; | |
| void SSLKeyLoggerImpl::WriteLine(const std::string& line) { | |
| core_->WriteLine(line); | |
| } | |
| } // namespace net |