|
@@ -1,2579 +1,2229 @@
|
|
|
-/**
|
|
|
- * Tencent is pleased to support the open source community by making Tars available.
|
|
|
- *
|
|
|
- * Copyright (C) 2016THL A29 Limited, a Tencent company. All rights reserved.
|
|
|
- *
|
|
|
- * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
|
|
|
- * in compliance with the License. You may obtain a copy of the License at
|
|
|
- *
|
|
|
- * https://opensource.org/licenses/BSD-3-Clause
|
|
|
- *
|
|
|
- * Unless required by applicable law or agreed to in writing, software distributed
|
|
|
- * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
|
- * CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
|
- * specific language governing permissions and limitations under the License.
|
|
|
- */
|
|
|
-
|
|
|
-#include "util/tc_http.h"
|
|
|
-#include "util/tc_port.h"
|
|
|
-#include "util/tc_common.h"
|
|
|
-#include "util/tc_clientsocket.h"
|
|
|
-#include "util/tc_network_buffer.h"
|
|
|
-#include <string.h>
|
|
|
-
|
|
|
-namespace tars
|
|
|
-{
|
|
|
-
|
|
|
-const char* strnstr(const char* s1, const char* s2, int pos1)
|
|
|
-{
|
|
|
- int l1, l2;
|
|
|
-
|
|
|
- l2 = strlen(s2);
|
|
|
- if (!l2)
|
|
|
- return (char *)s1;
|
|
|
- l1 = strlen(s1);
|
|
|
-
|
|
|
- pos1 = (pos1 > l1)?l1:pos1;
|
|
|
-
|
|
|
- while (pos1 >= l2) {
|
|
|
- pos1--;
|
|
|
- if (!memcmp(s1, s2, l2))
|
|
|
- return (char *)s1;
|
|
|
- s1++;
|
|
|
- }
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-unordered_map<string, int> TC_Http::HEADER = {
|
|
|
- {"GET", TC_HttpRequest::REQUEST_GET},
|
|
|
- {"POST", TC_HttpRequest::REQUEST_POST},
|
|
|
- {"PUT", TC_HttpRequest::REQUEST_PUT},
|
|
|
- {"PATCH", TC_HttpRequest::REQUEST_PATCH},
|
|
|
- {"OPTIONS", TC_HttpRequest::REQUEST_OPTIONS},
|
|
|
- {"PRI", TC_HttpRequest::REQUEST_PRI},
|
|
|
- {"DELETE", TC_HttpRequest::REQUEST_DELETE},
|
|
|
- {"HEAD", TC_HttpRequest::REQUEST_HEAD},
|
|
|
-};
|
|
|
-
|
|
|
-unordered_map<int, string> TC_Http::HEADER_REVERSE = {
|
|
|
- {TC_HttpRequest::REQUEST_GET, "GET"},
|
|
|
- {TC_HttpRequest::REQUEST_POST, "POST"},
|
|
|
- {TC_HttpRequest::REQUEST_PUT, "PUT"},
|
|
|
- {TC_HttpRequest::REQUEST_PATCH, "PATCH"},
|
|
|
- {TC_HttpRequest::REQUEST_OPTIONS, "OPTIONS"},
|
|
|
- {TC_HttpRequest::REQUEST_PRI, "PRI"},
|
|
|
- {TC_HttpRequest::REQUEST_DELETE, "DELETE"},
|
|
|
- {TC_HttpRequest::REQUEST_HEAD, "HEAD"},
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-bool TC_Http::CmpCase::operator()(const string &s1, const string &s2) const
|
|
|
-{
|
|
|
- if (TC_Port::strcasecmp(s1.c_str(), s2.c_str()) < 0)
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_URL::isValid() const
|
|
|
-{
|
|
|
- return !_sURL.empty();
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getURL() const
|
|
|
-{
|
|
|
- return _sURL;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::type2String() const
|
|
|
-{
|
|
|
- switch (_iURLType)
|
|
|
- {
|
|
|
- case HTTP:
|
|
|
- return "http";
|
|
|
- case HTTPS:
|
|
|
- return "https";
|
|
|
- case FTP:
|
|
|
- return "ftp";
|
|
|
- }
|
|
|
-
|
|
|
- return "http";
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getDefaultPort() const
|
|
|
-{
|
|
|
- switch (_iURLType)
|
|
|
- {
|
|
|
- case HTTP:
|
|
|
- return "80";
|
|
|
- case HTTPS:
|
|
|
- return "443";
|
|
|
- case FTP:
|
|
|
- return "21";
|
|
|
- }
|
|
|
-
|
|
|
- return "80";
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_URL::isDefaultPort() const
|
|
|
-{
|
|
|
- return _sPort == getDefaultPort();
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::toURL()
|
|
|
-{
|
|
|
- _sURL.clear();
|
|
|
-
|
|
|
- _sURL = _sScheme;
|
|
|
- _sURL += "://";
|
|
|
-
|
|
|
- if (!_sUser.empty())
|
|
|
- _sURL += _sUser;
|
|
|
-
|
|
|
- if (!_sUser.empty() && !_sPass.empty())
|
|
|
- {
|
|
|
- _sURL += ":";
|
|
|
- _sURL += _sPass;
|
|
|
- }
|
|
|
-
|
|
|
- if (!_sUser.empty())
|
|
|
- _sURL += "@";
|
|
|
-
|
|
|
- _sURL += _sDomain;
|
|
|
-
|
|
|
- if (!isDefaultPort())
|
|
|
- {
|
|
|
- _sURL += ":";
|
|
|
- _sURL += _sPort;
|
|
|
- }
|
|
|
-
|
|
|
- _sURL += getRequest();
|
|
|
-
|
|
|
- return _sURL;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getRequest() const
|
|
|
-{
|
|
|
- string sURL;
|
|
|
-
|
|
|
- if (!_sPath.empty())
|
|
|
- sURL += _sPath;
|
|
|
-
|
|
|
- if (!_sQuery.empty())
|
|
|
- sURL += "?" + _sQuery;
|
|
|
-
|
|
|
- if (!_sRef.empty())
|
|
|
- sURL += "#" + _sRef;
|
|
|
-
|
|
|
- return sURL;
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_URL::parseURL(const string &originRequest)
|
|
|
-{
|
|
|
- // string originRequest = TC_Common::trim(sURL, " ");
|
|
|
-
|
|
|
- if (originRequest.empty())
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- clear();
|
|
|
-
|
|
|
- int iPos = 0;
|
|
|
-
|
|
|
- if (TC_Port::strncasecmp(originRequest.c_str(), "http://" , 7) == 0)
|
|
|
- {
|
|
|
- //http开头
|
|
|
- _iURLType = HTTP;
|
|
|
- iPos = 7;
|
|
|
- _sScheme = "http";
|
|
|
- }
|
|
|
- else if (TC_Port::strncasecmp(originRequest.c_str(), "https://" , 8) == 0)
|
|
|
- {
|
|
|
- //https开头
|
|
|
- _iURLType = HTTPS;
|
|
|
- iPos = 8;
|
|
|
- _sScheme = "https";
|
|
|
- }
|
|
|
- else if (TC_Port::strncasecmp(originRequest.c_str(), "ftp://", 6) == 0)
|
|
|
- {
|
|
|
- //ftps开头
|
|
|
- _iURLType = FTP;
|
|
|
- iPos = 6;
|
|
|
- _sScheme = "ftp";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- //默认用http
|
|
|
- _iURLType = HTTP;
|
|
|
- iPos = 0;
|
|
|
- _sScheme = "http";
|
|
|
- }
|
|
|
-
|
|
|
- string::size_type index = originRequest.find("/", iPos);
|
|
|
- string::size_type nQuestionIndex = originRequest.find("?", iPos);
|
|
|
- string::size_type nNumbersignIndex = originRequest.find("#", iPos);
|
|
|
-
|
|
|
- string sUrlAuthority;
|
|
|
- string sUrlPath;
|
|
|
-
|
|
|
- if (nQuestionIndex < index)
|
|
|
- {
|
|
|
- sUrlAuthority = originRequest.substr(iPos, nQuestionIndex - iPos);
|
|
|
-
|
|
|
-// string sTemp = originRequest.substr(nQuestionIndex);
|
|
|
-
|
|
|
- sUrlPath += '/';
|
|
|
- sUrlPath += originRequest.substr(nQuestionIndex);
|
|
|
- }
|
|
|
- else if (nNumbersignIndex < index)
|
|
|
- {
|
|
|
- sUrlAuthority = originRequest.substr(iPos, nNumbersignIndex - iPos);
|
|
|
-
|
|
|
-// string sTemp = originRequest.substr(nNumbersignIndex);
|
|
|
-
|
|
|
- sUrlPath += '/';
|
|
|
- sUrlPath += originRequest.substr(nNumbersignIndex);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if (index == string::npos)
|
|
|
- {
|
|
|
- sUrlAuthority = originRequest.substr(iPos, index);
|
|
|
- sUrlPath = "";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- sUrlAuthority = originRequest.substr(iPos, index - iPos);
|
|
|
- sUrlPath = originRequest.substr(index);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //////解析Authority
|
|
|
- index = sUrlAuthority.find("@");
|
|
|
- if (index != string::npos)
|
|
|
- {
|
|
|
- _sUser = sUrlAuthority.substr(0, index);
|
|
|
- _sDomain = sUrlAuthority.substr(index + 1);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _sDomain = sUrlAuthority;
|
|
|
- }
|
|
|
-
|
|
|
- //////解析User:Pass
|
|
|
- index = _sUser.find(":");
|
|
|
- if (index != string::npos)
|
|
|
- {
|
|
|
- _sPass = _sUser.substr(index + 1);
|
|
|
- _sUser = _sUser.substr(0, index);
|
|
|
- }
|
|
|
-
|
|
|
- //////解析Host:Port
|
|
|
- index = _sDomain.find(":");
|
|
|
- if (index != string::npos)
|
|
|
- {
|
|
|
- _sPort = _sDomain.substr(index + 1);
|
|
|
-
|
|
|
- _sDomain = _sDomain.substr(0, index);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _sPort = getDefaultPort();
|
|
|
- }
|
|
|
-
|
|
|
- //////解析Path Query Ref
|
|
|
- index = sUrlPath.find("?");
|
|
|
- if (index != string::npos)
|
|
|
- {
|
|
|
- _sPath = sUrlPath.substr(0, index);
|
|
|
- _sQuery = sUrlPath.substr(index + 1);
|
|
|
-
|
|
|
- index = _sQuery.rfind("#");
|
|
|
- if (index != string::npos)
|
|
|
- {
|
|
|
- _sRef = _sQuery.substr(index + 1);
|
|
|
- _sQuery = _sQuery.substr(0, index);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _sPath = sUrlPath;
|
|
|
- _sQuery = "";
|
|
|
-
|
|
|
- index = _sPath.rfind("#");
|
|
|
- if (index != string::npos)
|
|
|
- {
|
|
|
- _sRef = _sPath.substr(index + 1);
|
|
|
- _sPath = _sPath.substr(0, index);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (_sPath.empty())
|
|
|
- {
|
|
|
- _sPath = "/";
|
|
|
- }
|
|
|
-
|
|
|
- toURL();
|
|
|
-
|
|
|
- //域名或者IP必须包含一个点
|
|
|
- if (_sDomain.find(".") == string::npos)
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_URL::specialize()
|
|
|
-{
|
|
|
- //规整化路径
|
|
|
- /*string sUrlPath = simplePath(getPath());
|
|
|
-
|
|
|
- _fragment["path"] = sUrlPath;*/
|
|
|
- _sPath = simplePath(getPath());
|
|
|
-
|
|
|
- _sURL = toURL();
|
|
|
-}
|
|
|
-
|
|
|
-void TC_URL::clear()
|
|
|
-{
|
|
|
- _sScheme = "";
|
|
|
- _sUser = "";
|
|
|
- _sPass = "";
|
|
|
- _sDomain = "";
|
|
|
- _sPort = "";
|
|
|
- _sPath = "";
|
|
|
- _sQuery = "";
|
|
|
- _sRef = "";
|
|
|
-
|
|
|
- _sURL.clear();
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getScheme() const
|
|
|
-{
|
|
|
- return _sScheme;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getUserName() const
|
|
|
-{
|
|
|
- return _sUser;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getPassword() const
|
|
|
-{
|
|
|
- return _sPass;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getDomain() const
|
|
|
-{
|
|
|
- return _sDomain;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getPort() const
|
|
|
-{
|
|
|
- return _sPort;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getPath() const
|
|
|
-{
|
|
|
- return _sPath;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getQuery() const
|
|
|
-{
|
|
|
- return _sQuery;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getRef() const
|
|
|
-{
|
|
|
- return _sRef;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getRelativePath() const
|
|
|
-{
|
|
|
- string sURL = getPath();
|
|
|
-
|
|
|
- string::size_type pos;
|
|
|
-
|
|
|
-
|
|
|
- pos = sURL.rfind("/");
|
|
|
-
|
|
|
- if (pos == string::npos)
|
|
|
- {
|
|
|
- return "/";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return sURL.substr(0, pos + 1);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::getRootPath() const
|
|
|
-{
|
|
|
- string sURL = _sScheme;
|
|
|
- sURL += "://";
|
|
|
-
|
|
|
- if (!_sUser.empty())
|
|
|
- sURL += _sUser;
|
|
|
-
|
|
|
- if (!_sUser.empty() && !_sPass.empty())
|
|
|
- {
|
|
|
- sURL += ":";
|
|
|
- sURL += _sPass;
|
|
|
- }
|
|
|
-
|
|
|
- if (!_sUser.empty())
|
|
|
- sURL += "@";
|
|
|
-
|
|
|
- sURL += _sDomain;
|
|
|
-
|
|
|
- if (!isDefaultPort())
|
|
|
- {
|
|
|
- sURL += ":";
|
|
|
- sURL += _sPort;
|
|
|
- }
|
|
|
-
|
|
|
- sURL += "/";
|
|
|
-
|
|
|
- return sURL;
|
|
|
-}
|
|
|
-
|
|
|
-TC_URL TC_URL::buildWithRelativePath(const string &sRelativePath) const
|
|
|
-{
|
|
|
- string sURL;
|
|
|
-
|
|
|
- if (!sRelativePath.empty() && sRelativePath[0] == '/')
|
|
|
- {
|
|
|
- //如果链接是用"/"开头的相对地址,那么应该用Host+相对地址
|
|
|
- sURL = sRelativePath.substr(1);
|
|
|
- }
|
|
|
- else if (sRelativePath[0] == '#')
|
|
|
- {
|
|
|
- //#
|
|
|
- sURL = getPath().substr(1);
|
|
|
-
|
|
|
- if (!getQuery().empty())
|
|
|
- sURL += "?" + getQuery();
|
|
|
-
|
|
|
- sURL += sRelativePath;
|
|
|
-
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- //相对地址
|
|
|
- sURL = getRelativePath().substr(1) + sRelativePath;
|
|
|
- }
|
|
|
-
|
|
|
- sURL = getRootPath() + simplePath("/" + sURL).substr(1);
|
|
|
-
|
|
|
- TC_URL url;
|
|
|
-
|
|
|
- url.parseURL(sURL);
|
|
|
-
|
|
|
- return url;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_URL::simplePath(const string &sPath) const
|
|
|
-{
|
|
|
- //所有./都去掉
|
|
|
- size_t pos = 0;
|
|
|
- string sNewPath = sPath;
|
|
|
-
|
|
|
- while (true)
|
|
|
- {
|
|
|
- size_t dotPos = sNewPath.find("./", pos);
|
|
|
-
|
|
|
- if (dotPos != string::npos)
|
|
|
- {
|
|
|
- if ((dotPos == 0) || (sNewPath.at(dotPos - 1) == '/'))
|
|
|
- {
|
|
|
- sNewPath.erase(dotPos, 2);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- pos = dotPos + 2;
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //如果路径是以.结尾的, 则.去掉
|
|
|
- if (((sNewPath.length() >= 2) && (sNewPath.substr(sNewPath.length() - 2) == "/.")) || (sNewPath == "."))
|
|
|
- {
|
|
|
- sNewPath.erase(sNewPath.length() - 1);
|
|
|
- }
|
|
|
-
|
|
|
- //处理/../的形式
|
|
|
- pos = 0;
|
|
|
- size_t startPos = 0;
|
|
|
-
|
|
|
- while (1)
|
|
|
- {
|
|
|
- size_t slashDot = sNewPath.find("/../", pos);
|
|
|
-
|
|
|
- if (slashDot != string::npos)
|
|
|
- {
|
|
|
- if (0 == slashDot)
|
|
|
- {
|
|
|
- sNewPath.erase(0, 3);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if ( (slashDot > 1) && (sNewPath.substr(slashDot - 2, 2) == "..") )
|
|
|
- {
|
|
|
- pos = slashDot + 4;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- startPos = sNewPath.rfind('/', slashDot - 1);
|
|
|
-
|
|
|
- if (startPos == string::npos) startPos = 0;
|
|
|
-
|
|
|
- sNewPath.erase(startPos, slashDot + 4 - startPos - 1);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //处理/..结尾的情况
|
|
|
- if ((sNewPath.size() >= 3) && (sNewPath.substr(sNewPath.size() - 3, 3) == "/.."))
|
|
|
- {
|
|
|
- size_t slashDot = sNewPath.size() - 3;
|
|
|
- if (!((slashDot > 1) && (sNewPath.substr(slashDot - 2, 2) == "..")))
|
|
|
- {
|
|
|
- startPos = sNewPath.rfind ('/', slashDot - 1);
|
|
|
- if (startPos == string::npos) startPos = 0;
|
|
|
- sNewPath.erase (startPos + 1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return sNewPath;
|
|
|
-}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-
|
|
|
-void TC_Http::setHeader(const string &sHeadName, const string &sHeadValue)
|
|
|
-{
|
|
|
- //Set-Cookie和Cookie可以有多个头
|
|
|
- const char * pStr1 = "SET-COOKIE";
|
|
|
- const char * pStr2 = "COOKIE";//原则上COOKIE只有一个,担心有兼容性问题,保留
|
|
|
- if ((TC_Port::strcasecmp(sHeadName.c_str(), pStr1) != 0) && (TC_Port::strcasecmp(sHeadName.c_str(), pStr2) != 0))
|
|
|
- {
|
|
|
- _headers.erase(sHeadName);
|
|
|
- }
|
|
|
-
|
|
|
- _headers.insert(multimap<string, string>::value_type(sHeadName, sHeadValue));
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_Http::hasHeader(const char *sHeader) const
|
|
|
-{
|
|
|
- http_header_type::const_iterator it = _headers.find(sHeader);
|
|
|
- if (it == _headers.end())
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_Http::checkHeader(const char *sHeader, const char *value) const
|
|
|
-{
|
|
|
- http_header_type::const_iterator it = _headers.find(sHeader);
|
|
|
- if (it == _headers.end())
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return TC_Port::strcasecmp(it->second.c_str(), value) == 0;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_Http::getHeader(const string& sHeader) const
|
|
|
-{
|
|
|
- http_header_type::const_iterator it = _headers.find(sHeader);
|
|
|
- if (it == _headers.end())
|
|
|
- {
|
|
|
- return "";
|
|
|
- }
|
|
|
-
|
|
|
- return it->second;
|
|
|
-}
|
|
|
-
|
|
|
-string TC_Http::getContentType() const
|
|
|
-{
|
|
|
- return getHeader("Content-Type");
|
|
|
-}
|
|
|
-
|
|
|
-string TC_Http::getHost() const
|
|
|
-{
|
|
|
- return getHeader("Host");
|
|
|
-}
|
|
|
-
|
|
|
-size_t TC_Http::getContentLength() const
|
|
|
-{
|
|
|
- string s = getHeader("Content-Length");
|
|
|
- if (s.empty())
|
|
|
- {
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- return TC_Common::strto<size_t>(s);
|
|
|
-}
|
|
|
-
|
|
|
-string TC_Http::genHeader() const
|
|
|
-{
|
|
|
- string sHttpHeader;
|
|
|
-
|
|
|
- for (http_header_type::const_iterator it = _headers.begin(); it != _headers.end(); ++it)
|
|
|
- {
|
|
|
- if (it->second != "")
|
|
|
- {
|
|
|
- sHttpHeader += it->first;
|
|
|
- sHttpHeader += ": ";
|
|
|
- sHttpHeader += it->second;
|
|
|
- sHttpHeader += "\r\n";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return sHttpHeader;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_Http::genHeader(string &sHttpHeader) const
|
|
|
-{
|
|
|
- for (http_header_type::const_iterator it = _headers.begin(); it != _headers.end(); ++it)
|
|
|
- {
|
|
|
- if (it->second != "")
|
|
|
- {
|
|
|
- sHttpHeader += it->first;
|
|
|
- sHttpHeader += ": ";
|
|
|
- sHttpHeader += it->second;
|
|
|
- sHttpHeader += "\r\n";
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-vector<string> TC_Http::getHeaderMulti(const string &sHeadName) const
|
|
|
-{
|
|
|
- vector<string> v;
|
|
|
-
|
|
|
- http_header_type::const_iterator itEnd = _headers.end();
|
|
|
-
|
|
|
- for ( http_header_type::const_iterator it = _headers.begin(); it != itEnd; ++it)
|
|
|
- {
|
|
|
- if (TC_Port::strcasecmp(it->first.c_str(), sHeadName.c_str()) == 0)
|
|
|
- {
|
|
|
- v.push_back(it->second);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return v;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_Http::reset()
|
|
|
-{
|
|
|
- _headers.clear();
|
|
|
- _version.clear();
|
|
|
- _headLength = 0;
|
|
|
- _content.clear();
|
|
|
- _bIsChunked = false;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_Http::getHeaders(map<string, string> &header)
|
|
|
-{
|
|
|
- for(auto it = _headers.begin(); it != _headers.end(); ++it)
|
|
|
- {
|
|
|
- header.insert(map<string, string>::value_type(it->first, it->second));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/********************* TC_HttpCookie ***********************/
|
|
|
-
|
|
|
-bool TC_HttpCookie::matchDomain(const string &sCookieDomain, const string &sDomain)
|
|
|
-{
|
|
|
- string sCookieDomainNew = TC_Common::lower(sCookieDomain);
|
|
|
-
|
|
|
- //没有点的自动加点
|
|
|
- if (sCookieDomainNew.find(".") != 0)
|
|
|
- {
|
|
|
- sCookieDomainNew = "." + sCookieDomainNew;
|
|
|
- }
|
|
|
-
|
|
|
- string::size_type pos = sCookieDomainNew.find(".");
|
|
|
-
|
|
|
- //sCookieDomain串至少有两个点
|
|
|
- if (pos == string::npos || (pos == 0 && sCookieDomainNew.rfind(".") == 0))
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- string sLowerDomain = TC_Common::lower(sDomain);
|
|
|
-
|
|
|
- //后边是子域名
|
|
|
- if (sDomain.length() >= sCookieDomainNew.length() &&
|
|
|
- sLowerDomain.compare(sDomain.length() - sCookieDomainNew.length(), sCookieDomainNew.length(), sCookieDomainNew) == 0)
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- //去掉.相等
|
|
|
- if (sLowerDomain == sCookieDomainNew.substr(1))
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-size_t TC_HttpCookie::matchPath(const string &sCookiePath, const string &sPath)
|
|
|
-{
|
|
|
- if (sCookiePath.empty() || sPath.empty()) return 0;
|
|
|
-
|
|
|
- //都在最后加/再匹配
|
|
|
- string sCookiePath1 = (sCookiePath.at(sCookiePath.length() - 1) == '/') ? sCookiePath : sCookiePath + "/";
|
|
|
-
|
|
|
- string sPath1 = (sPath.at(sPath.length() - 1) == '/') ? sPath : sPath + "/";
|
|
|
-
|
|
|
- if (sPath1.find(sCookiePath1) == 0)
|
|
|
- {
|
|
|
- return sCookiePath1.length();
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpCookie::clear()
|
|
|
-{
|
|
|
- _cookies.clear();
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpCookie::fixDomain(const string &sDomain, string &sFixDomain)
|
|
|
-{
|
|
|
- if (sDomain.empty())
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- sFixDomain = sDomain;
|
|
|
-
|
|
|
- //自动加.
|
|
|
- if (sDomain.at(0) != '.')
|
|
|
- {
|
|
|
- sFixDomain = "." + sDomain;
|
|
|
- }
|
|
|
-
|
|
|
- //domain至少两段
|
|
|
- if (sFixDomain.find(".") == sFixDomain.rfind("."))
|
|
|
- return false;
|
|
|
-
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpCookie::addCookie(const Cookie &cookie, list<Cookie> &cookies)
|
|
|
-{
|
|
|
- string sDomain;
|
|
|
-
|
|
|
- Cookie cookieNew = cookie;
|
|
|
-
|
|
|
- if (!fixDomain(cookieNew._domain, sDomain))
|
|
|
- return;
|
|
|
-
|
|
|
- cookieNew._domain = sDomain;
|
|
|
-
|
|
|
- if (cookieNew._path.empty())
|
|
|
- return;
|
|
|
-
|
|
|
- list<Cookie>::iterator it = cookies.begin();
|
|
|
-
|
|
|
- string sName;
|
|
|
- if (cookieNew._data.size() >= 1)
|
|
|
- {
|
|
|
- sName = cookieNew._data.begin()->first;
|
|
|
- }
|
|
|
-
|
|
|
- while (it != cookies.end())
|
|
|
- {
|
|
|
- //检查Cookie是否过期
|
|
|
- if (isCookieExpires(*it))
|
|
|
- {
|
|
|
- cookies.erase(it++);
|
|
|
- }
|
|
|
- else if (TC_Port::strcasecmp(it->_domain.c_str(), cookieNew._domain.c_str()) == 0
|
|
|
- && strcmp(it->_path.c_str(), cookieNew._path.c_str()) == 0
|
|
|
- && it->_isSecure == cookieNew._isSecure)
|
|
|
- {
|
|
|
- if (it->_expires == cookieNew._expires)
|
|
|
- {
|
|
|
- //domain/path/expires都匹配的情况下, 覆盖老cookie的数据
|
|
|
- cookieNew._data.insert(it->_data.begin(), it->_data.end());
|
|
|
-
|
|
|
- cookies.erase(it++);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- //超时间不一样,但存在同样的key,需删除
|
|
|
- if (it->_data.find(sName) != it->_data.end())
|
|
|
- {
|
|
|
- it->_data.erase(sName);
|
|
|
- }
|
|
|
- ++it;
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- ++it;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- cookies.push_back(cookieNew);
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpCookie::addCookie(const Cookie &cookie)
|
|
|
-{
|
|
|
- addCookie(cookie, _cookies);
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpCookie::addCookie(const list<Cookie> &cookie)
|
|
|
-{
|
|
|
- list<Cookie>::const_iterator it = cookie.begin();
|
|
|
-
|
|
|
- while (it != cookie.end())
|
|
|
- {
|
|
|
- addCookie(*it);
|
|
|
-
|
|
|
- ++it;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpCookie::addCookie(const string &sRspURL, const vector<string> &vCookies)
|
|
|
-{
|
|
|
- TC_URL cURL;
|
|
|
-
|
|
|
- cURL.parseURL(sRspURL);
|
|
|
-
|
|
|
- string sRspURLDomain = TC_Common::lower(cURL.getDomain());
|
|
|
-
|
|
|
- string sRspURLPath = cURL.getPath();
|
|
|
-
|
|
|
- for (size_t i = 0; i < vCookies.size(); i++)
|
|
|
- {
|
|
|
- //处理一行SetCookie
|
|
|
- vector<string> v = TC_Common::sepstr<string>(vCookies[i], ";");
|
|
|
-
|
|
|
- Cookie cookie;
|
|
|
-
|
|
|
- cookie._isSecure = false;
|
|
|
- cookie._expires = 0;
|
|
|
-
|
|
|
- //解析Cookie数据
|
|
|
- for (size_t j = 0; j < v.size(); ++j)
|
|
|
- {
|
|
|
- string::size_type index = v[j].find("=");
|
|
|
-
|
|
|
- string name;
|
|
|
-
|
|
|
- string value;
|
|
|
-
|
|
|
- if (index != string::npos)
|
|
|
- {
|
|
|
- name = TC_Common::trim(v[j].substr(0, index), " ");
|
|
|
-
|
|
|
- value = TC_Common::trim(v[j].substr(index + 1));
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- name = TC_Common::trim(v[j]);
|
|
|
- }
|
|
|
- if (TC_Port::strcasecmp(name.c_str(), "secure") == 0)
|
|
|
- {
|
|
|
- cookie._isSecure = true;
|
|
|
- }
|
|
|
- else if (TC_Port::strcasecmp(name.c_str(), "expires") == 0)
|
|
|
- {
|
|
|
- struct tm stTm;
|
|
|
- //兼容时间格式
|
|
|
- //expires=Mon, 09-May-41 08:39:32 GMT
|
|
|
- //expires=Thu, 01-Jan-1970 01:00:00 GMT
|
|
|
- value = TC_Common::replace(value, "-", " ");
|
|
|
- if (value.length() == 27 && value.at(11) == ' ' && value.at(14) == ' ')
|
|
|
- {
|
|
|
- int year = TC_Common::strto<int> (value.substr(12, 2));
|
|
|
- if (year >= 69 && year <= 99)
|
|
|
- value = value.substr(0, 12) + "19" + value.substr(12);
|
|
|
- else
|
|
|
- value = value.substr(0, 12) + "20" + value.substr(12);
|
|
|
- }
|
|
|
-
|
|
|
- TC_Common::strgmt2tm(value, stTm);
|
|
|
-
|
|
|
- cookie._expires = TC_Port::timegm(&stTm);
|
|
|
- }
|
|
|
- else if (TC_Port::strcasecmp(name.c_str(), "path") == 0)
|
|
|
- {
|
|
|
- cookie._path = value;
|
|
|
- }
|
|
|
- else if (TC_Port::strcasecmp(name.c_str(), "domain") == 0)
|
|
|
- {
|
|
|
- cookie._domain = value;
|
|
|
- }
|
|
|
- else if (TC_Port::strcasecmp(name.c_str(), "httponly") == 0)
|
|
|
- {
|
|
|
- //TODO
|
|
|
- //cookie._isHttpOnly = true;
|
|
|
- }
|
|
|
- else
|
|
|
- cookie._data.insert(http_cookie_data::value_type(name, value));
|
|
|
- }
|
|
|
-
|
|
|
- ///修正和匹配domain/////////////////////////////////////////
|
|
|
- if (cookie._domain.empty())
|
|
|
- cookie._domain = sRspURLDomain;
|
|
|
-
|
|
|
- if (!fixDomain(cookie._domain, cookie._domain))
|
|
|
- continue;
|
|
|
-
|
|
|
- //匹配域名
|
|
|
- if (!matchDomain(cookie._domain, sRspURLDomain))
|
|
|
- continue;
|
|
|
-
|
|
|
- //修改和匹配path/////////////////////////////
|
|
|
- if (cookie._path.empty())
|
|
|
- {
|
|
|
- string sCookiePath;
|
|
|
-
|
|
|
- //缺省是全路径
|
|
|
- string sRequest = sRspURLPath;
|
|
|
-
|
|
|
- string::size_type pos = sRequest.rfind("/");
|
|
|
-
|
|
|
- if (pos == string::npos)
|
|
|
- sCookiePath = "/";
|
|
|
- else
|
|
|
- sCookiePath = sRequest.substr(0, pos + 1);
|
|
|
-
|
|
|
- cookie._path = sCookiePath;
|
|
|
- }
|
|
|
-
|
|
|
- //URL在Path范围内,Cookie 有效
|
|
|
- if (!matchPath(cookie._path, sRspURLPath))
|
|
|
- continue;
|
|
|
-
|
|
|
- //添加Cookie
|
|
|
- addCookie(cookie);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpCookie::isCookieExpires(const Cookie &cookie) const
|
|
|
-{
|
|
|
- //过期了
|
|
|
- if (cookie._expires != 0 && cookie._expires < time(NULL))
|
|
|
- return true;
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-size_t TC_HttpCookie::isCookieMatch(const Cookie &cookie, const TC_URL &tURL) const
|
|
|
-{
|
|
|
- //域名没有匹配
|
|
|
- if (!matchDomain(cookie._domain, tURL.getDomain()))
|
|
|
- return 0;
|
|
|
-
|
|
|
- //路径没有匹配
|
|
|
- size_t len = matchPath(cookie._path, tURL.getPath());
|
|
|
- if (len == 0)
|
|
|
- return 0;
|
|
|
-
|
|
|
- //安全的cookie,不安全的URL
|
|
|
- if (cookie._isSecure && (tURL.getType() != TC_URL::HTTPS))
|
|
|
- return 0;
|
|
|
-
|
|
|
- return len;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpCookie::getCookieForURL(const string &sReqURL, list<TC_HttpCookie::Cookie> &cookies)
|
|
|
-{
|
|
|
- TC_URL tURL;
|
|
|
-
|
|
|
- tURL.parseURL(sReqURL);
|
|
|
-
|
|
|
- cookies.clear();
|
|
|
-
|
|
|
- list<Cookie>::iterator it = _cookies.begin();
|
|
|
-
|
|
|
- while (it != _cookies.end())
|
|
|
- {
|
|
|
- //检查Cookie是否过期
|
|
|
- if (isCookieExpires(*it))
|
|
|
- {
|
|
|
- _cookies.erase(it++);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- size_t len = isCookieMatch(*it, tURL);
|
|
|
- if (len == 0)
|
|
|
- {
|
|
|
- ++it;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- cookies.push_back(*it);
|
|
|
-
|
|
|
- ++it;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpCookie::getCookieForURL(const string &sReqURL, string &sCookie)
|
|
|
-{
|
|
|
- list<Cookie> cookies;
|
|
|
-
|
|
|
- sCookie.clear();
|
|
|
-
|
|
|
- getCookieForURL(sReqURL, cookies);
|
|
|
-
|
|
|
- list<Cookie>::iterator it = cookies.begin();
|
|
|
- while (it != cookies.end())
|
|
|
- {
|
|
|
- http_cookie_data::iterator itd = it->_data.begin();
|
|
|
-
|
|
|
- while (itd != it->_data.end())
|
|
|
- {
|
|
|
- //被删除的cookie不输出
|
|
|
- if (itd->first != "" && itd->second != "" && TC_Common::lower(itd->second) != "null"
|
|
|
- && TC_Common::lower(itd->second) != "deleted")
|
|
|
- sCookie += itd->first + "=" + itd->second + "; ";
|
|
|
-
|
|
|
- ++itd;
|
|
|
- }
|
|
|
-
|
|
|
- ++it;
|
|
|
- }
|
|
|
-
|
|
|
- //去掉末尾的 "; "
|
|
|
- if (sCookie.length() >= 2)
|
|
|
- sCookie = sCookie.substr(0, sCookie.length() - 2);
|
|
|
-}
|
|
|
-
|
|
|
-list<TC_HttpCookie::Cookie> TC_HttpCookie::getSerializeCookie(time_t t)
|
|
|
-{
|
|
|
- list<Cookie> cookies;
|
|
|
-
|
|
|
- list<Cookie>::iterator it = _cookies.begin();
|
|
|
-
|
|
|
- while (it != _cookies.end())
|
|
|
- {
|
|
|
- if (isCookieExpires(*it))
|
|
|
- {
|
|
|
- _cookies.erase(it++);
|
|
|
- continue;
|
|
|
- }
|
|
|
- else if (it->_expires != 0) //非当前会话的
|
|
|
- {
|
|
|
- cookies.push_back(*it);
|
|
|
-
|
|
|
- ++it;
|
|
|
- }
|
|
|
- else
|
|
|
- ++it;
|
|
|
- }
|
|
|
-
|
|
|
- return cookies;
|
|
|
-}
|
|
|
-
|
|
|
-const list<TC_HttpCookie::Cookie> & TC_HttpCookie::getAllCookie()
|
|
|
-{
|
|
|
- deleteExpires(time(NULL));
|
|
|
-
|
|
|
- return _cookies;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpCookie::deleteExpires(time_t t, bool bErase)
|
|
|
-{
|
|
|
- list<Cookie>::iterator it = _cookies.begin();
|
|
|
-
|
|
|
- while (it != _cookies.end())
|
|
|
- {
|
|
|
- if (bErase && it->_expires == 0)
|
|
|
- {
|
|
|
- _cookies.erase(it++);
|
|
|
- continue;
|
|
|
- }
|
|
|
- else if (isCookieExpires(*it))
|
|
|
- {
|
|
|
- _cookies.erase(it++);
|
|
|
- continue;
|
|
|
- }
|
|
|
- else
|
|
|
- ++it;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/********************* TC_HttpResponse ***********************/
|
|
|
-//
|
|
|
-//void TC_HttpResponse::parseResponseHeader(const char* szBuffer, const char* header)
|
|
|
-//{
|
|
|
-// auto it = strstr(szBuffer, "\r\n");
|
|
|
-//
|
|
|
-// assert(it != NULL);
|
|
|
-//
|
|
|
-// string sep = " ";
|
|
|
-//
|
|
|
-// auto f1 = std::search(szBuffer, it, sep.c_str(), sep.c_str() + sep.size());
|
|
|
-// if(f1 == it)
|
|
|
-// {
|
|
|
-// throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response parse version format error : " + string(szBuffer, it-szBuffer));
|
|
|
-// }
|
|
|
-//
|
|
|
-// auto f2 = std::search(f1 + 1, it, sep.c_str(), sep.c_str() + sep.size());
|
|
|
-// if(f1 == it)
|
|
|
-// {
|
|
|
-// throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response parse status format error : " + string(szBuffer, it-szBuffer));
|
|
|
-// }
|
|
|
-//
|
|
|
-// _headerLine = string(szBuffer, it-szBuffer);
|
|
|
-//
|
|
|
-// if(TC_Port::strncasecmp(_headerLine.c_str(), "HTTP/", 5) != 0)
|
|
|
-// {
|
|
|
-// throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response version is not start with 'HTTP/' : " + _headerLine);
|
|
|
-// }
|
|
|
-//
|
|
|
-// _version = string(szBuffer, f1);
|
|
|
-//
|
|
|
-// _status = TC_Common::strto<int>(string(f1 + 1, f2));
|
|
|
-//
|
|
|
-// _about = TC_Common::trim(string(f2 + 1, it));
|
|
|
-//
|
|
|
-// parseHeader(szBuffer, header, _headers);
|
|
|
-//}
|
|
|
-//
|
|
|
-//void TC_HttpResponse::parseResponseHeader(TC_NetWorkBuffer &buff, TC_NetWorkBuffer::buffer_iterator &headerIt)
|
|
|
-//{
|
|
|
-// string line = "\r\n";
|
|
|
-// auto it = buff.find(line.c_str(), line.size());
|
|
|
-//
|
|
|
-// assert(it != buff.end());
|
|
|
-//
|
|
|
-// string sep = " ";
|
|
|
-//
|
|
|
-// auto f1 = std::search(buff.begin(), it, sep.c_str(), sep.c_str() + sep.size());
|
|
|
-// if(f1 == it)
|
|
|
-// {
|
|
|
-// throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response parse version format error : " + buff.iteratorToIterator<string>(buff.begin(), it));
|
|
|
-// }
|
|
|
-//
|
|
|
-// auto f2 = std::search(f1 + 1, it, sep.c_str(), sep.c_str() + sep.size());
|
|
|
-// if(f1 == it)
|
|
|
-// {
|
|
|
-// throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response parse status format error : " + buff.iteratorToIterator<string>(buff.begin(), it));
|
|
|
-// }
|
|
|
-//
|
|
|
-// _headerLine = buff.iteratorToIterator<string>(buff.begin(), it);
|
|
|
-//
|
|
|
-// if(TC_Port::strncasecmp(_headerLine.c_str(), "HTTP/", 5) != 0)
|
|
|
-// {
|
|
|
-// throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response version is not start with 'HTTP/' : " + _headerLine);
|
|
|
-// }
|
|
|
-//
|
|
|
-// _version = buff.iteratorToIterator<string>(buff.begin(), f1);
|
|
|
-//
|
|
|
-// _status = TC_Common::strto<int>(buff.iteratorToIterator<string>(f1 + 1, f2));
|
|
|
-//
|
|
|
-// _about = TC_Common::trim(buff.iteratorToIterator<string>(f2 + 1, it));
|
|
|
-//
|
|
|
-// parseHeader(buff.begin(), headerIt, _headers);
|
|
|
-//
|
|
|
-//}
|
|
|
-
|
|
|
-void TC_HttpResponse::reset()
|
|
|
-{
|
|
|
- TC_Http::reset();
|
|
|
-
|
|
|
- _status = 200;
|
|
|
- _about = "OK";
|
|
|
- _version = "HTTP/1.1";
|
|
|
-
|
|
|
- _iTmpContentLength = 0;
|
|
|
-
|
|
|
- _iRecvContentLength = 0;
|
|
|
-}
|
|
|
-
|
|
|
-vector<string> TC_HttpResponse::getSetCookie() const
|
|
|
-{
|
|
|
- return getHeaderMulti("Set-Cookie");
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpResponse::addContent(const string &sBuffer)
|
|
|
-{
|
|
|
- _content += sBuffer;
|
|
|
-
|
|
|
- _iRecvContentLength += sBuffer.length();
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpResponse::addContent(const char *buffer, size_t length)
|
|
|
-{
|
|
|
- _content.append(buffer, length);
|
|
|
-
|
|
|
- _iRecvContentLength += length;
|
|
|
-}
|
|
|
-
|
|
|
-size_t TC_HttpResponse::parseResponseHeaderString(const char *beginIt, const char *headerIt)
|
|
|
-{
|
|
|
- auto it = strnstr(beginIt, "\r\n", headerIt - beginIt);
|
|
|
-
|
|
|
- assert(it != NULL);
|
|
|
-
|
|
|
- auto f1 = strnstr(beginIt, " ", it - beginIt);
|
|
|
- if(f1 == NULL)
|
|
|
- {
|
|
|
- throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response parse version format error : " + string(beginIt, headerIt - beginIt));
|
|
|
- }
|
|
|
-
|
|
|
- auto f2 = strnstr(f1 + 1, " ", it - (f1 + 1));
|
|
|
- if(f1 == NULL)
|
|
|
- {
|
|
|
- throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response parse status format error : " + string(beginIt, headerIt - beginIt));
|
|
|
- }
|
|
|
-
|
|
|
- _headerLine = string(beginIt, it - beginIt);
|
|
|
-
|
|
|
- if(TC_Port::strncasecmp(_headerLine.c_str(), "HTTP/", 5) != 0)
|
|
|
- {
|
|
|
- throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response version is not start with 'HTTP/' : " + _headerLine);
|
|
|
- }
|
|
|
-
|
|
|
- _version = string(beginIt, f1 - beginIt);
|
|
|
-
|
|
|
- _status = TC_Common::strto<int>(string(f1 + 1, f2));
|
|
|
-
|
|
|
- _about = string(f2 + 1, it);
|
|
|
-
|
|
|
- return parseHeaderString(beginIt, headerIt, _headers);
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpResponse::incrementDecode(TC_NetWorkBuffer &buff)
|
|
|
-{
|
|
|
- if(buff.empty())
|
|
|
- return false;
|
|
|
- //解析头部
|
|
|
- if (_headLength == 0)
|
|
|
- {
|
|
|
- //至少把header合并成一个buff
|
|
|
- buff.mergeBuffers();
|
|
|
-
|
|
|
- auto data = buff.getBufferPointer();
|
|
|
-
|
|
|
- const char * p = strnstr(data.first, "\r\n\r\n", data.second);
|
|
|
- if(p == NULL)
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- _headLength = p - data.first + 4;
|
|
|
-
|
|
|
- _iTmpContentLength = parseResponseHeaderString(data.first, data.first + data.second + 2);
|
|
|
-
|
|
|
- //304的返回码中头里本来就没有Content-Length,也不会有数据体,头收全了就是真正的收全了
|
|
|
- if ( (204 == _status) || (304 == _status) )
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-//
|
|
|
-// http_header_type::const_iterator it = _headers.find("Content-Length");
|
|
|
-// if (it != _headers.end())
|
|
|
-// {
|
|
|
-// _iTmpContentLength = getContentLength();
|
|
|
-// }
|
|
|
-// else
|
|
|
-// {
|
|
|
-// //没有指明ContentLength, 接收到服务器关闭连接
|
|
|
-// _iTmpContentLength = -1;
|
|
|
-// }
|
|
|
-
|
|
|
- buff.moveHeader(_headLength);
|
|
|
-
|
|
|
- //重定向就认为成功了
|
|
|
- if ((_status == 301 || _status == 302) && hasHeader("Location"))
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-// return true;
|
|
|
-
|
|
|
- //是否是chunk编码
|
|
|
- _bIsChunked = checkHeader("Transfer-Encoding", "chunked");
|
|
|
-// _bIsChunked = (getHeader("Transfer-Encoding") == "chunked");
|
|
|
- if(_bIsChunked) {
|
|
|
- //删除头部里面
|
|
|
- eraseHeader("Transfer-Encoding");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (_bIsChunked)
|
|
|
- {
|
|
|
- while (true)
|
|
|
- {
|
|
|
- const static string sep = "\r\n";
|
|
|
-
|
|
|
- auto sit = std::search(buff.begin(), buff.end(), sep.c_str(), sep.c_str() + sep.size());
|
|
|
-
|
|
|
- if (sit == buff.end())
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- string header = buff.iteratorToIterator<string>(buff.begin(), sit);
|
|
|
-
|
|
|
- int iChunkSize = strtol(header.c_str(), NULL, 16);
|
|
|
-
|
|
|
- if (iChunkSize <= 0)
|
|
|
- {
|
|
|
- break; //所有chunk都接收完毕
|
|
|
- }
|
|
|
-
|
|
|
- if (buff.getBufferLength() < header.size() + 2 + (size_t)iChunkSize + 2)
|
|
|
- {
|
|
|
- //没有接收完整的chunk
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- //接收到一个完整的chunk了
|
|
|
- buff.moveHeader(header.size() + 2);
|
|
|
- addContent(buff.getHeader<string>(iChunkSize));
|
|
|
-
|
|
|
- //删除一个chunk
|
|
|
- buff.moveHeader(iChunkSize + 2);
|
|
|
- }
|
|
|
-
|
|
|
- buff.clearBuffers();
|
|
|
-
|
|
|
- //接收到buffer长度设置好
|
|
|
- setContentLength(_iRecvContentLength);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if (_iTmpContentLength == 0)
|
|
|
- {
|
|
|
- //header长度为0, 但是有body数据
|
|
|
- addContent(buff.getBuffersString());
|
|
|
-
|
|
|
- buff.clearBuffers();
|
|
|
-
|
|
|
- setContentLength(_iRecvContentLength);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- else if (_iTmpContentLength == (size_t) - 1)
|
|
|
- {
|
|
|
- //304的返回码中头没有Content-Length,不会有数据体
|
|
|
- if (_status == 304 || _status == 302)
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- //header中没长度, 但是有body数据
|
|
|
- addContent(buff.getBuffersString());
|
|
|
-
|
|
|
- buff.clearBuffers();
|
|
|
-
|
|
|
- setContentLength(_iRecvContentLength);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- //头部有长度, 接收到长度大于头部为止
|
|
|
- addContent(buff.getBuffersString());
|
|
|
-
|
|
|
- buff.clearBuffers();
|
|
|
-
|
|
|
- //头部的长度小于接收的内容, 还需要继续增加解析后续的buffer
|
|
|
- if (_iTmpContentLength > _iRecvContentLength)
|
|
|
- return false;
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpResponse::decode(const string &sBuffer)
|
|
|
-{
|
|
|
- return decode(sBuffer.c_str(), sBuffer.length());
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpResponse::decode(const char *sBuffer, size_t iLength)
|
|
|
-{
|
|
|
- assert(sBuffer != NULL);
|
|
|
-
|
|
|
- const char *p = strstr(sBuffer, "\r\n\r\n");
|
|
|
- if ( p == NULL)
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- //解析头部
|
|
|
-// parseResponseHeader(sBuffer, p + 2);
|
|
|
- _iTmpContentLength = parseResponseHeaderString(sBuffer, p + 2);
|
|
|
-
|
|
|
- //304的返回码中头里本来就没有Content-Length,也不会有数据体,头收全了就是真正的收全了
|
|
|
- if ( (204 == _status) || (304 == _status) )
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-//
|
|
|
-// http_header_type::const_iterator it = _headers.find("Content-Length");
|
|
|
-// if (it != _headers.end())
|
|
|
-// {
|
|
|
-// _iTmpContentLength = getContentLength();
|
|
|
-// }
|
|
|
-// else
|
|
|
-// {
|
|
|
-// //没有指明ContentLength, 接收到服务器关闭连接
|
|
|
-// _iTmpContentLength = -1;
|
|
|
-// }
|
|
|
-
|
|
|
- _headLength = p - sBuffer + 4;
|
|
|
-
|
|
|
- sBuffer += _headLength;
|
|
|
-
|
|
|
- //重定向就认为成功了
|
|
|
- if ((_status == 301 || _status == 302) && hasHeader("Location"))
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- //是否是chunk编码
|
|
|
- _bIsChunked = checkHeader("Transfer-Encoding", "chunked");
|
|
|
- if(_bIsChunked) {
|
|
|
- //删除头部里面
|
|
|
- eraseHeader("Transfer-Encoding");
|
|
|
- }
|
|
|
-
|
|
|
-// _bIsChunked = (getHeader("Transfer-Encoding") == "chunked");
|
|
|
-
|
|
|
- //删除头部里面
|
|
|
-// eraseHeader("Transfer-Encoding");
|
|
|
-
|
|
|
- if (_bIsChunked)
|
|
|
- {
|
|
|
- while (true)
|
|
|
- {
|
|
|
- p = strstr(sBuffer, "\r\n");
|
|
|
- if (p == NULL)
|
|
|
- return false;
|
|
|
-
|
|
|
- size_t pos = p - sBuffer;
|
|
|
-
|
|
|
- //查找当前chunk的大小
|
|
|
- string sChunkSize;
|
|
|
- sChunkSize.insert(sChunkSize.end(), sBuffer, p);
|
|
|
- int iChunkSize = strtol(sChunkSize.c_str(), NULL, 16);
|
|
|
-
|
|
|
- if (iChunkSize <= 0) break; //所有chunk都接收完毕
|
|
|
-
|
|
|
- if (strlen(sBuffer) < pos + 2 + (size_t)iChunkSize + 2)
|
|
|
- {
|
|
|
- //没有接收完整的chunk
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- //接收到一个完整的chunk了
|
|
|
- addContent(p + 2, iChunkSize);
|
|
|
-
|
|
|
- //删除一个chunk
|
|
|
- sBuffer += pos + 2 + iChunkSize + 2;
|
|
|
- }
|
|
|
-
|
|
|
- //接收到buffer长度设置好
|
|
|
- setContentLength(_iRecvContentLength);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if (_iTmpContentLength == 0)
|
|
|
- {
|
|
|
- //header长度为0, 但是有body数据
|
|
|
- addContent(sBuffer);
|
|
|
-
|
|
|
- setContentLength(_iRecvContentLength);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- else if (_iTmpContentLength == (size_t) - 1)
|
|
|
- {
|
|
|
- //304的返回码中头没有Content-Length,不会有数据体
|
|
|
- if (_status == 304 || _status == 302)
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- //header中没长度, 但是有body数据
|
|
|
- addContent(sBuffer);
|
|
|
-
|
|
|
- setContentLength(_iRecvContentLength);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- //头部有长度, 接收到长度大于头部为止
|
|
|
- addContent(sBuffer);
|
|
|
-
|
|
|
- //头部的长度小于接收的内容, 还需要继续增加解析后续的buffer
|
|
|
- if (_iTmpContentLength <= _iRecvContentLength)
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //body内容长度为0或者没有content-length 且 非chunk模式, 则认为包收全了, 直接返回
|
|
|
- if ((_iTmpContentLength == 0 || _iTmpContentLength == (size_t) - 1) && !_bIsChunked)
|
|
|
- return true;
|
|
|
-
|
|
|
- return (getContentLength() + getHeadLength() <= iLength);
|
|
|
-}
|
|
|
-
|
|
|
-string TC_HttpResponse::encode() const
|
|
|
-{
|
|
|
- string sRet;
|
|
|
- sRet.reserve(128);
|
|
|
-
|
|
|
- sRet += _version;
|
|
|
- sRet += " ";
|
|
|
- sRet += TC_Common::tostr(_status);
|
|
|
- sRet += " ";
|
|
|
- sRet += _about;
|
|
|
- sRet += "\r\n";
|
|
|
- genHeader(sRet);
|
|
|
- sRet += "\r\n";
|
|
|
- sRet += _content;
|
|
|
-
|
|
|
- return sRet;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpResponse::encode(vector<char> &buffer) const
|
|
|
-{
|
|
|
- buffer.clear();
|
|
|
-
|
|
|
- string s = encode();
|
|
|
-
|
|
|
- buffer.resize(s.length());
|
|
|
- memcpy(buffer.data(), s.c_str(), s.length());
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpResponse::setResponse(int status, const string& about, const string& body)
|
|
|
-{
|
|
|
- _status = status;
|
|
|
- _about = about;
|
|
|
- _content = body;
|
|
|
-
|
|
|
- /*stringstream os;
|
|
|
-
|
|
|
- os << _version << " " << _status << " " << _about;
|
|
|
-
|
|
|
- _headerLine = os.str();*/
|
|
|
- _headerLine = "";
|
|
|
- _headerLine += _version;
|
|
|
- _headerLine += " ";
|
|
|
- _headerLine += TC_Common::tostr(_status);
|
|
|
- _headerLine += " ";
|
|
|
- _headerLine += _about;
|
|
|
-
|
|
|
- setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpResponse::setResponse(int status, const string& about, const char *sBuffer, size_t iLength)
|
|
|
-{
|
|
|
- _status = status;
|
|
|
- _about = about;
|
|
|
-
|
|
|
- /*stringstream os;
|
|
|
-
|
|
|
- os << _version << " " << _status << " " << _about;
|
|
|
-
|
|
|
- _headerLine = os.str();*/
|
|
|
- _headerLine = "";
|
|
|
- _headerLine += _version;
|
|
|
- _headerLine += " ";
|
|
|
- _headerLine += TC_Common::tostr(_status);
|
|
|
- _headerLine += " ";
|
|
|
- _headerLine += _about;
|
|
|
-
|
|
|
- if (sBuffer != NULL && iLength > 0)
|
|
|
- {
|
|
|
- _content.assign(sBuffer, iLength);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _content.clear();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpResponse::setResponse(const char *sBuffer, size_t iLength)
|
|
|
-{
|
|
|
- setResponse(200, "OK", sBuffer, iLength);
|
|
|
-}
|
|
|
-
|
|
|
-/********************* TC_HttpRequest ***********************/
|
|
|
-
|
|
|
-void TC_HttpRequest::reset()
|
|
|
-{
|
|
|
- TC_Http::reset();
|
|
|
-
|
|
|
- _httpURL.clear();
|
|
|
-}
|
|
|
-//
|
|
|
-//const char * TC_HttpRequest::requestType2str(int iRequestType) const
|
|
|
-//{
|
|
|
-// if(iRequestType == REQUEST_GET)
|
|
|
-// {
|
|
|
-// return "GET";
|
|
|
-// }
|
|
|
-// else if(iRequestType == REQUEST_HEAD)
|
|
|
-// {
|
|
|
-// return "HEAD";
|
|
|
-// }
|
|
|
-// else if(iRequestType == REQUEST_POST)
|
|
|
-// {
|
|
|
-// return "POST";
|
|
|
-// }
|
|
|
-// else if(iRequestType == REQUEST_OPTIONS)
|
|
|
-// {
|
|
|
-// return "OPTIONS";
|
|
|
-// }
|
|
|
-// else if(iRequestType == REQUEST_PUT)
|
|
|
-// {
|
|
|
-// return "PUT";
|
|
|
-// }
|
|
|
-// else if(iRequestType == REQUEST_DELETE)
|
|
|
-// {
|
|
|
-// return "DELETE";
|
|
|
-// }
|
|
|
-// else if(iRequestType == REQUEST_PATCH)
|
|
|
-// {
|
|
|
-// return "PATCH";
|
|
|
-// }
|
|
|
-//// assert(true);
|
|
|
-// return "";
|
|
|
-//}
|
|
|
-
|
|
|
-vector<string> TC_HttpRequest::getCookie()
|
|
|
-{
|
|
|
- vector<string> v;
|
|
|
-
|
|
|
- http_header_type::const_iterator itEnd = _headers.end();
|
|
|
-
|
|
|
- for ( http_header_type::const_iterator it = _headers.begin(); it != itEnd; ++it)
|
|
|
- {
|
|
|
- if (it->first == "Cookie")
|
|
|
- {
|
|
|
- v.push_back(it->second);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return v;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::parseURL(const string& sUrl)
|
|
|
-{
|
|
|
- _httpURL.parseURL(sUrl);
|
|
|
-
|
|
|
- //设置Host
|
|
|
- if (!hasHeader("Host"))
|
|
|
- {
|
|
|
- string sPort = _httpURL.isDefaultPort() ? "" : ":" + _httpURL.getPort();
|
|
|
-
|
|
|
- //缺省端口可以不用放进去
|
|
|
- setHost(_httpURL.getDomain() + sPort);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setRequest(const string& method, const string &sUrl, const std::string& body, bool bNewCreateHost)
|
|
|
-{
|
|
|
- // std::string lowMethod(method);
|
|
|
- // std::transform(method.begin(), method.end(), lowMethod.begin(), ::tolower);
|
|
|
-
|
|
|
- if (TC_Port::strncasecmp(method.c_str(), "GET", 3) == 0)
|
|
|
- setGetRequest(sUrl, bNewCreateHost);
|
|
|
- else if (TC_Port::strncasecmp(method.c_str(), "HEAD", 4) == 0)
|
|
|
- setHeadRequest(sUrl, bNewCreateHost);
|
|
|
- else if (TC_Port::strncasecmp(method.c_str(), "POST", 4) == 0)
|
|
|
- setPostRequest(sUrl, body, bNewCreateHost);
|
|
|
- else if (TC_Port::strncasecmp(method.c_str(), "PUT", 3) == 0)
|
|
|
- setPutRequest(sUrl, body, bNewCreateHost);
|
|
|
- else if (TC_Port::strncasecmp(method.c_str(), "DELETE", 6) == 0)
|
|
|
- setDeleteRequest(sUrl, body, bNewCreateHost);
|
|
|
- else if (TC_Port::strncasecmp(method.c_str(), "PATH", 5) == 0)
|
|
|
- setPatchRequest(sUrl, body, bNewCreateHost);
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setGetRequest(const string &sUrl, bool bNewCreateHost)
|
|
|
-{
|
|
|
- if (bNewCreateHost)
|
|
|
- {
|
|
|
- eraseHeader("Host");
|
|
|
- }
|
|
|
-
|
|
|
- parseURL(sUrl);
|
|
|
-
|
|
|
- _requestType = REQUEST_GET;
|
|
|
-
|
|
|
- _content = "";
|
|
|
-
|
|
|
- eraseHeader("Content-Length");
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setHeadRequest(const string &sUrl, bool bNewCreateHost)
|
|
|
-{
|
|
|
- if (bNewCreateHost)
|
|
|
- {
|
|
|
- eraseHeader("Host");
|
|
|
- }
|
|
|
-
|
|
|
- parseURL(sUrl);
|
|
|
-
|
|
|
- _requestType = REQUEST_HEAD;
|
|
|
-
|
|
|
- _content = "";
|
|
|
-
|
|
|
- eraseHeader("Content-Length");
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setPostRequest(const string &sUrl, const string &sPostBody, bool bNewCreateHost)
|
|
|
-{
|
|
|
- if (bNewCreateHost)
|
|
|
- {
|
|
|
- eraseHeader("Host");
|
|
|
- }
|
|
|
-
|
|
|
- parseURL(sUrl);
|
|
|
-
|
|
|
- _requestType = REQUEST_POST;
|
|
|
-
|
|
|
- _content = sPostBody;
|
|
|
-
|
|
|
- setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setOptionsRequest(const string &sUrl, bool bNewCreateHost)
|
|
|
-{
|
|
|
- if (bNewCreateHost)
|
|
|
- {
|
|
|
- eraseHeader("Host");
|
|
|
- }
|
|
|
-
|
|
|
- parseURL(sUrl);
|
|
|
-
|
|
|
- _requestType = REQUEST_OPTIONS;
|
|
|
-
|
|
|
- _content = "";
|
|
|
-
|
|
|
- eraseHeader("Content-Length");
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setPostRequest(const string &sUrl, const char *sBuffer, size_t iLength, bool bNewCreateHost)
|
|
|
-{
|
|
|
- assert(sBuffer != NULL);
|
|
|
-
|
|
|
- if (bNewCreateHost)
|
|
|
- {
|
|
|
- eraseHeader("Host");
|
|
|
- }
|
|
|
-
|
|
|
- parseURL(sUrl);
|
|
|
-
|
|
|
- _requestType = REQUEST_POST;
|
|
|
-
|
|
|
- if (iLength > 0)
|
|
|
- {
|
|
|
- _content.assign(sBuffer, iLength);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _content.clear();
|
|
|
- }
|
|
|
-
|
|
|
- setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-void TC_HttpRequest::setPutRequest(const string &sUrl, const string &sPostBody, bool bNewCreateHost )
|
|
|
-{
|
|
|
- if(bNewCreateHost)
|
|
|
- {
|
|
|
- eraseHeader("Host");
|
|
|
- }
|
|
|
-
|
|
|
- parseURL(sUrl);
|
|
|
-
|
|
|
- _requestType = REQUEST_PUT;
|
|
|
-
|
|
|
- _content = sPostBody;
|
|
|
-
|
|
|
- setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setPatchRequest(const string &sUrl, const string &sPostBody, bool bNewCreateHost )
|
|
|
-{
|
|
|
- if(bNewCreateHost)
|
|
|
- {
|
|
|
- eraseHeader("Host");
|
|
|
- }
|
|
|
-
|
|
|
- parseURL(sUrl);
|
|
|
-
|
|
|
- _requestType = REQUEST_PATCH;
|
|
|
-
|
|
|
- _content = sPostBody;
|
|
|
-
|
|
|
- setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-void TC_HttpRequest::setDeleteRequest(const string &sUrl, const string &sPostBody, bool bNewCreateHost )
|
|
|
-{
|
|
|
- if(bNewCreateHost)
|
|
|
- {
|
|
|
- eraseHeader("Host");
|
|
|
- }
|
|
|
-
|
|
|
- parseURL(sUrl);
|
|
|
-
|
|
|
- _requestType = REQUEST_DELETE;
|
|
|
-
|
|
|
- _content = sPostBody;
|
|
|
-
|
|
|
- setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-string TC_HttpRequest::encode()
|
|
|
-{
|
|
|
- string sRet;
|
|
|
- sRet.reserve(128);
|
|
|
- if(_requestType == REQUEST_GET || _requestType == REQUEST_OPTIONS || _requestType == REQUEST_HEAD)
|
|
|
- {
|
|
|
- sRet += getMethod();//(_requestType);
|
|
|
- sRet += " ";
|
|
|
- sRet += _httpURL.getRequest();
|
|
|
- sRet += " HTTP/1.1\r\n";
|
|
|
- genHeader(sRet);
|
|
|
- sRet += "\r\n";
|
|
|
- }
|
|
|
- else if(_requestType == REQUEST_POST || _requestType == REQUEST_PUT || _requestType == REQUEST_PATCH || _requestType == REQUEST_DELETE)
|
|
|
- {
|
|
|
- setContentLength(_content.length());
|
|
|
- sRet += getMethod();//requestType2str(_requestType);
|
|
|
- sRet += " ";
|
|
|
- sRet += _httpURL.getRequest();
|
|
|
- sRet += " HTTP/1.1\r\n";
|
|
|
- genHeader(sRet);
|
|
|
- sRet += "\r\n";
|
|
|
- sRet += _content;
|
|
|
- }
|
|
|
- return sRet;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::encode(vector<char> &sRet)
|
|
|
-{
|
|
|
- string s = encode();
|
|
|
- sRet.resize(s.length());
|
|
|
- memcpy(sRet.data(), s.c_str(), s.length());
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::encode(TC_NetWorkBuffer &buff)
|
|
|
-{
|
|
|
- buff.addBuffer(std::move(encode()));
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpRequest::decode(const string &sBuffer)
|
|
|
-{
|
|
|
- return decode(sBuffer.c_str(), sBuffer.length());
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpRequest::decode(const vector<char> &sBuffer)
|
|
|
-{
|
|
|
- return decode(sBuffer.data(), sBuffer.size());
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpRequest::decode(const char *sBuffer, size_t iLength)
|
|
|
-{
|
|
|
- assert(sBuffer != NULL);
|
|
|
-
|
|
|
- const char *p = strnstr(sBuffer, " ", 10);
|
|
|
- if(p == NULL)
|
|
|
- {
|
|
|
- throw runtime_error("[TC_HttpRequest::decode] http protocol parse error");
|
|
|
- }
|
|
|
-
|
|
|
- auto it = TC_Http::HEADER.find(string(sBuffer, p - sBuffer));
|
|
|
- if(it == TC_Http::HEADER.end())
|
|
|
- {
|
|
|
- throw runtime_error("[TC_HttpRequest::decode] protocol not support ");
|
|
|
- }
|
|
|
-
|
|
|
- p = strstr(sBuffer, "\r\n\r\n");
|
|
|
- if (p == NULL)
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- _requestType = it->second;
|
|
|
-
|
|
|
- parseRequestHeader(sBuffer, p);
|
|
|
-
|
|
|
- _headLength = p - sBuffer + 4;
|
|
|
-
|
|
|
- bool bChunk = checkHeader("Transfer-Encoding", "chunked"); //(getHeader("Transfer-Encoding") == "chunked");
|
|
|
-
|
|
|
- int iChunkSuffixLen = 0;
|
|
|
-
|
|
|
- if (bChunk)
|
|
|
- {
|
|
|
- p = sBuffer + _headLength;
|
|
|
-// string sTmp(sBuffer + _headLength, iLength - _headLength);
|
|
|
- while (true)
|
|
|
- {
|
|
|
-// string::size_type pos = sTmp.find("\r\n");
|
|
|
- const char *pos = strstr(p, "\r\n");
|
|
|
- if (pos == NULL)
|
|
|
- return false;
|
|
|
-
|
|
|
- //查找当前chunk的大小
|
|
|
-// string sChunkSize = sTmp.substr(0, pos);
|
|
|
- string sChunkSize(p, pos - p);
|
|
|
-// sTmp.substr(0, pos);
|
|
|
- int iChunkSize = strtol(sChunkSize.c_str(), NULL, 16);
|
|
|
-
|
|
|
- iChunkSuffixLen = iChunkSuffixLen + sChunkSize.length();
|
|
|
- if (iChunkSize <= 0)
|
|
|
- {
|
|
|
- iChunkSuffixLen = iChunkSuffixLen + 4;
|
|
|
- break; //所有chunk都接收完毕
|
|
|
- }
|
|
|
-// if (sTmp.length() >= pos + 2 + (size_t)iChunkSize + 2) //接收到一个完整的chunk了
|
|
|
- if ((sBuffer+iLength-p) >= (pos - p) + 2 + iChunkSize + 2) //接收到一个完整的chunk了
|
|
|
- {
|
|
|
- //获取一个chunk的内容
|
|
|
-// _content += sTmp.substr(pos + 2, iChunkSize);
|
|
|
- _content.append(pos + 2, iChunkSize);
|
|
|
-
|
|
|
- //删除一个chunk
|
|
|
-// sTmp = sTmp.substr(pos + 2 + iChunkSize + 2);
|
|
|
- p = pos + 2 + iChunkSize + 2;
|
|
|
- iChunkSuffixLen = iChunkSuffixLen + 4;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- //没有接收完整的chunk
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- setContentLength(getContent().length());
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _content.assign((sBuffer + _headLength), iLength - _headLength);
|
|
|
- }
|
|
|
-
|
|
|
- return (getContentLength() + getHeadLength() + iChunkSuffixLen == iLength);
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpRequest::checkRequest(TC_NetWorkBuffer &buff)
|
|
|
-{
|
|
|
- buff.mergeBuffers();
|
|
|
-
|
|
|
- return checkRequest(buff.getBufferPointer().first, buff.getBufferLength());
|
|
|
-//
|
|
|
-// size_t *headerLength = (size_t *)buff.getContextData();
|
|
|
-//
|
|
|
-// if(headerLength == NULL)
|
|
|
-// {
|
|
|
-// headerLength = new size_t(0);
|
|
|
-//
|
|
|
-// buff.setContextData(headerLength, [=]{ delete headerLength;});
|
|
|
-// }
|
|
|
-//
|
|
|
-// if(*headerLength == 0)
|
|
|
-// {
|
|
|
-// //header还没有解析出来, 合并buffer
|
|
|
-// buff.mergeBuffers();
|
|
|
-//
|
|
|
-// auto data = buff.getBufferPointer();
|
|
|
-//
|
|
|
-// const char *p = strnstr(data.first, " ", 10);
|
|
|
-// if(p == NULL)
|
|
|
-// {
|
|
|
-// throw runtime_error("[TC_HttpRequest::checkRequest] http protocol parse error");
|
|
|
-// }
|
|
|
-//
|
|
|
-// auto it = TC_Http::HEADER.find(string(data.first, p - data.first));
|
|
|
-// if(it == TC_Http::HEADER.end())
|
|
|
-// {
|
|
|
-// throw runtime_error("[TC_HttpRequest::checkRequest] protocol not support");
|
|
|
-// }
|
|
|
-//
|
|
|
-//// if (buff.getBufferLength() < 10)
|
|
|
-//// return false;
|
|
|
-//
|
|
|
-//// auto data = buff.getBufferPointer();
|
|
|
-//// if (TC_Port::strncasecmp(data.first, "GET ", 4) != 0 &&
|
|
|
-//// TC_Port::strncasecmp(data.first, "POST ", 5) != 0 &&
|
|
|
-//// TC_Port::strncasecmp(data.first, "PUT ", 4) != 0 &&
|
|
|
-//// TC_Port::strncasecmp(data.first, "PATCH ", 6) != 0 &&
|
|
|
-//// TC_Port::strncasecmp(data.first, "OPTIONS ", 8) != 0 &&
|
|
|
-//// TC_Port::strncasecmp(data.first, "PRI ", 4) != 0 &&
|
|
|
-//// TC_Port::strncasecmp(data.first, "DELETE ", 7) != 0 &&
|
|
|
-//// TC_Port::strncasecmp(data.first, "HEAD ", 5)) {
|
|
|
-//// throw runtime_error(
|
|
|
-//// "[TC_HttpRequest::decode] protocol not support, only support GET HEAD POST PUT PATCH DELETE and OPTIONS ");
|
|
|
-//// }
|
|
|
-//
|
|
|
-//// string sep = "\r\n\r\n";
|
|
|
-////
|
|
|
-//// auto sit = buff.find(sep.c_str(), sep.size());
|
|
|
-//// if (sit == buff.end()) {
|
|
|
-//// return false;
|
|
|
-//// }
|
|
|
-//
|
|
|
-// p = strnstr(data.first, "\r\n\r\n", data.second);
|
|
|
-// if(p == NULL) {
|
|
|
-// return false;
|
|
|
-// }
|
|
|
-//
|
|
|
-//// size_t pos = p ;
|
|
|
-//
|
|
|
-// *headerLength = p - data.first + 4;
|
|
|
-//
|
|
|
-//// bool bChunk = false;
|
|
|
-//
|
|
|
-//// sep = "\r\n";
|
|
|
-//
|
|
|
-// p = strnstr(data.first, "\r\n", data.second);
|
|
|
-//
|
|
|
-// assert(p != NULL);
|
|
|
-//// if(p == NULL) {
|
|
|
-//// return false;
|
|
|
-//// }
|
|
|
-////
|
|
|
-//// auto it = std::search(buff.begin(), buff.end(), sep.c_str(), sep.c_str() + sep.size());
|
|
|
-//// if (it == buff.end()) {
|
|
|
-//// //first line
|
|
|
-//// return false;
|
|
|
-//// }
|
|
|
-//
|
|
|
-//// it = it + sep.size();
|
|
|
-// //move to next line
|
|
|
-// p += 2;
|
|
|
-// }
|
|
|
-//
|
|
|
-// bool bChunk = false;
|
|
|
-//
|
|
|
-// size_t len = 0;
|
|
|
-//
|
|
|
-// //找到\r\n\r\n之前的长度表示
|
|
|
-// while (true)
|
|
|
-// {
|
|
|
-// size_t iMoveLen = it - buff.begin();
|
|
|
-// if (iMoveLen >= iHeadLen)
|
|
|
-// {
|
|
|
-// break;
|
|
|
-// }
|
|
|
-//
|
|
|
-// auto lineItStart = it;
|
|
|
-//
|
|
|
-// auto lineItEnd = std::search(lineItStart, buff.end(), sep.c_str(), sep.c_str() + sep.size());
|
|
|
-//
|
|
|
-// if (lineItEnd != buff.end())
|
|
|
-// {
|
|
|
-// it = lineItEnd + sep.size();
|
|
|
-//
|
|
|
-// std::function<bool(char, char)> cmp = [](char i, char j)
|
|
|
-// {
|
|
|
-// return toupper(i) == toupper(j);
|
|
|
-// };
|
|
|
-//
|
|
|
-// const char *TE = "Transfer-Encoding:";
|
|
|
-//
|
|
|
-// auto itTE = std::search(lineItStart, lineItEnd, TE, TE + strlen(TE), cmp);
|
|
|
-//
|
|
|
-// if (itTE != lineItEnd)
|
|
|
-// {
|
|
|
-// const char *CH = "chunked";
|
|
|
-//
|
|
|
-// auto itCH = std::search(lineItStart + strlen(TE), lineItEnd, CH, CH + strlen(CH), cmp);
|
|
|
-//
|
|
|
-// if (itCH != lineItEnd)
|
|
|
-// {
|
|
|
-// bChunk = true;
|
|
|
-// break;
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// const char *CL = "Content-Length:";
|
|
|
-//
|
|
|
-// auto itCL = std::search(lineItStart, lineItEnd, CL, CL + strlen(CL), cmp);
|
|
|
-// if (itCL == lineItEnd)
|
|
|
-// {
|
|
|
-// continue;
|
|
|
-// }
|
|
|
-//
|
|
|
-// string contentLength;
|
|
|
-// contentLength.resize(lineItEnd - lineItStart - strlen(CL));
|
|
|
-// std::copy(lineItStart + strlen(CL), lineItEnd, contentLength.begin());
|
|
|
-//
|
|
|
-// len = taf::TC_Common::strto<size_t>(TC_Common::trim(contentLength, " "));
|
|
|
-// }
|
|
|
-//
|
|
|
-// }
|
|
|
-//
|
|
|
-// if (bChunk)
|
|
|
-// {
|
|
|
-// int remain_len = buff.getBufferLength() - iHeadLen;
|
|
|
-// int move_len = 0;
|
|
|
-//
|
|
|
-// auto it = sit + 4;
|
|
|
-// while (true)
|
|
|
-// {
|
|
|
-// auto lineIt = std::search(it, buff.end(), sep.c_str(), sep.c_str() + sep.size());
|
|
|
-// if ( lineIt == buff.end() )
|
|
|
-// {
|
|
|
-// return false;
|
|
|
-// }
|
|
|
-//
|
|
|
-// //查找当前chunk的大小
|
|
|
-// string contentLength;
|
|
|
-// contentLength.resize(lineIt - it);
|
|
|
-// std::copy(it, lineIt, contentLength.begin());
|
|
|
-//
|
|
|
-// int iChunkSize = strtol(contentLength.c_str(), NULL, 16);
|
|
|
-// if (iChunkSize <= 0)
|
|
|
-// {
|
|
|
-// return true; //所有chunk都接收完毕
|
|
|
-// }
|
|
|
-//
|
|
|
-// move_len = (lineIt - it) + 2 + iChunkSize + 2;
|
|
|
-// if ( remain_len >= move_len ) //接收到一个完整的chunk了
|
|
|
-// {
|
|
|
-// //移动到下一个chunk
|
|
|
-// remain_len -= move_len;
|
|
|
-// it = lineIt + 2 + iChunkSize + 2;
|
|
|
-// }
|
|
|
-// else
|
|
|
-// {
|
|
|
-// return false;
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-// else if (len + pos + 4 <= buff.getBufferLength())
|
|
|
-// {
|
|
|
-// return true;
|
|
|
-// }
|
|
|
-//
|
|
|
-// return false;
|
|
|
-}
|
|
|
-
|
|
|
-bool TC_HttpRequest::checkRequest(const char* sBuffer, size_t iLen)
|
|
|
-{
|
|
|
- if(iLen < 10)
|
|
|
- return false;
|
|
|
-
|
|
|
- const char *p = strnstr(sBuffer, " ", 10);
|
|
|
- if(p == NULL)
|
|
|
- {
|
|
|
- throw runtime_error("[TC_HttpRequest::checkRequest] http protocol parse error");
|
|
|
- }
|
|
|
-
|
|
|
- auto it = TC_Http::HEADER.find(string(sBuffer, p - sBuffer));
|
|
|
- if(it == TC_Http::HEADER.end())
|
|
|
- {
|
|
|
- throw runtime_error("[TC_HttpRequest::checkRequest] protocol not support: " + string(sBuffer, 8));
|
|
|
- }
|
|
|
-//
|
|
|
-// if(TC_Port::strncasecmp(sBuffer, "GET " ,4) !=0 &&
|
|
|
-// TC_Port::strncasecmp(sBuffer, "POST " ,5) !=0 &&
|
|
|
-// TC_Port::strncasecmp(sBuffer, "PUT " ,4) !=0 &&
|
|
|
-// TC_Port::strncasecmp(sBuffer, "PATCH " ,6) !=0 &&
|
|
|
-// TC_Port::strncasecmp(sBuffer, "OPTIONS " ,8) !=0 &&
|
|
|
-// TC_Port::strncasecmp(sBuffer, "PRI " , 4) !=0 &&
|
|
|
-// TC_Port::strncasecmp(sBuffer, "DELETE " , 7) !=0 &&
|
|
|
-// TC_Port::strncasecmp(sBuffer, "HEAD " ,5))
|
|
|
-// {
|
|
|
-// throw runtime_error("[TC_HttpRequest::decode] protocol not support:" + string(sBuffer, 8));
|
|
|
-// }
|
|
|
-
|
|
|
- const char *header = strstr(sBuffer, "\r\n\r\n");
|
|
|
- if ( header == NULL)
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- size_t pos = header - sBuffer;
|
|
|
-
|
|
|
- size_t iHeadLen = pos + 4;
|
|
|
-
|
|
|
- p = strstr(sBuffer, "\r\n");
|
|
|
- if(p == NULL)
|
|
|
- {
|
|
|
- //first line
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- p += 2;
|
|
|
-
|
|
|
- bool bChunk = false;
|
|
|
-
|
|
|
- size_t len = 0;
|
|
|
-
|
|
|
- //找到\r\n\r\n之前的长度表示
|
|
|
- while (true)
|
|
|
- {
|
|
|
- size_t iMoveLen = p - sBuffer;
|
|
|
- if ( iMoveLen >= iHeadLen )
|
|
|
- {
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- const char *line = p;
|
|
|
- const char *lineEnd = strstr(line, "\r\n");
|
|
|
- if (lineEnd != NULL)
|
|
|
- {
|
|
|
- p = lineEnd + 2;
|
|
|
-
|
|
|
- if (TC_Port::strncasecmp(line, "Transfer-Encoding:", 18) == 0)
|
|
|
- {
|
|
|
- string chunked = TC_Common::trim(string(line + 18, lineEnd));
|
|
|
- bChunk = (chunked == "chunked");
|
|
|
- if (bChunk) break;
|
|
|
- }
|
|
|
- else if (TC_Port::strncasecmp(line, "Content-Length:", 15) != 0)
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- len = TC_Common::strto<size_t>(TC_Common::trim(string(line + 15, lineEnd), " "));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (bChunk)
|
|
|
- {
|
|
|
- int remain_len = iLen - iHeadLen;
|
|
|
- int move_len = 0;
|
|
|
- const char * pCur = header + 4;
|
|
|
- while (true)
|
|
|
- {
|
|
|
- p = strstr(pCur , "\r\n");
|
|
|
- if ( p == NULL )
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- //查找当前chunk的大小
|
|
|
- int iChunkSize = strtol(string(pCur, p - pCur).c_str(), NULL, 16);
|
|
|
- if (iChunkSize <= 0)
|
|
|
- {
|
|
|
- return true; //所有chunk都接收完毕
|
|
|
- }
|
|
|
-
|
|
|
- move_len = (p - pCur) + 2 + iChunkSize + 2;
|
|
|
- if ( remain_len >= move_len ) //接收到一个完整的chunk了
|
|
|
- {
|
|
|
- //移动到下一个chunk
|
|
|
- remain_len -= move_len;
|
|
|
- pCur = p + 2 + iChunkSize + 2;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else if (len + pos + 4 <= iLen)
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-size_t TC_Http::parseHeaderString(const char *beginIt, const char *headerIt, TC_Http::http_header_type &sHeader)
|
|
|
-{
|
|
|
- size_t contentLength = -1;
|
|
|
-
|
|
|
- sHeader.clear();
|
|
|
-
|
|
|
- bool first = true;
|
|
|
- auto lineStartIt= beginIt;
|
|
|
- bool isLastLine = false;
|
|
|
-
|
|
|
- while (true)
|
|
|
- {
|
|
|
- auto it = strnstr(lineStartIt, "\r\n", headerIt - lineStartIt);
|
|
|
- if(it == NULL)
|
|
|
- {
|
|
|
- it = headerIt;
|
|
|
- isLastLine = true;
|
|
|
- }
|
|
|
-
|
|
|
- //first line ignore
|
|
|
- if(!first)
|
|
|
- {
|
|
|
- auto itF = strnstr(lineStartIt, ":", it - lineStartIt);
|
|
|
- if (itF != NULL)
|
|
|
- {
|
|
|
- while(*lineStartIt == ' ')
|
|
|
- ++lineStartIt;
|
|
|
-
|
|
|
- const char *s = itF;
|
|
|
- while(*s == ' ')
|
|
|
- --s;
|
|
|
-
|
|
|
- string name(lineStartIt, s - lineStartIt);
|
|
|
-
|
|
|
- ++itF;
|
|
|
- while(*itF == ' ')
|
|
|
- ++itF;
|
|
|
-
|
|
|
- s = it;
|
|
|
- while(*s == ' ')
|
|
|
- --s;
|
|
|
-
|
|
|
- string value(itF, s-itF);
|
|
|
-
|
|
|
- if (TC_Port::strncasecmp(name.c_str(), "Content-Length", 14) == 0)
|
|
|
- {
|
|
|
- contentLength = TC_Common::strto<size_t>(value);
|
|
|
- }
|
|
|
-
|
|
|
- sHeader.insert(multimap<string, string>::value_type(std::move(name), std::move(value)));
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- first = false;
|
|
|
- }
|
|
|
-
|
|
|
- if (isLastLine)
|
|
|
- {
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- lineStartIt = it + 2;//sep.size();
|
|
|
- }
|
|
|
-
|
|
|
- return contentLength;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::parseRequestHeader(const char* szBuffer, const char *header)
|
|
|
-{
|
|
|
- const char *p = strstr(szBuffer, "\r\n");
|
|
|
-
|
|
|
- assert(p != NULL);
|
|
|
-
|
|
|
-// string sep = " ";
|
|
|
-// auto f1 = std::search(szBuffer, p, sep.c_str(), sep.c_str() + sep.size());
|
|
|
- auto f1 = strnstr(szBuffer, " ", p - szBuffer);
|
|
|
- if (f1 == p)
|
|
|
- {
|
|
|
- throw TC_HttpRequest_Exception("[TC_HttpRequest::parseRequestHeader] http request format error: " + string(szBuffer, p - szBuffer));
|
|
|
- }
|
|
|
-
|
|
|
-// auto f2 = std::search(f1 + 1, p, sep.c_str(), sep.c_str() + sep.size());
|
|
|
- auto f2 = strnstr(f1 + 1, " ", p - f1 + 1);//std::search(f1 + 1, p, sep.c_str(), sep.c_str() + sep.size());
|
|
|
- if (f2 == p || f1 >= f2)
|
|
|
- {
|
|
|
- throw TC_HttpRequest_Exception("[TC_HttpRequest::parseRequestHeader] http request format error: " + string(szBuffer, p - szBuffer));
|
|
|
- }
|
|
|
-
|
|
|
- //URL地址
|
|
|
- string sURL(f1 + 1, f2 - f1 - 1 );
|
|
|
-
|
|
|
- //HTTP协议版本
|
|
|
- _version.assign(f2 + 1, p - f2 - 1);
|
|
|
-
|
|
|
- parseHeaderString(szBuffer, header, _headers);
|
|
|
-
|
|
|
- if (TC_Port::strncasecmp(f1 + 1, "https://", 8) != 0 )
|
|
|
- {
|
|
|
- if (TC_Port::strncasecmp(f1 + 1, "http://", 7) != 0 )
|
|
|
- {
|
|
|
- sURL = "http://" + getHost() + sURL;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- parseURL(sURL);
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::getHostPort(string &sDomain, uint32_t &iPort)
|
|
|
-{
|
|
|
- sDomain = _httpURL.getDomain();
|
|
|
-
|
|
|
- iPort = TC_Common::strto<uint32_t>(_httpURL.getPort());
|
|
|
-}
|
|
|
-
|
|
|
-const string &TC_HttpRequest::getMethod() const
|
|
|
-{
|
|
|
- auto it = TC_Http::HEADER_REVERSE.find(_requestType);
|
|
|
- if(it != TC_Http::HEADER_REVERSE.end())
|
|
|
- {
|
|
|
- return it->second;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- throw TC_HttpRequest_Exception("[TC_HttpRequest::setMethod] http request command error: " + TC_Common::tostr(_requestType));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setMethod(const char * sMethod)
|
|
|
-{
|
|
|
- auto it = TC_Http::HEADER.find(sMethod);
|
|
|
- if(it != TC_Http::HEADER.end())
|
|
|
- {
|
|
|
- _requestType = it->second;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- throw TC_HttpRequest_Exception("[TC_HttpRequest::setMethod] http request command error: " + string(sMethod));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setPath(const char *path)
|
|
|
-{
|
|
|
- _httpURL._sPath = path;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setDomain(const char * sDomain)
|
|
|
-{
|
|
|
- _httpURL._sDomain = sDomain;
|
|
|
-}
|
|
|
-
|
|
|
-void TC_HttpRequest::setScheme(const char * sScheme)
|
|
|
-{
|
|
|
- _httpURL._sScheme = sScheme;
|
|
|
-}
|
|
|
-
|
|
|
-int TC_HttpRequest::doRequest(TC_TCPClient& tcpClient, TC_HttpResponse& stHttpRsp)
|
|
|
-{
|
|
|
- //只支持短连接模式
|
|
|
- setConnection("close");
|
|
|
-
|
|
|
- string sSendBuffer = encode();
|
|
|
-
|
|
|
- int iRet = tcpClient.send(sSendBuffer.c_str(), sSendBuffer.length());
|
|
|
- if (iRet != TC_ClientSocket::EM_SUCCESS)
|
|
|
- {
|
|
|
- return iRet;
|
|
|
- }
|
|
|
-
|
|
|
- stHttpRsp.reset();
|
|
|
-
|
|
|
- TC_NetWorkBuffer recvBuffer(NULL);
|
|
|
-
|
|
|
- while (true)
|
|
|
- {
|
|
|
- char buffer[8*1024];
|
|
|
- size_t iRecvLen = sizeof(buffer);
|
|
|
-
|
|
|
- iRet = tcpClient.recv(buffer, iRecvLen);
|
|
|
-
|
|
|
- if (iRet == TC_ClientSocket::EM_SUCCESS)
|
|
|
- {
|
|
|
- recvBuffer.addBuffer(buffer, iRecvLen);
|
|
|
- }
|
|
|
-
|
|
|
- switch (iRet)
|
|
|
- {
|
|
|
- case TC_ClientSocket::EM_SUCCESS:
|
|
|
- if (stHttpRsp.incrementDecode(recvBuffer))
|
|
|
- {
|
|
|
- return TC_ClientSocket::EM_SUCCESS;
|
|
|
- }
|
|
|
- continue;
|
|
|
- case TC_ClientSocket::EM_CLOSE:
|
|
|
- stHttpRsp.incrementDecode(recvBuffer);
|
|
|
- return TC_ClientSocket::EM_SUCCESS;
|
|
|
- default:
|
|
|
- return iRet;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- assert(true);
|
|
|
-
|
|
|
- return 0;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-int TC_HttpRequest::doRequest(TC_HttpResponse &stHttpRsp, int iTimeout)
|
|
|
-{
|
|
|
- //只支持短连接模式
|
|
|
- setConnection("close");
|
|
|
-
|
|
|
- string sSendBuffer = encode();
|
|
|
-
|
|
|
- string sHost;
|
|
|
- uint32_t iPort;
|
|
|
-
|
|
|
- getHostPort(sHost, iPort);
|
|
|
-
|
|
|
- TC_TCPClient tcpClient;
|
|
|
- tcpClient.init(sHost, iPort, iTimeout);
|
|
|
-
|
|
|
- int iRet = tcpClient.send(sSendBuffer.c_str(), sSendBuffer.length());
|
|
|
- if (iRet != TC_ClientSocket::EM_SUCCESS)
|
|
|
- {
|
|
|
- return iRet;
|
|
|
- }
|
|
|
-
|
|
|
- stHttpRsp.reset();
|
|
|
-
|
|
|
- TC_NetWorkBuffer recvBuffer(NULL);
|
|
|
-
|
|
|
- while (true)
|
|
|
- {
|
|
|
- char buffer[8*1024];
|
|
|
- size_t iRecvLen = sizeof(buffer);
|
|
|
-
|
|
|
- iRet = tcpClient.recv(buffer, iRecvLen);
|
|
|
-
|
|
|
- if (iRet == TC_ClientSocket::EM_SUCCESS)
|
|
|
- {
|
|
|
- recvBuffer.addBuffer(buffer, iRecvLen);
|
|
|
- }
|
|
|
-
|
|
|
- switch (iRet)
|
|
|
- {
|
|
|
- case TC_ClientSocket::EM_SUCCESS:
|
|
|
- if (stHttpRsp.incrementDecode(recvBuffer))
|
|
|
- {
|
|
|
- return TC_ClientSocket::EM_SUCCESS;
|
|
|
- }
|
|
|
- continue;
|
|
|
- case TC_ClientSocket::EM_CLOSE:
|
|
|
- stHttpRsp.incrementDecode(recvBuffer);
|
|
|
- return TC_ClientSocket::EM_SUCCESS;
|
|
|
- default:
|
|
|
- return iRet;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- assert(true);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
+#include "util/tc_http.h"
|
|
|
+#include "util/tc_port.h"
|
|
|
+#include "util/tc_common.h"
|
|
|
+#include "util/tc_clientsocket.h"
|
|
|
+#include "util/tc_network_buffer.h"
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+namespace tars
|
|
|
+{
|
|
|
+
|
|
|
+const char* strnstr(const char* s1, const char* s2, int pos1)
|
|
|
+{
|
|
|
+ int l1, l2;
|
|
|
+
|
|
|
+ l2 = strlen(s2);
|
|
|
+ if (!l2)
|
|
|
+ return (char *)s1;
|
|
|
+ l1 = strlen(s1);
|
|
|
+
|
|
|
+ pos1 = (pos1 > l1)?l1:pos1;
|
|
|
+
|
|
|
+ while (pos1 >= l2) {
|
|
|
+ pos1--;
|
|
|
+ if (!memcmp(s1, s2, l2))
|
|
|
+ return (char *)s1;
|
|
|
+ s1++;
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+unordered_map<string, int> TC_Http::HEADER = {
|
|
|
+ {"GET", TC_HttpRequest::REQUEST_GET},
|
|
|
+ {"POST", TC_HttpRequest::REQUEST_POST},
|
|
|
+ {"PUT", TC_HttpRequest::REQUEST_PUT},
|
|
|
+ {"PATCH", TC_HttpRequest::REQUEST_PATCH},
|
|
|
+ {"OPTIONS", TC_HttpRequest::REQUEST_OPTIONS},
|
|
|
+ {"PRI", TC_HttpRequest::REQUEST_PRI},
|
|
|
+ {"DELETE", TC_HttpRequest::REQUEST_DELETE},
|
|
|
+ {"HEAD", TC_HttpRequest::REQUEST_HEAD},
|
|
|
+};
|
|
|
+
|
|
|
+unordered_map<int, string> TC_Http::HEADER_REVERSE = {
|
|
|
+ {TC_HttpRequest::REQUEST_GET, "GET"},
|
|
|
+ {TC_HttpRequest::REQUEST_POST, "POST"},
|
|
|
+ {TC_HttpRequest::REQUEST_PUT, "PUT"},
|
|
|
+ {TC_HttpRequest::REQUEST_PATCH, "PATCH"},
|
|
|
+ {TC_HttpRequest::REQUEST_OPTIONS, "OPTIONS"},
|
|
|
+ {TC_HttpRequest::REQUEST_PRI, "PRI"},
|
|
|
+ {TC_HttpRequest::REQUEST_DELETE, "DELETE"},
|
|
|
+ {TC_HttpRequest::REQUEST_HEAD, "HEAD"},
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+bool TC_Http::CmpCase::operator()(const string &s1, const string &s2) const
|
|
|
+{
|
|
|
+ if (TC_Port::strcasecmp(s1.c_str(), s2.c_str()) < 0)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_URL::isValid() const
|
|
|
+{
|
|
|
+ return !_sURL.empty();
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getURL() const
|
|
|
+{
|
|
|
+ return _sURL;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::type2String() const
|
|
|
+{
|
|
|
+ switch (_iURLType)
|
|
|
+ {
|
|
|
+ case HTTP:
|
|
|
+ return "http";
|
|
|
+ case HTTPS:
|
|
|
+ return "https";
|
|
|
+ case FTP:
|
|
|
+ return "ftp";
|
|
|
+ }
|
|
|
+
|
|
|
+ return "http";
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getDefaultPort() const
|
|
|
+{
|
|
|
+ switch (_iURLType)
|
|
|
+ {
|
|
|
+ case HTTP:
|
|
|
+ return "80";
|
|
|
+ case HTTPS:
|
|
|
+ return "443";
|
|
|
+ case FTP:
|
|
|
+ return "21";
|
|
|
+ }
|
|
|
+
|
|
|
+ return "80";
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_URL::isDefaultPort() const
|
|
|
+{
|
|
|
+ return _sPort == getDefaultPort();
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::toURL()
|
|
|
+{
|
|
|
+ _sURL.clear();
|
|
|
+
|
|
|
+ _sURL = _sScheme;
|
|
|
+ _sURL += "://";
|
|
|
+
|
|
|
+ if (!_sUser.empty())
|
|
|
+ _sURL += _sUser;
|
|
|
+
|
|
|
+ if (!_sUser.empty() && !_sPass.empty())
|
|
|
+ {
|
|
|
+ _sURL += ":";
|
|
|
+ _sURL += _sPass;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!_sUser.empty())
|
|
|
+ _sURL += "@";
|
|
|
+
|
|
|
+ _sURL += _sDomain;
|
|
|
+
|
|
|
+ if (!isDefaultPort())
|
|
|
+ {
|
|
|
+ _sURL += ":";
|
|
|
+ _sURL += _sPort;
|
|
|
+ }
|
|
|
+
|
|
|
+ _sURL += getRequest();
|
|
|
+
|
|
|
+ return _sURL;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getRequest() const
|
|
|
+{
|
|
|
+ string sURL;
|
|
|
+
|
|
|
+ if (!_sPath.empty())
|
|
|
+ sURL += _sPath;
|
|
|
+
|
|
|
+ if (!_sQuery.empty())
|
|
|
+ sURL += "?" + _sQuery;
|
|
|
+
|
|
|
+ if (!_sRef.empty())
|
|
|
+ sURL += "#" + _sRef;
|
|
|
+
|
|
|
+ return sURL;
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_URL::parseURL(const string &originRequest)
|
|
|
+{
|
|
|
+// string originRequest = TC_Common::trim(sURL, " ");
|
|
|
+
|
|
|
+ if (originRequest.empty())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ clear();
|
|
|
+
|
|
|
+ int iPos = 0;
|
|
|
+
|
|
|
+ if (TC_Port::strncasecmp(originRequest.c_str(), "http://" , 7) == 0)
|
|
|
+ {
|
|
|
+ //http开头
|
|
|
+ _iURLType = HTTP;
|
|
|
+ iPos = 7;
|
|
|
+ _sScheme = "http";
|
|
|
+ }
|
|
|
+ else if (TC_Port::strncasecmp(originRequest.c_str(), "https://" , 8) == 0)
|
|
|
+ {
|
|
|
+ //https开头
|
|
|
+ _iURLType = HTTPS;
|
|
|
+ iPos = 8;
|
|
|
+ _sScheme = "https";
|
|
|
+ }
|
|
|
+ else if (TC_Port::strncasecmp(originRequest.c_str(), "ftp://", 6) == 0)
|
|
|
+ {
|
|
|
+ //ftps开头
|
|
|
+ _iURLType = FTP;
|
|
|
+ iPos = 6;
|
|
|
+ _sScheme = "ftp";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //默认用http
|
|
|
+ _iURLType = HTTP;
|
|
|
+ iPos = 0;
|
|
|
+ _sScheme = "http";
|
|
|
+ }
|
|
|
+
|
|
|
+ string::size_type index = originRequest.find("/", iPos);
|
|
|
+ string::size_type nQuestionIndex = originRequest.find("?", iPos);
|
|
|
+ string::size_type nNumbersignIndex = originRequest.find("#", iPos);
|
|
|
+
|
|
|
+ string sUrlAuthority;
|
|
|
+ string sUrlPath;
|
|
|
+
|
|
|
+ if (nQuestionIndex < index)
|
|
|
+ {
|
|
|
+ sUrlAuthority = originRequest.substr(iPos, nQuestionIndex - iPos);
|
|
|
+
|
|
|
+ sUrlPath += '/';
|
|
|
+ sUrlPath += originRequest.substr(nQuestionIndex);
|
|
|
+ }
|
|
|
+ else if (nNumbersignIndex < index)
|
|
|
+ {
|
|
|
+ sUrlAuthority = originRequest.substr(iPos, nNumbersignIndex - iPos);
|
|
|
+
|
|
|
+ sUrlPath += '/';
|
|
|
+ sUrlPath += originRequest.substr(nNumbersignIndex);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (index == string::npos)
|
|
|
+ {
|
|
|
+ sUrlAuthority = originRequest.substr(iPos, index);
|
|
|
+ sUrlPath = "";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ sUrlAuthority = originRequest.substr(iPos, index - iPos);
|
|
|
+ sUrlPath = originRequest.substr(index);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //////解析Authority
|
|
|
+ index = sUrlAuthority.find("@");
|
|
|
+ if (index != string::npos)
|
|
|
+ {
|
|
|
+ _sUser = sUrlAuthority.substr(0, index);
|
|
|
+ _sDomain = sUrlAuthority.substr(index + 1);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _sDomain = sUrlAuthority;
|
|
|
+ }
|
|
|
+
|
|
|
+ //////解析User:Pass
|
|
|
+ index = _sUser.find(":");
|
|
|
+ if (index != string::npos)
|
|
|
+ {
|
|
|
+ _sPass = _sUser.substr(index + 1);
|
|
|
+ _sUser = _sUser.substr(0, index);
|
|
|
+ }
|
|
|
+
|
|
|
+ //////解析Host:Port
|
|
|
+ index = _sDomain.find(":");
|
|
|
+ if (index != string::npos)
|
|
|
+ {
|
|
|
+ _sPort = _sDomain.substr(index + 1);
|
|
|
+
|
|
|
+ _sDomain = _sDomain.substr(0, index);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _sPort = getDefaultPort();
|
|
|
+ }
|
|
|
+
|
|
|
+ //////解析Path Query Ref
|
|
|
+ index = sUrlPath.find("?");
|
|
|
+ if (index != string::npos)
|
|
|
+ {
|
|
|
+ _sPath = sUrlPath.substr(0, index);
|
|
|
+ _sQuery = sUrlPath.substr(index + 1);
|
|
|
+
|
|
|
+ index = _sQuery.rfind("#");
|
|
|
+ if (index != string::npos)
|
|
|
+ {
|
|
|
+ _sRef = _sQuery.substr(index + 1);
|
|
|
+ _sQuery = _sQuery.substr(0, index);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _sPath = sUrlPath;
|
|
|
+ _sQuery = "";
|
|
|
+
|
|
|
+ index = _sPath.rfind("#");
|
|
|
+ if (index != string::npos)
|
|
|
+ {
|
|
|
+ _sRef = _sPath.substr(index + 1);
|
|
|
+ _sPath = _sPath.substr(0, index);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_sPath.empty())
|
|
|
+ {
|
|
|
+ _sPath = "/";
|
|
|
+ }
|
|
|
+
|
|
|
+ toURL();
|
|
|
+
|
|
|
+ //域名或者IP必须包含一个点
|
|
|
+ if (_sDomain.find(".") == string::npos)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_URL::specialize()
|
|
|
+{
|
|
|
+ //规整化路径
|
|
|
+ /*string sUrlPath = simplePath(getPath());
|
|
|
+
|
|
|
+ _fragment["path"] = sUrlPath;*/
|
|
|
+ _sPath = simplePath(getPath());
|
|
|
+
|
|
|
+ _sURL = toURL();
|
|
|
+}
|
|
|
+
|
|
|
+void TC_URL::clear()
|
|
|
+{
|
|
|
+ _sScheme = "";
|
|
|
+ _sUser = "";
|
|
|
+ _sPass = "";
|
|
|
+ _sDomain = "";
|
|
|
+ _sPort = "";
|
|
|
+ _sPath = "";
|
|
|
+ _sQuery = "";
|
|
|
+ _sRef = "";
|
|
|
+
|
|
|
+ _sURL.clear();
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getScheme() const
|
|
|
+{
|
|
|
+ return _sScheme;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getUserName() const
|
|
|
+{
|
|
|
+ return _sUser;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getPassword() const
|
|
|
+{
|
|
|
+ return _sPass;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getDomain() const
|
|
|
+{
|
|
|
+ return _sDomain;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getPort() const
|
|
|
+{
|
|
|
+ return _sPort;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getPath() const
|
|
|
+{
|
|
|
+ return _sPath;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getQuery() const
|
|
|
+{
|
|
|
+ return _sQuery;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getRef() const
|
|
|
+{
|
|
|
+ return _sRef;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getRelativePath() const
|
|
|
+{
|
|
|
+ string sURL = getPath();
|
|
|
+
|
|
|
+ string::size_type pos;
|
|
|
+
|
|
|
+
|
|
|
+ pos = sURL.rfind("/");
|
|
|
+
|
|
|
+ if (pos == string::npos)
|
|
|
+ {
|
|
|
+ return "/";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return sURL.substr(0, pos + 1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::getRootPath() const
|
|
|
+{
|
|
|
+ string sURL = _sScheme;
|
|
|
+ sURL += "://";
|
|
|
+
|
|
|
+ if (!_sUser.empty())
|
|
|
+ sURL += _sUser;
|
|
|
+
|
|
|
+ if (!_sUser.empty() && !_sPass.empty())
|
|
|
+ {
|
|
|
+ sURL += ":";
|
|
|
+ sURL += _sPass;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!_sUser.empty())
|
|
|
+ sURL += "@";
|
|
|
+
|
|
|
+ sURL += _sDomain;
|
|
|
+
|
|
|
+ if (!isDefaultPort())
|
|
|
+ {
|
|
|
+ sURL += ":";
|
|
|
+ sURL += _sPort;
|
|
|
+ }
|
|
|
+
|
|
|
+ sURL += "/";
|
|
|
+
|
|
|
+ return sURL;
|
|
|
+}
|
|
|
+
|
|
|
+TC_URL TC_URL::buildWithRelativePath(const string &sRelativePath) const
|
|
|
+{
|
|
|
+ string sURL;
|
|
|
+
|
|
|
+ if (!sRelativePath.empty() && sRelativePath[0] == '/')
|
|
|
+ {
|
|
|
+ //如果链接是用"/"开头的相对地址,那么应该用Host+相对地址
|
|
|
+ sURL = sRelativePath.substr(1);
|
|
|
+ }
|
|
|
+ else if (sRelativePath[0] == '#')
|
|
|
+ {
|
|
|
+ //#
|
|
|
+ sURL = getPath().substr(1);
|
|
|
+
|
|
|
+ if (!getQuery().empty())
|
|
|
+ sURL += "?" + getQuery();
|
|
|
+
|
|
|
+ sURL += sRelativePath;
|
|
|
+
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //相对地址
|
|
|
+ sURL = getRelativePath().substr(1) + sRelativePath;
|
|
|
+ }
|
|
|
+
|
|
|
+ sURL = getRootPath() + simplePath("/" + sURL).substr(1);
|
|
|
+
|
|
|
+ TC_URL url;
|
|
|
+
|
|
|
+ url.parseURL(sURL);
|
|
|
+
|
|
|
+ return url;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_URL::simplePath(const string &sPath) const
|
|
|
+{
|
|
|
+ //所有./都去掉
|
|
|
+ size_t pos = 0;
|
|
|
+ string sNewPath = sPath;
|
|
|
+
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ size_t dotPos = sNewPath.find("./", pos);
|
|
|
+
|
|
|
+ if (dotPos != string::npos)
|
|
|
+ {
|
|
|
+ if ((dotPos == 0) || (sNewPath.at(dotPos - 1) == '/'))
|
|
|
+ {
|
|
|
+ sNewPath.erase(dotPos, 2);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ pos = dotPos + 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //如果路径是以.结尾的, 则.去掉
|
|
|
+ if (((sNewPath.length() >= 2) && (sNewPath.substr(sNewPath.length() - 2) == "/.")) || (sNewPath == "."))
|
|
|
+ {
|
|
|
+ sNewPath.erase(sNewPath.length() - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ //处理/../的形式
|
|
|
+ pos = 0;
|
|
|
+ size_t startPos = 0;
|
|
|
+
|
|
|
+ while (1)
|
|
|
+ {
|
|
|
+ size_t slashDot = sNewPath.find("/../", pos);
|
|
|
+
|
|
|
+ if (slashDot != string::npos)
|
|
|
+ {
|
|
|
+ if (0 == slashDot)
|
|
|
+ {
|
|
|
+ sNewPath.erase(0, 3);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( (slashDot > 1) && (sNewPath.substr(slashDot - 2, 2) == "..") )
|
|
|
+ {
|
|
|
+ pos = slashDot + 4;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ startPos = sNewPath.rfind('/', slashDot - 1);
|
|
|
+
|
|
|
+ if (startPos == string::npos) startPos = 0;
|
|
|
+
|
|
|
+ sNewPath.erase(startPos, slashDot + 4 - startPos - 1);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //处理/..结尾的情况
|
|
|
+ if ((sNewPath.size() >= 3) && (sNewPath.substr(sNewPath.size() - 3, 3) == "/.."))
|
|
|
+ {
|
|
|
+ size_t slashDot = sNewPath.size() - 3;
|
|
|
+ if (!((slashDot > 1) && (sNewPath.substr(slashDot - 2, 2) == "..")))
|
|
|
+ {
|
|
|
+ startPos = sNewPath.rfind ('/', slashDot - 1);
|
|
|
+ if (startPos == string::npos) startPos = 0;
|
|
|
+ sNewPath.erase (startPos + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return sNewPath;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+void TC_Http::setHeader(const string &sHeadName, const string &sHeadValue)
|
|
|
+{
|
|
|
+ //Set-Cookie和Cookie可以有多个头
|
|
|
+ const char * pStr1 = "SET-COOKIE";
|
|
|
+ const char * pStr2 = "COOKIE";//原则上COOKIE只有一个,担心有兼容性问题,保留
|
|
|
+ if ((TC_Port::strcasecmp(sHeadName.c_str(), pStr1) != 0) && (TC_Port::strcasecmp(sHeadName.c_str(), pStr2) != 0))
|
|
|
+ {
|
|
|
+ _headers.erase(sHeadName);
|
|
|
+ }
|
|
|
+
|
|
|
+ _headers.insert(multimap<string, string>::value_type(sHeadName, sHeadValue));
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_Http::hasHeader(const char *sHeader) const
|
|
|
+{
|
|
|
+ http_header_type::const_iterator it = _headers.find(sHeader);
|
|
|
+ if (it == _headers.end())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_Http::checkHeader(const char *sHeader, const char *value) const
|
|
|
+{
|
|
|
+ http_header_type::const_iterator it = _headers.find(sHeader);
|
|
|
+ if (it == _headers.end())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return TC_Port::strcasecmp(it->second.c_str(), value) == 0;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_Http::getHeader(const string& sHeader) const
|
|
|
+{
|
|
|
+ http_header_type::const_iterator it = _headers.find(sHeader);
|
|
|
+ if (it == _headers.end())
|
|
|
+ {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ return it->second;
|
|
|
+}
|
|
|
+
|
|
|
+string TC_Http::getContentType() const
|
|
|
+{
|
|
|
+ return getHeader("Content-Type");
|
|
|
+}
|
|
|
+
|
|
|
+string TC_Http::getHost() const
|
|
|
+{
|
|
|
+ return getHeader("Host");
|
|
|
+}
|
|
|
+
|
|
|
+size_t TC_Http::getContentLength() const
|
|
|
+{
|
|
|
+ string s = getHeader("Content-Length");
|
|
|
+ if (s.empty())
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return TC_Common::strto<size_t>(s);
|
|
|
+}
|
|
|
+
|
|
|
+string TC_Http::genHeader() const
|
|
|
+{
|
|
|
+ string sHttpHeader;
|
|
|
+
|
|
|
+ for (http_header_type::const_iterator it = _headers.begin(); it != _headers.end(); ++it)
|
|
|
+ {
|
|
|
+ if (it->second != "")
|
|
|
+ {
|
|
|
+ sHttpHeader += it->first;
|
|
|
+ sHttpHeader += ": ";
|
|
|
+ sHttpHeader += it->second;
|
|
|
+ sHttpHeader += "\r\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return sHttpHeader;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_Http::genHeader(string &sHttpHeader) const
|
|
|
+{
|
|
|
+ for (http_header_type::const_iterator it = _headers.begin(); it != _headers.end(); ++it)
|
|
|
+ {
|
|
|
+ if (it->second != "")
|
|
|
+ {
|
|
|
+ sHttpHeader += it->first;
|
|
|
+ sHttpHeader += ": ";
|
|
|
+ sHttpHeader += it->second;
|
|
|
+ sHttpHeader += "\r\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+vector<string> TC_Http::getHeaderMulti(const string &sHeadName) const
|
|
|
+{
|
|
|
+ vector<string> v;
|
|
|
+
|
|
|
+ http_header_type::const_iterator itEnd = _headers.end();
|
|
|
+
|
|
|
+ for ( http_header_type::const_iterator it = _headers.begin(); it != itEnd; ++it)
|
|
|
+ {
|
|
|
+ if (TC_Port::strcasecmp(it->first.c_str(), sHeadName.c_str()) == 0)
|
|
|
+ {
|
|
|
+ v.push_back(it->second);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return v;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_Http::reset()
|
|
|
+{
|
|
|
+ _headers.clear();
|
|
|
+ _version.clear();
|
|
|
+ _headLength = 0;
|
|
|
+ _content.clear();
|
|
|
+ _bIsChunked = false;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_Http::getHeaders(map<string, string> &header)
|
|
|
+{
|
|
|
+ for(auto it = _headers.begin(); it != _headers.end(); ++it)
|
|
|
+ {
|
|
|
+ header.insert(map<string, string>::value_type(it->first, it->second));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/********************* TC_HttpCookie ***********************/
|
|
|
+
|
|
|
+bool TC_HttpCookie::matchDomain(const string &sCookieDomain, const string &sDomain)
|
|
|
+{
|
|
|
+ string sCookieDomainNew = TC_Common::lower(sCookieDomain);
|
|
|
+
|
|
|
+ //没有点的自动加点
|
|
|
+ if (sCookieDomainNew.find(".") != 0)
|
|
|
+ {
|
|
|
+ sCookieDomainNew = "." + sCookieDomainNew;
|
|
|
+ }
|
|
|
+
|
|
|
+ string::size_type pos = sCookieDomainNew.find(".");
|
|
|
+
|
|
|
+ //sCookieDomain串至少有两个点
|
|
|
+ if (pos == string::npos || (pos == 0 && sCookieDomainNew.rfind(".") == 0))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ string sLowerDomain = TC_Common::lower(sDomain);
|
|
|
+
|
|
|
+ //后边是子域名
|
|
|
+ if (sDomain.length() >= sCookieDomainNew.length() &&
|
|
|
+ sLowerDomain.compare(sDomain.length() - sCookieDomainNew.length(), sCookieDomainNew.length(), sCookieDomainNew) == 0)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ //去掉.相等
|
|
|
+ if (sLowerDomain == sCookieDomainNew.substr(1))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+size_t TC_HttpCookie::matchPath(const string &sCookiePath, const string &sPath)
|
|
|
+{
|
|
|
+ if (sCookiePath.empty() || sPath.empty()) return 0;
|
|
|
+
|
|
|
+ //都在最后加/再匹配
|
|
|
+ string sCookiePath1 = (sCookiePath.at(sCookiePath.length() - 1) == '/') ? sCookiePath : sCookiePath + "/";
|
|
|
+
|
|
|
+ string sPath1 = (sPath.at(sPath.length() - 1) == '/') ? sPath : sPath + "/";
|
|
|
+
|
|
|
+ if (sPath1.find(sCookiePath1) == 0)
|
|
|
+ {
|
|
|
+ return sCookiePath1.length();
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpCookie::clear()
|
|
|
+{
|
|
|
+ _cookies.clear();
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpCookie::fixDomain(const string &sDomain, string &sFixDomain)
|
|
|
+{
|
|
|
+ if (sDomain.empty())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ sFixDomain = sDomain;
|
|
|
+
|
|
|
+ //自动加.
|
|
|
+ if (sDomain.at(0) != '.')
|
|
|
+ {
|
|
|
+ sFixDomain = "." + sDomain;
|
|
|
+ }
|
|
|
+
|
|
|
+ //domain至少两段
|
|
|
+ if (sFixDomain.find(".") == sFixDomain.rfind("."))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpCookie::addCookie(const Cookie &cookie, list<Cookie> &cookies)
|
|
|
+{
|
|
|
+ string sDomain;
|
|
|
+
|
|
|
+ Cookie cookieNew = cookie;
|
|
|
+
|
|
|
+ if (!fixDomain(cookieNew._domain, sDomain))
|
|
|
+ return;
|
|
|
+
|
|
|
+ cookieNew._domain = sDomain;
|
|
|
+
|
|
|
+ if (cookieNew._path.empty())
|
|
|
+ return;
|
|
|
+
|
|
|
+ list<Cookie>::iterator it = cookies.begin();
|
|
|
+
|
|
|
+ string sName;
|
|
|
+ if (cookieNew._data.size() >= 1)
|
|
|
+ {
|
|
|
+ sName = cookieNew._data.begin()->first;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (it != cookies.end())
|
|
|
+ {
|
|
|
+ //检查Cookie是否过期
|
|
|
+ if (isCookieExpires(*it))
|
|
|
+ {
|
|
|
+ cookies.erase(it++);
|
|
|
+ }
|
|
|
+ else if (TC_Port::strcasecmp(it->_domain.c_str(), cookieNew._domain.c_str()) == 0
|
|
|
+ && strcmp(it->_path.c_str(), cookieNew._path.c_str()) == 0
|
|
|
+ && it->_isSecure == cookieNew._isSecure)
|
|
|
+ {
|
|
|
+ if (it->_expires == cookieNew._expires)
|
|
|
+ {
|
|
|
+ //domain/path/expires都匹配的情况下, 覆盖老cookie的数据
|
|
|
+ cookieNew._data.insert(it->_data.begin(), it->_data.end());
|
|
|
+
|
|
|
+ cookies.erase(it++);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //超时间不一样,但存在同样的key,需删除
|
|
|
+ if (it->_data.find(sName) != it->_data.end())
|
|
|
+ {
|
|
|
+ it->_data.erase(sName);
|
|
|
+ }
|
|
|
+ ++it;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ++it;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ cookies.push_back(cookieNew);
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpCookie::addCookie(const Cookie &cookie)
|
|
|
+{
|
|
|
+ addCookie(cookie, _cookies);
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpCookie::addCookie(const list<Cookie> &cookie)
|
|
|
+{
|
|
|
+ list<Cookie>::const_iterator it = cookie.begin();
|
|
|
+
|
|
|
+ while (it != cookie.end())
|
|
|
+ {
|
|
|
+ addCookie(*it);
|
|
|
+
|
|
|
+ ++it;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpCookie::addCookie(const string &sRspURL, const vector<string> &vCookies)
|
|
|
+{
|
|
|
+ TC_URL cURL;
|
|
|
+
|
|
|
+ cURL.parseURL(sRspURL);
|
|
|
+
|
|
|
+ string sRspURLDomain = TC_Common::lower(cURL.getDomain());
|
|
|
+
|
|
|
+ string sRspURLPath = cURL.getPath();
|
|
|
+
|
|
|
+ for (size_t i = 0; i < vCookies.size(); i++)
|
|
|
+ {
|
|
|
+ //处理一行SetCookie
|
|
|
+ vector<string> v = TC_Common::sepstr<string>(vCookies[i], ";");
|
|
|
+
|
|
|
+ Cookie cookie;
|
|
|
+
|
|
|
+ cookie._isSecure = false;
|
|
|
+ cookie._expires = 0;
|
|
|
+
|
|
|
+ //解析Cookie数据
|
|
|
+ for (size_t j = 0; j < v.size(); ++j)
|
|
|
+ {
|
|
|
+ string::size_type index = v[j].find("=");
|
|
|
+
|
|
|
+ string name;
|
|
|
+
|
|
|
+ string value;
|
|
|
+
|
|
|
+ if (index != string::npos)
|
|
|
+ {
|
|
|
+ name = TC_Common::trim(v[j].substr(0, index), " ");
|
|
|
+
|
|
|
+ value = TC_Common::trim(v[j].substr(index + 1));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ name = TC_Common::trim(v[j]);
|
|
|
+ }
|
|
|
+ if (TC_Port::strcasecmp(name.c_str(), "secure") == 0)
|
|
|
+ {
|
|
|
+ cookie._isSecure = true;
|
|
|
+ }
|
|
|
+ else if (TC_Port::strcasecmp(name.c_str(), "expires") == 0)
|
|
|
+ {
|
|
|
+ struct tm stTm;
|
|
|
+ //兼容时间格式
|
|
|
+ //expires=Mon, 09-May-41 08:39:32 GMT
|
|
|
+ //expires=Thu, 01-Jan-1970 01:00:00 GMT
|
|
|
+ value = TC_Common::replace(value, "-", " ");
|
|
|
+ if (value.length() == 27 && value.at(11) == ' ' && value.at(14) == ' ')
|
|
|
+ {
|
|
|
+ int year = TC_Common::strto<int> (value.substr(12, 2));
|
|
|
+ if (year >= 69 && year <= 99)
|
|
|
+ value = value.substr(0, 12) + "19" + value.substr(12);
|
|
|
+ else
|
|
|
+ value = value.substr(0, 12) + "20" + value.substr(12);
|
|
|
+ }
|
|
|
+
|
|
|
+ TC_Common::strgmt2tm(value, stTm);
|
|
|
+
|
|
|
+ cookie._expires = TC_Port::timegm(&stTm);
|
|
|
+ }
|
|
|
+ else if (TC_Port::strcasecmp(name.c_str(), "path") == 0)
|
|
|
+ {
|
|
|
+ cookie._path = value;
|
|
|
+ }
|
|
|
+ else if (TC_Port::strcasecmp(name.c_str(), "domain") == 0)
|
|
|
+ {
|
|
|
+ cookie._domain = value;
|
|
|
+ }
|
|
|
+ else if (TC_Port::strcasecmp(name.c_str(), "httponly") == 0)
|
|
|
+ {
|
|
|
+ //TODO
|
|
|
+ //cookie._isHttpOnly = true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ cookie._data.insert(http_cookie_data::value_type(name, value));
|
|
|
+ }
|
|
|
+
|
|
|
+ ///修正和匹配domain/////////////////////////////////////////
|
|
|
+ if (cookie._domain.empty())
|
|
|
+ cookie._domain = sRspURLDomain;
|
|
|
+
|
|
|
+ if (!fixDomain(cookie._domain, cookie._domain))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ //匹配域名
|
|
|
+ if (!matchDomain(cookie._domain, sRspURLDomain))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ //修改和匹配path/////////////////////////////
|
|
|
+ if (cookie._path.empty())
|
|
|
+ {
|
|
|
+ string sCookiePath;
|
|
|
+
|
|
|
+ //缺省是全路径
|
|
|
+ string sRequest = sRspURLPath;
|
|
|
+
|
|
|
+ string::size_type pos = sRequest.rfind("/");
|
|
|
+
|
|
|
+ if (pos == string::npos)
|
|
|
+ sCookiePath = "/";
|
|
|
+ else
|
|
|
+ sCookiePath = sRequest.substr(0, pos + 1);
|
|
|
+
|
|
|
+ cookie._path = sCookiePath;
|
|
|
+ }
|
|
|
+
|
|
|
+ //URL在Path范围内,Cookie 有效
|
|
|
+ if (!matchPath(cookie._path, sRspURLPath))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ //添加Cookie
|
|
|
+ addCookie(cookie);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpCookie::isCookieExpires(const Cookie &cookie) const
|
|
|
+{
|
|
|
+ //过期了
|
|
|
+ if (cookie._expires != 0 && cookie._expires < time(NULL))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+size_t TC_HttpCookie::isCookieMatch(const Cookie &cookie, const TC_URL &tURL) const
|
|
|
+{
|
|
|
+ //域名没有匹配
|
|
|
+ if (!matchDomain(cookie._domain, tURL.getDomain()))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ //路径没有匹配
|
|
|
+ size_t len = matchPath(cookie._path, tURL.getPath());
|
|
|
+ if (len == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ //安全的cookie,不安全的URL
|
|
|
+ if (cookie._isSecure && (tURL.getType() != TC_URL::HTTPS))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpCookie::getCookieForURL(const string &sReqURL, list<TC_HttpCookie::Cookie> &cookies)
|
|
|
+{
|
|
|
+ TC_URL tURL;
|
|
|
+
|
|
|
+ tURL.parseURL(sReqURL);
|
|
|
+
|
|
|
+ cookies.clear();
|
|
|
+
|
|
|
+ list<Cookie>::iterator it = _cookies.begin();
|
|
|
+
|
|
|
+ while (it != _cookies.end())
|
|
|
+ {
|
|
|
+ //检查Cookie是否过期
|
|
|
+ if (isCookieExpires(*it))
|
|
|
+ {
|
|
|
+ _cookies.erase(it++);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t len = isCookieMatch(*it, tURL);
|
|
|
+ if (len == 0)
|
|
|
+ {
|
|
|
+ ++it;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ cookies.push_back(*it);
|
|
|
+
|
|
|
+ ++it;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpCookie::getCookieForURL(const string &sReqURL, string &sCookie)
|
|
|
+{
|
|
|
+ list<Cookie> cookies;
|
|
|
+
|
|
|
+ sCookie.clear();
|
|
|
+
|
|
|
+ getCookieForURL(sReqURL, cookies);
|
|
|
+
|
|
|
+ list<Cookie>::iterator it = cookies.begin();
|
|
|
+ while (it != cookies.end())
|
|
|
+ {
|
|
|
+ http_cookie_data::iterator itd = it->_data.begin();
|
|
|
+
|
|
|
+ while (itd != it->_data.end())
|
|
|
+ {
|
|
|
+ //被删除的cookie不输出
|
|
|
+ if (itd->first != "" && itd->second != "" && TC_Common::lower(itd->second) != "null"
|
|
|
+ && TC_Common::lower(itd->second) != "deleted")
|
|
|
+ sCookie += itd->first + "=" + itd->second + "; ";
|
|
|
+
|
|
|
+ ++itd;
|
|
|
+ }
|
|
|
+
|
|
|
+ ++it;
|
|
|
+ }
|
|
|
+
|
|
|
+ //去掉末尾的 "; "
|
|
|
+ if (sCookie.length() >= 2)
|
|
|
+ sCookie = sCookie.substr(0, sCookie.length() - 2);
|
|
|
+}
|
|
|
+
|
|
|
+list<TC_HttpCookie::Cookie> TC_HttpCookie::getSerializeCookie(time_t t)
|
|
|
+{
|
|
|
+ list<Cookie> cookies;
|
|
|
+
|
|
|
+ list<Cookie>::iterator it = _cookies.begin();
|
|
|
+
|
|
|
+ while (it != _cookies.end())
|
|
|
+ {
|
|
|
+ if (isCookieExpires(*it))
|
|
|
+ {
|
|
|
+ _cookies.erase(it++);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if (it->_expires != 0) //非当前会话的
|
|
|
+ {
|
|
|
+ cookies.push_back(*it);
|
|
|
+
|
|
|
+ ++it;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ ++it;
|
|
|
+ }
|
|
|
+
|
|
|
+ return cookies;
|
|
|
+}
|
|
|
+
|
|
|
+const list<TC_HttpCookie::Cookie> & TC_HttpCookie::getAllCookie()
|
|
|
+{
|
|
|
+ deleteExpires(time(NULL));
|
|
|
+
|
|
|
+ return _cookies;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpCookie::deleteExpires(time_t t, bool bErase)
|
|
|
+{
|
|
|
+ list<Cookie>::iterator it = _cookies.begin();
|
|
|
+
|
|
|
+ while (it != _cookies.end())
|
|
|
+ {
|
|
|
+ if (bErase && it->_expires == 0)
|
|
|
+ {
|
|
|
+ _cookies.erase(it++);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if (isCookieExpires(*it))
|
|
|
+ {
|
|
|
+ _cookies.erase(it++);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ ++it;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/********************* TC_HttpResponse ***********************/
|
|
|
+void TC_HttpResponse::reset()
|
|
|
+{
|
|
|
+ TC_Http::reset();
|
|
|
+
|
|
|
+ _status = 200;
|
|
|
+ _about = "OK";
|
|
|
+ _version = "HTTP/1.1";
|
|
|
+
|
|
|
+ _iTmpContentLength = 0;
|
|
|
+
|
|
|
+ _iRecvContentLength = 0;
|
|
|
+}
|
|
|
+
|
|
|
+vector<string> TC_HttpResponse::getSetCookie() const
|
|
|
+{
|
|
|
+ return getHeaderMulti("Set-Cookie");
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpResponse::addContent(const string &sBuffer)
|
|
|
+{
|
|
|
+ _content += sBuffer;
|
|
|
+
|
|
|
+ _iRecvContentLength += sBuffer.length();
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpResponse::addContent(const char *buffer, size_t length)
|
|
|
+{
|
|
|
+ _content.append(buffer, length);
|
|
|
+
|
|
|
+ _iRecvContentLength += length;
|
|
|
+}
|
|
|
+
|
|
|
+size_t TC_HttpResponse::parseResponseHeaderString(const char *beginIt, const char *headerIt)
|
|
|
+{
|
|
|
+ auto it = strnstr(beginIt, "\r\n", headerIt - beginIt);
|
|
|
+
|
|
|
+ assert(it != NULL);
|
|
|
+
|
|
|
+ auto f1 = strnstr(beginIt, " ", it - beginIt);
|
|
|
+ if(f1 == NULL)
|
|
|
+ {
|
|
|
+ throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response parse version format error : " + string(beginIt, headerIt - beginIt));
|
|
|
+ }
|
|
|
+
|
|
|
+ auto f2 = strnstr(f1 + 1, " ", it - (f1 + 1));
|
|
|
+ if(f1 == NULL)
|
|
|
+ {
|
|
|
+ throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response parse status format error : " + string(beginIt, headerIt - beginIt));
|
|
|
+ }
|
|
|
+
|
|
|
+ _headerLine = string(beginIt, it - beginIt);
|
|
|
+
|
|
|
+ if(TC_Port::strncasecmp(_headerLine.c_str(), "HTTP/", 5) != 0)
|
|
|
+ {
|
|
|
+ throw TC_HttpResponse_Exception("[TC_HttpResponse_Exception::parseResponeHeader] http response version is not start with 'HTTP/' : " + _headerLine);
|
|
|
+ }
|
|
|
+
|
|
|
+ _version = string(beginIt, f1 - beginIt);
|
|
|
+
|
|
|
+ _status = TC_Common::strto<int>(string(f1 + 1, f2));
|
|
|
+
|
|
|
+ _about = string(f2 + 1, it);
|
|
|
+
|
|
|
+ return parseHeaderString(beginIt, headerIt, _headers);
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpResponse::incrementDecode(TC_NetWorkBuffer &buff)
|
|
|
+{
|
|
|
+ if(buff.empty())
|
|
|
+ return false;
|
|
|
+ //解析头部
|
|
|
+ if (_headLength == 0)
|
|
|
+ {
|
|
|
+ //至少把header合并成一个buff
|
|
|
+ buff.mergeBuffers();
|
|
|
+
|
|
|
+ auto data = buff.getBufferPointer();
|
|
|
+
|
|
|
+ const char * p = strnstr(data.first, "\r\n\r\n", data.second);
|
|
|
+ if(p == NULL)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ _headLength = p - data.first + 4;
|
|
|
+
|
|
|
+ _iTmpContentLength = parseResponseHeaderString(data.first, data.first + data.second);
|
|
|
+
|
|
|
+ //304的返回码中头里本来就没有Content-Length,也不会有数据体,头收全了就是真正的收全了
|
|
|
+ if ( (204 == _status) || (304 == _status) )
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ buff.moveHeader(_headLength);
|
|
|
+
|
|
|
+ //重定向就认为成功了
|
|
|
+ if ((_status == 301 || _status == 302) && hasHeader("Location"))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ //是否是chunk编码
|
|
|
+ _bIsChunked = checkHeader("Transfer-Encoding", "chunked");
|
|
|
+ if(_bIsChunked) {
|
|
|
+ //删除头部里面
|
|
|
+ eraseHeader("Transfer-Encoding");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_bIsChunked)
|
|
|
+ {
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ const static string sep = "\r\n";
|
|
|
+
|
|
|
+ auto sit = std::search(buff.begin(), buff.end(), sep.c_str(), sep.c_str() + sep.size());
|
|
|
+
|
|
|
+ if (sit == buff.end())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ string header = buff.iteratorToIterator<string>(buff.begin(), sit);
|
|
|
+
|
|
|
+ int iChunkSize = strtol(header.c_str(), NULL, 16);
|
|
|
+
|
|
|
+ if (iChunkSize <= 0)
|
|
|
+ {
|
|
|
+ break; //所有chunk都接收完毕
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buff.getBufferLength() < header.size() + 2 + (size_t)iChunkSize + 2)
|
|
|
+ {
|
|
|
+ //没有接收完整的chunk
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ //接收到一个完整的chunk了
|
|
|
+ buff.moveHeader(header.size() + 2);
|
|
|
+ addContent(buff.getHeader<string>(iChunkSize));
|
|
|
+
|
|
|
+ //删除一个chunk
|
|
|
+ buff.moveHeader(iChunkSize + 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ buff.clearBuffers();
|
|
|
+
|
|
|
+ //接收到buffer长度设置好
|
|
|
+ setContentLength(_iRecvContentLength);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (_iTmpContentLength == 0)
|
|
|
+ {
|
|
|
+ //header长度为0, 但是有body数据
|
|
|
+ addContent(buff.getBuffersString());
|
|
|
+
|
|
|
+ buff.clearBuffers();
|
|
|
+
|
|
|
+ if(_iRecvContentLength > 0) {
|
|
|
+ setContentLength(_iRecvContentLength);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ else if (_iTmpContentLength == (size_t) - 1)
|
|
|
+ {
|
|
|
+ //304的返回码中头没有Content-Length,不会有数据体
|
|
|
+ if (_status == 304 || _status == 302)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ //header中没长度, 但是有body数据
|
|
|
+ addContent(buff.getBuffersString());
|
|
|
+
|
|
|
+ buff.clearBuffers();
|
|
|
+
|
|
|
+ if(_iRecvContentLength > 0) {
|
|
|
+ setContentLength(_iRecvContentLength);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //头部有长度, 接收到长度大于头部为止
|
|
|
+ addContent(buff.getBuffersString());
|
|
|
+
|
|
|
+ buff.clearBuffers();
|
|
|
+
|
|
|
+ //头部的长度小于接收的内容, 还需要继续增加解析后续的buffer
|
|
|
+ if (_iTmpContentLength > _iRecvContentLength)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpResponse::decode(const string &sBuffer)
|
|
|
+{
|
|
|
+ return decode(sBuffer.c_str(), sBuffer.length());
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpResponse::decode(const char *sBuffer, size_t iLength)
|
|
|
+{
|
|
|
+ assert(sBuffer != NULL);
|
|
|
+
|
|
|
+ const char *pHeader = sBuffer;
|
|
|
+
|
|
|
+ const char *p = strnstr(sBuffer, "\r\n\r\n", iLength);
|
|
|
+ if ( p == NULL)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ //解析头部
|
|
|
+ _iTmpContentLength = parseResponseHeaderString(sBuffer, p + 2);
|
|
|
+
|
|
|
+ //304的返回码中头里本来就没有Content-Length,也不会有数据体,头收全了就是真正的收全了
|
|
|
+ if ( (204 == _status) || (304 == _status) )
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ _headLength = p - sBuffer + 4;
|
|
|
+
|
|
|
+ sBuffer += _headLength;
|
|
|
+
|
|
|
+ //重定向就认为成功了
|
|
|
+ if ((_status == 301 || _status == 302) && hasHeader("Location"))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ //是否是chunk编码
|
|
|
+ _bIsChunked = checkHeader("Transfer-Encoding", "chunked");
|
|
|
+ if(_bIsChunked) {
|
|
|
+ //删除头部里面
|
|
|
+ eraseHeader("Transfer-Encoding");
|
|
|
+ }
|
|
|
+
|
|
|
+// _bIsChunked = (getHeader("Transfer-Encoding") == "chunked");
|
|
|
+
|
|
|
+ //删除头部里面
|
|
|
+// eraseHeader("Transfer-Encoding");
|
|
|
+
|
|
|
+ if (_bIsChunked)
|
|
|
+ {
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ p = strnstr(sBuffer, "\r\n", iLength - (sBuffer - pHeader));
|
|
|
+ if (p == NULL)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ size_t pos = p - sBuffer;
|
|
|
+
|
|
|
+ //查找当前chunk的大小
|
|
|
+ string sChunkSize;
|
|
|
+ sChunkSize.insert(sChunkSize.end(), sBuffer, p);
|
|
|
+ int iChunkSize = strtol(sChunkSize.c_str(), NULL, 16);
|
|
|
+
|
|
|
+ if (iChunkSize <= 0) break; //所有chunk都接收完毕
|
|
|
+
|
|
|
+ if (strlen(sBuffer) < pos + 2 + (size_t)iChunkSize + 2)
|
|
|
+ {
|
|
|
+ //没有接收完整的chunk
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ //接收到一个完整的chunk了
|
|
|
+ addContent(p + 2, iChunkSize);
|
|
|
+
|
|
|
+ //删除一个chunk
|
|
|
+ sBuffer += pos + 2 + iChunkSize + 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ //接收到buffer长度设置好
|
|
|
+ setContentLength(_iRecvContentLength);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (_iTmpContentLength == 0)
|
|
|
+ {
|
|
|
+ //header长度为0, 但是有body数据
|
|
|
+ addContent(sBuffer);
|
|
|
+
|
|
|
+ setContentLength(_iRecvContentLength);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ else if (_iTmpContentLength == (size_t) - 1)
|
|
|
+ {
|
|
|
+ //304的返回码中头没有Content-Length,不会有数据体
|
|
|
+ if (_status == 304 || _status == 302)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ //header中没长度, 但是有body数据
|
|
|
+ addContent(sBuffer);
|
|
|
+
|
|
|
+ setContentLength(_iRecvContentLength);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //头部有长度, 接收到长度大于头部为止
|
|
|
+ addContent(sBuffer);
|
|
|
+
|
|
|
+ //头部的长度小于接收的内容, 还需要继续增加解析后续的buffer
|
|
|
+ if (_iTmpContentLength <= _iRecvContentLength)
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //body内容长度为0或者没有content-length 且 非chunk模式, 则认为包收全了, 直接返回
|
|
|
+ if ((_iTmpContentLength == 0 || _iTmpContentLength == (size_t) - 1) && !_bIsChunked)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return (getContentLength() + getHeadLength() <= iLength);
|
|
|
+}
|
|
|
+
|
|
|
+string TC_HttpResponse::encode() const
|
|
|
+{
|
|
|
+ string sRet;
|
|
|
+ sRet.reserve(128);
|
|
|
+
|
|
|
+ sRet += _version;
|
|
|
+ sRet += " ";
|
|
|
+ sRet += TC_Common::tostr(_status);
|
|
|
+ sRet += " ";
|
|
|
+ sRet += _about;
|
|
|
+ sRet += "\r\n";
|
|
|
+ genHeader(sRet);
|
|
|
+ sRet += "\r\n";
|
|
|
+ sRet += _content;
|
|
|
+
|
|
|
+ return sRet;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpResponse::encode(vector<char> &buffer) const
|
|
|
+{
|
|
|
+ buffer.clear();
|
|
|
+
|
|
|
+ string s = encode();
|
|
|
+
|
|
|
+ buffer.resize(s.length());
|
|
|
+ memcpy(buffer.data(), s.c_str(), s.length());
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpResponse::setResponse(int status, const string& about, const string& body)
|
|
|
+{
|
|
|
+ _status = status;
|
|
|
+ _about = about;
|
|
|
+ _content = body;
|
|
|
+
|
|
|
+ /*stringstream os;
|
|
|
+
|
|
|
+ os << _version << " " << _status << " " << _about;
|
|
|
+
|
|
|
+ _headerLine = os.str();*/
|
|
|
+ _headerLine = "";
|
|
|
+ _headerLine += _version;
|
|
|
+ _headerLine += " ";
|
|
|
+ _headerLine += TC_Common::tostr(_status);
|
|
|
+ _headerLine += " ";
|
|
|
+ _headerLine += _about;
|
|
|
+
|
|
|
+ setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpResponse::setResponse(int status, const string& about, const char *sBuffer, size_t iLength)
|
|
|
+{
|
|
|
+ _status = status;
|
|
|
+ _about = about;
|
|
|
+
|
|
|
+ /*stringstream os;
|
|
|
+
|
|
|
+ os << _version << " " << _status << " " << _about;
|
|
|
+
|
|
|
+ _headerLine = os.str();*/
|
|
|
+ _headerLine = "";
|
|
|
+ _headerLine += _version;
|
|
|
+ _headerLine += " ";
|
|
|
+ _headerLine += TC_Common::tostr(_status);
|
|
|
+ _headerLine += " ";
|
|
|
+ _headerLine += _about;
|
|
|
+
|
|
|
+ if (sBuffer != NULL && iLength > 0)
|
|
|
+ {
|
|
|
+ _content.assign(sBuffer, iLength);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _content.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpResponse::setResponse(const char *sBuffer, size_t iLength)
|
|
|
+{
|
|
|
+ setResponse(200, "OK", sBuffer, iLength);
|
|
|
+}
|
|
|
+
|
|
|
+/********************* TC_HttpRequest ***********************/
|
|
|
+
|
|
|
+void TC_HttpRequest::reset()
|
|
|
+{
|
|
|
+ TC_Http::reset();
|
|
|
+
|
|
|
+ _httpURL.clear();
|
|
|
+}
|
|
|
+
|
|
|
+vector<string> TC_HttpRequest::getCookie()
|
|
|
+{
|
|
|
+ vector<string> v;
|
|
|
+
|
|
|
+ http_header_type::const_iterator itEnd = _headers.end();
|
|
|
+
|
|
|
+ for ( http_header_type::const_iterator it = _headers.begin(); it != itEnd; ++it)
|
|
|
+ {
|
|
|
+ if (it->first == "Cookie")
|
|
|
+ {
|
|
|
+ v.push_back(it->second);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return v;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::parseURL(const string& sUrl)
|
|
|
+{
|
|
|
+ _httpURL.parseURL(sUrl);
|
|
|
+
|
|
|
+ //设置Host
|
|
|
+ if (!hasHeader("Host"))
|
|
|
+ {
|
|
|
+ string sPort = _httpURL.isDefaultPort() ? "" : ":" + _httpURL.getPort();
|
|
|
+
|
|
|
+ //缺省端口可以不用放进去
|
|
|
+ setHost(_httpURL.getDomain() + sPort);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setRequest(const string& method, const string &sUrl, const std::string& body, bool bNewCreateHost)
|
|
|
+{
|
|
|
+ if (TC_Port::strncasecmp(method.c_str(), "GET", 3) == 0)
|
|
|
+ setGetRequest(sUrl, bNewCreateHost);
|
|
|
+ else if (TC_Port::strncasecmp(method.c_str(), "HEAD", 4) == 0)
|
|
|
+ setHeadRequest(sUrl, bNewCreateHost);
|
|
|
+ else if (TC_Port::strncasecmp(method.c_str(), "POST", 4) == 0)
|
|
|
+ setPostRequest(sUrl, body, bNewCreateHost);
|
|
|
+ else if (TC_Port::strncasecmp(method.c_str(), "PUT", 3) == 0)
|
|
|
+ setPutRequest(sUrl, body, bNewCreateHost);
|
|
|
+ else if (TC_Port::strncasecmp(method.c_str(), "DELETE", 6) == 0)
|
|
|
+ setDeleteRequest(sUrl, body, bNewCreateHost);
|
|
|
+ else if (TC_Port::strncasecmp(method.c_str(), "PATH", 5) == 0)
|
|
|
+ setPatchRequest(sUrl, body, bNewCreateHost);
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setGetRequest(const string &sUrl, bool bNewCreateHost)
|
|
|
+{
|
|
|
+ if (bNewCreateHost)
|
|
|
+ {
|
|
|
+ eraseHeader("Host");
|
|
|
+ }
|
|
|
+
|
|
|
+ parseURL(sUrl);
|
|
|
+
|
|
|
+ _requestType = REQUEST_GET;
|
|
|
+
|
|
|
+ _content = "";
|
|
|
+
|
|
|
+ eraseHeader("Content-Length");
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setHeadRequest(const string &sUrl, bool bNewCreateHost)
|
|
|
+{
|
|
|
+ if (bNewCreateHost)
|
|
|
+ {
|
|
|
+ eraseHeader("Host");
|
|
|
+ }
|
|
|
+
|
|
|
+ parseURL(sUrl);
|
|
|
+
|
|
|
+ _requestType = REQUEST_HEAD;
|
|
|
+
|
|
|
+ _content = "";
|
|
|
+
|
|
|
+ eraseHeader("Content-Length");
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setPostRequest(const string &sUrl, const string &sPostBody, bool bNewCreateHost)
|
|
|
+{
|
|
|
+ if (bNewCreateHost)
|
|
|
+ {
|
|
|
+ eraseHeader("Host");
|
|
|
+ }
|
|
|
+
|
|
|
+ parseURL(sUrl);
|
|
|
+
|
|
|
+ _requestType = REQUEST_POST;
|
|
|
+
|
|
|
+ _content = sPostBody;
|
|
|
+
|
|
|
+ setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setOptionsRequest(const string &sUrl, bool bNewCreateHost)
|
|
|
+{
|
|
|
+ if (bNewCreateHost)
|
|
|
+ {
|
|
|
+ eraseHeader("Host");
|
|
|
+ }
|
|
|
+
|
|
|
+ parseURL(sUrl);
|
|
|
+
|
|
|
+ _requestType = REQUEST_OPTIONS;
|
|
|
+
|
|
|
+ _content = "";
|
|
|
+
|
|
|
+ eraseHeader("Content-Length");
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setPostRequest(const string &sUrl, const char *sBuffer, size_t iLength, bool bNewCreateHost)
|
|
|
+{
|
|
|
+ assert(sBuffer != NULL);
|
|
|
+
|
|
|
+ if (bNewCreateHost)
|
|
|
+ {
|
|
|
+ eraseHeader("Host");
|
|
|
+ }
|
|
|
+
|
|
|
+ parseURL(sUrl);
|
|
|
+
|
|
|
+ _requestType = REQUEST_POST;
|
|
|
+
|
|
|
+ if (iLength > 0)
|
|
|
+ {
|
|
|
+ _content.assign(sBuffer, iLength);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _content.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void TC_HttpRequest::setPutRequest(const string &sUrl, const string &sPostBody, bool bNewCreateHost )
|
|
|
+{
|
|
|
+ if(bNewCreateHost)
|
|
|
+ {
|
|
|
+ eraseHeader("Host");
|
|
|
+ }
|
|
|
+
|
|
|
+ parseURL(sUrl);
|
|
|
+
|
|
|
+ _requestType = REQUEST_PUT;
|
|
|
+
|
|
|
+ _content = sPostBody;
|
|
|
+
|
|
|
+ setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setPatchRequest(const string &sUrl, const string &sPostBody, bool bNewCreateHost )
|
|
|
+{
|
|
|
+ if(bNewCreateHost)
|
|
|
+ {
|
|
|
+ eraseHeader("Host");
|
|
|
+ }
|
|
|
+
|
|
|
+ parseURL(sUrl);
|
|
|
+
|
|
|
+ _requestType = REQUEST_PATCH;
|
|
|
+
|
|
|
+ _content = sPostBody;
|
|
|
+
|
|
|
+ setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void TC_HttpRequest::setDeleteRequest(const string &sUrl, const string &sPostBody, bool bNewCreateHost )
|
|
|
+{
|
|
|
+ if(bNewCreateHost)
|
|
|
+ {
|
|
|
+ eraseHeader("Host");
|
|
|
+ }
|
|
|
+
|
|
|
+ parseURL(sUrl);
|
|
|
+
|
|
|
+ _requestType = REQUEST_DELETE;
|
|
|
+
|
|
|
+ _content = sPostBody;
|
|
|
+
|
|
|
+ setHeader("Content-Length", TC_Common::tostr(_content.length()));
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+string TC_HttpRequest::encode()
|
|
|
+{
|
|
|
+ string sRet;
|
|
|
+ sRet.reserve(128);
|
|
|
+ if(_requestType == REQUEST_GET || _requestType == REQUEST_OPTIONS || _requestType == REQUEST_HEAD)
|
|
|
+ {
|
|
|
+ sRet += getMethod();//(_requestType);
|
|
|
+ sRet += " ";
|
|
|
+ sRet += _httpURL.getRequest();
|
|
|
+ sRet += " HTTP/1.1\r\n";
|
|
|
+ genHeader(sRet);
|
|
|
+ sRet += "\r\n";
|
|
|
+ }
|
|
|
+ else if(_requestType == REQUEST_POST || _requestType == REQUEST_PUT || _requestType == REQUEST_PATCH || _requestType == REQUEST_DELETE)
|
|
|
+ {
|
|
|
+ setContentLength(_content.length());
|
|
|
+ sRet += getMethod();//requestType2str(_requestType);
|
|
|
+ sRet += " ";
|
|
|
+ sRet += _httpURL.getRequest();
|
|
|
+ sRet += " HTTP/1.1\r\n";
|
|
|
+ genHeader(sRet);
|
|
|
+ sRet += "\r\n";
|
|
|
+ sRet += _content;
|
|
|
+ }
|
|
|
+ return sRet;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::encode(vector<char> &sRet)
|
|
|
+{
|
|
|
+ string s = encode();
|
|
|
+ sRet.resize(s.length());
|
|
|
+ memcpy(sRet.data(), s.c_str(), s.length());
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::encode(TC_NetWorkBuffer &buff)
|
|
|
+{
|
|
|
+ buff.addBuffer(std::move(encode()));
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpRequest::decode(const string &sBuffer)
|
|
|
+{
|
|
|
+ return decode(sBuffer.c_str(), sBuffer.length());
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpRequest::decode(const vector<char> &sBuffer)
|
|
|
+{
|
|
|
+ return decode(sBuffer.data(), sBuffer.size());
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpRequest::decode(const char *sBuffer, size_t iLength)
|
|
|
+{
|
|
|
+ assert(sBuffer != NULL);
|
|
|
+
|
|
|
+ const char *p = strnstr(sBuffer, " ", 10);
|
|
|
+ if(p == NULL)
|
|
|
+ {
|
|
|
+ throw runtime_error("[TC_HttpRequest::decode] http protocol parse error");
|
|
|
+ }
|
|
|
+
|
|
|
+ auto it = TC_Http::HEADER.find(string(sBuffer, p - sBuffer));
|
|
|
+ if(it == TC_Http::HEADER.end())
|
|
|
+ {
|
|
|
+ throw runtime_error("[TC_HttpRequest::decode] protocol not support ");
|
|
|
+ }
|
|
|
+
|
|
|
+ p = strnstr(sBuffer, "\r\n\r\n", iLength);
|
|
|
+ if (p == NULL)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ _requestType = it->second;
|
|
|
+
|
|
|
+ parseRequestHeader(sBuffer, p);
|
|
|
+
|
|
|
+ _headLength = p - sBuffer + 4;
|
|
|
+
|
|
|
+ bool bChunk = checkHeader("Transfer-Encoding", "chunked"); //(getHeader("Transfer-Encoding") == "chunked");
|
|
|
+
|
|
|
+ int iChunkSuffixLen = 0;
|
|
|
+
|
|
|
+ if (bChunk)
|
|
|
+ {
|
|
|
+ p = sBuffer + _headLength;
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ const char *pos = strnstr(p, "\r\n", iLength - (p - sBuffer));
|
|
|
+
|
|
|
+// const char *pos = strstr(p, "\r\n");
|
|
|
+ if (pos == NULL)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ //查找当前chunk的大小
|
|
|
+ string sChunkSize(p, pos - p);
|
|
|
+ int iChunkSize = strtol(sChunkSize.c_str(), NULL, 16);
|
|
|
+
|
|
|
+ iChunkSuffixLen = iChunkSuffixLen + sChunkSize.length();
|
|
|
+ if (iChunkSize <= 0)
|
|
|
+ {
|
|
|
+ iChunkSuffixLen = iChunkSuffixLen + 4;
|
|
|
+ break; //所有chunk都接收完毕
|
|
|
+ }
|
|
|
+ if ((sBuffer+iLength-p) >= (pos - p) + 2 + iChunkSize + 2) //接收到一个完整的chunk了
|
|
|
+ {
|
|
|
+ //获取一个chunk的内容
|
|
|
+ _content.append(pos + 2, iChunkSize);
|
|
|
+
|
|
|
+ //删除一个chunk
|
|
|
+ p = pos + 2 + iChunkSize + 2;
|
|
|
+ iChunkSuffixLen = iChunkSuffixLen + 4;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //没有接收完整的chunk
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ setContentLength(getContent().length());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _content.assign((sBuffer + _headLength), iLength - _headLength);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (getContentLength() + getHeadLength() + iChunkSuffixLen == iLength);
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpRequest::checkRequest(TC_NetWorkBuffer &buff)
|
|
|
+{
|
|
|
+ buff.mergeBuffers();
|
|
|
+
|
|
|
+ return checkRequest(buff.getBufferPointer().first, buff.getBufferLength());
|
|
|
+}
|
|
|
+
|
|
|
+bool TC_HttpRequest::checkRequest(const char* sBuffer, size_t iLen)
|
|
|
+{
|
|
|
+ if(iLen < 10)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ const char *p = strnstr(sBuffer, " ", 10);
|
|
|
+ if(p == NULL)
|
|
|
+ {
|
|
|
+ throw runtime_error("[TC_HttpRequest::checkRequest] http protocol parse error");
|
|
|
+ }
|
|
|
+
|
|
|
+ auto it = TC_Http::HEADER.find(string(sBuffer, p - sBuffer));
|
|
|
+ if(it == TC_Http::HEADER.end())
|
|
|
+ {
|
|
|
+ throw runtime_error("[TC_HttpRequest::checkRequest] protocol not support: " + string(sBuffer, 8));
|
|
|
+ }
|
|
|
+
|
|
|
+ const char *header = strnstr(sBuffer, "\r\n\r\n", iLen);
|
|
|
+ if ( header == NULL)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t pos = header - sBuffer;
|
|
|
+
|
|
|
+ size_t iHeadLen = pos + 4;
|
|
|
+
|
|
|
+ p = strnstr(sBuffer, "\r\n", iHeadLen);
|
|
|
+ if(p == NULL)
|
|
|
+ {
|
|
|
+ //first line
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ p += 2;
|
|
|
+
|
|
|
+ bool bChunk = false;
|
|
|
+
|
|
|
+ size_t len = 0;
|
|
|
+
|
|
|
+ //找到\r\n\r\n之前的长度表示
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ size_t iMoveLen = p - sBuffer;
|
|
|
+ if ( iMoveLen >= iHeadLen )
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ const char *line = p;
|
|
|
+// const char *lineEnd = strnstr(line, "\r\n", iLen - (line - sBuffer));
|
|
|
+
|
|
|
+ const char *lineEnd = strstr(line, "\r\n");
|
|
|
+ if (lineEnd != NULL)
|
|
|
+ {
|
|
|
+ p = lineEnd + 2;
|
|
|
+
|
|
|
+ if (TC_Port::strncasecmp(line, "Transfer-Encoding:", 18) == 0)
|
|
|
+ {
|
|
|
+ string chunked = TC_Common::trim(string(line + 18, lineEnd));
|
|
|
+ bChunk = (chunked == "chunked");
|
|
|
+ if (bChunk) break;
|
|
|
+ }
|
|
|
+ else if (TC_Port::strncasecmp(line, "Content-Length:", 15) != 0)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ len = TC_Common::strto<size_t>(TC_Common::trim(string(line + 15, lineEnd), " "));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bChunk)
|
|
|
+ {
|
|
|
+ int remain_len = iLen - iHeadLen;
|
|
|
+ int move_len = 0;
|
|
|
+ const char * pCur = header + 4;
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ p = strnstr(pCur, "\r\n", remain_len);
|
|
|
+
|
|
|
+// p = strstr(pCur , "\r\n");
|
|
|
+ if ( p == NULL )
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ //查找当前chunk的大小
|
|
|
+ int iChunkSize = strtol(string(pCur, p - pCur).c_str(), NULL, 16);
|
|
|
+ if (iChunkSize <= 0)
|
|
|
+ {
|
|
|
+ return true; //所有chunk都接收完毕
|
|
|
+ }
|
|
|
+
|
|
|
+ move_len = (p - pCur) + 2 + iChunkSize + 2;
|
|
|
+ if ( remain_len >= move_len ) //接收到一个完整的chunk了
|
|
|
+ {
|
|
|
+ //移动到下一个chunk
|
|
|
+ remain_len -= move_len;
|
|
|
+ pCur = p + 2 + iChunkSize + 2;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (len + pos + 4 <= iLen)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+size_t TC_Http::parseHeaderString(const char *beginIt, const char *headerIt, TC_Http::http_header_type &sHeader)
|
|
|
+{
|
|
|
+ size_t contentLength = -1;
|
|
|
+
|
|
|
+ sHeader.clear();
|
|
|
+
|
|
|
+ bool first = true;
|
|
|
+ auto lineStartIt= beginIt;
|
|
|
+ bool isLastLine = false;
|
|
|
+
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ auto it = strnstr(lineStartIt, "\r\n", headerIt - lineStartIt);
|
|
|
+ if(it == NULL)
|
|
|
+ {
|
|
|
+ it = headerIt;
|
|
|
+ isLastLine = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ //first line ignore
|
|
|
+ if(!first)
|
|
|
+ {
|
|
|
+ auto itF = strnstr(lineStartIt, ":", it - lineStartIt);
|
|
|
+ if (itF != NULL)
|
|
|
+ {
|
|
|
+ while(*lineStartIt == ' ')
|
|
|
+ ++lineStartIt;
|
|
|
+
|
|
|
+ const char *s = itF;
|
|
|
+ while(*s == ' ')
|
|
|
+ --s;
|
|
|
+
|
|
|
+ string name(lineStartIt, s - lineStartIt);
|
|
|
+
|
|
|
+ ++itF;
|
|
|
+ while(*itF == ' ')
|
|
|
+ ++itF;
|
|
|
+
|
|
|
+ s = it;
|
|
|
+ while(*s == ' ')
|
|
|
+ --s;
|
|
|
+
|
|
|
+ string value(itF, s-itF);
|
|
|
+
|
|
|
+ if (TC_Port::strncasecmp(name.c_str(), "Content-Length", 14) == 0)
|
|
|
+ {
|
|
|
+ contentLength = TC_Common::strto<size_t>(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(name.empty())
|
|
|
+ {
|
|
|
+ assert(false);
|
|
|
+ }
|
|
|
+ sHeader.insert(multimap<string, string>::value_type(std::move(name), std::move(value)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ first = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isLastLine)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ lineStartIt = it + 2;//sep.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ return contentLength;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::parseRequestHeader(const char* szBuffer, const char *header)
|
|
|
+{
|
|
|
+ const char *p = strstr(szBuffer, "\r\n");
|
|
|
+
|
|
|
+ assert(p != NULL);
|
|
|
+
|
|
|
+ auto f1 = strnstr(szBuffer, " ", p - szBuffer);
|
|
|
+ if (f1 == p)
|
|
|
+ {
|
|
|
+ throw TC_HttpRequest_Exception("[TC_HttpRequest::parseRequestHeader] http request format error: " + string(szBuffer, p - szBuffer));
|
|
|
+ }
|
|
|
+
|
|
|
+ auto f2 = strnstr(f1 + 1, " ", p - f1 + 1);//std::search(f1 + 1, p, sep.c_str(), sep.c_str() + sep.size());
|
|
|
+ if (f2 == p || f1 >= f2)
|
|
|
+ {
|
|
|
+ throw TC_HttpRequest_Exception("[TC_HttpRequest::parseRequestHeader] http request format error: " + string(szBuffer, p - szBuffer));
|
|
|
+ }
|
|
|
+
|
|
|
+ //URL地址
|
|
|
+ string sURL(f1 + 1, f2 - f1 - 1 );
|
|
|
+
|
|
|
+ //HTTP协议版本
|
|
|
+ _version.assign(f2 + 1, p - f2 - 1);
|
|
|
+
|
|
|
+ parseHeaderString(szBuffer, header, _headers);
|
|
|
+
|
|
|
+ if (TC_Port::strncasecmp(f1 + 1, "https://", 8) != 0 )
|
|
|
+ {
|
|
|
+ if (TC_Port::strncasecmp(f1 + 1, "http://", 7) != 0 )
|
|
|
+ {
|
|
|
+ sURL = "http://" + getHost() + sURL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ parseURL(sURL);
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::getHostPort(string &sDomain, uint32_t &iPort)
|
|
|
+{
|
|
|
+ sDomain = _httpURL.getDomain();
|
|
|
+
|
|
|
+ iPort = TC_Common::strto<uint32_t>(_httpURL.getPort());
|
|
|
+}
|
|
|
+
|
|
|
+const string &TC_HttpRequest::getMethod() const
|
|
|
+{
|
|
|
+ auto it = TC_Http::HEADER_REVERSE.find(_requestType);
|
|
|
+ if(it != TC_Http::HEADER_REVERSE.end())
|
|
|
+ {
|
|
|
+ return it->second;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ throw TC_HttpRequest_Exception("[TC_HttpRequest::setMethod] http request command error: " + TC_Common::tostr(_requestType));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setMethod(const char * sMethod)
|
|
|
+{
|
|
|
+ auto it = TC_Http::HEADER.find(sMethod);
|
|
|
+ if(it != TC_Http::HEADER.end())
|
|
|
+ {
|
|
|
+ _requestType = it->second;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ throw TC_HttpRequest_Exception("[TC_HttpRequest::setMethod] http request command error: " + string(sMethod));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setPath(const char *path)
|
|
|
+{
|
|
|
+ _httpURL._sPath = path;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setDomain(const char * sDomain)
|
|
|
+{
|
|
|
+ _httpURL._sDomain = sDomain;
|
|
|
+}
|
|
|
+
|
|
|
+void TC_HttpRequest::setScheme(const char * sScheme)
|
|
|
+{
|
|
|
+ _httpURL._sScheme = sScheme;
|
|
|
+}
|
|
|
+
|
|
|
+int TC_HttpRequest::doRequest(TC_TCPClient& tcpClient, TC_HttpResponse& stHttpRsp)
|
|
|
+{
|
|
|
+ //只支持短连接模式
|
|
|
+ setConnection("close");
|
|
|
+
|
|
|
+ string sSendBuffer = encode();
|
|
|
+
|
|
|
+ int iRet = tcpClient.send(sSendBuffer.c_str(), sSendBuffer.length());
|
|
|
+ if (iRet != TC_ClientSocket::EM_SUCCESS)
|
|
|
+ {
|
|
|
+ return iRet;
|
|
|
+ }
|
|
|
+
|
|
|
+ stHttpRsp.reset();
|
|
|
+
|
|
|
+ TC_NetWorkBuffer recvBuffer(NULL);
|
|
|
+
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ char buffer[8*1024];
|
|
|
+ size_t iRecvLen = sizeof(buffer);
|
|
|
+
|
|
|
+ iRet = tcpClient.recv(buffer, iRecvLen);
|
|
|
+
|
|
|
+ if (iRet == TC_ClientSocket::EM_SUCCESS)
|
|
|
+ {
|
|
|
+ recvBuffer.addBuffer(buffer, iRecvLen);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (iRet)
|
|
|
+ {
|
|
|
+ case TC_ClientSocket::EM_SUCCESS:
|
|
|
+ if (stHttpRsp.incrementDecode(recvBuffer))
|
|
|
+ {
|
|
|
+ return TC_ClientSocket::EM_SUCCESS;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ case TC_ClientSocket::EM_CLOSE:
|
|
|
+ stHttpRsp.incrementDecode(recvBuffer);
|
|
|
+ return TC_ClientSocket::EM_SUCCESS;
|
|
|
+ default:
|
|
|
+ return iRet;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ assert(true);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+int TC_HttpRequest::doRequest(TC_HttpResponse &stHttpRsp, int iTimeout)
|
|
|
+{
|
|
|
+ //只支持短连接模式
|
|
|
+ setConnection("close");
|
|
|
+
|
|
|
+ string sSendBuffer = encode();
|
|
|
+
|
|
|
+ string sHost;
|
|
|
+ uint32_t iPort;
|
|
|
+
|
|
|
+ getHostPort(sHost, iPort);
|
|
|
+
|
|
|
+ TC_TCPClient tcpClient;
|
|
|
+ tcpClient.init(sHost, iPort, iTimeout);
|
|
|
+
|
|
|
+ int iRet = tcpClient.send(sSendBuffer.c_str(), sSendBuffer.length());
|
|
|
+ if (iRet != TC_ClientSocket::EM_SUCCESS)
|
|
|
+ {
|
|
|
+ return iRet;
|
|
|
+ }
|
|
|
+
|
|
|
+ stHttpRsp.reset();
|
|
|
+
|
|
|
+ TC_NetWorkBuffer recvBuffer(NULL);
|
|
|
+
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ char buffer[8*1024];
|
|
|
+ size_t iRecvLen = sizeof(buffer);
|
|
|
+
|
|
|
+ iRet = tcpClient.recv(buffer, iRecvLen);
|
|
|
+
|
|
|
+ if (iRet == TC_ClientSocket::EM_SUCCESS)
|
|
|
+ {
|
|
|
+ recvBuffer.addBuffer(buffer, iRecvLen);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (iRet)
|
|
|
+ {
|
|
|
+ case TC_ClientSocket::EM_SUCCESS:
|
|
|
+ if (stHttpRsp.incrementDecode(recvBuffer))
|
|
|
+ {
|
|
|
+ return TC_ClientSocket::EM_SUCCESS;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ case TC_ClientSocket::EM_CLOSE:
|
|
|
+ stHttpRsp.incrementDecode(recvBuffer);
|
|
|
+ return TC_ClientSocket::EM_SUCCESS;
|
|
|
+ default:
|
|
|
+ return iRet;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ assert(true);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|