28 Star 119 Fork 47

linuxmail/lib-zc

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
httpd.cpp 14.49 KB
一键复制 编辑 原始数据 按行查看 历史
linuxmail 提交于 2026-03-04 20:11 +08:00 . 语法检查
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
/*
* ================================
* eli960@qq.com
* http://linuxmail.cn/
* 2017-07-22
* ================================
*/
#include "zcc/zcc_http.h"
zcc_namespace_begin;
httpd::response_options httpd::default_response_options;
httpd::httpd(stream &fp)
{
fp_ = &fp;
}
httpd::~httpd()
{
loop_clear();
if (fp_)
{
delete fp_;
}
}
void httpd::loop_clear()
{
url_.reset();
method_.clear();
version_.clear();
uri_.clear();
host_.clear();
request_content_length_ = -1;
request_post_vars_.clear();
request_headers_.clear();
request_cookies_.clear();
request_uploaded_files_.clear();
if (post_data_parser_)
{
delete post_data_parser_;
post_data_parser_ = nullptr;
}
if (!post_data_pathname_.empty())
{
unlink(post_data_pathname_);
}
post_data_pathname_.clear();
version_code_ = -1;
request_gzip_dealed_ = false;
request_gzip_ = false;
request_deflate_dealed_ = false;
request_deflate_ = false;
request_cookies_dealed_ = false;
request_url_dealed_ = false;
request_keep_alive_ = false;
first_request_ = false;
}
void httpd::request_read_first_header_line()
{
if (exception_)
{
return;
}
std::string linebuf;
const char *p, *ps;
int ret;
bool first = first_request_;
/* read first header line */
if (first)
{
first_request_ = false;
ret = fp_->timed_read_wait(keep_alive_timeout_);
if (ret < 1)
{
exception_ = true;
return;
}
}
ret = (int)fp_->gets(linebuf, header_line_max_size_);
if (ret < 0)
{
exception_ = true;
log_error("exception: read banner line");
return;
}
if (ret == 0)
{
if (first)
{
exception_ = true;
log_error("exception: read banner line");
return;
}
stop_ = true;
return;
}
trim_line_end_rn(linebuf);
ps = linebuf.c_str();
p = std::strchr(ps, ' ');
if (!p)
{
exception_ = true;
log_error("exception: banner no blank");
return;
}
method_.append(ps, p - ps);
toupper(method_);
ps = p + 1;
p = std::strchr(ps, ' ');
if (p)
{
version_ = p + 1;
toupper(version_);
uri_.append(ps, p - ps);
}
else
{
version_ = "HTTP/1.0";
uri_ = "/";
}
}
void httpd::request_read_other_header_line(bool &header_over)
{
header_over = false;
if (exception_)
{
return;
}
std::string linebuf, key;
const char *p, *ps;
if (fp_->gets(linebuf, header_line_max_size_) < 1)
{
exception_ = true;
log_error("exception: read header line");
return;
}
if (linebuf.back() != '\n')
{
exception_ = true;
log_error("exception: header line too long");
return;
}
trim_line_end_rn(linebuf);
if (linebuf.empty())
{
header_over = true;
return;
}
ps = linebuf.c_str();
p = std::strchr(ps, ':');
if (!p)
{
return;
}
key.append(ps, p - ps);
tolower(key);
ps = p + 1;
while (*ps == ' ')
{
ps++;
}
request_headers_[key] = ps;
p = key.c_str();
if (p[0] == 'h')
{
if (key == "host")
{
p = std::strchr(ps, ':');
if (p)
{
host_.clear();
host_.append(ps, p - ps);
port_ = std::atoi(p + 1);
}
else
{
host_ = ps;
port_ = -1;
}
tolower(host_);
}
}
else if (p[0] == 'c')
{
if (key == "content-length")
{
request_content_length_ = std::atol(ps);
}
else if (key == "connection")
{
if (strcasestr(ps, "keep-alive"))
{
request_keep_alive_ = true;
}
}
}
}
void httpd::request_read_header()
{
bool header_over = false;
request_read_first_header_line();
if (exception_)
{
return;
}
while (!header_over)
{
request_read_other_header_line(header_over);
if (exception_)
{
return;
}
}
}
bool httpd::request_read_body_prepare()
{
if (exception_ || stop_ || fp_->is_exception())
{
return false;
}
if (request_content_length_ < 1)
{
return false;
}
if ((max_length_for_post_ > 0) && (request_content_length_ > max_length_for_post_))
{
exception_ = true;
log_error("exception: Content-Length>max_length_for_post");
return false;
}
return true;
}
void httpd::request_read_body_x_www_form_urlencoded()
{
std::string linebuf;
if (fp_->readn(linebuf, (int)request_content_length_) < request_content_length_)
{
exception_ = true;
log_error("exception: read");
return;
}
request_post_vars_ = http_url::parse_query(linebuf);
}
void httpd::request_read_body_disabled_form_data()
{
// FIXME
char header[] = " 501 Not implemented\r\nServer: LIBZC HTTPD\r\nConnection: close\r\n"
"Content-Length: ";
char body[] = "501 Not implemented, unsupport multipart/form-data";
fp_->append(version_);
fp_->write(header, sizeof(header) - 1);
fp_->append(std::to_string(sizeof(body) - 1)).append("\r\n\r\n");
fp_->write(body, sizeof(body) - 1);
response_flush();
}
std::string httpd::request_read_body_save_tmpfile(const char *content_type)
{
std::string linebuf;
std::string r;
if (tmp_path_.empty())
{
#ifdef _WIN64
r = "%userprofile%/AppData/Local/Temp/";
#else // __linux__
r = "/tmp/";
#endif // __linux__
}
else
{
r = tmp_path_;
}
if (r.back() != '/')
{
r.push_back('/');
}
r.append(build_unique_id());
const char *data_pathname = r.c_str();
FILE *tmp_fp = fopen(data_pathname, "wb+");
if (!tmp_fp)
{
exception_ = true;
log_error("exception: open temp file");
return "";
}
fprintf(tmp_fp, "Content-Type: %s\r\n\r\n", content_type);
int64_t left = request_content_length_;
while (left > 0)
{
int64_t rlen = left;
if (rlen > 4096)
{
rlen = 4096;
}
rlen = fp_->readn(linebuf, (int)rlen);
if (rlen < 1)
{
exception_ = true;
log_error("exception: read post data body");
break;
}
fwrite(linebuf.c_str(), 1, linebuf.size(), tmp_fp);
left -= rlen;
if (ferror(tmp_fp))
{
exception_ = true;
log_error("exception: write post data body");
break;
}
}
if (!exception_)
{
if (fflush(tmp_fp) < 0)
{
exception_ = true;
log_error("exception: write post data body");
}
}
fclose(tmp_fp);
if (exception_)
{
unlink(data_pathname);
return "";
}
return r;
}
void httpd::request_read_body_uploaded_dump_file(mail_parser::mime_node *mime, const std::string &name, const std::string &pathname)
{
int64_t wlen = 0, raw_len = 0;
const std::string &encoding = mime->get_encoding();
raw_len = mime->get_body_size();
if (encoding.empty())
{
wlen = raw_len;
}
else
{
wlen = -1;
}
httpd_uploaded_file fo(this);
fo.name_ = name;
fo.pathname_ = pathname;
fo.size_ = wlen;
fo.offset_ = mime->get_body_offset();
fo.raw_size_ = raw_len;
if (encoding == "base64")
{
fo.encoding_ = var_encoding_type_base64;
}
else if (encoding == "quoted-printable")
{
fo.encoding_ = var_encoding_type_qp;
}
else
{
fo.encoding_ = var_encoding_type_none;
}
request_uploaded_files_.push_back(fo);
}
void httpd::request_read_body_form_data_one_mime(mail_parser::mime_node *mime)
{
const std::string &disposition = mime->get_disposition();
if (disposition != "form-data")
{
return;
}
std::string content_disposition = mime->get_header_line_value("content-disposition");
std::string tmp_bf;
dict params;
mail_parser::header_line_get_params(content_disposition, tmp_bf, params);
std::string name = get_cstring(params, "name", "");
const std::string &ctype = mime->get_content_type();
if (std::strncmp(ctype.c_str(), "multipart/", 10))
{
std::string pathname = get_cstring(params, "filename", "");
if (pathname.empty())
{
if (!name.empty())
{
request_post_vars_[name] = mime->get_decoded_content();
}
}
else
{
request_read_body_uploaded_dump_file(mime, name, pathname);
}
}
else
{
for (auto child = mime->get_child(); !child; child = child->get_next())
{
const std::string &ctype = child->get_content_type();
if (!strncmp(ctype.c_str(), "multipart/", 10))
{
continue;
}
std::string content_disposition = mime->get_header_line_value("content-disposition");
std::string tmp_bf;
dict params;
mail_parser::header_line_get_params(content_disposition, tmp_bf, params);
std::string name = get_cstring(params, "name", "");
std::string pathname = get_cstring(params, "filename", "");
request_read_body_uploaded_dump_file(mime, name, pathname);
if (exception_)
{
break;
}
}
}
}
void httpd::request_read_body_form_data(const char *content_type_raw)
{
if (!enable_form_data_)
{
request_read_body_disabled_form_data();
return;
}
std::string data_pathname = request_read_body_save_tmpfile(content_type_raw);
if (exception_)
{
return;
}
if (!data_pathname.empty())
{
post_data_parser_ = mail_parser::create_from_file(data_pathname);
}
auto &mvec = post_data_parser_->get_all_mimes();
for (auto it = mvec.begin(); it != mvec.end(); it++)
{
if (exception_)
{
break;
}
request_read_body_form_data_one_mime(*it);
}
post_data_pathname_ = data_pathname;
}
void httpd::request_read_body()
{
if (!request_read_body_prepare())
{
return;
}
const char *content_type = get_cstring(request_headers_, "content-type");
if (empty(content_type))
{
return;
}
if (ZCC_STR_N_CASE_EQ(content_type, "application/x-www-form-urlencoded", 33))
{
request_read_body_x_www_form_urlencoded();
return;
}
if (!ZCC_STR_N_CASE_EQ(content_type, "multipart/form-data", 19))
{
return;
}
request_read_body_form_data(content_type);
}
bool httpd::request_read_all()
{
loop_clear();
request_read_header();
request_read_body();
if (exception_ || stop_)
{
return false;
}
return true;
}
bool httpd::maybe_continue()
{
if (exception_ || stop_ || (!request_keep_alive_))
{
return false;
}
return true;
}
int httpd::request_get_version_code_deal()
{
if (version_code_ == -1)
{
if (std::strstr(version_.c_str(), "1.0"))
{
version_code_ = 0;
}
else
{
version_code_ = 1;
}
}
return version_code_;
}
bool httpd::request_is_gzip_deal()
{
if (!request_gzip_dealed_)
{
request_gzip_dealed_ = true;
request_gzip_ = (strcasestr(get_cstring(request_headers_, "accept-encoding", ""), "gzip") ? true : false);
}
return request_gzip_;
}
bool httpd::request_is_deflate_deal()
{
if (!request_deflate_dealed_)
{
request_deflate_dealed_ = true;
request_deflate_ = (strcasestr(get_cstring(request_headers_, "accept-encoding", ""), "deflate") ? true : false);
}
return request_deflate_;
}
const dict &httpd::request_cookies_deal()
{
if (!request_cookies_dealed_)
{
request_cookies_dealed_ = true;
const char *ps = get_cstring(request_headers_, "cookie", "");
request_cookies_ = http_cookie_parse(ps);
}
return request_cookies_;
}
const http_url &httpd::request_url_deal()
{
if (!request_url_dealed_)
{
request_url_dealed_ = true;
url_ = http_url(uri_);
}
return url_;
}
void httpd::response_header_initialization(const char *version, const char *status)
{
if (!empty(version))
{
fp_->append(version);
}
else
{
fp_->append(version_);
}
fp_->append(" ").append(status).append("\r\n");
}
void httpd::response_header(const char *name, const char *value)
{
fp_->append(name).append(": ").append(value).append("\r\n");
}
void httpd::response_header(const char *name, int64_t value)
{
std::string r = std::to_string(value);
response_header(name, r.c_str());
}
void httpd::response_header_date(const char *name, int64_t value)
{
response_header(name, rfc7231_time(value));
}
void httpd::response_header_content_type(const char *value, const char *charset)
{
std::string v;
v.append(value);
if (empty(charset))
{
charset = "UTF-8";
}
v.append("; charset=").append(charset);
response_header("Content-Type", v);
}
void httpd::response_header_set_cookie(const char *name, const char *value, int64_t expires, const char *path, const char *domain, bool secure, bool httponly)
{
std::string r = http_cookie_build_item(name, value, expires, path, domain, secure, httponly);
response_header("Set-Cookie", r);
}
void httpd::response_header_over()
{
fp_->append("\r\n");
}
httpd &httpd::response_write(const void *data, int len)
{
fp_->write(data, len);
return *this;
}
bool httpd::response_printf_1024(const char *format, ...)
{
va_list ap;
char buf[1024 + 1];
int len;
if (format == 0)
{
format = "";
}
va_start(ap, format);
len = std::vsnprintf(buf, 1024, format, ap);
len = ((len < 1024) ? len : (1024 - 1));
va_end(ap);
return fp_->write(buf, len) > 0;
}
bool httpd::response_flush()
{
if (stop_ || exception_)
{
return false;
}
if (fp_->flush() < 0)
{
exception_ = true;
log_error("exception: write");
return false;
}
return true;
}
stream *httpd::detach_stream()
{
stop_ = true;
auto fp = fp_;
fp_ = nullptr;
return fp;
}
zcc_namespace_end;
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/linuxmail/lib-zc.git
git@gitee.com:linuxmail/lib-zc.git
linuxmail
lib-zc
lib-zc
master

搜索帮助