393 lines
16 KiB
C++
393 lines
16 KiB
C++
#ifndef WEBCC_GLOBALS_H_
|
|
#define WEBCC_GLOBALS_H_
|
|
|
|
#include <cassert>
|
|
#include <exception>
|
|
#include <functional>
|
|
#include <iosfwd>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "boost/asio/buffer.hpp" // for const_buffer
|
|
|
|
#include "webcc/config.h"
|
|
|
|
#if WEBCC_USE_STD_STRING_VIEW
|
|
#include <string_view>
|
|
#else
|
|
#include "boost/utility/string_view.hpp"
|
|
#endif // WEBCC_USE_STD_STRING_VIEW
|
|
|
|
namespace webcc {
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
#if WEBCC_USE_STD_STRING_VIEW
|
|
using string_view = std::string_view;
|
|
#else
|
|
using string_view = boost::string_view;
|
|
#endif // WEBCC_USE_STD_STRING_VIEW
|
|
|
|
inline std::string ToString(string_view sv) {
|
|
#if WEBCC_USE_STD_STRING_VIEW
|
|
return std::string{ sv.begin(), sv.end() };
|
|
#else
|
|
return sv.to_string();
|
|
#endif // WEBCC_USE_STD_STRING_VIEW
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using Strings = std::vector<std::string>;
|
|
|
|
// Regex sub-matches of the URL (usually resource ID's).
|
|
// Could also be considered as arguments, so named as UrlArgs.
|
|
using UrlArgs = std::vector<std::string>;
|
|
|
|
using Payload = std::vector<boost::asio::const_buffer>;
|
|
|
|
using ProgressCallback =
|
|
std::function<void(std::size_t length, std::size_t total_length)>;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
const char* const kCRLF = "\r\n";
|
|
|
|
const std::size_t kInvalidLength = -1;
|
|
|
|
// Default timeout for reading response.
|
|
const int kMaxReadSeconds = 30;
|
|
|
|
// Max size of the HTTP body to dump/log.
|
|
// If the HTTP, e.g., response, has a very large content, it will be truncated
|
|
// when dumped/logged.
|
|
const std::size_t kMaxDumpSize = 2048;
|
|
|
|
// Default buffer size for socket reading.
|
|
const std::size_t kBufferSize = 1024;
|
|
|
|
// Why 1400? See the following page:
|
|
// https://www.itworld.com/article/2693941/why-it-doesn-t-make-sense-to-
|
|
// gzip-all-content-from-your-web-server.html
|
|
const std::size_t kGzipThreshold = 1400;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
namespace literal_buffers {
|
|
|
|
// Buffers for composing payload.
|
|
// Literal strings can't be used because they have an extra '\0'.
|
|
|
|
extern const char HEADER_SEPARATOR[2];
|
|
extern const char CRLF[2];
|
|
extern const char DOUBLE_DASHES[2];
|
|
|
|
} // namespace literal_buffers
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
namespace methods {
|
|
|
|
// HTTP methods (verbs) in string.
|
|
// Don't use enum to avoid converting back and forth.
|
|
|
|
const char* const kGet = "GET";
|
|
const char* const kHead = "HEAD";
|
|
const char* const kPost = "POST";
|
|
const char* const kPut = "PUT";
|
|
const char* const kDelete = "DELETE";
|
|
const char* const kConnect = "CONNECT";
|
|
const char* const kOptions = "OPTIONS";
|
|
const char* const kTrace = "TRACE";
|
|
const char* const kPatch = "PATCH";
|
|
|
|
} // namespace methods
|
|
|
|
// HTTP status codes.
|
|
// Don't use "enum class" for converting to/from int easily.
|
|
// The full list is available here:
|
|
// https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
|
enum Status {
|
|
// 此临时响应表明客户端应继续请求,或者如果请求已完成,则忽略此响应。
|
|
kContinue = 100,
|
|
// 此代码是在响应客户端的 Upgrade 请求标头时发送的,用于指示服务器即将切换到的协议。
|
|
kSwitchingProtocols = 101,
|
|
// 此代码曾在 WebDAV 上下文中使用,表示服务器已收到请求,但在响应时无法提供状态。
|
|
kProcessing = 102,
|
|
// 此状态码主要与 Link 标头一起使用,允许用户代理在服务器准备响应时开始预加载资源,或预连接到页面需要资源的源站。
|
|
kEarlyHints = 103,
|
|
|
|
// 请求成功。
|
|
kOK = 200,
|
|
// 请求成功,并因此创建了一个新资源。
|
|
kCreated = 201,
|
|
// 请求已被接收但尚未处理。
|
|
kAccepted = 202,
|
|
// 此响应代码表示返回的元数据与原始服务器上可用的不完全相同,而是从本地或第三方副本收集的。这主要用于另一个资源的镜像或备份。
|
|
kNonAuthoritativeInformation = 203,
|
|
// 对于此请求,没有内容可发送,但响应头可能有用。
|
|
kNoContent = 204,
|
|
// 告知用户代理重置发送此请求的文档。
|
|
kResetContent = 205,
|
|
// 当客户端请求了资源的一部分时,使用此响应代码进行响应。
|
|
kPartialContent = 206,
|
|
// 在可能需要多个状态码的情况下,传递关于多个资源的信息。
|
|
kMultiStatus = 207,
|
|
// 在 <dav:propstat> 响应元素内部使用,以避免重复枚举同一集合的多个绑定的内部成员。
|
|
kAlreadyReported = 208,
|
|
// 服务器已完成了对资源的 GET 请求,并且响应是对当前实例应用了一个或多个实例操作后的结果表示。
|
|
kIMUsed = 226,
|
|
|
|
// 在代理驱动(agent-driven)的内容协商中,请求有多个可能的响应,用户代理或用户应选择其中之一。
|
|
kMultipleChoices = 300,
|
|
// 请求资源的 URL 已永久更改。新 URL 在响应中给出。
|
|
kMovedPermanently = 301,
|
|
// 此响应代码意味着请求资源的 URI 已暂时更改。未来可能还会对 URI 进行进一步更改,因此客户端在未来的请求中应使用相同的 URI。
|
|
kFound = 302,
|
|
// 服务器发送此响应以指示客户端使用 GET 请求在另一个 URI 获取请求的资源。
|
|
kSeeOther = 303,
|
|
// 用于缓存目的。它告知客户端响应未被修改,因此客户端可以继续使用相同的缓存响应版本。
|
|
kNotModified = 304,
|
|
// 在 HTTP 规范的前一版本中定义,表示请求的响应必须通过代理访问。由于涉及代理带内配置的安全问题,此状态码已被弃用。
|
|
kUseProxy = 305,
|
|
// 此响应代码不再使用,但被保留。它曾在 HTTP/1.1 规范的先前版本中使用。
|
|
k__Unused = 306,
|
|
// 服务器发送此响应以指示客户端使用与先前请求相同的方法在另一个 URI 获取请求的资源。其语义与 302 Found 响应代码相同,但用户代理不得更改使用的 HTTP 方法:如果在第一个请求中使用了 POST,则在重定向请求中也必须使用 POST。
|
|
kTemporaryRedirect = 307,
|
|
// 表示资源现在永久位于另一个 URI,由 Location 响应头指定。其语义与 301 Moved Permanently HTTP 响应代码相同,但用户代理不得更改使用的 HTTP 方法:如果在第一个请求中使用了 POST,则在第二个请求中也必须使用 POST。
|
|
kPermanentRedirect = 308,
|
|
|
|
// 由于被认为是客户端错误的原因(例如,格式错误的请求语法、无效的请求消息结构或欺骗性的请求路由),服务器无法或不会处理该请求。
|
|
kBadRequest = 400,
|
|
// 尽管 HTTP 标准指定为 "unauthorized",但从语义上讲,此响应的意思是 "unauthenticated"。即,客户端必须进行身份验证才能获得请求的响应。
|
|
kUnauthorized = 401,
|
|
// 此代码最初用于数字支付系统,但此状态码很少使用,且不存在标准约定。
|
|
kPaymentRequired = 402,
|
|
// 客户端没有访问内容的权利;也就是说,它是未授权的,因此服务器拒绝提供请求的资源。与 401 Unauthorized 不同,服务器知道客户端的身份。
|
|
kForbidden = 403,
|
|
// 服务器找不到请求的资源。
|
|
kNotFound = 404,
|
|
// 服务器知道请求方法,但目标资源不支持该方法。
|
|
kMethodNotAllowed = 405,
|
|
// 当 Web 服务器执行服务器驱动的内容协商后,找不到任何符合用户代理给定条件的内容时,会发送此响应。
|
|
kNotAcceptable = 406,
|
|
// 类似于 401 Unauthorized,但需要通过代理进行身份验证。
|
|
kProxyAuthenticationRequired = 407,
|
|
// 某些服务器会在空闲连接上发送此响应,即使客户端之前没有任何请求。这意味着服务器希望关闭此未使用的连接。
|
|
kRequestTimeout = 408,
|
|
// 当请求与服务器的当前状态冲突时,发送此响应。在
|
|
kConflict = 409,
|
|
// 当请求的内容已从服务器永久删除,且没有转发地址时,发送此响应。
|
|
kGone = 410,
|
|
// 服务器拒绝了请求,因为未定义 Content-Length 标头字段,而服务器需要它。
|
|
kLengthRequired = 411,
|
|
// 在条件请求中,客户端在其标头中指明了服务器不满足的前提条件。
|
|
kPreconditionFailed = 412,
|
|
// 请求体大于服务器定义的限制。
|
|
kContentTooLarge = 413,
|
|
// 客户端请求的 URI 长度超过了服务器愿意解释的长度。
|
|
kURITooLong = 414,
|
|
// 服务器不支持请求数据的媒体格式,因此服务器拒绝该请求。
|
|
kUnsupportedMediaType = 415,
|
|
// 无法满足请求中 Range 标头字段指定的范围。可能范围超出了目标资源数据的大小。
|
|
kRangeNotSatisfiable = 416,
|
|
// 此响应代码表示服务器无法满足 Expect 请求标头字段指示的期望。
|
|
kExpectationFailed = 417,
|
|
// 服务器拒绝尝试用茶壶煮咖啡。
|
|
kIamATeapot = 418,
|
|
// 请求被发送到了一个无法产生响应的服务器。
|
|
kMisdirectedRequest = 421,
|
|
// 请求格式正确,但由于语义错误而无法被遵循。
|
|
kUnprocessableContent = 422,
|
|
// 正在访问的资源已被锁定。
|
|
kLocked = 423,
|
|
// 由于先前的请求失败,导致当前请求失败。
|
|
kFailedDependency = 424,
|
|
// 表示服务器不愿意冒险处理一个可能被重放的请求。
|
|
kTooEarly = 425,
|
|
// 服务器拒绝使用当前协议执行请求,但可能在客户端升级到其他协议后愿意执行。服务器在 426 响应中发送 Upgrade 标头以指示所需的协议。
|
|
kUpgradeRequired = 426,
|
|
// 原始服务器要求请求是有条件的。此响应旨在防止"丢失更新"问题,即客户端 GET 资源状态,修改后 PUT 回服务器,而同时第三方已修改了服务器上的状态,导致冲突。
|
|
kPreconditionRequired = 428,
|
|
// 用户在给定的时间内发送了太多请求(速率限制)。
|
|
kTooManyRequests = 429,
|
|
// 服务器因请求头字段太大而不愿意处理该请求。
|
|
kRequestHeaderFieldsTooLarge = 431,
|
|
// 用户代理请求了一个无法合法提供的资源,例如被政府审查的网页。
|
|
kUnavailableForLegalReasons = 451,
|
|
|
|
// 服务器遇到了不知道如何处理的情况。此错误是通用性的,表示服务器找不到更合适的 5XX 状态码来响应。
|
|
kInternalServerError = 500,
|
|
// 服务器不支持请求方法,无法处理。
|
|
kNotImplemented = 501,
|
|
// 此错误响应意味着服务器作为网关或代理时,收到了一个无效的响应。
|
|
kBadGateway = 502,
|
|
// 服务器尚未准备好处理请求。
|
|
kServiceUnavailable = 503,
|
|
// 当服务器作为网关或代理,无法及时获得响应时,会给出此错误响应。
|
|
kGatewayTimeout = 504,
|
|
// 服务器不支持请求中使用的 HTTP 版本。
|
|
kHTTPVersionNotSupported = 505,
|
|
// 服务器存在内部配置错误:在内容协商过程中,被选中的变体被配置为自身参与内容协商,这导致在创建响应时出现循环引用。
|
|
kVariantAlsoNegotiates = 506,
|
|
// 由于服务器无法存储成功完成请求所需的表示,因此无法对资源执行该方法。
|
|
kInsufficientStorage = 507,
|
|
// 服务器在处理请求时检测到无限循环。
|
|
kLoopDetected = 508,
|
|
// 客户端请求声明了一个应使用 HTTP 扩展(RFC 2774)来处理请求,但该扩展不受支持。
|
|
kNotExtended = 510,
|
|
// 表示客户端需要进行身份验证才能获得网络访问权限。
|
|
kNetworkAuthenticationRequired = 511,
|
|
|
|
//Not Standard Code By UnknownObject
|
|
|
|
// 请求载体的格式错误,如:无法解析的JSON等。
|
|
k_uRequestFormatError = 489,
|
|
// 请求无效。可能是由于未正确携带数据等必要信息。
|
|
k_uRequestInvalid = 490,
|
|
// 请求URL超范围。此响应表示请求的URL是错误的。
|
|
k_uURLOutOfRange = 492,
|
|
// 无效的请求主机。指示请求时使用了错误的域名/IP。
|
|
k_uInvalidRequestHost = 493,
|
|
// IP地址被封禁。
|
|
k_uIPBlocked = 494,
|
|
// 非法上传请求。指示本次上传请求不符合服务器规定。
|
|
k_uIllegalUpload = 495,
|
|
// 文件格式错误。指示上传的文件格式不符合服务器规定。
|
|
k_uFileFormatError = 496,
|
|
// 无效文件。处理请求所需的文件已过期/无法访问。
|
|
k_uInvalidFile = 497,
|
|
// 上传的文件过大。非文件上传时应使用 413 Content Too Large。
|
|
k_uFileTooLarge = 498,
|
|
// 每秒请求数过多。仅在一些特殊API中使用,常规情况需使用 429 Too Many Requests。
|
|
k_uRPSLimited = 499,
|
|
|
|
// 子过程失败。服务器在处理请求的某个步骤中遇到无法恢复的错误。
|
|
k_uSubProcessFalied = 533,
|
|
// 服务器检测到漏洞利用/可执行文件上传等网络攻击行为。
|
|
k_uServerHateYou = 540,
|
|
// 检测到拒绝服务漏洞攻击。
|
|
k_uDoSFound = 550,
|
|
// 检测到分布式拒绝服务漏洞攻击。
|
|
k_uDDoSFound = 551,
|
|
// 未知的服务器错误。当服务器无法定位错误来源时返回。否则应使用 500 Internal Server Error。
|
|
k_uUnknownServerError = 560
|
|
};
|
|
|
|
namespace headers {
|
|
|
|
// NOTE: Field names are case-insensitive.
|
|
// See https://stackoverflow.com/a/5259004 for more details.
|
|
|
|
const char* const kHost = "Host";
|
|
const char* const kDate = "Date";
|
|
const char* const kAuthorization = "Authorization";
|
|
const char* const kContentType = "Content-Type";
|
|
const char* const kContentLength = "Content-Length";
|
|
const char* const kContentEncoding = "Content-Encoding";
|
|
const char* const kContentMD5 = "Content-MD5";
|
|
const char* const kContentDisposition = "Content-Disposition";
|
|
const char* const kConnection = "Connection";
|
|
const char* const kTransferEncoding = "Transfer-Encoding";
|
|
const char* const kAccept = "Accept";
|
|
const char* const kAcceptEncoding = "Accept-Encoding";
|
|
const char* const kUserAgent = "User-Agent";
|
|
const char* const kServer = "Server";
|
|
|
|
} // namespace headers
|
|
|
|
namespace media_types {
|
|
|
|
// See the following link for the full list of media types:
|
|
// https://www.iana.org/assignments/media-types/media-types.xhtml
|
|
|
|
const char* const kApplicationJson = "application/json";
|
|
const char* const kApplicationSoapXml = "application/soap+xml";
|
|
const char* const kApplicationFormUrlEncoded =
|
|
"application/x-www-form-urlencoded";
|
|
const char* const kTextPlain = "text/plain";
|
|
const char* const kTextXml = "text/xml";
|
|
|
|
// Get media type from file extension.
|
|
std::string FromExtension(const std::string& ext);
|
|
|
|
} // namespace media_types
|
|
|
|
namespace charsets {
|
|
|
|
const char* const kUtf8 = "utf-8";
|
|
|
|
} // namespace charsets
|
|
|
|
enum class ContentEncoding {
|
|
kUnknown,
|
|
kGzip,
|
|
kDeflate,
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Error or exception (for client only).
|
|
class Error : public std::exception {
|
|
public:
|
|
enum Code {
|
|
kUnknownError = -1,
|
|
kOK = 0,
|
|
kStateError,
|
|
kSyntaxError,
|
|
kResolveError,
|
|
kConnectError,
|
|
kSocketReadError,
|
|
kSocketWriteError,
|
|
kParseError,
|
|
kFileError,
|
|
kDataError,
|
|
};
|
|
|
|
public:
|
|
explicit Error(Code code = kOK, string_view message = "")
|
|
: code_(code), message_(message) {
|
|
}
|
|
|
|
// Note that `noexcept` is required by GCC.
|
|
const char* what() const noexcept override {
|
|
return message_.c_str();
|
|
}
|
|
|
|
Code code() const {
|
|
return code_;
|
|
}
|
|
|
|
const std::string& message() const {
|
|
return message_;
|
|
}
|
|
|
|
void Set(Code code, string_view message) {
|
|
code_ = code;
|
|
message_ = ToString(message);
|
|
}
|
|
|
|
bool timeout() const {
|
|
return timeout_;
|
|
}
|
|
|
|
void set_timeout(bool timeout) {
|
|
timeout_ = timeout;
|
|
}
|
|
|
|
operator bool() const {
|
|
return code_ != kOK;
|
|
}
|
|
|
|
private:
|
|
Code code_;
|
|
std::string message_;
|
|
bool timeout_ = false;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const Error& error);
|
|
|
|
} // namespace webcc
|
|
|
|
#endif // WEBCC_GLOBALS_H_
|