Index: chrome/browser/extensions/api/web_request/post_data_parser.cc |
diff --git a/chrome/browser/extensions/api/web_request/post_data_parser.cc b/chrome/browser/extensions/api/web_request/post_data_parser.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f9649f910bbfc2e6600a46bead7379a43687f82a |
--- /dev/null |
+++ b/chrome/browser/extensions/api/web_request/post_data_parser.cc |
@@ -0,0 +1,269 @@ |
+// Copyright (c) 2012 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. |
+ |
+#include "chrome/browser/extensions/api/web_request/post_data_parser.h" |
+ |
+#include "net/url_request/url_request.h" |
+#include "net/base/escape.h" |
+ |
+namespace { |
+const char kContentDisposition[] = "Content-Disposition:"; |
+} |
+ |
+namespace extensions { |
+ |
+// Implementation of PostDataParser and PostDataParser::Result . |
+ |
+PostDataParser::Result::Result() {} |
+ |
+void PostDataParser::Result::Reset() { |
+ key_.erase(); |
+ val_.erase(); |
+} |
+ |
+void PostDataParser::Result::SetKey(const char* s, size_t n) { |
+ key_.replace(0, std::string::npos, s, n); |
+} |
+ |
+void PostDataParser::Result::SetVal(const char* s, size_t n) { |
+ val_.replace(0, std::string::npos, s, n); |
+} |
+ |
+// static |
+scoped_ptr<PostDataParser> PostDataParser::CreatePostDataParser( |
+ net::URLRequest* request) { |
+ std::string value; |
+ const bool found = request->extra_request_headers().GetHeader( |
+ "Content-Type", &value); |
+ std::string content_type = value.substr(0, value.find(';')); |
+ if (!found || content_type == "application/x-www-form-urlencoded") { |
+ return scoped_ptr<PostDataParser>(new PostDataParserUrlEncoded()); |
+ } else if (content_type == "multipart/form-data") { |
+ const char kBoundaryString[] = "boundary="; |
+ size_t offset = value.find(kBoundaryString); |
+ if (offset == std::string::npos) { |
+ // Malformed header. |
+ return scoped_ptr<PostDataParser>(); |
+ } |
+ offset += strlen(kBoundaryString); |
+ std::string boundary = value.substr(offset, value.find(';', offset)); |
+ return scoped_ptr<PostDataParser>(new PostDataParserMultipart(boundary)); |
+ } else if (content_type == "text/plain") { |
+ // Unable to parse, may be ambiguous. |
+ return scoped_ptr<PostDataParser>(); |
+ } else { |
+ // Another error. |
+ return scoped_ptr<PostDataParser>(); |
+ } |
+} |
+ |
+// Implementation of PostDataParserUrlEncoded. |
+ |
+PostDataParserUrlEncoded::PostDataParserUrlEncoded() : source_(NULL) {} |
+ |
+PostDataParserUrlEncoded::~PostDataParserUrlEncoded() {} |
+ |
+bool PostDataParserUrlEncoded::AllDataReadOK() { |
+ return source_ != NULL && offset_ == source_->end(); |
+} |
+ |
+bool PostDataParserUrlEncoded::GetNextPair(Result* result) { |
+ result->Reset(); |
+ if (source_ == NULL) |
+ return false; |
+ if (offset_ == source_->end()) |
+ return false; |
+ std::vector<char>::const_iterator seek = offset_; |
+ // (*) Now we have |seek| >= |offset_| until the end of this function: |
+ while (seek != source_->end() && *seek != '=') |
+ ++seek; |
+ if (seek == source_->end()) { |
+ // This means the data is malformed. |
+ offset_ = seek; |
+ return false; |
+ } |
+ std::string encoded_key(&(*offset_), seek - offset_); // Safe, see (*). |
+ const net::UnescapeRule::Type unescape_rules = |
+ net::UnescapeRule::URL_SPECIAL_CHARS | net::UnescapeRule::CONTROL_CHARS | |
+ net::UnescapeRule::SPACES | net::UnescapeRule::REPLACE_PLUS_WITH_SPACE; |
+ result->get_key() = net::UnescapeURLComponent(encoded_key, unescape_rules); |
battre
2012/07/13 15:20:04
I think this would be more natural as
result->set_
vabr (Chromium)
2012/07/16 15:40:51
Done.
|
+ offset_ = ++seek; |
+ while (seek != source_->end() && *seek != '&') |
+ ++seek; |
+ std::string encoded_val(&(*offset_), seek - offset_); // Safe, see (*). |
+ result->get_val() = net::UnescapeURLComponent(encoded_val, unescape_rules); |
battre
2012/07/13 15:20:04
same as above.
vabr (Chromium)
2012/07/16 15:40:51
Done.
|
+ offset_ = (seek == source_->end()) ? seek : seek + 1; |
+ return true; |
+} |
+ |
+bool PostDataParserUrlEncoded::SetSource(const std::vector<char>* source) { |
+ if (source_ != NULL) |
+ return false; |
+ source_ = source; |
+ offset_ = source_->begin(); |
+ return true; |
+} |
+ |
+// Implementation of PostDataParserMultipart. |
+ |
+PostDataParserMultipart::PostDataParserMultipart( |
+ const std::string& boundary_separator) |
+ : source_(NULL), |
+ length_(0), // Dummy value. |
+ line_start_(0), // Dummy value. |
+ line_end_(0), // Dummy value. |
+ next_line_(0), // Dummy value. |
+ boundary_("--" + boundary_separator), |
+ final_boundary_(boundary_+"--"), |
battre
2012/07/13 15:20:04
nit: spaces around +
vabr (Chromium)
2012/07/16 15:40:51
Done.
|
+ state_(kInit), |
+ line_type_(kEmpty) // Dummy value. |
+{} |
+ |
+PostDataParserMultipart::~PostDataParserMultipart() {} |
+ |
+bool PostDataParserMultipart::AllDataReadOK() { |
+ return source_ != NULL && next_line_ >= length_ && state_ == kFinal; |
+} |
+ |
+bool PostDataParserMultipart::GetNextPair(Result* result) { |
battre
2012/07/13 15:20:04
Can you add more explanation what this function do
vabr (Chromium)
2012/07/16 15:40:51
Done.
|
+ result->Reset(); |
+ if (state_ == kError) |
+ return false; |
+ while (state_ != kSkip) { |
battre
2012/07/13 15:20:04
Can you explain this logic, maybe in a comment. Wh
vabr (Chromium)
2012/07/16 15:40:51
I changed kSkip and other state names to describe
|
+ if (!DoStep()) |
+ return false; |
+ } |
+ bool val_extracted = false; |
+ bool name_parsed = ParseHead(result, &val_extracted); |
+ while (state_ != kBody) { |
+ if (!DoStep()) |
+ return false; |
+ } |
+ size_t val_start; |
+ size_t val_end = 0; // Dummy value, replaced below, see (*). |
+ // There may not be more to read from |source_| if the current result comes |
+ // from a "file" input element. But then |result| is complete already. |
+ if (!DoStep()) |
+ return val_extracted; |
+ val_start = line_start_; |
+ // (*) Now state_ == kBody, so val_end gets updated below. |
+ while (state_ != kFirst && state_ != kFinal) { |
+ val_end = line_end_; |
+ if (!DoStep()) break; |
+ } |
+ if (name_parsed && !val_extracted) { |
+ result->SetVal(source_ + val_start, val_end - val_start); |
+ } |
+ return name_parsed; |
+} |
+ |
+bool PostDataParserMultipart::SetSource(const std::vector<char>* source) { |
+ if (state_ == kError) |
+ return false; |
+ if (source_ != NULL && next_line_ < length_) |
+ return false; |
+ source_ = &(source->front()); |
+ length_ = source->size(); |
+ next_line_ = 0; |
+ return true; |
+} |
+ |
+bool PostDataParserMultipart::DoStep() { |
+ if (!GetNextLine()) |
+ return false; |
+ switch (state_) { |
+ case kInit: |
+ if (line_type_ == kBoundary) |
+ state_ = kFirst; |
+ else |
+ state_ = kError; |
+ break; |
+ case kFirst: |
+ if (line_type_ == kDisposition) |
+ state_ = kSkip; |
+ else |
+ state_ = kHead; |
+ break; |
+ case kHead: |
+ if (line_type_ == kDisposition) |
+ state_ = kSkip; |
+ break; |
+ case kSkip: |
+ if (line_type_ == kEmpty) |
+ state_ = kBody; |
+ break; |
+ case kBody: |
+ if (line_type_ == kBoundary) |
+ state_ = kFirst; |
+ else if (line_type_ == kEndBoundary) |
+ state_ = kFinal; |
+ break; |
+ case kFinal: |
+ if (line_type_ != kEmpty) |
+ state_ = kError; |
+ case kError: |
+ break; |
+ } |
+ return true; |
+} |
+ |
+// Contract: only to be called from GetNextLine(). |
battre
2012/07/13 15:20:04
optional: What do you think of changing this to
Li
vabr (Chromium)
2012/07/16 15:40:51
Done.
|
+void PostDataParserMultipart::GetLineType() { |
battre
2012/07/13 15:20:04
I think if you don't return anything I would renam
vabr (Chromium)
2012/07/16 15:40:51
Changed to returning Lines.
|
+ const size_t line_length = line_end_ - line_start_; |
+ const base::StringPiece line(source_ + line_start_, line_length); |
+ if (line == boundary_) |
+ line_type_ = kBoundary; |
+ else if (line == final_boundary_) |
+ line_type_ = kEndBoundary; |
+ else if (line.starts_with(kContentDisposition)) |
+ line_type_ = kDisposition; |
+ else if (line_start_ == line_end_) |
+ line_type_ = kEmpty; |
+ else |
+ line_type_ = kOther; |
+} |
+ |
+// Contract: only to be called from DoStep(). |
+bool PostDataParserMultipart::GetNextLine() { |
battre
2012/07/13 15:20:04
opt: SeekNextLine?
vabr (Chromium)
2012/07/16 15:40:51
Done.
|
+ if (source_ == NULL || state_ == kError) |
+ return false; |
+ if (next_line_ >= length_) |
+ return false; |
+ line_start_ = next_line_; |
+ size_t seek = line_start_; |
+ while (seek < length_ && *(source_ + seek) != '\r') |
+ ++seek; |
+ line_end_ = seek; |
battre
2012/07/13 15:20:04
so line_end_ points to after the end of the line,
vabr (Chromium)
2012/07/16 15:40:51
Done.
|
+ GetLineType(); |
+ if (seek < length_ && *(source_ + seek + 1) != '\n') |
battre
2012/07/13 15:20:04
source_ + seek + 1 could point beyond the end of t
vabr (Chromium)
2012/07/16 15:40:51
Corrected.
|
+ return false; |
+ next_line_ = seek + 2; |
+ return true; |
+} |
+ |
+// Contract: line_type_ == kDisposition. |
+bool PostDataParserMultipart::ParseHead(Result* result, bool* val_extracted) { |
+ DCHECK_EQ(kDisposition, line_type_); |
+ base::StringPiece line(source_ + line_start_, line_end_ - line_start_); |
+ const char kNameEquals[] = " name=\""; |
+ const char kFilenameEquals[] = " filename=\""; |
+ size_t key_offset = line.find(kNameEquals); |
+ if (key_offset == base::StringPiece::npos) |
+ return false; |
+ key_offset += strlen(kNameEquals); |
+ result->SetKey(source_ + line_start_ + key_offset, |
+ line.find('"', key_offset) - key_offset); |
+ size_t val_offset = line.find(kFilenameEquals); |
+ if (val_offset == std::string::npos) { |
+ *val_extracted = false; |
+ } else { |
+ *val_extracted = true; |
+ val_offset += strlen(kFilenameEquals); |
+ result->SetVal(source_ + line_start_ + val_offset, |
+ line.find('"', val_offset) - val_offset); |
+ } |
+ return true; |
+} |
+ |
+} // namespace extensions |