Fixed #30
This commit is contained in:
parent
cca90184aa
commit
459f197ed0
152
httplib.h
152
httplib.h
@ -130,6 +130,9 @@ public:
|
|||||||
virtual int read(char* ptr, size_t size) = 0;
|
virtual int read(char* ptr, size_t size) = 0;
|
||||||
virtual int write(const char* ptr, size_t size1) = 0;
|
virtual int write(const char* ptr, size_t size1) = 0;
|
||||||
virtual int write(const char* ptr) = 0;
|
virtual int write(const char* ptr) = 0;
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
void write_format(const char* fmt, const Args& ...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SocketStream : public Stream {
|
class SocketStream : public Stream {
|
||||||
@ -209,7 +212,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool read_response_line(Stream& strm, Response& res);
|
bool read_response_line(Stream& strm, Response& res);
|
||||||
void add_default_headers(Request& req);
|
void write_request(Stream& strm, const Request& req, const char* ver);
|
||||||
|
|
||||||
virtual bool read_and_close_socket(socket_t sock, const Request& req, Response& res);
|
virtual bool read_and_close_socket(socket_t sock, const Request& req, Response& res);
|
||||||
};
|
};
|
||||||
@ -277,6 +280,8 @@ void split(const char* b, const char* e, char d, Fn fn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
|
||||||
|
// to store data. The call can set memory on stack for performance.
|
||||||
class stream_line_reader {
|
class stream_line_reader {
|
||||||
public:
|
public:
|
||||||
stream_line_reader(Stream& strm, char* fixed_buffer, size_t fixed_buffer_size)
|
stream_line_reader(Stream& strm, char* fixed_buffer, size_t fixed_buffer_size)
|
||||||
@ -341,32 +346,6 @@ private:
|
|||||||
std::string glowable_buffer_;
|
std::string glowable_buffer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ...Args>
|
|
||||||
inline void stream_write_format(Stream& strm, const char* fmt, const Args& ...args)
|
|
||||||
{
|
|
||||||
const auto bufsiz = 2048;
|
|
||||||
char buf[bufsiz];
|
|
||||||
|
|
||||||
auto n = snprintf(buf, bufsiz - 1, fmt, args...);
|
|
||||||
if (n > 0) {
|
|
||||||
if (n >= bufsiz - 1) {
|
|
||||||
std::vector<char> glowable_buf(bufsiz);
|
|
||||||
|
|
||||||
while (n >= static_cast<int>(glowable_buf.size() - 1)) {
|
|
||||||
glowable_buf.resize(glowable_buf.size() * 2);
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
||||||
n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...);
|
|
||||||
#else
|
|
||||||
n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
strm.write(&glowable_buf[0], n);
|
|
||||||
} else {
|
|
||||||
strm.write(buf, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int close_socket(socket_t sock)
|
inline int close_socket(socket_t sock)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -733,25 +712,13 @@ inline void write_headers(Stream& strm, const T& info)
|
|||||||
|
|
||||||
for (const auto& x: info.headers) {
|
for (const auto& x: info.headers) {
|
||||||
if (x.first != "Content-Type" && x.first != "Content-Length") {
|
if (x.first != "Content-Type" && x.first != "Content-Length") {
|
||||||
stream_write_format(strm, "%s: %s\r\n", x.first.c_str(), x.second.c_str());
|
strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto t = get_header_value(info.headers, "Content-Type", "text/plain");
|
auto t = get_header_value(info.headers, "Content-Type", "text/plain");
|
||||||
stream_write_format(strm, "Content-Type: %s\r\n", t);
|
strm.write_format("Content-Type: %s\r\n", t);
|
||||||
stream_write_format(strm, "Content-Length: %ld\r\n", info.body.size());
|
strm.write_format("Content-Length: %ld\r\n", info.body.size());
|
||||||
strm.write("\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void write_response(Stream& strm, const Request& req, const Response& res)
|
|
||||||
{
|
|
||||||
stream_write_format(strm, "HTTP/1.0 %d %s\r\n", res.status, status_message(res.status));
|
|
||||||
|
|
||||||
write_headers(strm, res);
|
|
||||||
|
|
||||||
if (!res.body.empty() && req.method != "HEAD") {
|
|
||||||
strm.write(res.body.c_str(), res.body.size());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string encode_url(const std::string& s)
|
inline std::string encode_url(const std::string& s)
|
||||||
@ -886,23 +853,6 @@ inline std::string decode_url(const std::string& s)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void write_request(Stream& strm, const Request& req, const char* ver)
|
|
||||||
{
|
|
||||||
auto path = encode_url(req.path);
|
|
||||||
stream_write_format(strm, "%s %s %s\r\n", req.method.c_str(), path.c_str(), ver);
|
|
||||||
|
|
||||||
write_headers(strm, req);
|
|
||||||
|
|
||||||
if (!req.body.empty()) {
|
|
||||||
if (req.has_header("application/x-www-form-urlencoded")) {
|
|
||||||
auto str = encode_url(req.body);
|
|
||||||
strm.write(str.c_str(), str.size());
|
|
||||||
} else {
|
|
||||||
strm.write(req.body.c_str(), req.body.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void parse_query_text(const std::string& s, MultiMap& params)
|
inline void parse_query_text(const std::string& s, MultiMap& params)
|
||||||
{
|
{
|
||||||
split(&s[0], &s[s.size()], '&', [&](const char* b, const char* e) {
|
split(&s[0], &s[s.size()], '&', [&](const char* b, const char* e) {
|
||||||
@ -1110,6 +1060,33 @@ inline void Response::set_content(const std::string& s, const char* content_type
|
|||||||
set_header("Content-Type", content_type);
|
set_header("Content-Type", content_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rstream implementation
|
||||||
|
template <typename ...Args>
|
||||||
|
inline void Stream::write_format(const char* fmt, const Args& ...args)
|
||||||
|
{
|
||||||
|
const auto bufsiz = 2048;
|
||||||
|
char buf[bufsiz];
|
||||||
|
|
||||||
|
auto n = snprintf(buf, bufsiz - 1, fmt, args...);
|
||||||
|
if (n > 0) {
|
||||||
|
if (n >= bufsiz - 1) {
|
||||||
|
std::vector<char> glowable_buf(bufsiz);
|
||||||
|
|
||||||
|
while (n >= static_cast<int>(glowable_buf.size() - 1)) {
|
||||||
|
glowable_buf.resize(glowable_buf.size() * 2);
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...);
|
||||||
|
#else
|
||||||
|
n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
write(&glowable_buf[0], n);
|
||||||
|
} else {
|
||||||
|
write(buf, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Socket stream implementation
|
// Socket stream implementation
|
||||||
inline SocketStream::SocketStream(socket_t sock): sock_(sock)
|
inline SocketStream::SocketStream(socket_t sock): sock_(sock)
|
||||||
{
|
{
|
||||||
@ -1252,7 +1229,16 @@ inline void Server::write_response(Stream& strm, const Request& req, Response& r
|
|||||||
error_handler_(req, res);
|
error_handler_(req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::write_response(strm, req, res);
|
strm.write_format(
|
||||||
|
"HTTP/1.0 %d %s\r\n",
|
||||||
|
res.status, detail::status_message(res.status));
|
||||||
|
|
||||||
|
detail::write_headers(strm, res);
|
||||||
|
strm.write("\r\n");
|
||||||
|
|
||||||
|
if (!res.body.empty() && req.method != "HEAD") {
|
||||||
|
strm.write(res.body.c_str(), res.body.size());
|
||||||
|
}
|
||||||
|
|
||||||
if (logger_) {
|
if (logger_) {
|
||||||
logger_(req, res);
|
logger_(req, res);
|
||||||
@ -1410,11 +1396,45 @@ inline bool Client::send(const Request& req, Response& res)
|
|||||||
return read_and_close_socket(sock, req, res);
|
return read_and_close_socket(sock, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Client::write_request(Stream& strm, const Request& req, const char* ver)
|
||||||
|
{
|
||||||
|
auto path = detail::encode_url(req.path);
|
||||||
|
|
||||||
|
// Request line
|
||||||
|
strm.write_format(
|
||||||
|
"%s %s %s\r\n", req.method.c_str(), path.c_str(), ver);
|
||||||
|
|
||||||
|
// Headers
|
||||||
|
strm.write_format("Host: %s\r\n", host_and_port_.c_str());
|
||||||
|
|
||||||
|
if (!req.has_header("Accept")) {
|
||||||
|
strm.write("Accept: */*\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.has_header("User-Agent")) {
|
||||||
|
strm.write("User-Agent: cpp-httplib/0.1\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
detail::write_headers(strm, req);
|
||||||
|
|
||||||
|
strm.write("\r\n");
|
||||||
|
|
||||||
|
// Body
|
||||||
|
if (!req.body.empty()) {
|
||||||
|
if (req.has_header("application/x-www-form-urlencoded")) {
|
||||||
|
auto str = detail::encode_url(req.body);
|
||||||
|
strm.write(str.c_str(), str.size());
|
||||||
|
} else {
|
||||||
|
strm.write(req.body.c_str(), req.body.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline bool Client::process_request(Stream& strm, const Request& req, Response& res)
|
inline bool Client::process_request(Stream& strm, const Request& req, Response& res)
|
||||||
{
|
{
|
||||||
// Send request
|
// Send request
|
||||||
auto ver = detail::http_version_strings[static_cast<size_t>(http_version_)];
|
auto ver = detail::http_version_strings[static_cast<size_t>(http_version_)];
|
||||||
detail::write_request(strm, req, ver);
|
write_request(strm, req, ver);
|
||||||
|
|
||||||
// Receive response
|
// Receive response
|
||||||
if (!read_response_line(strm, res) ||
|
if (!read_response_line(strm, res) ||
|
||||||
@ -1438,20 +1458,12 @@ inline bool Client::read_and_close_socket(socket_t sock, const Request& req, Res
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Client::add_default_headers(Request& req)
|
|
||||||
{
|
|
||||||
req.set_header("Host", host_and_port_.c_str());
|
|
||||||
req.set_header("Accept", "*/*");
|
|
||||||
req.set_header("User-Agent", "cpp-httplib/0.1");
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::shared_ptr<Response> Client::get(const char* path, Progress callback)
|
inline std::shared_ptr<Response> Client::get(const char* path, Progress callback)
|
||||||
{
|
{
|
||||||
Request req;
|
Request req;
|
||||||
req.method = "GET";
|
req.method = "GET";
|
||||||
req.path = path;
|
req.path = path;
|
||||||
req.progress = callback;
|
req.progress = callback;
|
||||||
add_default_headers(req);
|
|
||||||
|
|
||||||
auto res = std::make_shared<Response>();
|
auto res = std::make_shared<Response>();
|
||||||
|
|
||||||
@ -1463,7 +1475,6 @@ inline std::shared_ptr<Response> Client::head(const char* path)
|
|||||||
Request req;
|
Request req;
|
||||||
req.method = "HEAD";
|
req.method = "HEAD";
|
||||||
req.path = path;
|
req.path = path;
|
||||||
add_default_headers(req);
|
|
||||||
|
|
||||||
auto res = std::make_shared<Response>();
|
auto res = std::make_shared<Response>();
|
||||||
|
|
||||||
@ -1476,7 +1487,6 @@ inline std::shared_ptr<Response> Client::post(
|
|||||||
Request req;
|
Request req;
|
||||||
req.method = "POST";
|
req.method = "POST";
|
||||||
req.path = path;
|
req.path = path;
|
||||||
add_default_headers(req);
|
|
||||||
|
|
||||||
req.set_header("Content-Type", content_type);
|
req.set_header("Content-Type", content_type);
|
||||||
req.body = body;
|
req.body = body;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user