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

Unified 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: Typo fixed Created 8 years, 5 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 side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698