From 63ca646d71167b2e659415e744740d4c0d4dc9f6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 13 Nov 2018 23:59:06 +0800 Subject: [PATCH] =?UTF-8?q?http=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=94=AF?= =?UTF-8?q?=E6=8C=81Transfer-Encoding:=20chunked?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpChunkedSplitter.cpp | 28 +++++++++++++++ src/Http/HttpChunkedSplitter.h | 60 ++++++++++++++++++++++++++++++++ src/Http/HttpClient.cpp | 20 ++++++++++- src/Http/HttpClient.h | 2 ++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/Http/HttpChunkedSplitter.cpp create mode 100644 src/Http/HttpChunkedSplitter.h diff --git a/src/Http/HttpChunkedSplitter.cpp b/src/Http/HttpChunkedSplitter.cpp new file mode 100644 index 00000000..60b01e67 --- /dev/null +++ b/src/Http/HttpChunkedSplitter.cpp @@ -0,0 +1,28 @@ +// +// Created by xzl on 2018/11/13. +// + +#include "HttpChunkedSplitter.h" + +namespace mediakit{ + +const char *HttpChunkedSplitter::onSearchPacketTail(const char *data, int len) { + auto pos = strstr(data,"\r\n"); + if(!pos){ + return nullptr; + } + return pos + 2; +} + +void HttpChunkedSplitter::onRecvContent(const char *data, uint64_t len) { + onRecvChunk(data,len - 2); +} + +int64_t HttpChunkedSplitter::onRecvHeader(const char *data, uint64_t len) { + string str(data,len - 2); + int ret; + sscanf(str.data(),"%X",&ret); + return ret + 2; +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Http/HttpChunkedSplitter.h b/src/Http/HttpChunkedSplitter.h new file mode 100644 index 00000000..9cc47abc --- /dev/null +++ b/src/Http/HttpChunkedSplitter.h @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2016 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef ZLMEDIAKIT_HTTPCHUNKEDSPLITTER_H +#define ZLMEDIAKIT_HTTPCHUNKEDSPLITTER_H + +#include +#include "HttpRequestSplitter.h" + +namespace mediakit{ + +class HttpChunkedSplitter : public HttpRequestSplitter { +public: + /** + * len == 0时代表结束 + */ + typedef std::function onChunkData; + + HttpChunkedSplitter(const onChunkData &cb){ + _onChunkData = cb; + }; + ~HttpChunkedSplitter() override {} ; +protected: + int64_t onRecvHeader(const char *data,uint64_t len) override; + void onRecvContent(const char *data,uint64_t len) override; + const char *onSearchPacketTail(const char *data,int len) override; +protected: + virtual void onRecvChunk(const char *data,uint64_t len){ + if(_onChunkData){ + _onChunkData(data,len); + } + }; +private: + onChunkData _onChunkData; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_HTTPCHUNKEDSPLITTER_H diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp index 385619fe..6c5d13b9 100644 --- a/src/Http/HttpClient.cpp +++ b/src/Http/HttpClient.cpp @@ -117,6 +117,7 @@ void HttpClient::onConnect(const SockException &ex) { _totalBodySize = 0; _recvedBodySize = 0; HttpRequestSplitter::reset(); + _chunkedSplitter.reset(); _StrPrinter printer; printer << _method + " " << _path + " HTTP/1.1\r\n"; @@ -156,6 +157,20 @@ int64_t HttpClient::onRecvHeader(const char *data, uint64_t len) { _totalBodySize = atoll(_parser["Content-Length"].data()); } + if(_parser["Transfer-Encoding"] == "chunked"){ + //如果Transfer-Encoding字段等于chunked,则认为后续的content是不限制长度的 + _totalBodySize = -1; + _chunkedSplitter = std::make_shared([this](const char *data,uint64_t len){ + if(len > 0){ + auto recvedBodySize = _recvedBodySize + len; + onResponseBody(data, len, recvedBodySize, INT64_MAX); + _recvedBodySize = recvedBodySize; + }else{ + onResponseCompleted_l(); + } + }); + } + if(_totalBodySize == 0){ //后续没content,本次http请求结束 onResponseCompleted_l(); @@ -171,6 +186,10 @@ int64_t HttpClient::onRecvHeader(const char *data, uint64_t len) { } void HttpClient::onRecvContent(const char *data, uint64_t len) { + if(_chunkedSplitter){ + _chunkedSplitter->input(data,len); + return; + } auto recvedBodySize = _recvedBodySize + len; if(_totalBodySize < 0){ //不限长度的content,最大支持INT64_MAX个字节 @@ -231,7 +250,6 @@ void HttpClient::onManager() { void HttpClient::onResponseCompleted_l() { _totalBodySize = 0; _recvedBodySize = 0; - HttpRequestSplitter::reset(); onResponseCompleted(); } diff --git a/src/Http/HttpClient.h b/src/Http/HttpClient.h index acc28a1c..68e76169 100644 --- a/src/Http/HttpClient.h +++ b/src/Http/HttpClient.h @@ -36,6 +36,7 @@ #include "Network/TcpClient.h" #include "HttpRequestSplitter.h" #include "HttpCookie.h" +#include "HttpChunkedSplitter.h" using namespace std; using namespace toolkit; @@ -314,6 +315,7 @@ private: string _lastHost; Ticker _aliveTicker; float _fTimeOutSec = 0; + std::shared_ptr _chunkedSplitter; }; } /* namespace mediakit */