summaryrefslogtreecommitdiffstats
path: root/net/filter/gzip_filter.h
blob: 42dc3202509c7773d03646d1fa5d495826d6e8d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// GZipFilter applies gzip and deflate content encoding/decoding to a data
// stream. As specified by HTTP 1.1, with gzip encoding the content is
// wrapped with a gzip header, and with deflate encoding the content is in
// a raw, headerless DEFLATE stream.
//
// Internally GZipFilter uses zlib inflate to do decoding.
//
// GZipFilter is a subclass of Filter. See the latter's header file filter.h
// for sample usage.

#ifndef NET_FILTER_GZIP_FILTER_H_
#define NET_FILTER_GZIP_FILTER_H_

#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "net/filter/filter.h"

typedef struct z_stream_s z_stream;

namespace net {

class GZipHeader;

class GZipFilter : public Filter {
 public:
  ~GZipFilter() override;

  // Initializes filter decoding mode and internal control blocks.
  // Parameter filter_type specifies the type of filter, which corresponds to
  // either gzip or deflate decoding. The function returns true if success and
  // false otherwise.
  // The filter can only be initialized once.
  bool InitDecoding(Filter::FilterType filter_type);

  // Decodes the pre-filter data and writes the output into the dest_buffer
  // passed in.
  // The function returns FilterStatus. See filter.h for its description.
  //
  // Upon entry, *dest_len is the total size (in number of chars) of the
  // destination buffer. Upon exit, *dest_len is the actual number of chars
  // written into the destination buffer.
  //
  // This function will fail if there is no pre-filter data in the
  // stream_buffer_. On the other hand, *dest_len can be 0 upon successful
  // return. For example, the internal zlib may process some pre-filter data
  // but not produce output yet.
  FilterStatus ReadFilteredData(char* dest_buffer, int* dest_len) override;

 private:
  enum DecodingStatus {
    DECODING_UNINITIALIZED,
    DECODING_IN_PROGRESS,
    DECODING_DONE,
    DECODING_ERROR
  };

  enum DecodingMode {
    DECODE_MODE_GZIP,
    DECODE_MODE_DEFLATE,
    DECODE_MODE_UNKNOWN
  };

  enum GZipCheckHeaderState {
    GZIP_CHECK_HEADER_IN_PROGRESS,
    GZIP_GET_COMPLETE_HEADER,
    GZIP_GET_INVALID_HEADER
  };

  static const int kGZipFooterSize = 8;

  // Only to be instantiated by Filter::Factory.
  GZipFilter(FilterType type);
  friend class Filter;

  // Parses and verifies the GZip header.
  // Upon exit, the function updates gzip_header_status_ accordingly.
  //
  // The function returns Filter::FILTER_OK if it gets a complete header and
  // there are more data in the pre-filter buffer.
  // The function returns Filter::FILTER_NEED_MORE_DATA if it parses all data
  // in the pre-filter buffer, either getting a complete header or a partial
  // header. The caller needs to check gzip_header_status_ and call this
  // function again for partial header.
  // The function returns Filter::FILTER_ERROR if error occurs.
  FilterStatus CheckGZipHeader();

  // Internal function to decode the pre-filter data and writes the output into
  // the dest_buffer passed in.
  //
  // This is the internal version of ReadFilteredData. See the latter's
  // comments for the use of function.
  FilterStatus DoInflate(char* dest_buffer, int* dest_len);

  // Inserts a zlib header to the data stream before calling zlib inflate.
  // This is used to work around server bugs. See more comments at the place
  // it is called in gzip_filter.cc.
  // The function returns true on success and false otherwise.
  bool InsertZlibHeader();

  // Skip the 8 byte GZip footer after z_stream_end
  void SkipGZipFooter();

  // Tracks the status of decoding.
  // This variable is initialized by InitDecoding and updated only by
  // ReadFilteredData.
  DecodingStatus decoding_status_;

  // Indicates the type of content decoding the GZipFilter is performing.
  // This variable is set only once by InitDecoding.
  DecodingMode decoding_mode_;

  // Used to parse the gzip header in gzip stream.
  // It is used when the decoding_mode_ is DECODE_MODE_GZIP.
  scoped_ptr<GZipHeader> gzip_header_;

  // Tracks the progress of parsing gzip header.
  // This variable is maintained by gzip_header_.
  GZipCheckHeaderState gzip_header_status_;

  // A flag used by InsertZlibHeader to record whether we've successfully added
  // a zlib header to this stream.
  bool zlib_header_added_;

  // Tracks how many bytes of gzip footer have been received.
  int gzip_footer_bytes_;

  // The control block of zlib which actually does the decoding.
  // This data structure is initialized by InitDecoding and updated only by
  // DoInflate, with InsertZlibHeader being the exception as a workaround.
  scoped_ptr<z_stream> zlib_stream_;

  // For robustness, when we see the solo sdch filter, we chain in a gzip filter
  // in front of it, with this flag to indicate that the gzip decoding might not
  // be needed.  This handles a strange case where "Content-Encoding: sdch,gzip"
  // is reduced by an errant proxy to "Content-Encoding: sdch", while the
  // content is indeed really gzipped result of sdch :-/.
  // If this flag is set, then we will revert to being a pass through filter if
  // we don't get a valid gzip header.
  bool possible_sdch_pass_through_;

  DISALLOW_COPY_AND_ASSIGN(GZipFilter);
};

}  // namespace net

#endif  // NET_FILTER_GZIP_FILTER_H__