204 lines
7.9 KiB
C++
204 lines
7.9 KiB
C++
// Copyright 2014 The Chromium OS 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 <brillo/http/http_form_data.h>
|
|
|
|
#include <set>
|
|
#include <utility>
|
|
|
|
#include <base/files/file_util.h>
|
|
#include <base/files/scoped_temp_dir.h>
|
|
#include <brillo/mime_utils.h>
|
|
#include <brillo/streams/file_stream.h>
|
|
#include <brillo/streams/input_stream_set.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
namespace brillo {
|
|
namespace http {
|
|
namespace {
|
|
std::string GetFormFieldData(FormField* field) {
|
|
std::vector<StreamPtr> streams;
|
|
CHECK(field->ExtractDataStreams(&streams));
|
|
StreamPtr stream = InputStreamSet::Create(std::move(streams), nullptr);
|
|
|
|
std::vector<uint8_t> data(stream->GetSize());
|
|
EXPECT_TRUE(stream->ReadAllBlocking(data.data(), data.size(), nullptr));
|
|
return std::string{data.begin(), data.end()};
|
|
}
|
|
} // anonymous namespace
|
|
|
|
TEST(HttpFormData, TextFormField) {
|
|
TextFormField form_field{"field1", "abcdefg", mime::text::kPlain, "7bit"};
|
|
const char expected_header[] =
|
|
"Content-Disposition: form-data; name=\"field1\"\r\n"
|
|
"Content-Type: text/plain\r\n"
|
|
"Content-Transfer-Encoding: 7bit\r\n"
|
|
"\r\n";
|
|
EXPECT_EQ(expected_header, form_field.GetContentHeader());
|
|
EXPECT_EQ("abcdefg", GetFormFieldData(&form_field));
|
|
}
|
|
|
|
TEST(HttpFormData, FileFormField) {
|
|
base::ScopedTempDir dir;
|
|
ASSERT_TRUE(dir.CreateUniqueTempDir());
|
|
std::string file_content{"text line1\ntext line2\n"};
|
|
base::FilePath file_name = dir.GetPath().Append("sample.txt");
|
|
ASSERT_EQ(file_content.size(),
|
|
static_cast<size_t>(base::WriteFile(
|
|
file_name, file_content.data(), file_content.size())));
|
|
|
|
StreamPtr stream = FileStream::Open(file_name, Stream::AccessMode::READ,
|
|
FileStream::Disposition::OPEN_EXISTING,
|
|
nullptr);
|
|
ASSERT_NE(nullptr, stream);
|
|
FileFormField form_field{"test_file",
|
|
std::move(stream),
|
|
"sample.txt",
|
|
content_disposition::kFormData,
|
|
mime::text::kPlain,
|
|
""};
|
|
const char expected_header[] =
|
|
"Content-Disposition: form-data; name=\"test_file\";"
|
|
" filename=\"sample.txt\"\r\n"
|
|
"Content-Type: text/plain\r\n"
|
|
"\r\n";
|
|
EXPECT_EQ(expected_header, form_field.GetContentHeader());
|
|
EXPECT_EQ(file_content, GetFormFieldData(&form_field));
|
|
}
|
|
|
|
TEST(HttpFormData, MultiPartFormField) {
|
|
base::ScopedTempDir dir;
|
|
ASSERT_TRUE(dir.CreateUniqueTempDir());
|
|
std::string file1{"text line1\ntext line2\n"};
|
|
base::FilePath filename1 = dir.GetPath().Append("sample.txt");
|
|
ASSERT_EQ(file1.size(),
|
|
static_cast<size_t>(
|
|
base::WriteFile(filename1, file1.data(), file1.size())));
|
|
std::string file2{"\x01\x02\x03\x04\x05"};
|
|
base::FilePath filename2 = dir.GetPath().Append("test.bin");
|
|
ASSERT_EQ(file2.size(),
|
|
static_cast<size_t>(
|
|
base::WriteFile(filename2, file2.data(), file2.size())));
|
|
|
|
MultiPartFormField form_field{"foo", mime::multipart::kFormData, "Delimiter"};
|
|
form_field.AddTextField("name", "John Doe");
|
|
EXPECT_TRUE(form_field.AddFileField("file1",
|
|
filename1,
|
|
content_disposition::kFormData,
|
|
mime::text::kPlain,
|
|
nullptr));
|
|
EXPECT_TRUE(form_field.AddFileField("file2",
|
|
filename2,
|
|
content_disposition::kFormData,
|
|
mime::application::kOctet_stream,
|
|
nullptr));
|
|
const char expected_header[] =
|
|
"Content-Disposition: form-data; name=\"foo\"\r\n"
|
|
"Content-Type: multipart/form-data; boundary=Delimiter\r\n"
|
|
"\r\n";
|
|
EXPECT_EQ(expected_header, form_field.GetContentHeader());
|
|
const char expected_data[] =
|
|
"--Delimiter\r\n"
|
|
"Content-Disposition: form-data; name=\"name\"\r\n"
|
|
"\r\n"
|
|
"John Doe\r\n"
|
|
"--Delimiter\r\n"
|
|
"Content-Disposition: form-data; name=\"file1\";"
|
|
" filename=\"sample.txt\"\r\n"
|
|
"Content-Type: text/plain\r\n"
|
|
"Content-Transfer-Encoding: binary\r\n"
|
|
"\r\n"
|
|
"text line1\ntext line2\n\r\n"
|
|
"--Delimiter\r\n"
|
|
"Content-Disposition: form-data; name=\"file2\";"
|
|
" filename=\"test.bin\"\r\n"
|
|
"Content-Type: application/octet-stream\r\n"
|
|
"Content-Transfer-Encoding: binary\r\n"
|
|
"\r\n"
|
|
"\x01\x02\x03\x04\x05\r\n"
|
|
"--Delimiter--\r\n";
|
|
EXPECT_EQ(expected_data, GetFormFieldData(&form_field));
|
|
}
|
|
|
|
TEST(HttpFormData, MultiPartBoundary) {
|
|
const int count = 10;
|
|
std::set<std::string> boundaries;
|
|
for (int i = 0; i < count; i++) {
|
|
MultiPartFormField field{""};
|
|
std::string boundary = field.GetBoundary();
|
|
boundaries.insert(boundary);
|
|
// Our generated boundary must be 16 character long and contain lowercase
|
|
// hexadecimal digits only.
|
|
EXPECT_EQ(16u, boundary.size());
|
|
EXPECT_EQ(std::string::npos,
|
|
boundary.find_first_not_of("0123456789abcdef"));
|
|
}
|
|
// Now make sure the boundary strings were generated at random, so we should
|
|
// get |count| unique boundary strings. However since the strings are random,
|
|
// there is a very slim change of generating the same string twice, so
|
|
// expect at least 90% of unique strings. 90% is picked arbitrarily here.
|
|
int expected_min_unique = count * 9 / 10;
|
|
EXPECT_GE(boundaries.size(), expected_min_unique);
|
|
}
|
|
|
|
TEST(HttpFormData, FormData) {
|
|
base::ScopedTempDir dir;
|
|
ASSERT_TRUE(dir.CreateUniqueTempDir());
|
|
std::string file1{"text line1\ntext line2\n"};
|
|
base::FilePath filename1 = dir.GetPath().Append("sample.txt");
|
|
ASSERT_EQ(file1.size(),
|
|
static_cast<size_t>(
|
|
base::WriteFile(filename1, file1.data(), file1.size())));
|
|
std::string file2{"\x01\x02\x03\x04\x05"};
|
|
base::FilePath filename2 = dir.GetPath().Append("test.bin");
|
|
ASSERT_EQ(file2.size(),
|
|
static_cast<size_t>(
|
|
base::WriteFile(filename2, file2.data(), file2.size())));
|
|
|
|
FormData form_data{"boundary1"};
|
|
form_data.AddTextField("name", "John Doe");
|
|
std::unique_ptr<MultiPartFormField> files{
|
|
new MultiPartFormField{"files", "", "boundary 2"}};
|
|
EXPECT_TRUE(files->AddFileField(
|
|
"", filename1, content_disposition::kFile, mime::text::kPlain, nullptr));
|
|
EXPECT_TRUE(files->AddFileField("",
|
|
filename2,
|
|
content_disposition::kFile,
|
|
mime::application::kOctet_stream,
|
|
nullptr));
|
|
form_data.AddCustomField(std::move(files));
|
|
EXPECT_EQ("multipart/form-data; boundary=boundary1",
|
|
form_data.GetContentType());
|
|
|
|
StreamPtr stream = form_data.ExtractDataStream();
|
|
std::vector<uint8_t> data(stream->GetSize());
|
|
EXPECT_TRUE(stream->ReadAllBlocking(data.data(), data.size(), nullptr));
|
|
const char expected_data[] =
|
|
"--boundary1\r\n"
|
|
"Content-Disposition: form-data; name=\"name\"\r\n"
|
|
"\r\n"
|
|
"John Doe\r\n"
|
|
"--boundary1\r\n"
|
|
"Content-Disposition: form-data; name=\"files\"\r\n"
|
|
"Content-Type: multipart/mixed; boundary=\"boundary 2\"\r\n"
|
|
"\r\n"
|
|
"--boundary 2\r\n"
|
|
"Content-Disposition: file; filename=\"sample.txt\"\r\n"
|
|
"Content-Type: text/plain\r\n"
|
|
"Content-Transfer-Encoding: binary\r\n"
|
|
"\r\n"
|
|
"text line1\ntext line2\n\r\n"
|
|
"--boundary 2\r\n"
|
|
"Content-Disposition: file; filename=\"test.bin\"\r\n"
|
|
"Content-Type: application/octet-stream\r\n"
|
|
"Content-Transfer-Encoding: binary\r\n"
|
|
"\r\n"
|
|
"\x01\x02\x03\x04\x05\r\n"
|
|
"--boundary 2--\r\n\r\n"
|
|
"--boundary1--\r\n";
|
|
EXPECT_EQ(expected_data, (std::string{data.begin(), data.end()}));
|
|
}
|
|
} // namespace http
|
|
} // namespace brillo
|