我正在编写自己的 http 服务器。我需要检查给定列表中的每个标题(如果给出的值无效)。我也不能使用任何第三方库。
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
是的,我正在寻找解决方案,我已经看到了以下问题和其他问题:
Parse HTTP headers in C++
How to correctly parse incoming HTTP requests
How to parse HTTP response using c++
我还试图在这里找到在 libcurl 中实现它的源文件\示例,但我不能。
https://curl.se/libcurl/c/example.html
根据这篇文章我自己的工作:
https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header
void HttpServer::ParseQuery(HttpServer::Connection socket_) noexcept{
std::vector<std::string> elems_ = split(socket_.data,"\r\n");
std::vector<std::string> request_line_ = split( elems_[0]+=" " ," ");
std::map<std::string,HttpServer::HeaderValue> header_fields_;
//if( (request_line_.size()< 3) || !strcmp(request_line_[0].c_str(),"GET") || !strcmp(request_line_[2].c_str(),"HTTP/1.1")) return;
std::size_t index = 1,pos;
while(index < elems_.size()){
if ((pos = elems_[index].find(":")) == std::string::npos){
}
else{
std::string first = elems_[index].substr(0,pos), second = elems_[index].substr(pos,elems_[index].length()-pos);
std::transform(first.begin(), first.end(), first.begin(),
[](unsigned char c){
return std::tolower(c);
});
if( second[0] == ' ')
second.erase(0,1);
if( second[second.length()-1] == ' ')
second.erase(second.length()-1,1);
header_fields_[first] = {second , 1 };
}
++index;
}
for( auto &a : header_fields_){
//For any header: the value’s length can't be greater than 128.
//For Accept-Language and Content-Language: can only have values consisting of 0-9, A-Z, a-z, space or *,-.;=.
if(!strcmp(a.first.c_str(),"accept-language")|| !strcmp(a.first.c_str(),"content-language"))
{
if( (a.second.field_name.length() > 128) || ((pos = a.second.field_name.find_first_not_of("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM*,-.;=") )!= std::string::npos))
a.second.correct = 0;
}
//For Accept and Content-Type: can't contain a CORS-unsafe request header byte: 0x00-0x1F (except for 0x09 (HT), which is allowed), "():<>?@[\]{}, and 0x7F (DEL).
else if ((a.second.field_name.length() > 128) || (!strcmp(a.first.c_str(),"accept")|| !strcmp(a.first.c_str(),"content-type"))){
if( (pos = a.second.field_name.find_first_of("\x01\x02\x03\x04\x05\x06\x07\x08\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\0x7f():<>?@[\\]{}") )!= std::string::npos)
a.second.correct = 0;
}
}
}
数据类型在哪里:
struct Connection
{
Connection(const int &socket,const std::chrono::time_point<std::chrono::system_clock> &tp);
int socket;
std::chrono::time_point<std::chrono::system_clock> tp;
std::string data;
std::string respons;
};
struct HeaderValue
{
std::string field_name;
bool correct = 1;
};
上面的函数将通过网络接收到的请求分成行,请求的第一行再分成 3 部分并将其存储在向量中。接下来,我删除该值前后的 OWS(如果有)。地图形成后,我检查,例如,这两个标题。 我的代码测试如下所示:
wget http://localhost:8080/server.cpp
--2021-02-22 18:07:33-- http://localhost:8080/server.cpp
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:8080... connected.
HTTP request sent, awaiting response... 200 No headers, assuming HTTP/0.9
Length: unspecified
Saving to: ‘server.cpp.6’
server.cpp.6 [ <=> ] 16,39K --.-KB/s
我的编译器:
g++ --version
g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
我的问题是,对于每个可能的标题,是否有比大量 else-if 更好的方法呢?
答案 0 :(得分:1)
我的问题是,对于每个可能的标题,是否有比大量 else-if 更好的方法呢?
对于任何其他硬编码大量魔法值的情况,答案完全相同:停止它。
将所有硬编码的魔法值组合在一个地方,这样至少它们不会污染您的逻辑:构建一个标头名称字符串到验证器的映射。如果您需要更大的灵活性,验证器可以是正则表达式或实际的函子(例如,std::function<bool(std::string)>
)。
你的代码变得像
for (auto &a : header_fields_) {
auto v = header_validators_.find(a.first);
if (v == header_validators_.end()) {
// error, unknown header
} else {
if (!v->second(a.first, a.second)) {
// error, invalid header
}
}
}
从文件中加载魔法值而不是硬编码它们通常更好,但是实现成本不一定合理,除非能够在不重新编译的情况下编辑该文件是一个实际的好处.