Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: chrome/browser/extensions/api/web_request/post_data_parser.cc

Issue 10694055: Add read-only access to POST data for webRequest's onBeforeRequest (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Wrong strcasecmp :) Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "chrome/browser/extensions/api/web_request/post_data_parser.h"
6
7 #include "base/values.h"
8 #include "net/base/escape.h"
9 #include "net/base/upload_data.h"
10 #include "net/url_request/url_request.h"
11
12 namespace {
13 const char kContentDisposition[] = "Content-Disposition:";
14
15 // Takes |dictionary| of <string, list of strings> pairs, and gets the list
16 // for |key|, creating it if necessary.
17 ListValue* GetOrCreateList(DictionaryValue* dictionary,
18 const std::string& key) {
19 ListValue* list = NULL;
20 if (!dictionary->GetList(key, &list)) {
21 list = new ListValue();
22 dictionary->Set(key, list);
23 }
24 return list;
25 }
26 }
27
28 namespace extensions {
29
30 // Implementation of PostDataParser and PostDataParser::Result .
31
32 PostDataParser::Result::Result() {}
33 PostDataParser::Result::~Result() {}
34
35 void PostDataParser::Result::Reset() {
36 key_.erase();
37 val_.erase();
38 }
39
40 void PostDataParser::Result::SetKey(const char* s, size_t n) {
41 key_.replace(0, std::string::npos, s, n);
42 }
43
44 void PostDataParser::Result::SetVal(const char* s, size_t n) {
45 val_.replace(0, std::string::npos, s, n);
46 }
47
48 void PostDataParser::Result::SetKey(const std::string& str) {
49 key_ = str;
50 }
51
52 void PostDataParser::Result::SetVal(const std::string& str) {
53 val_ = str;
54 }
55
56 PostDataParser::~PostDataParser() {}
57
58 // static
59 scoped_ptr<PostDataParser> PostDataParser::CreatePostDataParser(
60 const net::URLRequest* request) {
61 std::string value;
62 const bool found = request->extra_request_headers().GetHeader(
63 net::HttpRequestHeaders::kContentType, &value);
64 return CreatePostDataParser(found ? &value : NULL);
65 }
66
67 // static
68 scoped_ptr<PostDataParser> PostDataParser::CreatePostDataParser(
69 const std::string* content_type_header) {
70 enum ParserChoice {kUrlEncoded, kMultipart, kError};
71 ParserChoice choice = kError;
72 std::string boundary;
73
74 if (content_type_header == NULL) {
75 choice = kUrlEncoded;
76 } else {
77 const std::string content_type(
78 content_type_header->substr(0, content_type_header->find(';')));
79 if (content_type == "application/x-www-form-urlencoded") {
80 choice = kUrlEncoded;
81 } else if (content_type == "multipart/form-data") {
82 const char kBoundaryString[] = "boundary=";
83 size_t offset = content_type_header->find(kBoundaryString);
84 if (offset == std::string::npos) {
85 // Malformed header.
86 return scoped_ptr<PostDataParser>();
87 }
88 offset += strlen(kBoundaryString);
89 boundary = content_type_header->substr(
90 offset, content_type_header->find(';', offset));
91 choice = kMultipart;
92 }
93 }
94 // Other cases are unparseable, including when |content_type| is "text/plain".
95
96 switch (choice) {
97 case kUrlEncoded:
98 return scoped_ptr<PostDataParser>(new PostDataParserUrlEncoded());
99 case kMultipart:
100 return scoped_ptr<PostDataParser>(new PostDataParserMultipart(boundary));
101 default: // In other words, case kError:
102 return scoped_ptr<PostDataParser>();
103 }
104 }
105
106 // static
107 scoped_ptr<base::DictionaryValue> PostDataParser::ParseURLRequestData(
108 const net::URLRequest* request) {
109 if (request->method() != "POST")
110 return scoped_ptr<base::DictionaryValue>();
111 const std::vector<net::UploadData::Element>* elements =
112 request->get_upload()->elements();
113 scoped_ptr<PostDataParser> parser = CreatePostDataParser(request);
114 if (parser.get() == NULL) {
115 // No parser means most probably unsupported form encoding.
116 return scoped_ptr<base::DictionaryValue>();
117 }
118 scoped_ptr<base::DictionaryValue> form_data(new base::DictionaryValue);
119 std::vector<net::UploadData::Element>::const_iterator element;
120 bool data_valid = true;
121 for (element = elements->begin();
122 data_valid && element != elements->end(); ++element) {
123 if (element->type() != net::UploadData::TYPE_BYTES) {
124 // We do not handle data including blobs or chunks.
125 if (element->type() != net::UploadData::TYPE_FILE)
126 data_valid = false;
127 continue;
128 }
129 if (!parser->SetSource(&(element->bytes()))) continue;
battre 2012/07/30 17:54:25 nit: could you move the continue to the next line?
vabr (Chromium) 2012/07/31 09:03:18 Done.
130 Result result;
131 while (parser->GetNextPair(&result)) {
132 GetOrCreateList(form_data.get(), result.get_key())->Append(
133 new StringValue(result.get_val()));
134 }
135 }
136 if (data_valid && parser->AllDataReadOK())
137 return form_data.Pass();
138 else
139 return scoped_ptr<base::DictionaryValue>();
140 }
141
142 // Implementation of PostDataParserUrlEncoded.
143
144 PostDataParserUrlEncoded::PostDataParserUrlEncoded() : source_(NULL) {}
145
146 PostDataParserUrlEncoded::~PostDataParserUrlEncoded() {}
147
148 bool PostDataParserUrlEncoded::AllDataReadOK() {
149 return source_ != NULL && offset_ == source_->end();
150 }
151
152 bool PostDataParserUrlEncoded::GetNextPair(Result* result) {
153 result->Reset();
154 if (source_ == NULL)
155 return false;
156 if (offset_ == source_->end())
157 return false;
158 std::vector<char>::const_iterator seek = offset_;
159 // (*) Now we have |seek| >= |offset_| until the end of this function:
160 while (seek != source_->end() && *seek != '=')
161 ++seek;
162 if (seek == source_->end()) {
163 // This means the data is malformed.
164 offset_ = seek;
165 return false;
166 }
167 std::string encoded_key(&(*offset_), seek - offset_); // Safe, see (*).
168 const net::UnescapeRule::Type unescape_rules =
169 net::UnescapeRule::URL_SPECIAL_CHARS | net::UnescapeRule::CONTROL_CHARS |
170 net::UnescapeRule::SPACES | net::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
171 result->SetKey(net::UnescapeURLComponent(encoded_key, unescape_rules));
172 offset_ = ++seek;
173 while (seek != source_->end() && *seek != '&')
174 ++seek;
175 std::string encoded_val(&(*offset_), seek - offset_); // Safe, see (*).
176 result->SetVal(net::UnescapeURLComponent(encoded_val, unescape_rules));
177 offset_ = (seek == source_->end()) ? seek : seek + 1;
178 return true;
179 }
180
181 bool PostDataParserUrlEncoded::SetSource(const std::vector<char>* source) {
182 if (source_ != NULL)
183 return false;
184 source_ = source;
185 offset_ = source_->begin();
186 return true;
187 }
188
189 // Implementation of PostDataParserMultipart.
190
191 PostDataParserMultipart::PostDataParserMultipart(
192 const std::string& boundary_separator)
193 : source_(NULL),
194 length_(0), // Dummy value.
195 line_start_(0), // Dummy value.
196 line_end_(0), // Dummy value.
197 next_line_(0), // Dummy value.
198 boundary_("--" + boundary_separator),
199 final_boundary_(boundary_ + "--"),
200 state_(kInit),
201 line_type_(kEmpty) // Dummy value.
202 {}
203
204 PostDataParserMultipart::~PostDataParserMultipart() {}
205
206 bool PostDataParserMultipart::AllDataReadOK() {
207 return source_ != NULL && next_line_ >= length_ && state_ == kFinal;
208 }
209
210 // This function reads one block of the data, between two boundaries.
211 // First it reads the header to learn the key, and possibly also the
212 // value, if this block is for a file input element.
213 // Otherwise it then reads the value from the body.
214 bool PostDataParserMultipart::GetNextPair(Result* result) {
215 result->Reset();
216 if (state_ == kError)
217 return false;
218 while (state_ != kHeadRead) {
219 if (!DoStep())
220 return false;
221 }
222 bool val_extracted = false;
223 bool name_parsed = ParseHead(result, &val_extracted);
224 while (state_ != kBody) {
225 if (!DoStep())
226 return false;
227 }
228 size_t val_start;
229 size_t val_end = 0; // Dummy value, replaced below, see (*).
230 // There may not be more to read from |source_| if the current result comes
231 // from a "file" input element. But then |result| is complete already.
232 if (!DoStep())
233 return val_extracted;
234 val_start = line_start_;
235 // (*) Now state_ == kBody, so val_end gets updated below.
236 while (state_ != kHeadStart && state_ != kFinal) {
237 val_end = line_end_;
238 if (!DoStep()) break;
239 }
240 if (name_parsed && !val_extracted) {
241 result->SetVal(source_ + val_start, val_end - val_start);
242 }
243 return name_parsed;
244 }
245
246 bool PostDataParserMultipart::SetSource(const std::vector<char>* source) {
247 if (state_ == kError)
248 return false;
249 if (source_ != NULL && next_line_ < length_)
250 return false;
251 source_ = &(source->front());
252 length_ = source->size();
253 next_line_ = 0;
254 return true;
255 }
256
257 bool PostDataParserMultipart::DoStep() {
258 if (!SeekNextLine())
259 return false;
260 switch (state_) {
261 case kInit:
262 if (line_type_ == kBoundary)
263 state_ = kHeadStart;
264 else
265 state_ = kError;
266 break;
267 case kHeadStart:
268 if (line_type_ == kDisposition)
269 state_ = kHeadRead;
270 else
271 state_ = kHead;
272 break;
273 case kHead:
274 if (line_type_ == kDisposition)
275 state_ = kHeadRead;
276 break;
277 case kHeadRead:
278 if (line_type_ == kEmpty)
279 state_ = kBody;
280 break;
281 case kBody:
282 if (line_type_ == kBoundary)
283 state_ = kHeadStart;
284 else if (line_type_ == kEndBoundary)
285 state_ = kFinal;
286 break;
287 case kFinal:
288 if (line_type_ != kEmpty)
289 state_ = kError;
290 case kError:
291 break;
292 }
293 return true;
294 }
295
296 PostDataParserMultipart::LineType PostDataParserMultipart::GetLineType() {
297 const size_t line_length = line_end_ - line_start_;
298 const base::StringPiece line(source_ + line_start_, line_length);
299 if (line == boundary_)
300 return kBoundary;
301 else if (line == final_boundary_)
302 return kEndBoundary;
303 else if (line.starts_with(kContentDisposition))
304 return kDisposition;
305 else if (line_start_ == line_end_)
306 return kEmpty;
307 else
308 return kOther;
309 }
310
311 // Contract: only to be called from DoStep().
312 bool PostDataParserMultipart::SeekNextLine() {
313 if (source_ == NULL || state_ == kError)
314 return false;
315 if (next_line_ >= length_)
316 return false;
317 line_start_ = next_line_;
318 size_t seek = line_start_;
319 while (seek < length_ && *(source_ + seek) != '\r')
320 ++seek;
321 line_end_ = seek;
322 line_type_ = GetLineType();
323 if ((seek+1) < length_ && strncmp(source_ + seek, "\r\n", 2) != 0)
324 return false;
325 next_line_ = seek + 2;
326 return true;
327 }
328
329 // Contract: line_type_ == kDisposition.
330 bool PostDataParserMultipart::ParseHead(Result* result, bool* val_extracted) {
331 DCHECK_EQ(kDisposition, line_type_);
332 base::StringPiece line(source_ + line_start_, line_end_ - line_start_);
333 const char kNameEquals[] = " name=\"";
334 const char kFilenameEquals[] = " filename=\"";
335 size_t key_offset = line.find(kNameEquals);
336 if (key_offset == base::StringPiece::npos)
337 return false;
338 key_offset += strlen(kNameEquals);
339 result->SetKey(source_ + line_start_ + key_offset,
340 line.find('"', key_offset) - key_offset);
341 size_t val_offset = line.find(kFilenameEquals);
342 if (val_offset == std::string::npos) {
343 *val_extracted = false;
344 } else {
345 *val_extracted = true;
346 val_offset += strlen(kFilenameEquals);
347 result->SetVal(source_ + line_start_ + val_offset,
348 line.find('"', val_offset) - val_offset);
349 }
350 return true;
351 }
352
353 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698