@@ -369,6 +369,14 @@ using ContentProviderWithoutLength =
369
369
370
370
using ContentProviderResourceReleaser = std::function<void (bool success)>;
371
371
372
+ struct MultipartFormDataProvider {
373
+ std::string name;
374
+ ContentProviderWithoutLength provider;
375
+ std::string filename;
376
+ std::string content_type;
377
+ };
378
+ using MultipartFormDataProviderItems = std::vector<MultipartFormDataProvider>;
379
+
372
380
using ContentReceiverWithProgress =
373
381
std::function<bool (const char *data, size_t data_length, uint64_t offset,
374
382
uint64_t total_length)>;
@@ -934,6 +942,9 @@ class ClientImpl {
934
942
const MultipartFormDataItems &items);
935
943
Result Post (const std::string &path, const Headers &headers,
936
944
const MultipartFormDataItems &items, const std::string &boundary);
945
+ Result Post (const std::string &path, const Headers &headers,
946
+ const MultipartFormDataItems &items,
947
+ const MultipartFormDataProviderItems &provider_items);
937
948
938
949
Result Put (const std::string &path);
939
950
Result Put (const std::string &path, const char *body, size_t content_length,
@@ -963,6 +974,9 @@ class ClientImpl {
963
974
const MultipartFormDataItems &items);
964
975
Result Put (const std::string &path, const Headers &headers,
965
976
const MultipartFormDataItems &items, const std::string &boundary);
977
+ Result Put (const std::string &path, const Headers &headers,
978
+ const MultipartFormDataItems &items,
979
+ const MultipartFormDataProviderItems &provider_items);
966
980
967
981
Result Patch (const std::string &path);
968
982
Result Patch (const std::string &path, const char *body, size_t content_length,
@@ -1201,6 +1215,9 @@ class ClientImpl {
1201
1215
ContentProvider content_provider,
1202
1216
ContentProviderWithoutLength content_provider_without_length,
1203
1217
const std::string &content_type);
1218
+ ContentProviderWithoutLength get_multipart_content_provider (
1219
+ const std::string &boundary, const MultipartFormDataItems &items,
1220
+ const MultipartFormDataProviderItems &provider_items);
1204
1221
1205
1222
std::string adjust_host_string (const std::string &host) const ;
1206
1223
@@ -1296,6 +1313,10 @@ class Client {
1296
1313
const MultipartFormDataItems &items);
1297
1314
Result Post (const std::string &path, const Headers &headers,
1298
1315
const MultipartFormDataItems &items, const std::string &boundary);
1316
+ Result Post (const std::string &path, const Headers &headers,
1317
+ const MultipartFormDataItems &items,
1318
+ const MultipartFormDataProviderItems &provider_items);
1319
+
1299
1320
Result Put (const std::string &path);
1300
1321
Result Put (const std::string &path, const char *body, size_t content_length,
1301
1322
const std::string &content_type);
@@ -1324,6 +1345,10 @@ class Client {
1324
1345
const MultipartFormDataItems &items);
1325
1346
Result Put (const std::string &path, const Headers &headers,
1326
1347
const MultipartFormDataItems &items, const std::string &boundary);
1348
+ Result Put (const std::string &path, const Headers &headers,
1349
+ const MultipartFormDataItems &items,
1350
+ const MultipartFormDataProviderItems &provider_items);
1351
+
1327
1352
Result Patch (const std::string &path);
1328
1353
Result Patch (const std::string &path, const char *body, size_t content_length,
1329
1354
const std::string &content_type);
@@ -2854,8 +2879,7 @@ inline socket_t create_client_socket(
2854
2879
}
2855
2880
2856
2881
inline bool get_ip_and_port (const struct sockaddr_storage &addr,
2857
- socklen_t addr_len, std::string &ip,
2858
- int &port) {
2882
+ socklen_t addr_len, std::string &ip, int &port) {
2859
2883
if (addr.ss_family == AF_INET) {
2860
2884
port = ntohs (reinterpret_cast <const struct sockaddr_in *>(&addr)->sin_port );
2861
2885
} else if (addr.ss_family == AF_INET6) {
@@ -4129,29 +4153,48 @@ inline bool is_multipart_boundary_chars_valid(const std::string &boundary) {
4129
4153
return valid;
4130
4154
}
4131
4155
4156
+ template <typename T>
4157
+ inline std::string
4158
+ serialize_multipart_formdata_item_begin (const T &item,
4159
+ const std::string &boundary) {
4160
+ std::string body = " --" + boundary + " \r\n " ;
4161
+ body += " Content-Disposition: form-data; name=\" " + item.name + " \" " ;
4162
+ if (!item.filename .empty ()) {
4163
+ body += " ; filename=\" " + item.filename + " \" " ;
4164
+ }
4165
+ body += " \r\n " ;
4166
+ if (!item.content_type .empty ()) {
4167
+ body += " Content-Type: " + item.content_type + " \r\n " ;
4168
+ }
4169
+ body += " \r\n " ;
4170
+
4171
+ return body;
4172
+ }
4173
+
4174
+ inline std::string serialize_multipart_formdata_item_end () { return " \r\n " ; }
4175
+
4176
+ inline std::string
4177
+ serialize_multipart_formdata_finish (const std::string &boundary) {
4178
+ return " --" + boundary + " --\r\n " ;
4179
+ }
4180
+
4181
+ inline std::string
4182
+ serialize_multipart_formdata_get_content_type (const std::string &boundary) {
4183
+ return " multipart/form-data; boundary=" + boundary;
4184
+ }
4185
+
4132
4186
inline std::string
4133
4187
serialize_multipart_formdata (const MultipartFormDataItems &items,
4134
- const std::string &boundary,
4135
- std::string &content_type) {
4188
+ const std::string &boundary, bool finish = true ) {
4136
4189
std::string body;
4137
4190
4138
4191
for (const auto &item : items) {
4139
- body += " --" + boundary + " \r\n " ;
4140
- body += " Content-Disposition: form-data; name=\" " + item.name + " \" " ;
4141
- if (!item.filename .empty ()) {
4142
- body += " ; filename=\" " + item.filename + " \" " ;
4143
- }
4144
- body += " \r\n " ;
4145
- if (!item.content_type .empty ()) {
4146
- body += " Content-Type: " + item.content_type + " \r\n " ;
4147
- }
4148
- body += " \r\n " ;
4149
- body += item.content + " \r\n " ;
4192
+ body += serialize_multipart_formdata_item_begin (item, boundary);
4193
+ body += item.content + serialize_multipart_formdata_item_end ();
4150
4194
}
4151
4195
4152
- body += " -- " + boundary + " -- \r\n " ;
4196
+ if (finish) body += serialize_multipart_formdata_finish ( boundary) ;
4153
4197
4154
- content_type = " multipart/form-data; boundary=" + boundary;
4155
4198
return body;
4156
4199
}
4157
4200
@@ -4536,8 +4579,8 @@ inline void hosted_at(const std::string &hostname,
4536
4579
*reinterpret_cast <struct sockaddr_storage *>(rp->ai_addr );
4537
4580
std::string ip;
4538
4581
int dummy = -1 ;
4539
- if (detail::get_ip_and_port (addr, sizeof (struct sockaddr_storage ),
4540
- ip, dummy)) {
4582
+ if (detail::get_ip_and_port (addr, sizeof (struct sockaddr_storage ), ip,
4583
+ dummy)) {
4541
4584
addrs.push_back (ip);
4542
4585
}
4543
4586
}
@@ -6647,6 +6690,49 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
6647
6690
return true ;
6648
6691
}
6649
6692
6693
+ inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider (
6694
+ const std::string &boundary, const MultipartFormDataItems &items,
6695
+ const MultipartFormDataProviderItems &provider_items) {
6696
+ size_t cur_item = 0 , cur_start = 0 ;
6697
+ // cur_item and cur_start are copied to within the std::function and maintain
6698
+ // state between successive calls
6699
+ return [&, cur_item, cur_start](size_t offset,
6700
+ DataSink &sink) mutable -> bool {
6701
+ if (!offset && items.size ()) {
6702
+ sink.os << detail::serialize_multipart_formdata (items, boundary, false );
6703
+ return true ;
6704
+ } else if (cur_item < provider_items.size ()) {
6705
+ if (!cur_start) {
6706
+ const auto &begin = detail::serialize_multipart_formdata_item_begin (
6707
+ provider_items[cur_item], boundary);
6708
+ offset += begin.size ();
6709
+ cur_start = offset;
6710
+ sink.os << begin;
6711
+ }
6712
+
6713
+ DataSink cur_sink;
6714
+ bool has_data = true ;
6715
+ cur_sink.write = sink.write ;
6716
+ cur_sink.done = [&]() { has_data = false ; };
6717
+ cur_sink.is_writable = sink.is_writable ;
6718
+
6719
+ if (!provider_items[cur_item].provider (offset - cur_start, cur_sink))
6720
+ return false ;
6721
+
6722
+ if (!has_data) {
6723
+ sink.os << detail::serialize_multipart_formdata_item_end ();
6724
+ cur_item++;
6725
+ cur_start = 0 ;
6726
+ }
6727
+ return true ;
6728
+ } else {
6729
+ sink.os << detail::serialize_multipart_formdata_finish (boundary);
6730
+ sink.done ();
6731
+ return true ;
6732
+ }
6733
+ };
6734
+ }
6735
+
6650
6736
inline bool
6651
6737
ClientImpl::process_socket (const Socket &socket,
6652
6738
std::function<bool (Stream &strm)> callback) {
@@ -6869,9 +6955,10 @@ inline Result ClientImpl::Post(const std::string &path,
6869
6955
6870
6956
inline Result ClientImpl::Post (const std::string &path, const Headers &headers,
6871
6957
const MultipartFormDataItems &items) {
6872
- std::string content_type;
6873
- const auto &body = detail::serialize_multipart_formdata (
6874
- items, detail::make_multipart_data_boundary (), content_type);
6958
+ const auto &boundary = detail::make_multipart_data_boundary ();
6959
+ const auto &content_type =
6960
+ detail::serialize_multipart_formdata_get_content_type (boundary);
6961
+ const auto &body = detail::serialize_multipart_formdata (items, boundary);
6875
6962
return Post (path, headers, body, content_type.c_str ());
6876
6963
}
6877
6964
@@ -6882,12 +6969,25 @@ inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
6882
6969
return Result{nullptr , Error::UnsupportedMultipartBoundaryChars};
6883
6970
}
6884
6971
6885
- std::string content_type;
6886
- const auto &body =
6887
- detail::serialize_multipart_formdata (items, boundary, content_type );
6972
+ const auto & content_type =
6973
+ detail::serialize_multipart_formdata_get_content_type (boundary);
6974
+ const auto &body = detail::serialize_multipart_formdata (items, boundary);
6888
6975
return Post (path, headers, body, content_type.c_str ());
6889
6976
}
6890
6977
6978
+ inline Result
6979
+ ClientImpl::Post (const std::string &path, const Headers &headers,
6980
+ const MultipartFormDataItems &items,
6981
+ const MultipartFormDataProviderItems &provider_items) {
6982
+ const auto &boundary = detail::make_multipart_data_boundary ();
6983
+ const auto &content_type =
6984
+ detail::serialize_multipart_formdata_get_content_type (boundary);
6985
+ return send_with_content_provider (
6986
+ " POST" , path, headers, nullptr , 0 , nullptr ,
6987
+ get_multipart_content_provider (boundary, items, provider_items),
6988
+ content_type);
6989
+ }
6990
+
6891
6991
inline Result ClientImpl::Put (const std::string &path) {
6892
6992
return Put (path, std::string (), std::string ());
6893
6993
}
@@ -6964,9 +7064,10 @@ inline Result ClientImpl::Put(const std::string &path,
6964
7064
6965
7065
inline Result ClientImpl::Put (const std::string &path, const Headers &headers,
6966
7066
const MultipartFormDataItems &items) {
6967
- std::string content_type;
6968
- const auto &body = detail::serialize_multipart_formdata (
6969
- items, detail::make_multipart_data_boundary (), content_type);
7067
+ const auto &boundary = detail::make_multipart_data_boundary ();
7068
+ const auto &content_type =
7069
+ detail::serialize_multipart_formdata_get_content_type (boundary);
7070
+ const auto &body = detail::serialize_multipart_formdata (items, boundary);
6970
7071
return Put (path, headers, body, content_type);
6971
7072
}
6972
7073
@@ -6977,12 +7078,24 @@ inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
6977
7078
return Result{nullptr , Error::UnsupportedMultipartBoundaryChars};
6978
7079
}
6979
7080
6980
- std::string content_type;
6981
- const auto &body =
6982
- detail::serialize_multipart_formdata (items, boundary, content_type );
7081
+ const auto & content_type =
7082
+ detail::serialize_multipart_formdata_get_content_type (boundary);
7083
+ const auto &body = detail::serialize_multipart_formdata (items, boundary);
6983
7084
return Put (path, headers, body, content_type);
6984
7085
}
6985
7086
7087
+ inline Result
7088
+ ClientImpl::Put (const std::string &path, const Headers &headers,
7089
+ const MultipartFormDataItems &items,
7090
+ const MultipartFormDataProviderItems &provider_items) {
7091
+ const auto &boundary = detail::make_multipart_data_boundary ();
7092
+ const auto &content_type =
7093
+ detail::serialize_multipart_formdata_get_content_type (boundary);
7094
+ return send_with_content_provider (
7095
+ " PUT" , path, headers, nullptr , 0 , nullptr ,
7096
+ get_multipart_content_provider (boundary, items, provider_items),
7097
+ content_type);
7098
+ }
6986
7099
inline Result ClientImpl::Patch (const std::string &path) {
6987
7100
return Patch (path, std::string (), std::string ());
6988
7101
}
@@ -7443,7 +7556,7 @@ inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
7443
7556
}
7444
7557
7445
7558
inline void SSLSocketStream::get_local_ip_and_port (std::string &ip,
7446
- int &port) const {
7559
+ int &port) const {
7447
7560
detail::get_local_ip_and_port (sock_, ip, port);
7448
7561
}
7449
7562
@@ -8147,6 +8260,12 @@ inline Result Client::Post(const std::string &path, const Headers &headers,
8147
8260
const std::string &boundary) {
8148
8261
return cli_->Post (path, headers, items, boundary);
8149
8262
}
8263
+ inline Result
8264
+ Client::Post (const std::string &path, const Headers &headers,
8265
+ const MultipartFormDataItems &items,
8266
+ const MultipartFormDataProviderItems &provider_items) {
8267
+ return cli_->Post (path, headers, items, provider_items);
8268
+ }
8150
8269
inline Result Client::Put (const std::string &path) { return cli_->Put (path); }
8151
8270
inline Result Client::Put (const std::string &path, const char *body,
8152
8271
size_t content_length,
@@ -8210,6 +8329,12 @@ inline Result Client::Put(const std::string &path, const Headers &headers,
8210
8329
const std::string &boundary) {
8211
8330
return cli_->Put (path, headers, items, boundary);
8212
8331
}
8332
+ inline Result
8333
+ Client::Put (const std::string &path, const Headers &headers,
8334
+ const MultipartFormDataItems &items,
8335
+ const MultipartFormDataProviderItems &provider_items) {
8336
+ return cli_->Put (path, headers, items, provider_items);
8337
+ }
8213
8338
inline Result Client::Patch (const std::string &path) {
8214
8339
return cli_->Patch (path);
8215
8340
}
0 commit comments