mirror of
https://github.com/yhirose/cpp-httplib
synced 2024-11-21 14:29:10 -07:00
Fixed #30
This commit is contained in:
parent
cca90184aa
commit
459f197ed0
1 changed files with 81 additions and 71 deletions
152
httplib.h
152
httplib.h
|
@ -130,6 +130,9 @@ public:
|
|||
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) = 0;
|
||||
|
||||
template <typename ...Args>
|
||||
void write_format(const char* fmt, const Args& ...args);
|
||||
};
|
||||
|
||||
class SocketStream : public Stream {
|
||||
|
@ -209,7 +212,7 @@ protected:
|
|||
|
||||
private:
|
||||
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);
|
||||
};
|
||||
|
@ -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 {
|
||||
public:
|
||||
stream_line_reader(Stream& strm, char* fixed_buffer, size_t fixed_buffer_size)
|
||||
|
@ -341,32 +346,6 @@ private:
|
|||
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)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
@ -733,25 +712,13 @@ inline void write_headers(Stream& strm, const T& info)
|
|||
|
||||
for (const auto& x: info.headers) {
|
||||
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");
|
||||
stream_write_format(strm, "Content-Type: %s\r\n", t);
|
||||
stream_write_format(strm, "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());
|
||||
}
|
||||
strm.write_format("Content-Type: %s\r\n", t);
|
||||
strm.write_format("Content-Length: %ld\r\n", info.body.size());
|
||||
}
|
||||
|
||||
inline std::string encode_url(const std::string& s)
|
||||
|
@ -886,23 +853,6 @@ inline std::string decode_url(const std::string& s)
|
|||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// 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
|
||||
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);
|
||||
}
|
||||
|
||||
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_) {
|
||||
logger_(req, res);
|
||||
|
@ -1410,11 +1396,45 @@ inline bool Client::send(const Request& req, Response& 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)
|
||||
{
|
||||
// Send request
|
||||
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
|
||||
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)
|
||||
{
|
||||
Request req;
|
||||
req.method = "GET";
|
||||
req.path = path;
|
||||
req.progress = callback;
|
||||
add_default_headers(req);
|
||||
|
||||
auto res = std::make_shared<Response>();
|
||||
|
||||
|
@ -1463,7 +1475,6 @@ inline std::shared_ptr<Response> Client::head(const char* path)
|
|||
Request req;
|
||||
req.method = "HEAD";
|
||||
req.path = path;
|
||||
add_default_headers(req);
|
||||
|
||||
auto res = std::make_shared<Response>();
|
||||
|
||||
|
@ -1476,7 +1487,6 @@ inline std::shared_ptr<Response> Client::post(
|
|||
Request req;
|
||||
req.method = "POST";
|
||||
req.path = path;
|
||||
add_default_headers(req);
|
||||
|
||||
req.set_header("Content-Type", content_type);
|
||||
req.body = body;
|
||||
|
|
Loading…
Reference in a new issue