1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/tools/content_decoder_tool/content_decoder_tool.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/containers/adapters.h"
11 #include "base/logging.h"
12 #include "base/strings/string_util.h"
13 #include "net/base/completion_once_callback.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/test_completion_callback.h"
16 #include "net/filter/brotli_source_stream.h"
17 #include "net/filter/gzip_source_stream.h"
18 #include "net/filter/source_stream.h"
19
20 namespace net {
21
22 namespace {
23
24 const int kBufferLen = 4096;
25
26 const char kDeflate[] = "deflate";
27 const char kGZip[] = "gzip";
28 const char kXGZip[] = "x-gzip";
29 const char kBrotli[] = "br";
30
31 class StdinSourceStream : public SourceStream {
32 public:
StdinSourceStream(std::istream * input_stream)33 explicit StdinSourceStream(std::istream* input_stream)
34 : SourceStream(SourceStream::TYPE_NONE), input_stream_(input_stream) {}
35
36 StdinSourceStream(const StdinSourceStream&) = delete;
37 StdinSourceStream& operator=(const StdinSourceStream&) = delete;
38
39 ~StdinSourceStream() override = default;
40
41 // SourceStream implementation.
Read(IOBuffer * dest_buffer,int buffer_size,CompletionOnceCallback callback)42 int Read(IOBuffer* dest_buffer,
43 int buffer_size,
44 CompletionOnceCallback callback) override {
45 if (input_stream_->eof())
46 return OK;
47 if (input_stream_) {
48 input_stream_->read(dest_buffer->data(), buffer_size);
49 int bytes = input_stream_->gcount();
50 return bytes;
51 }
52 return ERR_FAILED;
53 }
54
Description() const55 std::string Description() const override { return ""; }
56
MayHaveMoreBytes() const57 bool MayHaveMoreBytes() const override { return true; }
58
59 private:
60 std::istream* input_stream_;
61 };
62
63 } // namespace
64
65 // static
ContentDecoderToolProcessInput(std::vector<std::string> content_encodings,std::istream * input_stream,std::ostream * output_stream)66 bool ContentDecoderToolProcessInput(std::vector<std::string> content_encodings,
67 std::istream* input_stream,
68 std::ostream* output_stream) {
69 std::unique_ptr<SourceStream> upstream(
70 std::make_unique<StdinSourceStream>(input_stream));
71 for (const auto& content_encoding : base::Reversed(content_encodings)) {
72 std::unique_ptr<SourceStream> downstream;
73 if (base::EqualsCaseInsensitiveASCII(content_encoding, kBrotli)) {
74 downstream = CreateBrotliSourceStream(std::move(upstream));
75 } else if (base::EqualsCaseInsensitiveASCII(content_encoding, kDeflate)) {
76 downstream = GzipSourceStream::Create(std::move(upstream),
77 SourceStream::TYPE_DEFLATE);
78 } else if (base::EqualsCaseInsensitiveASCII(content_encoding, kGZip) ||
79 base::EqualsCaseInsensitiveASCII(content_encoding, kXGZip)) {
80 downstream = GzipSourceStream::Create(std::move(upstream),
81 SourceStream::TYPE_GZIP);
82 } else {
83 LOG(ERROR) << "Unsupported decoder '" << content_encoding << "'.";
84 return false;
85 }
86 if (downstream == nullptr) {
87 LOG(ERROR) << "Couldn't create the decoder.";
88 return false;
89 }
90 upstream = std::move(downstream);
91 }
92 if (!upstream) {
93 LOG(ERROR) << "Couldn't create the decoder.";
94 return false;
95 }
96 scoped_refptr<IOBuffer> read_buffer =
97 base::MakeRefCounted<IOBufferWithSize>(kBufferLen);
98 while (true) {
99 TestCompletionCallback callback;
100 int bytes_read =
101 upstream->Read(read_buffer.get(), kBufferLen, callback.callback());
102 if (bytes_read == ERR_IO_PENDING)
103 bytes_read = callback.WaitForResult();
104
105 if (bytes_read < 0) {
106 LOG(ERROR) << "Couldn't decode stdin.";
107 return false;
108 }
109 output_stream->write(read_buffer->data(), bytes_read);
110 // If EOF is read, break out the while loop.
111 if (bytes_read == 0)
112 break;
113 }
114 return true;
115 }
116
117 } // namespace net
118