Add HTTPMultipartBuilder and its test.
BUG=https://crbug.com/415544 R=mark@chromium.org Review URL: https://codereview.chromium.org/681303003
This commit is contained in:
Родитель
4ee00568b7
Коммит
9db5d6f773
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "util/net/http_body_test_util.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
|
@ -92,23 +93,6 @@ TEST(StringHTTPBodyStream, MultipleReads) {
|
|||
}
|
||||
}
|
||||
|
||||
std::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size) {
|
||||
scoped_ptr<uint8_t[]> buf(new uint8_t[buffer_size]);
|
||||
std::string result;
|
||||
|
||||
ssize_t bytes_read;
|
||||
while ((bytes_read = stream->GetBytesBuffer(buf.get(), buffer_size)) != 0) {
|
||||
if (bytes_read < 0) {
|
||||
ADD_FAILURE() << "Failed to read from stream: " << bytes_read;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
result.append(reinterpret_cast<char*>(buf.get()), bytes_read);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST(FileHTTPBodyStream, ReadASCIIFile) {
|
||||
// TODO(rsesek): Use a more robust mechanism to locate testdata
|
||||
// <https://code.google.com/p/crashpad/issues/detail?id=4>.
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "util/net/http_body_test_util.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "util/net/http_body.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
|
||||
std::string ReadStreamToString(HTTPBodyStream* stream) {
|
||||
return ReadStreamToString(stream, 32);
|
||||
}
|
||||
|
||||
std::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size) {
|
||||
scoped_ptr<uint8_t[]> buf(new uint8_t[buffer_size]);
|
||||
std::string result;
|
||||
|
||||
ssize_t bytes_read;
|
||||
while ((bytes_read = stream->GetBytesBuffer(buf.get(), buffer_size)) != 0) {
|
||||
if (bytes_read < 0) {
|
||||
ADD_FAILURE() << "Failed to read from stream: " << bytes_read;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
result.append(reinterpret_cast<char*>(buf.get()), bytes_read);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_
|
||||
#define CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
class HTTPBodyStream;
|
||||
|
||||
namespace test {
|
||||
|
||||
//! \brief Reads a HTTPBodyStream to a string. If an error occurs, adds a
|
||||
//! test failure and returns an empty string.
|
||||
//!
|
||||
//! \param[in] stream The stream from which to read.
|
||||
//!
|
||||
//! \return The contents of the stream, or an empty string on failure.
|
||||
std::string ReadStreamToString(HTTPBodyStream* stream);
|
||||
|
||||
//! \brief Reads a HTTPBodyStream to a string. If an error occurs, adds a
|
||||
//! test failure and returns an empty string.
|
||||
//!
|
||||
//! \param[in] stream The stream from which to read.
|
||||
//! \param[in] buffer_size The size of the buffer to use when reading from the
|
||||
//! stream.
|
||||
//!
|
||||
//! \return The contents of the stream, or an empty string on failure.
|
||||
std::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size);
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_
|
|
@ -0,0 +1,190 @@
|
|||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "util/net/http_multipart_builder.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "util/net/http_body.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kCRLF[] = "\r\n";
|
||||
|
||||
const char kBoundaryCRLF[] = "\r\n\r\n";
|
||||
|
||||
// Generates a random string suitable for use as a multipart boundary.
|
||||
std::string GenerateBoundaryString() {
|
||||
// RFC 2046 §5.1.1 says that the boundary string may be 1 to 70 characters
|
||||
// long, choosing from the set of alphanumeric characters along with
|
||||
// characters from the set “'()+_,-./:=? ”, and not ending in a space.
|
||||
// However, some servers have been observed as dealing poorly with certain
|
||||
// nonalphanumeric characters. See
|
||||
// blink/Source/platform/network/FormDataBuilder.cpp
|
||||
// blink::FormDataBuilder::generateUniqueBoundaryString().
|
||||
//
|
||||
// This implementation produces a 56-character string with over 190 bits of
|
||||
// randomness (62^32 > 2^190).
|
||||
std::string boundary_string = "---MultipartBoundary-";
|
||||
for (int index = 0; index < 32; ++index) {
|
||||
const char kCharacters[] =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
int random_value = base::RandGenerator(strlen(kCharacters));
|
||||
boundary_string += kCharacters[random_value];
|
||||
}
|
||||
boundary_string += "---";
|
||||
return boundary_string;
|
||||
}
|
||||
|
||||
// Escapes the specified name to be suitable for the name field of a
|
||||
// form-data part.
|
||||
std::string EncodeMIMEField(const std::string& name) {
|
||||
// RFC 2388 §3 says to encode non-ASCII field names according to RFC 2047, but
|
||||
// no browsers implement that behavior. Instead, they send field names in the
|
||||
// page hosting the form’s encoding. However, some form of escaping is needed.
|
||||
// This URL-escapes the quote character and newline characters, per Blink. See
|
||||
// blink/Source/platform/network/FormDataBuilder.cpp
|
||||
// blink::appendQuotedString().
|
||||
//
|
||||
// TODO(mark): This encoding is not necessarily correct, and the same code in
|
||||
// Blink is marked with a FIXME. Blink does not escape the '%' character,
|
||||
// that’s a local addition, but it seems appropriate to be able to decode the
|
||||
// string properly.
|
||||
std::string encoded;
|
||||
for (char character : name) {
|
||||
switch (character) {
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '"':
|
||||
case '%':
|
||||
encoded += base::StringPrintf("%%%02x", character);
|
||||
break;
|
||||
default:
|
||||
encoded += character;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
// Returns a string, formatted with a multipart boundary and a field name,
|
||||
// after which the contents of the part at |name| can be appended.
|
||||
std::string GetFormDataBoundary(const std::string& boundary,
|
||||
const std::string& name) {
|
||||
return base::StringPrintf(
|
||||
"--%s%sContent-Disposition: form-data; name=\"%s\"",
|
||||
boundary.c_str(),
|
||||
kCRLF,
|
||||
EncodeMIMEField(name).c_str());
|
||||
}
|
||||
|
||||
void AssertSafeMIMEType(const std::string& string) {
|
||||
for (size_t i = 0; i < string.length(); ++i) {
|
||||
char c = string[i];
|
||||
CHECK((c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '/' ||
|
||||
c == '.' ||
|
||||
c == '_' ||
|
||||
c == '-');
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HTTPMultipartBuilder::HTTPMultipartBuilder()
|
||||
: boundary_(GenerateBoundaryString()), form_data_(), file_attachments_() {
|
||||
}
|
||||
|
||||
HTTPMultipartBuilder::~HTTPMultipartBuilder() {
|
||||
}
|
||||
|
||||
void HTTPMultipartBuilder::SetFormData(const std::string& key,
|
||||
const std::string& value) {
|
||||
EraseKey(key);
|
||||
form_data_[key] = value;
|
||||
}
|
||||
|
||||
void HTTPMultipartBuilder::SetFileAttachment(
|
||||
const std::string& key,
|
||||
const std::string& upload_file_name,
|
||||
const base::FilePath& path,
|
||||
const std::string& content_type) {
|
||||
EraseKey(upload_file_name);
|
||||
|
||||
FileAttachment attachment;
|
||||
attachment.filename = EncodeMIMEField(upload_file_name);
|
||||
attachment.path = path;
|
||||
|
||||
if (content_type.empty()) {
|
||||
attachment.content_type = "application/octet-stream";
|
||||
} else {
|
||||
AssertSafeMIMEType(content_type);
|
||||
attachment.content_type = content_type;
|
||||
}
|
||||
|
||||
file_attachments_[key] = attachment;
|
||||
}
|
||||
|
||||
scoped_ptr<HTTPBodyStream> HTTPMultipartBuilder::GetBodyStream() {
|
||||
// The objects inserted into this vector will be owned by the returned
|
||||
// CompositeHTTPBodyStream. Take care to not early-return without deleting
|
||||
// this memory.
|
||||
std::vector<HTTPBodyStream*> streams;
|
||||
|
||||
for (const auto& pair : form_data_) {
|
||||
std::string field = GetFormDataBoundary(boundary(), pair.first);
|
||||
field += kBoundaryCRLF;
|
||||
field += pair.second;
|
||||
field += kCRLF;
|
||||
streams.push_back(new StringHTTPBodyStream(field));
|
||||
}
|
||||
|
||||
for (const auto& pair : file_attachments_) {
|
||||
const FileAttachment& attachment = pair.second;
|
||||
std::string header = GetFormDataBoundary(boundary(), pair.first);
|
||||
header += base::StringPrintf("; filename=\"%s\"%s",
|
||||
attachment.filename.c_str(), kCRLF);
|
||||
header += base::StringPrintf("Content-Type: %s%s",
|
||||
attachment.content_type.c_str(), kBoundaryCRLF);
|
||||
|
||||
streams.push_back(new StringHTTPBodyStream(header));
|
||||
streams.push_back(new FileHTTPBodyStream(attachment.path));
|
||||
streams.push_back(new StringHTTPBodyStream(kCRLF));
|
||||
}
|
||||
|
||||
streams.push_back(
|
||||
new StringHTTPBodyStream("--" + boundary() + "--" + kCRLF));
|
||||
|
||||
return scoped_ptr<HTTPBodyStream>(new CompositeHTTPBodyStream(streams));
|
||||
}
|
||||
|
||||
void HTTPMultipartBuilder::EraseKey(const std::string& key) {
|
||||
auto data_it = form_data_.find(key);
|
||||
if (data_it != form_data_.end())
|
||||
form_data_.erase(data_it);
|
||||
|
||||
auto file_it = file_attachments_.find(key);
|
||||
if (file_it != file_attachments_.end())
|
||||
file_attachments_.erase(file_it);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_
|
||||
#define CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
class HTTPBodyStream;
|
||||
|
||||
//! \brief This class is used to build a MIME multipart message, conforming to
|
||||
//! RFC 2046, for use as a HTTP request body.
|
||||
class HTTPMultipartBuilder {
|
||||
public:
|
||||
HTTPMultipartBuilder();
|
||||
~HTTPMultipartBuilder();
|
||||
|
||||
//! \brief Sets a `Content-Disposition: form-data` key-value pair.
|
||||
//!
|
||||
//! \param[in] key The key of the form data, specified as the `name` in the
|
||||
//! multipart message. Any data previously set on this class with this
|
||||
//! key will be overwritten.
|
||||
//! \param[in] value The value to set at the \a key.
|
||||
void SetFormData(const std::string& key, const std::string& value);
|
||||
|
||||
//! \brief Specifies the file at \a path to have its contents uploaded as
|
||||
//! multipart data, available at `name` of \a upload_file_name.
|
||||
//!
|
||||
//! \param[in] key The key of the form data, specified as the `name` in the
|
||||
//! multipart message. Any data previously set on this class with this
|
||||
//! key will be overwritten.
|
||||
//! \param[in] upload_file_name The `filename` to specify for this multipart
|
||||
//! data attachment.
|
||||
//! \param[in] path The path of the file whose contents will be uploaded.
|
||||
//! \param[in] content_type The `Content-Type` to specify for the attachment.
|
||||
//! If this is empty, `"application/octet-stream"` will be used.
|
||||
void SetFileAttachment(const std::string& key,
|
||||
const std::string& upload_file_name,
|
||||
const base::FilePath& path,
|
||||
const std::string& content_type);
|
||||
|
||||
//! \brief Generates the HTTPBodyStream for the data currently supplied to
|
||||
//! the builder.
|
||||
//!
|
||||
//! \return A caller-owned HTTPBodyStream object.
|
||||
scoped_ptr<HTTPBodyStream> GetBodyStream();
|
||||
|
||||
//! \brief Gets the boundary that will be used in GetBodyStream().
|
||||
std::string boundary() const { return boundary_; }
|
||||
|
||||
private:
|
||||
struct FileAttachment {
|
||||
std::string filename;
|
||||
std::string content_type;
|
||||
base::FilePath path;
|
||||
};
|
||||
|
||||
// Removes elements from both data maps at the specified |key|, to ensure
|
||||
// uniqueness across the entire HTTP body.
|
||||
void EraseKey(const std::string& key);
|
||||
|
||||
std::string boundary_;
|
||||
std::map<std::string, std::string> form_data_;
|
||||
std::map<std::string, FileAttachment> file_attachments_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(HTTPMultipartBuilder);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_
|
|
@ -0,0 +1,264 @@
|
|||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "util/net/http_multipart_builder.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "util/net/http_body.h"
|
||||
#include "util/net/http_body_test_util.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
std::vector<std::string> SplitCRLF(const std::string& string) {
|
||||
std::vector<std::string> lines;
|
||||
size_t last_line = 0;
|
||||
for (size_t i = 0; i < string.length(); ++i) {
|
||||
if (string[i] == '\r' && i+1 < string.length() && string[i+1] == '\n') {
|
||||
lines.push_back(string.substr(last_line, i - last_line));
|
||||
last_line = i + 2;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
// Append any remainder.
|
||||
if (last_line < string.length()) {
|
||||
lines.push_back(string.substr(last_line));
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
// In the tests below, the form data pairs don’t appear in the order they were
|
||||
// added. The current implementation uses a std::map which sorts keys, so the
|
||||
// entires appear in alphabetical order. However, this is an implementation
|
||||
// detail, and it’s OK if the writer stops sorting in this order. Testing for
|
||||
// a specific order is just the easiest way to write this test while the writer
|
||||
// will output things in a known order.
|
||||
|
||||
TEST(HTTPMultipartBuilder, ThreeStringFields) {
|
||||
HTTPMultipartBuilder builder;
|
||||
|
||||
const char kKey1[] = "key1";
|
||||
const char kValue1[] = "test";
|
||||
builder.SetFormData(kKey1, kValue1);
|
||||
|
||||
const char kKey2[] = "key2";
|
||||
const char kValue2[] = "This is another test.";
|
||||
builder.SetFormData(kKey2, kValue2);
|
||||
|
||||
const char kKey3[] = "key-three";
|
||||
const char kValue3[] = "More tests";
|
||||
builder.SetFormData(kKey3, kValue3);
|
||||
|
||||
scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
|
||||
ASSERT_TRUE(body.get());
|
||||
std::string contents = ReadStreamToString(body.get());
|
||||
auto lines = SplitCRLF(contents);
|
||||
auto lines_it = lines.begin();
|
||||
|
||||
// The first line is the boundary. All subsequent boundaries must match this.
|
||||
const std::string& boundary = *lines_it++;
|
||||
EXPECT_GE(boundary.length(), 1u);
|
||||
EXPECT_LE(boundary.length(), 70u);
|
||||
|
||||
EXPECT_EQ("Content-Disposition: form-data; name=\"key-three\"", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ(kValue3, *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary, *lines_it++);
|
||||
EXPECT_EQ("Content-Disposition: form-data; name=\"key1\"", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ(kValue1, *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary, *lines_it++);
|
||||
EXPECT_EQ("Content-Disposition: form-data; name=\"key2\"", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ(kValue2, *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary + "--", *lines_it++);
|
||||
|
||||
EXPECT_EQ(lines.end(), lines_it);
|
||||
}
|
||||
|
||||
TEST(HTTPMultipartBuilder, ThreeFileAttachments) {
|
||||
HTTPMultipartBuilder builder;
|
||||
// TODO(rsesek): Use a more robust mechanism to locate testdata
|
||||
// <https://code.google.com/p/crashpad/issues/detail?id=4>.
|
||||
builder.SetFileAttachment("first", "minidump.dmp",
|
||||
base::FilePath("util/net/testdata/ascii_http_body.txt"), "");
|
||||
builder.SetFileAttachment("second", "minidump.dmp",
|
||||
base::FilePath("util/net/testdata/ascii_http_body.txt"), "text/plain");
|
||||
builder.SetFileAttachment("\"third 50% silly\"", "test%foo.txt",
|
||||
base::FilePath("util/net/testdata/ascii_http_body.txt"), "text/plain");
|
||||
|
||||
const char kFileContents[] = "This is a test.\n";
|
||||
|
||||
scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
|
||||
ASSERT_TRUE(body.get());
|
||||
std::string contents = ReadStreamToString(body.get());
|
||||
auto lines = SplitCRLF(contents);
|
||||
auto lines_it = lines.begin();
|
||||
|
||||
const std::string& boundary = *lines_it++;
|
||||
EXPECT_GE(boundary.length(), 1u);
|
||||
EXPECT_LE(boundary.length(), 70u);
|
||||
|
||||
EXPECT_EQ("Content-Disposition: form-data; "
|
||||
"name=\"%22third 50%25 silly%22\"; filename=\"test%25foo.txt\"",
|
||||
*lines_it++);
|
||||
EXPECT_EQ("Content-Type: text/plain", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ(kFileContents, *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary, *lines_it++);
|
||||
EXPECT_EQ("Content-Disposition: form-data; "
|
||||
"name=\"first\"; filename=\"minidump.dmp\"",
|
||||
*lines_it++);
|
||||
EXPECT_EQ("Content-Type: application/octet-stream", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ(kFileContents, *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary, *lines_it++);
|
||||
EXPECT_EQ("Content-Disposition: form-data; "
|
||||
"name=\"second\"; filename=\"minidump.dmp\"",
|
||||
*lines_it++);
|
||||
EXPECT_EQ("Content-Type: text/plain", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ(kFileContents, *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary + "--", *lines_it++);
|
||||
|
||||
EXPECT_EQ(lines.end(), lines_it);
|
||||
}
|
||||
|
||||
TEST(HTTPMultipartBuilder, OverwriteFormDataWithEscapedKey) {
|
||||
HTTPMultipartBuilder builder;
|
||||
const char kKey[] = "a 100% \"silly\"\r\ntest";
|
||||
builder.SetFormData(kKey, "some dummy value");
|
||||
builder.SetFormData(kKey, "overwrite");
|
||||
scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
|
||||
ASSERT_TRUE(body.get());
|
||||
std::string contents = ReadStreamToString(body.get());
|
||||
auto lines = SplitCRLF(contents);
|
||||
auto lines_it = lines.begin();
|
||||
|
||||
const std::string& boundary = *lines_it++;
|
||||
EXPECT_GE(boundary.length(), 1u);
|
||||
EXPECT_LE(boundary.length(), 70u);
|
||||
|
||||
EXPECT_EQ(
|
||||
"Content-Disposition: form-data; name=\"a 100%25 %22silly%22%0d%0atest\"",
|
||||
*lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ("overwrite", *lines_it++);
|
||||
EXPECT_EQ(boundary + "--", *lines_it++);
|
||||
EXPECT_EQ(lines.end(), lines_it);
|
||||
}
|
||||
|
||||
TEST(HTTPMultipartBuilder, OverwriteFileAttachment) {
|
||||
HTTPMultipartBuilder builder;
|
||||
const char kValue[] = "1 2 3 test";
|
||||
builder.SetFormData("a key", kValue);
|
||||
// TODO(rsesek): Use a more robust mechanism to locate testdata
|
||||
// <https://code.google.com/p/crashpad/issues/detail?id=4>.
|
||||
builder.SetFileAttachment("minidump", "minidump.dmp",
|
||||
base::FilePath("util/net/testdata/binary_http_body.dat"), "");
|
||||
builder.SetFileAttachment("minidump2", "minidump.dmp",
|
||||
base::FilePath("util/net/testdata/binary_http_body.dat"), "");
|
||||
builder.SetFileAttachment("minidump", "minidump.dmp",
|
||||
base::FilePath("util/net/testdata/ascii_http_body.txt"), "text/plain");
|
||||
scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
|
||||
ASSERT_TRUE(body.get());
|
||||
std::string contents = ReadStreamToString(body.get());
|
||||
auto lines = SplitCRLF(contents);
|
||||
auto lines_it = lines.begin();
|
||||
|
||||
const std::string& boundary = *lines_it++;
|
||||
EXPECT_GE(boundary.length(), 1u);
|
||||
EXPECT_LE(boundary.length(), 70u);
|
||||
|
||||
EXPECT_EQ("Content-Disposition: form-data; name=\"a key\"", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ(kValue, *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary, *lines_it++);
|
||||
EXPECT_EQ("Content-Disposition: form-data; "
|
||||
"name=\"minidump\"; filename=\"minidump.dmp\"",
|
||||
*lines_it++);
|
||||
EXPECT_EQ("Content-Type: text/plain", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ("This is a test.\n", *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary, *lines_it++);
|
||||
EXPECT_EQ("Content-Disposition: form-data; "
|
||||
"name=\"minidump2\"; filename=\"minidump.dmp\"",
|
||||
*lines_it++);
|
||||
EXPECT_EQ("Content-Type: application/octet-stream", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ("\xFE\xED\xFA\xCE\xA1\x1A\x15", *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary + "--", *lines_it++);
|
||||
|
||||
EXPECT_EQ(lines.end(), lines_it);
|
||||
}
|
||||
|
||||
TEST(HTTPMultipartBuilder, SharedFormDataAndAttachmentKeyNamespace) {
|
||||
HTTPMultipartBuilder builder;
|
||||
const char kValue1[] = "11111";
|
||||
builder.SetFormData("one", kValue1);
|
||||
builder.SetFileAttachment("minidump", "minidump.dmp",
|
||||
base::FilePath("util/net/testdata/ascii_http_body.txt"), "");
|
||||
const char kValue2[] = "this is not a file";
|
||||
builder.SetFormData("minidump", kValue2);
|
||||
|
||||
scoped_ptr<HTTPBodyStream> body(builder.GetBodyStream());
|
||||
ASSERT_TRUE(body.get());
|
||||
std::string contents = ReadStreamToString(body.get());
|
||||
auto lines = SplitCRLF(contents);
|
||||
auto lines_it = lines.begin();
|
||||
|
||||
const std::string& boundary = *lines_it++;
|
||||
EXPECT_GE(boundary.length(), 1u);
|
||||
EXPECT_LE(boundary.length(), 70u);
|
||||
|
||||
EXPECT_EQ("Content-Disposition: form-data; name=\"minidump\"", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ(kValue2, *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary, *lines_it++);
|
||||
EXPECT_EQ("Content-Disposition: form-data; name=\"one\"", *lines_it++);
|
||||
EXPECT_EQ("", *lines_it++);
|
||||
EXPECT_EQ(kValue1, *lines_it++);
|
||||
|
||||
EXPECT_EQ(boundary + "--", *lines_it++);
|
||||
|
||||
EXPECT_EQ(lines.end(), lines_it);
|
||||
}
|
||||
|
||||
TEST(HTTPMultipartBuilder, AssertUnsafeMIMEType) {
|
||||
HTTPMultipartBuilder builder;
|
||||
// Invalid and potentially dangerous:
|
||||
ASSERT_DEATH(builder.SetFileAttachment("", "", base::FilePath(), "\r\n"), "");
|
||||
ASSERT_DEATH(builder.SetFileAttachment("", "", base::FilePath(), "\""), "");
|
||||
ASSERT_DEATH(builder.SetFileAttachment("", "", base::FilePath(), "\x12"), "");
|
||||
ASSERT_DEATH(builder.SetFileAttachment("", "", base::FilePath(), "<>"), "");
|
||||
// Invalid but safe:
|
||||
builder.SetFileAttachment("", "", base::FilePath(), "0/totally/-invalid.pdf");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
|
@ -71,6 +71,8 @@
|
|||
'misc/uuid.h',
|
||||
'net/http_body.cc',
|
||||
'net/http_body.h',
|
||||
'net/http_multipart_builder.cc',
|
||||
'net/http_multipart_builder.h',
|
||||
'numeric/checked_range.h',
|
||||
'numeric/in_range_cast.h',
|
||||
'numeric/int128.h',
|
||||
|
@ -215,6 +217,9 @@
|
|||
'misc/scoped_forbid_return_test.cc',
|
||||
'misc/uuid_test.cc',
|
||||
'net/http_body_test.cc',
|
||||
'net/http_body_test_util.cc',
|
||||
'net/http_body_test_util.h',
|
||||
'net/http_multipart_builder_test.cc',
|
||||
'numeric/checked_range_test.cc',
|
||||
'numeric/in_range_cast_test.cc',
|
||||
'numeric/int128_test.cc',
|
||||
|
|
Загрузка…
Ссылка в новой задаче