Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Z
ZLMediaKit
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
张翔宇
ZLMediaKit
Commits
f89abfaf
Commit
f89abfaf
authored
Dec 24, 2021
by
ziyue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
HTTP: 优化http客户端代码,并修复重定向时超时的bug: #1306
parent
4e01c298
显示空白字符变更
内嵌
并排
正在显示
3 个修改的文件
包含
73 行增加
和
78 行删除
+73
-78
src/Http/HttpClient.cpp
+62
-68
src/Http/HttpClient.h
+10
-9
src/Http/HttpClientImp.cpp
+1
-1
没有找到文件。
src/Http/HttpClient.cpp
查看文件 @
f89abfaf
...
...
@@ -15,28 +15,28 @@
namespace
mediakit
{
void
HttpClient
::
sendRequest
(
const
string
&
strUrl
,
float
fTimeOutS
ec
)
{
_aliveTicker
.
resetTim
e
();
_url
=
strU
rl
;
auto
protocol
=
FindField
(
strU
rl
.
data
(),
NULL
,
"://"
);
uint16_t
default
P
ort
;
bool
is
H
ttps
;
void
HttpClient
::
sendRequest
(
const
string
&
url
,
float
timeout_s
ec
)
{
clearRespons
e
();
_url
=
u
rl
;
auto
protocol
=
FindField
(
u
rl
.
data
(),
NULL
,
"://"
);
uint16_t
default
_p
ort
;
bool
is
_h
ttps
;
if
(
strcasecmp
(
protocol
.
data
(),
"http"
)
==
0
)
{
default
P
ort
=
80
;
is
H
ttps
=
false
;
default
_p
ort
=
80
;
is
_h
ttps
=
false
;
}
else
if
(
strcasecmp
(
protocol
.
data
(),
"https"
)
==
0
)
{
default
P
ort
=
443
;
is
H
ttps
=
true
;
default
_p
ort
=
443
;
is
_h
ttps
=
true
;
}
else
{
auto
strErr
=
StrPrinter
<<
"非法的http url:"
<<
strU
rl
<<
endl
;
auto
strErr
=
StrPrinter
<<
"非法的http url:"
<<
u
rl
<<
endl
;
throw
std
::
invalid_argument
(
strErr
);
}
auto
host
=
FindField
(
strU
rl
.
data
(),
"://"
,
"/"
);
auto
host
=
FindField
(
u
rl
.
data
(),
"://"
,
"/"
);
if
(
host
.
empty
())
{
host
=
FindField
(
strU
rl
.
data
(),
"://"
,
NULL
);
host
=
FindField
(
u
rl
.
data
(),
"://"
,
NULL
);
}
_path
=
FindField
(
strU
rl
.
data
(),
host
.
data
(),
NULL
);
_path
=
FindField
(
u
rl
.
data
(),
host
.
data
(),
NULL
);
if
(
_path
.
empty
())
{
_path
=
"/"
;
}
...
...
@@ -51,30 +51,28 @@ void HttpClient::sendRequest(const string &strUrl, float fTimeOutSec) {
uint16_t
port
=
atoi
(
FindField
(
host
.
data
(),
":"
,
NULL
).
data
());
if
(
port
<=
0
)
{
//默认端口
port
=
default
P
ort
;
port
=
default
_p
ort
;
}
else
{
//服务器域名
host
=
FindField
(
host
.
data
(),
NULL
,
":"
);
}
_header
.
emplace
(
"Host"
,
host_header
);
_header
.
emplace
(
"
Tools
"
,
kServerName
);
_header
.
emplace
(
"
User-Agent
"
,
kServerName
);
_header
.
emplace
(
"Connection"
,
"keep-alive"
);
_header
.
emplace
(
"Accept"
,
"*/*"
);
_header
.
emplace
(
"Accept-Language"
,
"zh-CN,zh;q=0.8"
);
_header
.
emplace
(
"User-Agent"
,
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"
);
if
(
_body
&&
_body
->
remainSize
())
{
_header
.
emplace
(
"Content-Length"
,
to_string
(
_body
->
remainSize
()));
_header
.
emplace
(
"Content-Type"
,
"application/x-www-form-urlencoded; charset=UTF-8"
);
}
bool
bChanged
=
(
_lastHost
!=
host
+
":"
+
to_string
(
port
))
||
(
_isHttps
!=
isH
ttps
);
_last
H
ost
=
host
+
":"
+
to_string
(
port
);
_is
Https
=
isH
ttps
;
_
fTimeOutSec
=
fTimeOutS
ec
;
bool
host_changed
=
(
_last_host
!=
host
+
":"
+
to_string
(
port
))
||
(
_is_https
!=
is_h
ttps
);
_last
_h
ost
=
host
+
":"
+
to_string
(
port
);
_is
_https
=
is_h
ttps
;
_
timeout_second
=
timeout_s
ec
;
auto
cookies
=
HttpCookieStorage
::
Instance
().
get
(
_last
H
ost
,
_path
);
auto
cookies
=
HttpCookieStorage
::
Instance
().
get
(
_last
_h
ost
,
_path
);
_StrPrinter
printer
;
for
(
auto
&
cookie
:
cookies
)
{
printer
<<
cookie
->
getKey
()
<<
"="
<<
cookie
->
getVal
()
<<
";"
;
...
...
@@ -84,8 +82,8 @@ void HttpClient::sendRequest(const string &strUrl, float fTimeOutSec) {
_header
.
emplace
(
"Cookie"
,
printer
);
}
if
(
!
alive
()
||
bC
hanged
)
{
startConnect
(
host
,
port
,
fTimeOutS
ec
);
if
(
!
alive
()
||
host_c
hanged
)
{
startConnect
(
host
,
port
,
timeout_s
ec
);
}
else
{
SockException
ex
;
onConnect_l
(
ex
);
...
...
@@ -98,17 +96,16 @@ void HttpClient::clear() {
_body
.
reset
();
_method
.
clear
();
_path
.
clear
();
_aliveTicker
.
resetTime
();
_chunkedSplitter
.
reset
();
_fTimeOutSec
=
0
;
clearResponse
();
}
void
HttpClient
::
clearResponse
()
{
_recved
BodyS
ize
=
0
;
_total
BodyS
ize
=
0
;
_recved
_body_s
ize
=
0
;
_total
_body_s
ize
=
0
;
_parser
.
Clear
();
_chunkedSplitter
=
nullptr
;
_chunked_splitter
=
nullptr
;
_recv_timeout_ticker
.
resetTime
();
_total_timeout_ticker
.
resetTime
();
HttpRequestSplitter
::
reset
();
}
...
...
@@ -150,14 +147,12 @@ void HttpClient::onConnect(const SockException &ex) {
}
void
HttpClient
::
onConnect_l
(
const
SockException
&
ex
)
{
_
aliveT
icker
.
resetTime
();
_
recv_timeout_t
icker
.
resetTime
();
if
(
ex
)
{
onDisconnect
(
ex
);
return
;
}
clearResponse
();
_StrPrinter
printer
;
printer
<<
_method
+
" "
<<
_path
+
" HTTP/1.1
\r\n
"
;
for
(
auto
&
pr
:
_header
)
{
...
...
@@ -169,12 +164,12 @@ void HttpClient::onConnect_l(const SockException &ex) {
}
void
HttpClient
::
onRecv
(
const
Buffer
::
Ptr
&
pBuf
)
{
_
aliveT
icker
.
resetTime
();
_
recv_timeout_t
icker
.
resetTime
();
HttpRequestSplitter
::
input
(
pBuf
->
data
(),
pBuf
->
size
());
}
void
HttpClient
::
onErr
(
const
SockException
&
ex
)
{
if
(
ex
.
getErrCode
()
==
Err_eof
&&
_total
BodyS
ize
<
0
)
{
if
(
ex
.
getErrCode
()
==
Err_eof
&&
_total
_body_s
ize
<
0
)
{
//如果Content-Length未指定 但服务器断开链接
//则认为本次http请求完成
onResponseCompleted_l
();
...
...
@@ -185,42 +180,41 @@ void HttpClient::onErr(const SockException &ex) {
ssize_t
HttpClient
::
onRecvHeader
(
const
char
*
data
,
size_t
len
)
{
_parser
.
Parse
(
data
);
if
(
_parser
.
Url
()
==
"302"
||
_parser
.
Url
()
==
"301"
)
{
auto
new
U
rl
=
_parser
[
"Location"
];
if
(
new
U
rl
.
empty
())
{
auto
new
_u
rl
=
_parser
[
"Location"
];
if
(
new
_u
rl
.
empty
())
{
shutdown
(
SockException
(
Err_shutdown
,
"未找到Location字段(跳转url)"
));
return
0
;
}
if
(
onRedirectUrl
(
newUrl
,
_parser
.
Url
()
==
"302"
))
{
HttpClient
::
clear
();
if
(
onRedirectUrl
(
new_url
,
_parser
.
Url
()
==
"302"
))
{
setMethod
(
"GET"
);
HttpClient
::
sendRequest
(
new
Url
,
_fTimeOutSec
);
HttpClient
::
sendRequest
(
new
_url
,
_timeout_second
);
return
0
;
}
}
checkCookie
(
_parser
.
getHeader
());
_total
BodyS
ize
=
onResponseHeader
(
_parser
.
Url
(),
_parser
.
getHeader
());
_total
_body_s
ize
=
onResponseHeader
(
_parser
.
Url
(),
_parser
.
getHeader
());
if
(
!
_parser
[
"Content-Length"
].
empty
())
{
//有Content-Length字段时忽略onResponseHeader的返回值
_total
BodyS
ize
=
atoll
(
_parser
[
"Content-Length"
].
data
());
_total
_body_s
ize
=
atoll
(
_parser
[
"Content-Length"
].
data
());
}
if
(
_parser
[
"Transfer-Encoding"
]
==
"chunked"
)
{
//如果Transfer-Encoding字段等于chunked,则认为后续的content是不限制长度的
_total
BodyS
ize
=
-
1
;
_chunked
S
plitter
=
std
::
make_shared
<
HttpChunkedSplitter
>
([
this
](
const
char
*
data
,
size_t
len
)
{
_total
_body_s
ize
=
-
1
;
_chunked
_s
plitter
=
std
::
make_shared
<
HttpChunkedSplitter
>
([
this
](
const
char
*
data
,
size_t
len
)
{
if
(
len
>
0
)
{
auto
recved
BodySize
=
_recvedBodyS
ize
+
len
;
onResponseBody
(
data
,
len
,
recved
BodyS
ize
,
SIZE_MAX
);
_recved
BodySize
=
recvedBodyS
ize
;
auto
recved
_body_size
=
_recved_body_s
ize
+
len
;
onResponseBody
(
data
,
len
,
recved
_body_s
ize
,
SIZE_MAX
);
_recved
_body_size
=
recved_body_s
ize
;
}
else
{
onResponseCompleted_l
();
}
});
}
if
(
_total
BodyS
ize
==
0
)
{
if
(
_total
_body_s
ize
==
0
)
{
//后续没content,本次http请求结束
onResponseCompleted_l
();
return
0
;
...
...
@@ -230,46 +224,46 @@ ssize_t HttpClient::onRecvHeader(const char *data, size_t len) {
//虽然我们在_totalBodySize >0 时知道content的确切大小,
//但是由于我们没必要等content接收完毕才回调onRecvContent(因为这样浪费内存并且要多次拷贝数据)
//所以返回-1代表我们接下来分段接收content
_recved
BodyS
ize
=
0
;
_recved
_body_s
ize
=
0
;
return
-
1
;
}
void
HttpClient
::
onRecvContent
(
const
char
*
data
,
size_t
len
)
{
if
(
_chunked
S
plitter
)
{
_chunked
S
plitter
->
input
(
data
,
len
);
if
(
_chunked
_s
plitter
)
{
_chunked
_s
plitter
->
input
(
data
,
len
);
return
;
}
auto
recved
BodySize
=
_recvedBodyS
ize
+
len
;
if
(
_total
BodyS
ize
<
0
)
{
auto
recved
_body_size
=
_recved_body_s
ize
+
len
;
if
(
_total
_body_s
ize
<
0
)
{
//不限长度的content,最大支持SIZE_MAX个字节
onResponseBody
(
data
,
len
,
recved
BodyS
ize
,
SIZE_MAX
);
_recved
BodySize
=
recvedBodyS
ize
;
onResponseBody
(
data
,
len
,
recved
_body_s
ize
,
SIZE_MAX
);
_recved
_body_size
=
recved_body_s
ize
;
return
;
}
//固定长度的content
if
(
recved
BodySize
<
(
size_t
)
_totalBodyS
ize
)
{
if
(
recved
_body_size
<
(
size_t
)
_total_body_s
ize
)
{
//content还未接收完毕
onResponseBody
(
data
,
len
,
recved
BodySize
,
_totalBodyS
ize
);
_recved
BodySize
=
recvedBodyS
ize
;
onResponseBody
(
data
,
len
,
recved
_body_size
,
_total_body_s
ize
);
_recved
_body_size
=
recved_body_s
ize
;
return
;
}
//content接收完毕
onResponseBody
(
data
,
_total
BodySize
-
_recvedBodySize
,
_totalBodySize
,
_totalBodyS
ize
);
bool
bigger
ThanExpected
=
recvedBodySize
>
(
size_t
)
_totalBodyS
ize
;
onResponseBody
(
data
,
_total
_body_size
-
_recved_body_size
,
_total_body_size
,
_total_body_s
ize
);
bool
bigger
_than_expected
=
recved_body_size
>
(
size_t
)
_total_body_s
ize
;
onResponseCompleted_l
();
if
(
bigger
ThanE
xpected
)
{
if
(
bigger
_than_e
xpected
)
{
//声明的content数据比真实的小,那么我们只截取前面部分的并断开链接
shutdown
(
SockException
(
Err_shutdown
,
"http response content size bigger than expected"
));
}
}
void
HttpClient
::
onFlush
()
{
_
aliveT
icker
.
resetTime
();
GET_CONFIG
(
uint32_t
,
send
BufS
ize
,
Http
::
kSendBufSize
);
_
recv_timeout_t
icker
.
resetTime
();
GET_CONFIG
(
uint32_t
,
send
_buf_s
ize
,
Http
::
kSendBufSize
);
while
(
_body
&&
_body
->
remainSize
()
&&
!
isSocketBusy
())
{
auto
buffer
=
_body
->
readData
(
send
BufS
ize
);
auto
buffer
=
_body
->
readData
(
send
_buf_s
ize
);
if
(
!
buffer
)
{
//数据发送结束或读取数据异常
break
;
...
...
@@ -283,13 +277,13 @@ void HttpClient::onFlush() {
}
void
HttpClient
::
onManager
()
{
if
(
_
aliveTicker
.
elapsedTime
()
>
3
*
1000
&&
_totalBodySize
<
0
&&
!
_chunkedS
plitter
)
{
if
(
_
recv_timeout_ticker
.
elapsedTime
()
>
3
*
1000
&&
_total_body_size
<
0
&&
!
_chunked_s
plitter
)
{
//如果Content-Length未指定 但接收数据超时
//则认为本次http请求完成
onResponseCompleted_l
();
}
if
(
_
fTimeOutSec
>
0
&&
_aliveTicker
.
elapsedTime
()
>
_fTimeOutSec
*
1000
)
{
if
(
_
timeout_second
>
0
&&
_total_timeout_ticker
.
elapsedTime
()
>
_timeout_second
*
1000
)
{
//超时
shutdown
(
SockException
(
Err_timeout
,
"http request timeout"
));
}
...
...
@@ -304,7 +298,7 @@ void HttpClient::checkCookie(HttpClient::HttpHeader &headers) {
for
(
auto
it_set_cookie
=
headers
.
find
(
"Set-Cookie"
);
it_set_cookie
!=
headers
.
end
();
++
it_set_cookie
)
{
auto
key_val
=
Parser
::
parseArgs
(
it_set_cookie
->
second
,
";"
,
"="
);
HttpCookie
::
Ptr
cookie
=
std
::
make_shared
<
HttpCookie
>
();
cookie
->
setHost
(
_last
H
ost
);
cookie
->
setHost
(
_last
_h
ost
);
int
index
=
0
;
auto
arg_vec
=
split
(
it_set_cookie
->
second
,
";"
);
...
...
src/Http/HttpClient.h
查看文件 @
f89abfaf
...
...
@@ -61,9 +61,9 @@ public:
/**
* 发送http[s]请求
* @param url 请求url
* @param
fTimeOutS
ec 超时时间
* @param
timeout_s
ec 超时时间
*/
virtual
void
sendRequest
(
const
string
&
url
,
float
fTimeOutS
ec
);
virtual
void
sendRequest
(
const
string
&
url
,
float
timeout_s
ec
);
/**
* 重置对象
...
...
@@ -170,7 +170,7 @@ private:
void
clearResponse
();
protected
:
bool
_is
H
ttps
;
bool
_is
_h
ttps
;
private
:
string
_url
;
...
...
@@ -178,15 +178,16 @@ private:
HttpBody
::
Ptr
_body
;
string
_method
;
string
_path
;
string
_lastHost
;
Ticker
_aliveTicker
;
float
_fTimeOutSec
=
0
;
string
_last_host
;
Ticker
_recv_timeout_ticker
;
Ticker
_total_timeout_ticker
;
float
_timeout_second
=
0
;
//recv
size_t
_recved
BodyS
ize
;
ssize_t
_total
BodyS
ize
;
size_t
_recved
_body_s
ize
;
ssize_t
_total
_body_s
ize
;
Parser
_parser
;
std
::
shared_ptr
<
HttpChunkedSplitter
>
_chunked
S
plitter
;
std
::
shared_ptr
<
HttpChunkedSplitter
>
_chunked
_s
plitter
;
};
}
/* namespace mediakit */
...
...
src/Http/HttpClientImp.cpp
查看文件 @
f89abfaf
...
...
@@ -13,7 +13,7 @@
namespace
mediakit
{
void
HttpClientImp
::
onConnect
(
const
SockException
&
ex
)
{
if
(
!
_is
H
ttps
){
if
(
!
_is
_h
ttps
){
HttpClient
::
onConnect
(
ex
);
}
else
{
TcpClientWithSSL
<
HttpClient
>::
onConnect
(
ex
);
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论