mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-07-28 13:04:07 +08:00
新增支持http文件上传功能
This commit is contained in:
@@ -213,6 +213,27 @@ void api_regist(const string &api_path, const function<void(API_ARGS_STRING_ASYN
|
|||||||
s_map_api.emplace(api_path, toApi(func));
|
s_map_api.emplace(api_path, toApi(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// 文件上传处理器注册机制
|
||||||
|
// File upload handler registration: intercepts PUT/POST body via kBroadcastBeforeHttpRequest
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Handler signature: same as API_ARGS_MAP + HttpBody::Ptr &body, so CHECK_SECRET() etc. work inside.
|
||||||
|
using HttpBodyHandler = std::function<void(UPLOAD_ARGS_MAP)>;
|
||||||
|
static std::map<std::string, HttpBodyHandler, StrCaseCompare> s_map_file_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a handler for a URL path. When a PUT/POST request arrives at that path,
|
||||||
|
* the handler is invoked to create an HttpBody (e.g. HttpFileStorage) that receives
|
||||||
|
* the request body stream directly. CHECK_SECRET() can be used inside the handler.
|
||||||
|
*
|
||||||
|
* @param url_path the HTTP URL path to match
|
||||||
|
* @param handler callback that sets body; may throw AuthException to reject
|
||||||
|
*/
|
||||||
|
void upload_regist(const std::string &url_path, HttpBodyHandler handler) {
|
||||||
|
s_map_file_handler.emplace(url_path, std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
// 获取HTTP请求中url参数、content参数 [AUTO-TRANSLATED:d161a1e1]
|
// 获取HTTP请求中url参数、content参数 [AUTO-TRANSLATED:d161a1e1]
|
||||||
// Get URL parameters and content parameters from the HTTP request
|
// Get URL parameters and content parameters from the HTTP request
|
||||||
ApiArgsType getAllArgs(const Parser &parser) {
|
ApiArgsType getAllArgs(const Parser &parser) {
|
||||||
@@ -321,6 +342,19 @@ static inline void addHttpListener(){
|
|||||||
}
|
}
|
||||||
},false);
|
},false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听 kBroadcastBeforeHttpRequest:对已注册的 URL 路径设置 HttpBody 以接管 PUT/POST 请求体
|
||||||
|
// Listen for kBroadcastBeforeHttpRequest: set HttpBody for registered URL paths to take over PUT/POST body
|
||||||
|
NoticeCenter::Instance().addListener(&web_api_tag, Broadcast::kBroadcastBeforeHttpRequest, [](BroadcastBeforeHttpRequestArgs) {
|
||||||
|
auto it = s_map_file_handler.find(parser.url());
|
||||||
|
if (it == s_map_file_handler.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HttpSession::KeyValue headerOut;
|
||||||
|
Json::Value val;
|
||||||
|
auto args = getAllArgs(parser);
|
||||||
|
it->second(sender, headerOut, ArgsMap(parser, args), val, body);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拉流代理器列表 [AUTO-TRANSLATED:6dcfb11f]
|
// 拉流代理器列表 [AUTO-TRANSLATED:6dcfb11f]
|
||||||
@@ -771,6 +805,7 @@ void check_secret(toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &he
|
|||||||
if (api_secret != allArgs["secret"]) {
|
if (api_secret != allArgs["secret"]) {
|
||||||
throw AuthException("Incorrect secret");
|
throw AuthException("Incorrect secret");
|
||||||
}
|
}
|
||||||
|
val.removeMember("cookie");
|
||||||
return;
|
return;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
// 未提供secret或secret不匹配,这个异常隐藏
|
// 未提供secret或secret不匹配,这个异常隐藏
|
||||||
|
|||||||
@@ -192,6 +192,9 @@ using ArgsString = HttpAllArgs<std::string>;
|
|||||||
#define API_ARGS_STRING_ASYNC API_ARGS_STRING, const mediakit::HttpSession::HttpResponseInvoker &invoker
|
#define API_ARGS_STRING_ASYNC API_ARGS_STRING, const mediakit::HttpSession::HttpResponseInvoker &invoker
|
||||||
#define API_ARGS_VALUE sender, headerOut, allArgs, val
|
#define API_ARGS_VALUE sender, headerOut, allArgs, val
|
||||||
|
|
||||||
|
// upload_regist 参数宏,与 API_ARGS_MAP 相同 + HttpBody::Ptr &body,支持 CHECK_SECRET()
|
||||||
|
#define UPLOAD_ARGS_MAP toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const ArgsMap &allArgs, Json::Value &val, mediakit::HttpBody::Ptr &body
|
||||||
|
|
||||||
// 注册http请求参数是map<string, variant, StrCaseCompare>类型的http api [AUTO-TRANSLATED:8a273897]
|
// 注册http请求参数是map<string, variant, StrCaseCompare>类型的http api [AUTO-TRANSLATED:8a273897]
|
||||||
// Register http request parameters as map<string, variant, StrCaseCompare> type http api
|
// Register http request parameters as map<string, variant, StrCaseCompare> type http api
|
||||||
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP)> &func);
|
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP)> &func);
|
||||||
|
|||||||
@@ -130,12 +130,17 @@ const string &Parser::content() const {
|
|||||||
return _content;
|
return _content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HttpBody::Ptr &Parser::body() const {
|
||||||
|
return _body;
|
||||||
|
}
|
||||||
|
|
||||||
void Parser::clear() {
|
void Parser::clear() {
|
||||||
_method.clear();
|
_method.clear();
|
||||||
_url.clear();
|
_url.clear();
|
||||||
_params.clear();
|
_params.clear();
|
||||||
_protocol.clear();
|
_protocol.clear();
|
||||||
_content.clear();
|
_content.clear();
|
||||||
|
_body = nullptr;
|
||||||
_headers.clear();
|
_headers.clear();
|
||||||
_url_args.clear();
|
_url_args.clear();
|
||||||
}
|
}
|
||||||
@@ -152,6 +157,10 @@ void Parser::setContent(string content) {
|
|||||||
_content = std::move(content);
|
_content = std::move(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Parser::setBody(HttpBody::Ptr body) {
|
||||||
|
_body = std::move(body);
|
||||||
|
}
|
||||||
|
|
||||||
StrCaseMap &Parser::getHeader() const {
|
StrCaseMap &Parser::getHeader() const {
|
||||||
return _headers;
|
return _headers;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "Util/util.h"
|
#include "Util/util.h"
|
||||||
|
#include "Http/HttpBody.h"
|
||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
|
|
||||||
@@ -95,6 +96,7 @@ public:
|
|||||||
// 获取http body或sdp [AUTO-TRANSLATED:d6fd1803]
|
// 获取http body或sdp [AUTO-TRANSLATED:d6fd1803]
|
||||||
// Get http body or sdp
|
// Get http body or sdp
|
||||||
const std::string &content() const;
|
const std::string &content() const;
|
||||||
|
const HttpBody::Ptr &body() const;
|
||||||
|
|
||||||
// 清空,为了重用 [AUTO-TRANSLATED:cb7a16dd]
|
// 清空,为了重用 [AUTO-TRANSLATED:cb7a16dd]
|
||||||
// Clear, for reuse
|
// Clear, for reuse
|
||||||
@@ -111,6 +113,7 @@ public:
|
|||||||
// 重新设置content [AUTO-TRANSLATED:ac8fc8c0]
|
// 重新设置content [AUTO-TRANSLATED:ac8fc8c0]
|
||||||
// Reset content
|
// Reset content
|
||||||
void setContent(std::string content);
|
void setContent(std::string content);
|
||||||
|
void setBody(HttpBody::Ptr body);
|
||||||
|
|
||||||
// 获取header列表 [AUTO-TRANSLATED:90d90b03]
|
// 获取header列表 [AUTO-TRANSLATED:90d90b03]
|
||||||
// Get header list
|
// Get header list
|
||||||
@@ -131,6 +134,7 @@ private:
|
|||||||
std::string _url;
|
std::string _url;
|
||||||
std::string _protocol;
|
std::string _protocol;
|
||||||
std::string _content;
|
std::string _content;
|
||||||
|
HttpBody::Ptr _body;
|
||||||
std::string _params;
|
std::string _params;
|
||||||
mutable StrCaseMap _headers;
|
mutable StrCaseMap _headers;
|
||||||
mutable StrCaseMap _url_args;
|
mutable StrCaseMap _url_args;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ const string kBroadcastMediaChanged = "kBroadcastMediaChanged";
|
|||||||
const string kBroadcastRecordMP4 = "kBroadcastRecordMP4";
|
const string kBroadcastRecordMP4 = "kBroadcastRecordMP4";
|
||||||
const string kBroadcastRecordTs = "kBroadcastRecordTs";
|
const string kBroadcastRecordTs = "kBroadcastRecordTs";
|
||||||
const string kBroadcastHttpRequest = "kBroadcastHttpRequest";
|
const string kBroadcastHttpRequest = "kBroadcastHttpRequest";
|
||||||
|
const string kBroadcastBeforeHttpRequest = "kBroadcastBeforeHttpRequest";
|
||||||
const string kBroadcastHttpAccess = "kBroadcastHttpAccess";
|
const string kBroadcastHttpAccess = "kBroadcastHttpAccess";
|
||||||
const string kBroadcastOnGetRtspRealm = "kBroadcastOnGetRtspRealm";
|
const string kBroadcastOnGetRtspRealm = "kBroadcastOnGetRtspRealm";
|
||||||
const string kBroadcastOnRtspAuth = "kBroadcastOnRtspAuth";
|
const string kBroadcastOnRtspAuth = "kBroadcastOnRtspAuth";
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ extern const std::string kBroadcastRecordTs;
|
|||||||
extern const std::string kBroadcastHttpRequest;
|
extern const std::string kBroadcastHttpRequest;
|
||||||
#define BroadcastHttpRequestArgs const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, toolkit::SockInfo &sender
|
#define BroadcastHttpRequestArgs const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, toolkit::SockInfo &sender
|
||||||
|
|
||||||
|
// 收到http PUT/POST请求body前的广播,监听者可以设置body以接管请求体
|
||||||
|
// Broadcast before receiving http PUT/POST request body, listener can set body to take over the request body
|
||||||
|
extern const std::string kBroadcastBeforeHttpRequest;
|
||||||
|
#define BroadcastBeforeHttpRequestArgs const Parser &parser, HttpBody::Ptr &body, HttpSession &sender
|
||||||
|
|
||||||
// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 [AUTO-TRANSLATED:2de426b4]
|
// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 [AUTO-TRANSLATED:2de426b4]
|
||||||
// In the http file server, broadcast for receiving http access to files or directories. Control access permissions to the http directory through this event.
|
// In the http file server, broadcast for receiving http access to files or directories. Control access permissions to the http directory through this event.
|
||||||
extern const std::string kBroadcastHttpAccess;
|
extern const std::string kBroadcastHttpAccess;
|
||||||
|
|||||||
@@ -400,4 +400,50 @@ Buffer::Ptr HttpBufferBody::readData(size_t size) {
|
|||||||
return Buffer::Ptr(std::move(_buffer));
|
return Buffer::Ptr(std::move(_buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mediakit
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// HttpFileStorage — write-only body backed by a file on disk
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
HttpFileStorage::HttpFileStorage(std::string file_path) {
|
||||||
|
_fp = fopen(file_path.data(), "ab");
|
||||||
|
if (!_fp) {
|
||||||
|
throw std::runtime_error("HttpFileStorage: failed to open file: " + file_path + ", err: " + toolkit::get_uv_errmsg());
|
||||||
|
}
|
||||||
|
_path = std::move(file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpFileStorage::~HttpFileStorage() {
|
||||||
|
if (_fp) {
|
||||||
|
fflush(_fp);
|
||||||
|
fclose(_fp);
|
||||||
|
_fp = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpFileStorage::writeData(const char *data, size_t size) {
|
||||||
|
if (!_fp) {
|
||||||
|
throw std::runtime_error("HttpFileStorage: file not open");
|
||||||
|
}
|
||||||
|
if (size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto written = fwrite(data, 1, size, _fp);
|
||||||
|
if (written != size) {
|
||||||
|
throw std::runtime_error("HttpFileStorage: fwrite failed, expected " + std::to_string(size) + ", got " + std::to_string(written));
|
||||||
|
}
|
||||||
|
_written += written;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t HttpFileStorage::remainSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::Ptr HttpFileStorage::readData(size_t size) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &HttpFileStorage::filePath() const {
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace kule
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ namespace mediakit {
|
|||||||
class HttpBody : public std::enable_shared_from_this<HttpBody>{
|
class HttpBody : public std::enable_shared_from_this<HttpBody>{
|
||||||
public:
|
public:
|
||||||
using Ptr = std::shared_ptr<HttpBody>;
|
using Ptr = std::shared_ptr<HttpBody>;
|
||||||
virtual ~HttpBody() = default;
|
virtual ~HttpBody() {
|
||||||
|
if (_on_completed) {
|
||||||
|
_on_completed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 剩余数据大小,如果返回-1, 那么就不设置content-length
|
* 剩余数据大小,如果返回-1, 那么就不设置content-length
|
||||||
@@ -88,6 +92,19 @@ public:
|
|||||||
virtual int sendFile(int fd) {
|
virtual int sendFile(int fd) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入数据,默认抛出异常表示不支持写入
|
||||||
|
* Write data, default throws exception indicating write not supported
|
||||||
|
*/
|
||||||
|
virtual void writeData(const char *data, size_t size) {
|
||||||
|
throw std::runtime_error("HttpBody::writeData not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOnCompleted(std::function<void()> on_completed) { _on_completed = std::move(on_completed); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<void()> _on_completed;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,13 +144,21 @@ private:
|
|||||||
toolkit::Buffer::Ptr _buffer;
|
toolkit::Buffer::Ptr _buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class HttpFileBodyBase : public HttpBody {
|
||||||
|
public:
|
||||||
|
using Ptr = std::shared_ptr<HttpFileBodyBase>;
|
||||||
|
|
||||||
|
virtual void setRange(uint64_t offset, uint64_t max_size) = 0;
|
||||||
|
virtual ~HttpFileBodyBase() = default;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件类型的content
|
* 文件类型的content
|
||||||
* File type content
|
* File type content
|
||||||
|
|
||||||
* [AUTO-TRANSLATED:baf9c0f3]
|
* [AUTO-TRANSLATED:baf9c0f3]
|
||||||
*/
|
*/
|
||||||
class HttpFileBody : public HttpBody {
|
class HttpFileBody : public HttpFileBodyBase {
|
||||||
public:
|
public:
|
||||||
using Ptr = std::shared_ptr<HttpFileBody>;
|
using Ptr = std::shared_ptr<HttpFileBody>;
|
||||||
|
|
||||||
@@ -159,7 +184,7 @@ public:
|
|||||||
|
|
||||||
* [AUTO-TRANSLATED:30532a4e]
|
* [AUTO-TRANSLATED:30532a4e]
|
||||||
*/
|
*/
|
||||||
void setRange(uint64_t offset, uint64_t max_size);
|
void setRange(uint64_t offset, uint64_t max_size) override;
|
||||||
|
|
||||||
int64_t remainSize() override;
|
int64_t remainSize() override;
|
||||||
toolkit::Buffer::Ptr readData(size_t size) override;
|
toolkit::Buffer::Ptr readData(size_t size) override;
|
||||||
@@ -173,6 +198,32 @@ private:
|
|||||||
toolkit::ResourcePool<toolkit::BufferRaw> _pool;
|
toolkit::ResourcePool<toolkit::BufferRaw> _pool;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件写入类型的content,支持通过writeData向文件追加写入
|
||||||
|
* File storage content, supports appending data to a file via writeData
|
||||||
|
*/
|
||||||
|
class HttpFileStorage : public HttpBody {
|
||||||
|
public:
|
||||||
|
using Ptr = std::shared_ptr<HttpFileStorage>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param file_path 文件路径,文件以二进制追加模式打开
|
||||||
|
* @param file_path File path, opened in binary append mode
|
||||||
|
*/
|
||||||
|
HttpFileStorage(std::string file_path);
|
||||||
|
~HttpFileStorage() override;
|
||||||
|
|
||||||
|
void writeData(const char *data, size_t size) override;
|
||||||
|
int64_t remainSize() override;
|
||||||
|
toolkit::Buffer::Ptr readData(size_t size) override;
|
||||||
|
const std::string& filePath() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE *_fp = nullptr;
|
||||||
|
int64_t _written = 0;
|
||||||
|
std::string _path;
|
||||||
|
};
|
||||||
|
|
||||||
class HttpArgs;
|
class HttpArgs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,7 +263,7 @@ private:
|
|||||||
int64_t _totalSize;
|
int64_t _totalSize;
|
||||||
std::string _bodyPrefix;
|
std::string _bodyPrefix;
|
||||||
std::string _bodySuffix;
|
std::string _bodySuffix;
|
||||||
HttpFileBody::Ptr _fileBody;
|
HttpFileBodyBase::Ptr _fileBody;
|
||||||
};
|
};
|
||||||
|
|
||||||
}//namespace mediakit
|
}//namespace mediakit
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ void HttpRequestSplitter::input(const char *data,size_t len) {
|
|||||||
// _content_len < 0; Data is processed according to variable length content
|
// _content_len < 0; Data is processed according to variable length content
|
||||||
onRecvContent(ptr,_remain_data_size);//消费掉所有剩余数据
|
onRecvContent(ptr,_remain_data_size);//消费掉所有剩余数据
|
||||||
_remain_data.clear();
|
_remain_data.clear();
|
||||||
|
_remain_data_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequestSplitter::setContentLen(ssize_t content_len) {
|
void HttpRequestSplitter::setContentLen(ssize_t content_len) {
|
||||||
|
|||||||
@@ -109,19 +109,28 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) {
|
|||||||
return _on_recv_body ? -1 : 0;
|
return _on_recv_body ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content_len > _max_req_size) {
|
HttpBody::Ptr body;
|
||||||
|
if (_parser.method() == "PUT" || _parser.method() == "POST") {
|
||||||
|
NOTICE_EMIT(BroadcastBeforeHttpRequestArgs, Broadcast::kBroadcastBeforeHttpRequest, _parser, body, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content_len > _max_req_size || body) {
|
||||||
// // 不定长body或超大body //// [AUTO-TRANSLATED:8d66ee77]
|
// // 不定长body或超大body //// [AUTO-TRANSLATED:8d66ee77]
|
||||||
// // Indefinite length body or oversized body ////
|
// // Indefinite length body or oversized body ////
|
||||||
if (content_len != SIZE_MAX) {
|
if (content_len != SIZE_MAX && !body) {
|
||||||
WarnL << "Http body size is too huge: " << content_len << " > " << _max_req_size
|
WarnL << "Http body size is too huge: " << content_len << " > " << _max_req_size
|
||||||
<< ", please set " << Http::kMaxReqSize << " in config.ini file.";
|
<< ", please set " << Http::kMaxReqSize << " in config.ini file.";
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t received = 0;
|
size_t received = 0;
|
||||||
auto parser = std::move(_parser);
|
_on_recv_body = [this, received, content_len, body, it](const char *data, size_t len) mutable {
|
||||||
_on_recv_body = [this, parser, received, content_len](const char *data, size_t len) mutable {
|
|
||||||
received += len;
|
received += len;
|
||||||
onRecvUnlimitedContent(parser, data, len, content_len, received);
|
if (body) {
|
||||||
|
body->writeData(data, len);
|
||||||
|
} else {
|
||||||
|
onRecvUnlimitedContent(_parser, data, len, content_len, received);
|
||||||
|
}
|
||||||
|
|
||||||
if (received < content_len) {
|
if (received < content_len) {
|
||||||
// 还没收满 [AUTO-TRANSLATED:cecc867e]
|
// 还没收满 [AUTO-TRANSLATED:cecc867e]
|
||||||
// Not yet received
|
// Not yet received
|
||||||
@@ -131,6 +140,13 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) {
|
|||||||
// 收满了 [AUTO-TRANSLATED:0c9cebd7]
|
// 收满了 [AUTO-TRANSLATED:0c9cebd7]
|
||||||
// Received full
|
// Received full
|
||||||
setContentLen(0);
|
setContentLen(0);
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
_parser.setBody(std::move(body));
|
||||||
|
(this->*(it->second))();
|
||||||
|
}
|
||||||
|
_parser.clear();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
// 声明后续都是body;Http body在本对象缓冲,不通过HttpRequestSplitter保存 [AUTO-TRANSLATED:0012b6c1]
|
// 声明后续都是body;Http body在本对象缓冲,不通过HttpRequestSplitter保存 [AUTO-TRANSLATED:0012b6c1]
|
||||||
|
|||||||
Reference in New Issue
Block a user