Refine the support for uploading files.
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
**upload**: files for testing upload.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
@@ -0,0 +1,17 @@
|
|||||||
|
Remember
|
||||||
|
BY CHRISTINA ROSSETTI
|
||||||
|
|
||||||
|
Remember me when I am gone away,
|
||||||
|
Gone far away into the silent land;
|
||||||
|
When you can no more hold me by the hand,
|
||||||
|
Nor I half turn to go yet turning stay.
|
||||||
|
Remember me when no more day by day
|
||||||
|
You tell me of our future that you plann'd:
|
||||||
|
Only remember me; you understand
|
||||||
|
It will be late to counsel then or pray.
|
||||||
|
Yet if you should forget me for a while
|
||||||
|
And afterwards remember, do not grieve:
|
||||||
|
For if the darkness and corruption leave
|
||||||
|
A vestige of the thoughts that once I had,
|
||||||
|
Better by far you should forget and smile
|
||||||
|
Than that you should remember and be sad.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
+13
-16
@@ -4,8 +4,6 @@
|
|||||||
#include "webcc/http_client_session.h"
|
#include "webcc/http_client_session.h"
|
||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
using namespace webcc;
|
|
||||||
|
|
||||||
#if (defined(WIN32) || defined(_WIN64))
|
#if (defined(WIN32) || defined(_WIN64))
|
||||||
// You need to set environment variable SSL_CERT_FILE properly to enable
|
// You need to set environment variable SSL_CERT_FILE properly to enable
|
||||||
// SSL verification.
|
// SSL verification.
|
||||||
@@ -65,7 +63,7 @@ void ExampleHttps() {
|
|||||||
// ExampleKeepAlive("https://api.github.com/events");
|
// ExampleKeepAlive("https://api.github.com/events");
|
||||||
//
|
//
|
||||||
void ExampleKeepAlive(const std::string& url) {
|
void ExampleKeepAlive(const std::string& url) {
|
||||||
HttpClientSession session;
|
webcc::HttpClientSession session;
|
||||||
session.set_ssl_verify(kSslVerify);
|
session.set_ssl_verify(kSslVerify);
|
||||||
|
|
||||||
// Keep-Alive
|
// Keep-Alive
|
||||||
@@ -79,7 +77,7 @@ void ExampleKeepAlive(const std::string& url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ExampleCompression() {
|
void ExampleCompression() {
|
||||||
HttpClientSession session;
|
webcc::HttpClientSession session;
|
||||||
|
|
||||||
auto r = session.Get("http://httpbin.org/gzip");
|
auto r = session.Get("http://httpbin.org/gzip");
|
||||||
|
|
||||||
@@ -93,7 +91,7 @@ void ExampleCompression() {
|
|||||||
// Get an image from HttpBin.org and save to the given file path.
|
// Get an image from HttpBin.org and save to the given file path.
|
||||||
// E.g., ExampleImage("E:\\example.jpg")
|
// E.g., ExampleImage("E:\\example.jpg")
|
||||||
void ExampleImage(const std::string& path) {
|
void ExampleImage(const std::string& path) {
|
||||||
HttpClientSession session;
|
webcc::HttpClientSession session;
|
||||||
|
|
||||||
auto r = session.Get("http://httpbin.org/image/jpeg");
|
auto r = session.Get("http://httpbin.org/image/jpeg");
|
||||||
|
|
||||||
@@ -107,9 +105,9 @@ void ExampleImage(const std::string& path) {
|
|||||||
|
|
||||||
// Post/upload files.
|
// Post/upload files.
|
||||||
void ExamplePostFiles() {
|
void ExamplePostFiles() {
|
||||||
HttpClientSession session;
|
webcc::HttpClientSession session;
|
||||||
|
|
||||||
auto r = session.Request(HttpRequestBuilder{}
|
auto r = session.Request(webcc::HttpRequestBuilder{}
|
||||||
.Post()
|
.Post()
|
||||||
.Url("http://httpbin.org/post")
|
.Url("http://httpbin.org/post")
|
||||||
.FileData("file1", "report.xls", "<xls report data>", "application/vnd.ms-excel")
|
.FileData("file1", "report.xls", "<xls report data>", "application/vnd.ms-excel")
|
||||||
@@ -119,29 +117,28 @@ void ExamplePostFiles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Post/upload files by file path.
|
// Post/upload files by file path.
|
||||||
void ExamplePostFiles(const std::string& name,
|
void ExamplePostFiles(const std::string& url,
|
||||||
|
const std::string& name,
|
||||||
const std::string& file_name,
|
const std::string& file_name,
|
||||||
const std::string& file_path,
|
const std::string& file_path,
|
||||||
const std::string& content_type) {
|
const std::string& content_type) {
|
||||||
HttpClientSession session;
|
webcc::HttpClientSession session;
|
||||||
|
|
||||||
auto r =
|
auto r = session.Request(webcc::HttpRequestBuilder{}.Post().
|
||||||
session.Request(HttpRequestBuilder{}
|
Url(url).
|
||||||
.Post()
|
File(name, file_name, file_path, content_type)());
|
||||||
.Url("http://httpbin.org/post")
|
|
||||||
.File(name, file_name, file_path, content_type)());
|
|
||||||
|
|
||||||
std::cout << r->content() << std::endl;
|
std::cout << r->content() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
WEBCC_LOG_INIT("", LOG_CONSOLE);
|
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
ExampleBasic();
|
ExampleBasic();
|
||||||
|
|
||||||
} catch (const Exception& e) {
|
} catch (const webcc::Exception& e) {
|
||||||
std::cout << "Exception: " << e.what() << std::endl;
|
std::cout << "Exception: " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,33 @@
|
|||||||
#include "webcc/globals.h"
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "boost/filesystem/path.hpp"
|
||||||
|
|
||||||
#include "webcc/version.h"
|
#include "webcc/version.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Read entire file into string.
|
||||||
|
static bool ReadFile(const std::string& path, std::string* output) {
|
||||||
|
std::ifstream ifs{path, std::ios::binary | std::ios::ate};
|
||||||
|
if (!ifs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto size = ifs.tellg();
|
||||||
|
output->resize(size, '\0');
|
||||||
|
ifs.seekg(0);
|
||||||
|
ifs.read(&(*output)[0], size); // TODO: Error handling
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
const std::string& UserAgent() {
|
const std::string& UserAgent() {
|
||||||
@@ -11,6 +35,66 @@ const std::string& UserAgent() {
|
|||||||
return s_user_agent;
|
return s_user_agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File::File(const std::string& file_path) {
|
||||||
|
if (!ReadFile(file_path, &data)) {
|
||||||
|
throw Exception(kFileIOError, "Cannot read the file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace bfs = boost::filesystem;
|
||||||
|
|
||||||
|
// Determine file name from file path.
|
||||||
|
//if (file_name.empty()) {
|
||||||
|
file_name = bfs::path(file_path).filename().string();
|
||||||
|
//} else {
|
||||||
|
// file_name = file_name;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Determine content type from file extension.
|
||||||
|
//if (mime_type.empty()) {
|
||||||
|
std::string extension = bfs::path(file_path).extension().string();
|
||||||
|
mime_type = http::media_types::FromExtension(extension, false);
|
||||||
|
//} else {
|
||||||
|
//mime_type = mime_type;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace media_types {
|
||||||
|
|
||||||
|
// TODO: Add more.
|
||||||
|
static void InitMap(std::map<std::string, std::string>& map) {
|
||||||
|
map["gif"] = "image/gif";
|
||||||
|
map["htm"] = "text/html";
|
||||||
|
map["html"] = "text/html";
|
||||||
|
map["jpg"] = "image/jpeg";
|
||||||
|
map["jpeg"] = "image/jpeg";
|
||||||
|
map["png"] = "image/png";
|
||||||
|
map["txt"] = "text/plain";
|
||||||
|
map[""] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Ignore case on Windows.
|
||||||
|
std::string FromExtension(const std::string& extension,
|
||||||
|
bool default_to_plain_text) {
|
||||||
|
static std::map<std::string, std::string> s_map;
|
||||||
|
|
||||||
|
if (s_map.empty()) {
|
||||||
|
InitMap(s_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = s_map.find(extension);
|
||||||
|
if (it != s_map.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (default_to_plain_text) {
|
||||||
|
return "text/plain";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media_types
|
||||||
|
|
||||||
} // namespace http
|
} // namespace http
|
||||||
|
|
||||||
const char* DescribeError(Error error) {
|
const char* DescribeError(Error error) {
|
||||||
|
|||||||
+26
-7
@@ -24,8 +24,9 @@ const std::size_t kMaxDumpSize = 2048;
|
|||||||
// Default buffer size for socket reading.
|
// Default buffer size for socket reading.
|
||||||
const std::size_t kBufferSize = 1024;
|
const std::size_t kBufferSize = 1024;
|
||||||
|
|
||||||
// Default ports.
|
// Default port for HTTP.
|
||||||
const char* const kPort80 = "80";
|
const char* const kPort80 = "80";
|
||||||
|
// Default port for HTTPS.
|
||||||
const char* const kPort443 = "443";
|
const char* const kPort443 = "443";
|
||||||
|
|
||||||
// Why 1400? See the following page:
|
// Why 1400? See the following page:
|
||||||
@@ -36,7 +37,6 @@ const std::size_t kGzipThreshold = 1400;
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// HTTP headers.
|
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
namespace methods {
|
namespace methods {
|
||||||
@@ -95,16 +95,17 @@ const char* const kServer = "Server";
|
|||||||
|
|
||||||
namespace media_types {
|
namespace media_types {
|
||||||
|
|
||||||
// NOTE:
|
// See the following link for the full list of media types:
|
||||||
// According to www.w3.org when placing SOAP messages in HTTP bodies, the HTTP
|
// https://www.iana.org/assignments/media-types/media-types.xhtml
|
||||||
// Content-type header must be chosen as "application/soap+xml" [RFC 3902].
|
|
||||||
// But in practice, many web servers cannot understand it.
|
|
||||||
// See: https://www.w3.org/TR/2007/REC-soap12-part0-20070427/#L26854
|
|
||||||
|
|
||||||
const char* const kApplicationJson = "application/json";
|
const char* const kApplicationJson = "application/json";
|
||||||
const char* const kApplicationSoapXml = "application/soap+xml";
|
const char* const kApplicationSoapXml = "application/soap+xml";
|
||||||
const char* const kTextXml = "text/xml";
|
const char* const kTextXml = "text/xml";
|
||||||
|
|
||||||
|
// Get media type from file extension.
|
||||||
|
std::string FromExtension(const std::string& extension,
|
||||||
|
bool default_to_plain_text = true);
|
||||||
|
|
||||||
} // namespace media_types
|
} // namespace media_types
|
||||||
|
|
||||||
namespace charsets {
|
namespace charsets {
|
||||||
@@ -122,6 +123,24 @@ enum class ContentEncoding {
|
|||||||
// Return default user agent for HTTP headers.
|
// Return default user agent for HTTP headers.
|
||||||
const std::string& UserAgent();
|
const std::string& UserAgent();
|
||||||
|
|
||||||
|
// File for HTTP transfer (upload/download).
|
||||||
|
class File {
|
||||||
|
public:
|
||||||
|
File() = default;
|
||||||
|
|
||||||
|
File(const std::string& file_path);
|
||||||
|
|
||||||
|
// Binary file data.
|
||||||
|
// TODO: don't use std::string?
|
||||||
|
std::string data;
|
||||||
|
|
||||||
|
// E.g., example.jpg
|
||||||
|
std::string file_name;
|
||||||
|
|
||||||
|
// E.g., image/jpeg
|
||||||
|
std::string mime_type;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace http
|
} // namespace http
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ void HttpClient::DoReadResponse(Error* error) {
|
|||||||
// Stop the deadline timer once the read has started (or failed).
|
// Stop the deadline timer once the read has started (or failed).
|
||||||
CancelTimer();
|
CancelTimer();
|
||||||
|
|
||||||
|
// TODO: Is it necessary to check `length == 0`?
|
||||||
if (ec || length == 0) {
|
if (ec || length == 0) {
|
||||||
Close();
|
Close();
|
||||||
*error = kSocketReadError;
|
*error = kSocketReadError;
|
||||||
|
|||||||
@@ -67,6 +67,20 @@ HttpResponsePtr HttpClientSession::Post(
|
|||||||
return Request(builder());
|
return Request(builder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HttpResponsePtr HttpClientSession::PostFile(const std::string& url,
|
||||||
|
const std::string& name,
|
||||||
|
http::File&& file,
|
||||||
|
const std::vector<std::string>& headers) {
|
||||||
|
HttpRequestBuilder builder{http::methods::kPost};
|
||||||
|
builder.Url(url);
|
||||||
|
|
||||||
|
SetHeaders(headers, &builder);
|
||||||
|
|
||||||
|
builder.File(name, std::move(file));
|
||||||
|
|
||||||
|
return Request(builder());
|
||||||
|
}
|
||||||
|
|
||||||
HttpResponsePtr HttpClientSession::Put(
|
HttpResponsePtr HttpClientSession::Put(
|
||||||
const std::string& url, std::string&& data, bool json,
|
const std::string& url, std::string&& data, bool json,
|
||||||
const std::vector<std::string>& headers) {
|
const std::vector<std::string>& headers) {
|
||||||
|
|||||||
@@ -59,6 +59,12 @@ public:
|
|||||||
HttpResponsePtr Post(const std::string& url, std::string&& data, bool json,
|
HttpResponsePtr Post(const std::string& url, std::string&& data, bool json,
|
||||||
const std::vector<std::string>& headers = {});
|
const std::vector<std::string>& headers = {});
|
||||||
|
|
||||||
|
// Post a file.
|
||||||
|
HttpResponsePtr PostFile(const std::string& url,
|
||||||
|
const std::string& name,
|
||||||
|
http::File&& file,
|
||||||
|
const std::vector<std::string>& headers = {});
|
||||||
|
|
||||||
// Shortcut for PUT request.
|
// Shortcut for PUT request.
|
||||||
HttpResponsePtr Put(const std::string& url, std::string&& data, bool json,
|
HttpResponsePtr Put(const std::string& url, std::string&& data, bool json,
|
||||||
const std::vector<std::string>& headers = {});
|
const std::vector<std::string>& headers = {});
|
||||||
|
|||||||
@@ -166,7 +166,9 @@ void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
|||||||
|
|
||||||
os << indent_str << std::endl;
|
os << indent_str << std::endl;
|
||||||
|
|
||||||
// NOTE: The content will be truncated if it's too large to display.
|
// NOTE:
|
||||||
|
// - The content will be truncated if it's too large to display.
|
||||||
|
// - Binary content will not be dumped (TODO).
|
||||||
|
|
||||||
if (!content_.empty()) {
|
if (!content_.empty()) {
|
||||||
if (indent == 0) {
|
if (indent == 0) {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "webcc/http_request_builder.h"
|
#include "webcc/http_request_builder.h"
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "webcc/base64.h"
|
#include "webcc/base64.h"
|
||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
#include "webcc/utility.h"
|
#include "webcc/utility.h"
|
||||||
@@ -9,25 +7,6 @@
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Read entire file into string.
|
|
||||||
static bool ReadFile(const std::string& path, std::string* output) {
|
|
||||||
std::ifstream ifs{path, std::ios::binary | std::ios::ate};
|
|
||||||
if (!ifs) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto size = ifs.tellg();
|
|
||||||
output->resize(size, '\0');
|
|
||||||
ifs.seekg(0);
|
|
||||||
ifs.read(&(*output)[0], size); // TODO: Error handling
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
HttpRequestPtr HttpRequestBuilder::Build() {
|
HttpRequestPtr HttpRequestBuilder::Build() {
|
||||||
assert(parameters_.size() % 2 == 0);
|
assert(parameters_.size() % 2 == 0);
|
||||||
assert(headers_.size() % 2 == 0);
|
assert(headers_.size() % 2 == 0);
|
||||||
@@ -72,15 +51,33 @@ HttpRequestPtr HttpRequestBuilder::Build() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HttpRequestBuilder& HttpRequestBuilder::File(const std::string& name,
|
HttpRequestBuilder& HttpRequestBuilder::File(const std::string& name,
|
||||||
const std::string& file_name,
|
|
||||||
const std::string& file_path,
|
const std::string& file_path,
|
||||||
const std::string& content_type) {
|
const std::string& file_name,
|
||||||
std::string file_data;
|
const std::string& mime_type) {
|
||||||
if (!ReadFile(file_path, &file_data)) {
|
assert(!name.empty());
|
||||||
throw Exception(kFileIOError, "Cannot read the file.");
|
|
||||||
|
// TODO
|
||||||
|
files_[name] = http::File(file_path/*, file_name, mime_type*/);
|
||||||
|
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
files_.push_back({name, file_name, std::move(file_data), content_type});
|
HttpRequestBuilder& HttpRequestBuilder::File(const std::string& name,
|
||||||
|
http::File&& file) {
|
||||||
|
files_[name] = std::move(file);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestBuilder& HttpRequestBuilder::FileData(const std::string& name,
|
||||||
|
std::string&& file_data,
|
||||||
|
const std::string& file_name,
|
||||||
|
const std::string& mime_type) {
|
||||||
|
http::File file;
|
||||||
|
file.data = std::move(file_data);
|
||||||
|
file.file_name = file_name;
|
||||||
|
file.mime_type = mime_type;
|
||||||
|
|
||||||
|
files_[name] = std::move(file);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -116,29 +113,29 @@ void HttpRequestBuilder::SetContent(HttpRequestPtr request,
|
|||||||
|
|
||||||
void HttpRequestBuilder::CreateFormData(std::string* data,
|
void HttpRequestBuilder::CreateFormData(std::string* data,
|
||||||
const std::string& boundary) {
|
const std::string& boundary) {
|
||||||
for (UploadFile& file : files_) {
|
for (auto& pair : files_) {
|
||||||
data->append("--" + boundary + kCRLF);
|
data->append("--" + boundary + kCRLF);
|
||||||
|
|
||||||
// Content-Disposition header
|
// Content-Disposition header
|
||||||
data->append("Content-Disposition: form-data");
|
data->append("Content-Disposition: form-data");
|
||||||
if (!file.name.empty()) {
|
if (!pair.first.empty()) {
|
||||||
data->append("; name=\"" + file.name + "\"");
|
data->append("; name=\"" + pair.first + "\"");
|
||||||
}
|
}
|
||||||
if (!file.file_name.empty()) {
|
if (!pair.second.file_name.empty()) {
|
||||||
data->append("; filename=\"" + file.file_name + "\"");
|
data->append("; filename=\"" + pair.second.file_name + "\"");
|
||||||
}
|
}
|
||||||
data->append(kCRLF);
|
data->append(kCRLF);
|
||||||
|
|
||||||
// Content-Type header
|
// Content-Type header
|
||||||
if (!file.content_type.empty()) {
|
if (!pair.second.mime_type.empty()) {
|
||||||
data->append("Content-Type: " + file.content_type);
|
data->append("Content-Type: " + pair.second.mime_type);
|
||||||
data->append(kCRLF);
|
data->append(kCRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
data->append(kCRLF);
|
data->append(kCRLF);
|
||||||
|
|
||||||
// Payload
|
// Payload
|
||||||
data->append(file.file_data);
|
data->append(pair.second.data);
|
||||||
|
|
||||||
data->append(kCRLF);
|
data->append(kCRLF);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef WEBCC_HTTP_REQUEST_BUILDER_H_
|
#ifndef WEBCC_HTTP_REQUEST_BUILDER_H_
|
||||||
#define WEBCC_HTTP_REQUEST_BUILDER_H_
|
#define WEBCC_HTTP_REQUEST_BUILDER_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -67,18 +68,17 @@ public:
|
|||||||
// Upload a file with its path.
|
// Upload a file with its path.
|
||||||
// TODO: UNICODE file path.
|
// TODO: UNICODE file path.
|
||||||
HttpRequestBuilder& File(const std::string& name,
|
HttpRequestBuilder& File(const std::string& name,
|
||||||
const std::string& file_name,
|
|
||||||
const std::string& file_path,
|
const std::string& file_path,
|
||||||
const std::string& content_type = "");
|
const std::string& file_name = "",
|
||||||
|
const std::string& mime_type = "");
|
||||||
|
|
||||||
|
HttpRequestBuilder& File(const std::string& name, http::File&& file);
|
||||||
|
|
||||||
// Upload a file with its data.
|
// Upload a file with its data.
|
||||||
HttpRequestBuilder& FileData(const std::string& name,
|
HttpRequestBuilder& FileData(const std::string& name,
|
||||||
const std::string& file_name,
|
|
||||||
std::string&& file_data,
|
std::string&& file_data,
|
||||||
const std::string& content_type = "") {
|
const std::string& file_name = "",
|
||||||
files_.push_back({name, file_name, file_data, content_type});
|
const std::string& mime_type = "");
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestBuilder& Gzip(bool gzip = true) {
|
HttpRequestBuilder& Gzip(bool gzip = true) {
|
||||||
gzip_ = gzip;
|
gzip_ = gzip;
|
||||||
@@ -122,19 +122,8 @@ private:
|
|||||||
// Is the data to send a JSON string?
|
// Is the data to send a JSON string?
|
||||||
bool json_ = false;
|
bool json_ = false;
|
||||||
|
|
||||||
// A file to upload.
|
|
||||||
// Examples:
|
|
||||||
// { "images", "example.jpg", "BinaryData", "image/jpeg" }
|
|
||||||
// { "file", "report.csv", "BinaryData", "" }
|
|
||||||
struct UploadFile {
|
|
||||||
std::string name;
|
|
||||||
std::string file_name;
|
|
||||||
std::string file_data; // Binary file data
|
|
||||||
std::string content_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Files to upload for a POST (or PUT?) request.
|
// Files to upload for a POST (or PUT?) request.
|
||||||
std::vector<UploadFile> files_;
|
std::map<std::string, http::File> files_;
|
||||||
|
|
||||||
// Compress the request content.
|
// Compress the request content.
|
||||||
// NOTE: Most servers don't support compressed requests.
|
// NOTE: Most servers don't support compressed requests.
|
||||||
|
|||||||
@@ -51,6 +51,12 @@ bool SoapClient::Request(const std::string& operation,
|
|||||||
|
|
||||||
http_request->SetContent(std::move(http_content), true);
|
http_request->SetContent(std::move(http_content), true);
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// According to www.w3.org when placing SOAP messages in HTTP bodies, the HTTP
|
||||||
|
// Content-type header must be chosen as "application/soap+xml" [RFC 3902].
|
||||||
|
// But in practice, many web servers cannot understand it.
|
||||||
|
// See: https://www.w3.org/TR/2007/REC-soap12-part0-20070427/#L26854
|
||||||
|
|
||||||
if (soap_version_ == kSoapV11) {
|
if (soap_version_ == kSoapV11) {
|
||||||
http_request->SetContentType(http::media_types::kTextXml,
|
http_request->SetContentType(http::media_types::kTextXml,
|
||||||
http::charsets::kUtf8);
|
http::charsets::kUtf8);
|
||||||
|
|||||||
Reference in New Issue
Block a user