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

Side by Side Diff: util/net/http_multipart_builder.cc

Issue 681303003: Add HTTPMultipartBuilder and its test. (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Assert safe MIME types Created 6 years, 1 month 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 unified diff | Download patch
« no previous file with comments | « util/net/http_multipart_builder.h ('k') | util/net/http_multipart_builder_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "util/net/http_multipart_builder.h"
16
17 #include <vector>
18
19 #include "base/logging.h"
20 #include "base/rand_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "util/net/http_body.h"
23
24 namespace crashpad {
25
26 namespace {
27
28 const char kCRLF[] = "\r\n";
29
30 const char kBoundaryCRLF[] = "\r\n\r\n";
31
32 // Generates a random string suitable for use as a multipart boundary.
33 std::string GenerateBoundaryString() {
34 // RFC 2046 §5.1.1 says that the boundary string may be 1 to 70 characters
35 // long, choosing from the set of alphanumeric characters along with
36 // characters from the set “'()+_,-./:=? ”, and not ending in a space.
37 // However, some servers have been observed as dealing poorly with certain
38 // nonalphanumeric characters. See
39 // blink/Source/platform/network/FormDataBuilder.cpp
40 // blink::FormDataBuilder::generateUniqueBoundaryString().
41 //
42 // This implementation produces a 56-character string with over 190 bits of
43 // randomness (62^32 > 2^190).
44 std::string boundary_string = "---MultipartBoundary-";
45 for (int index = 0; index < 32; ++index) {
46 const char kCharacters[] =
47 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
48 int random_value = base::RandGenerator(strlen(kCharacters));
49 boundary_string += kCharacters[random_value];
50 }
51 boundary_string += "---";
52 return boundary_string;
53 }
54
55 // Escapes the specified name to be suitable for the name field of a
56 // form-data part.
57 std::string EncodeMIMEField(const std::string& name) {
58 // RFC 2388 §3 says to encode non-ASCII field names according to RFC 2047, but
59 // no browsers implement that behavior. Instead, they send field names in the
60 // page hosting the form’s encoding. However, some form of escaping is needed.
61 // This URL-escapes the quote character and newline characters, per Blink. See
62 // blink/Source/platform/network/FormDataBuilder.cpp
63 // blink::appendQuotedString().
64 //
65 // TODO(mark): This encoding is not necessarily correct, and the same code in
66 // Blink is marked with a FIXME. Blink does not escape the '%' character,
67 // that’s a local addition, but it seems appropriate to be able to decode the
68 // string properly.
69 std::string encoded;
70 for (char character : name) {
71 switch (character) {
72 case '\r':
73 case '\n':
74 case '"':
75 case '%':
76 encoded += base::StringPrintf("%%%02x", character);
77 break;
78 default:
79 encoded += character;
80 break;
81 }
82 }
83
84 return encoded;
85 }
86
87 // Returns a string, formatted with a multipart boundary and a field name,
88 // after which the contents of the part at |name| can be appended.
89 std::string GetFormDataBoundary(const std::string& boundary,
90 const std::string& name) {
91 return base::StringPrintf(
92 "--%s%sContent-Disposition: form-data; name=\"%s\"",
93 boundary.c_str(),
94 kCRLF,
95 EncodeMIMEField(name).c_str());
96 }
97
98 void AssertSafeMIMEType(const std::string& string) {
99 for (size_t i = 0; i < string.length(); ++i) {
100 char c = string[i];
101 CHECK((c >= 'a' && c <= 'z') ||
102 (c >= 'A' && c <= 'Z') ||
103 (c >= '0' && c <= '9') ||
104 c == '/' ||
105 c == '.' ||
106 c == '_' ||
107 c == '-');
Mark Mentovai 2014/10/29 23:13:59 You need '+' also. application/xml+xhtml, for exam
108 }
109 }
110
111 } // namespace
112
113 HTTPMultipartBuilder::HTTPMultipartBuilder()
114 : boundary_(GenerateBoundaryString()), form_data_(), file_attachments_() {
115 }
116
117 HTTPMultipartBuilder::~HTTPMultipartBuilder() {
118 }
119
120 void HTTPMultipartBuilder::SetFormData(const std::string& key,
121 const std::string& value) {
122 EraseKey(key);
123 form_data_[key] = value;
124 }
125
126 void HTTPMultipartBuilder::SetFileAttachment(
127 const std::string& key,
128 const std::string& upload_file_name,
129 const base::FilePath& path,
130 const std::string& content_type) {
131 EraseKey(upload_file_name);
132
133 FileAttachment attachment;
134 attachment.filename = EncodeMIMEField(upload_file_name);
135 attachment.path = path;
136
137 if (content_type.empty()) {
138 attachment.content_type = "application/octet-stream";
139 } else {
140 AssertSafeMIMEType(content_type);
141 attachment.content_type = content_type;
142 }
143
144 file_attachments_[key] = attachment;
145 }
146
147 scoped_ptr<HTTPBodyStream> HTTPMultipartBuilder::GetBodyStream() {
148 // The objects inserted into this vector will be owned by the returned
149 // CompositeHTTPBodyStream. Take care to not early-return without deleting
150 // this memory.
151 std::vector<HTTPBodyStream*> streams;
152
153 for (const auto& pair : form_data_) {
154 std::string field = GetFormDataBoundary(boundary(), pair.first);
155 field += kBoundaryCRLF;
156 field += pair.second;
157 field += kCRLF;
158 streams.push_back(new StringHTTPBodyStream(field));
159 }
160
161 for (const auto& pair : file_attachments_) {
162 const FileAttachment& attachment = pair.second;
163 std::string header = GetFormDataBoundary(boundary(), pair.first);
164 header += base::StringPrintf("; filename=\"%s\"%s",
165 attachment.filename.c_str(), kCRLF);
166 header += base::StringPrintf("Content-Type: %s%s",
167 attachment.content_type.c_str(), kBoundaryCRLF);
168
169 streams.push_back(new StringHTTPBodyStream(header));
170 streams.push_back(new FileHTTPBodyStream(attachment.path));
171 streams.push_back(new StringHTTPBodyStream(kCRLF));
172 }
173
174 streams.push_back(
175 new StringHTTPBodyStream("--" + boundary() + "--" + kCRLF));
176
177 return scoped_ptr<HTTPBodyStream>(new CompositeHTTPBodyStream(streams));
178 }
179
180 void HTTPMultipartBuilder::EraseKey(const std::string& key) {
181 auto data_it = form_data_.find(key);
182 if (data_it != form_data_.end())
183 form_data_.erase(data_it);
184
185 auto file_it = file_attachments_.find(key);
186 if (file_it != file_attachments_.end())
187 file_attachments_.erase(file_it);
188 }
189
190 } // namespace crashpad
OLDNEW
« no previous file with comments | « util/net/http_multipart_builder.h ('k') | util/net/http_multipart_builder_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698