1 // Copyright 2016 The Chromium Authors. All rights reserved.
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/logging.h"
11 #include "base/strings/string_util.h"
12 #include "net/base/completion_once_callback.h"
13 #include "net/base/io_buffer.h"
14 #include "net/base/test_completion_callback.h"
15 #include "net/filter/brotli_source_stream.h"
16 #include "net/filter/gzip_source_stream.h"
17 #include "net/filter/source_stream.h"
18 
19 namespace net {
20 
21 namespace {
22 
23 const int kBufferLen = 4096;
24 
25 const char kDeflate[] = "deflate";
26 const char kGZip[] = "gzip";
27 const char kXGZip[] = "x-gzip";
28 const char kBrotli[] = "br";
29 
30 class StdinSourceStream : public SourceStream {
31  public:
StdinSourceStream(std::istream * input_stream)32   explicit StdinSourceStream(std::istream* input_stream)
33       : SourceStream(SourceStream::TYPE_NONE), input_stream_(input_stream) {}
34   ~StdinSourceStream() override = default;
35 
36   // SourceStream implementation.
Read(IOBuffer * dest_buffer,int buffer_size,CompletionOnceCallback callback)37   int Read(IOBuffer* dest_buffer,
38            int buffer_size,
39            CompletionOnceCallback callback) override {
40     if (input_stream_->eof())
41       return OK;
42     if (input_stream_) {
43       input_stream_->read(dest_buffer->data(), buffer_size);
44       int bytes = input_stream_->gcount();
45       return bytes;
46     }
47     return ERR_FAILED;
48   }
49 
Description() const50   std::string Description() const override { return ""; }
51 
MayHaveMoreBytes() const52   bool MayHaveMoreBytes() const override { return true; }
53 
54  private:
55   std::istream* input_stream_;
56 
57   DISALLOW_COPY_AND_ASSIGN(StdinSourceStream);
58 };
59 
60 }  // namespace
61 
62 // static
ContentDecoderToolProcessInput(std::vector<std::string> content_encodings,std::istream * input_stream,std::ostream * output_stream)63 bool ContentDecoderToolProcessInput(std::vector<std::string> content_encodings,
64                                     std::istream* input_stream,
65                                     std::ostream* output_stream) {
66   std::unique_ptr<SourceStream> upstream(
67       std::make_unique<StdinSourceStream>(input_stream));
68   for (std::vector<std::string>::const_reverse_iterator riter =
69            content_encodings.rbegin();
70        riter != content_encodings.rend(); ++riter) {
71     std::string content_encoding = *riter;
72     std::unique_ptr<SourceStream> downstream = nullptr;
73     if (base::LowerCaseEqualsASCII(content_encoding, kBrotli)) {
74       downstream = CreateBrotliSourceStream(std::move(upstream));
75     } else if (base::LowerCaseEqualsASCII(content_encoding, kDeflate)) {
76       downstream = GzipSourceStream::Create(std::move(upstream),
77                                             SourceStream::TYPE_DEFLATE);
78     } else if (base::LowerCaseEqualsASCII(content_encoding, kGZip) ||
79                base::LowerCaseEqualsASCII(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