Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 base::StringPiece& str) { | |
| 41 key_.replace(0, std::string::npos, str.data(), str.size()); | |
|
Matt Perry
2012/07/31 09:32:14
can also do: key_ = str.as_string();
vabr (Chromium)
2012/07/31 12:17:58
Yes, but then the bytes would be copied twice, fir
Matt Perry
2012/07/31 12:24:28
Compilers will likely optimize it as a single copy
vabr (Chromium)
2012/07/31 12:55:27
Thanks for the interesting link about the copy-con
| |
| 42 } | |
| 43 | |
| 44 void PostDataParser::Result::SetVal(const base::StringPiece& str) { | |
| 45 val_.replace(0, std::string::npos, str.data(), str.size()); | |
| 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()))) | |
| 130 continue; | |
| 131 Result result; | |
| 132 while (parser->GetNextPair(&result)) { | |
| 133 GetOrCreateList(form_data.get(), result.key())->Append( | |
| 134 new StringValue(result.val())); | |
| 135 } | |
| 136 } | |
| 137 if (data_valid && parser->AllDataReadOK()) | |
| 138 return form_data.Pass(); | |
| 139 else | |
| 140 return scoped_ptr<base::DictionaryValue>(); | |
| 141 } | |
| 142 | |
| 143 // Implementation of PostDataParserUrlEncoded. | |
| 144 | |
| 145 PostDataParserUrlEncoded::PostDataParserUrlEncoded() : source_(NULL) {} | |
| 146 | |
| 147 PostDataParserUrlEncoded::~PostDataParserUrlEncoded() {} | |
| 148 | |
| 149 bool PostDataParserUrlEncoded::AllDataReadOK() { | |
| 150 return source_ != NULL && offset_ == source_->end(); | |
| 151 } | |
| 152 | |
| 153 bool PostDataParserUrlEncoded::GetNextPair(Result* result) { | |
| 154 result->Reset(); | |
| 155 if (source_ == NULL) | |
| 156 return false; | |
| 157 if (offset_ == source_->end()) | |
| 158 return false; | |
| 159 std::vector<char>::const_iterator seek = offset_; | |
| 160 // (*) Now we have |seek| >= |offset_| until the end of this function: | |
| 161 while (seek != source_->end() && *seek != '=') | |
| 162 ++seek; | |
| 163 if (seek == source_->end()) { | |
| 164 // This means the data is malformed. | |
| 165 offset_ = seek; | |
| 166 return false; | |
| 167 } | |
| 168 std::string encoded_key(&(*offset_), seek - offset_); // Safe, see (*). | |
| 169 const net::UnescapeRule::Type unescape_rules = | |
| 170 net::UnescapeRule::URL_SPECIAL_CHARS | net::UnescapeRule::CONTROL_CHARS | | |
| 171 net::UnescapeRule::SPACES | net::UnescapeRule::REPLACE_PLUS_WITH_SPACE; | |
| 172 result->SetKey(net::UnescapeURLComponent(encoded_key, unescape_rules)); | |
| 173 offset_ = ++seek; | |
| 174 while (seek != source_->end() && *seek != '&') | |
| 175 ++seek; | |
| 176 std::string encoded_val(&(*offset_), seek - offset_); // Safe, see (*). | |
| 177 result->SetVal(net::UnescapeURLComponent(encoded_val, unescape_rules)); | |
| 178 offset_ = (seek == source_->end()) ? seek : seek + 1; | |
| 179 return true; | |
| 180 } | |
| 181 | |
| 182 bool PostDataParserUrlEncoded::SetSource(const std::vector<char>* source) { | |
| 183 if (source_ != NULL) | |
| 184 return false; | |
| 185 source_ = source; | |
| 186 offset_ = source_->begin(); | |
| 187 return true; | |
| 188 } | |
| 189 | |
| 190 // Implementation of PostDataParserMultipart. | |
| 191 | |
| 192 PostDataParserMultipart::PostDataParserMultipart( | |
| 193 const std::string& boundary_separator) | |
| 194 : source_(NULL), | |
| 195 length_(0), // Dummy value. | |
| 196 line_start_(0), // Dummy value. | |
| 197 line_end_(0), // Dummy value. | |
| 198 next_line_(0), // Dummy value. | |
| 199 boundary_("--" + boundary_separator), | |
| 200 final_boundary_(boundary_ + "--"), | |
| 201 state_(kInit), | |
| 202 line_type_(kEmpty) // Dummy value. | |
| 203 {} | |
| 204 | |
| 205 PostDataParserMultipart::~PostDataParserMultipart() {} | |
| 206 | |
| 207 bool PostDataParserMultipart::AllDataReadOK() { | |
| 208 return source_ != NULL && next_line_ >= length_ && state_ == kFinal; | |
| 209 } | |
| 210 | |
| 211 // This function reads one block of the data, between two boundaries. | |
| 212 // First it reads the header to learn the key, and possibly also the | |
| 213 // value, if this block is for a file input element. | |
| 214 // Otherwise it then reads the value from the body. | |
| 215 bool PostDataParserMultipart::GetNextPair(Result* result) { | |
| 216 result->Reset(); | |
| 217 if (state_ == kError) | |
| 218 return false; | |
| 219 while (state_ != kHeadRead) { | |
| 220 if (!DoStep()) | |
| 221 return false; | |
| 222 } | |
| 223 bool val_extracted = false; | |
| 224 bool name_parsed = ParseHead(result, &val_extracted); | |
| 225 while (state_ != kBody) { | |
| 226 if (!DoStep()) | |
| 227 return false; | |
| 228 } | |
| 229 size_t val_start; | |
| 230 size_t val_end = 0; // Dummy value, replaced below, see (*). | |
| 231 // There may not be more to read from |source_| if the current result comes | |
| 232 // from a "file" input element. But then |result| is complete already. | |
| 233 if (!DoStep()) | |
| 234 return val_extracted; | |
| 235 val_start = line_start_; | |
| 236 // (*) Now state_ == kBody, so val_end gets updated below. | |
| 237 while (state_ != kHeadStart && state_ != kFinal) { | |
| 238 val_end = line_end_; | |
| 239 if (!DoStep()) break; | |
| 240 } | |
| 241 if (name_parsed && !val_extracted) { | |
| 242 result->SetVal(base::StringPiece(source_ + val_start, val_end - val_start)); | |
| 243 } | |
| 244 return name_parsed; | |
| 245 } | |
| 246 | |
| 247 bool PostDataParserMultipart::SetSource(const std::vector<char>* source) { | |
| 248 if (state_ == kError) | |
| 249 return false; | |
| 250 if (source_ != NULL && next_line_ < length_) | |
| 251 return false; | |
| 252 source_ = &(source->front()); | |
| 253 length_ = source->size(); | |
| 254 next_line_ = 0; | |
| 255 return true; | |
| 256 } | |
| 257 | |
| 258 bool PostDataParserMultipart::DoStep() { | |
| 259 if (!SeekNextLine()) | |
| 260 return false; | |
| 261 switch (state_) { | |
| 262 case kInit: | |
| 263 if (line_type_ == kBoundary) | |
| 264 state_ = kHeadStart; | |
| 265 else | |
| 266 state_ = kError; | |
| 267 break; | |
| 268 case kHeadStart: | |
| 269 if (line_type_ == kDisposition) | |
| 270 state_ = kHeadRead; | |
| 271 else | |
| 272 state_ = kHead; | |
| 273 break; | |
| 274 case kHead: | |
| 275 if (line_type_ == kDisposition) | |
| 276 state_ = kHeadRead; | |
| 277 break; | |
| 278 case kHeadRead: | |
| 279 if (line_type_ == kEmpty) | |
| 280 state_ = kBody; | |
| 281 break; | |
| 282 case kBody: | |
| 283 if (line_type_ == kBoundary) | |
| 284 state_ = kHeadStart; | |
| 285 else if (line_type_ == kEndBoundary) | |
| 286 state_ = kFinal; | |
| 287 break; | |
| 288 case kFinal: | |
| 289 if (line_type_ != kEmpty) | |
| 290 state_ = kError; | |
| 291 case kError: | |
| 292 break; | |
| 293 } | |
| 294 return true; | |
| 295 } | |
| 296 | |
| 297 PostDataParserMultipart::LineType PostDataParserMultipart::GetLineType() { | |
| 298 const size_t line_length = line_end_ - line_start_; | |
| 299 const base::StringPiece line(source_ + line_start_, line_length); | |
| 300 if (line == boundary_) | |
| 301 return kBoundary; | |
| 302 else if (line == final_boundary_) | |
| 303 return kEndBoundary; | |
| 304 else if (line.starts_with(kContentDisposition)) | |
| 305 return kDisposition; | |
| 306 else if (line_start_ == line_end_) | |
| 307 return kEmpty; | |
| 308 else | |
| 309 return kOther; | |
| 310 } | |
| 311 | |
| 312 // Contract: only to be called from DoStep(). | |
| 313 bool PostDataParserMultipart::SeekNextLine() { | |
| 314 if (source_ == NULL || state_ == kError) | |
| 315 return false; | |
| 316 if (next_line_ >= length_) | |
| 317 return false; | |
| 318 line_start_ = next_line_; | |
| 319 size_t seek = line_start_; | |
| 320 while (seek < length_ && *(source_ + seek) != '\r') | |
| 321 ++seek; | |
| 322 line_end_ = seek; | |
| 323 line_type_ = GetLineType(); | |
| 324 if ((seek+1) < length_ && strncmp(source_ + seek, "\r\n", 2) != 0) | |
| 325 return false; | |
| 326 next_line_ = seek + 2; | |
| 327 return true; | |
| 328 } | |
| 329 | |
| 330 // Contract: line_type_ == kDisposition. | |
| 331 bool PostDataParserMultipart::ParseHead(Result* result, bool* val_extracted) { | |
| 332 DCHECK_EQ(kDisposition, line_type_); | |
| 333 base::StringPiece line(source_ + line_start_, line_end_ - line_start_); | |
| 334 const char kNameEquals[] = " name=\""; | |
| 335 const char kFilenameEquals[] = " filename=\""; | |
| 336 size_t key_offset = line.find(kNameEquals); | |
| 337 if (key_offset == base::StringPiece::npos) | |
| 338 return false; | |
| 339 key_offset += strlen(kNameEquals); | |
| 340 result->SetKey(base::StringPiece(source_ + line_start_ + key_offset, | |
| 341 line.find('"', key_offset) - key_offset)); | |
| 342 size_t val_offset = line.find(kFilenameEquals); | |
| 343 if (val_offset == std::string::npos) { | |
| 344 *val_extracted = false; | |
| 345 } else { | |
| 346 *val_extracted = true; | |
| 347 val_offset += strlen(kFilenameEquals); | |
| 348 result->SetVal(base::StringPiece(source_ + line_start_ + val_offset, | |
| 349 line.find('"', val_offset) - val_offset)); | |
| 350 } | |
| 351 return true; | |
| 352 } | |
| 353 | |
| 354 } // namespace extensions | |
| OLD | NEW |