/xbmc/utils/HttpHeader.cpp
http://github.com/xbmc/xbmc · C++ · 239 lines · 175 code · 45 blank · 19 comment · 49 complexity · 3ae257b0e416b4ae2302d8eb596ee486 MD5 · raw file
- /*
- * Copyright (C) 2005-2018 Team Kodi
- * This file is part of Kodi - https://kodi.tv
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- * See LICENSES/README.md for more information.
- */
- #include "HttpHeader.h"
- #include "utils/StringUtils.h"
- // header white space characters according to RFC 2616
- const char* const CHttpHeader::m_whitespaceChars = " \t";
- CHttpHeader::CHttpHeader()
- {
- m_headerdone = false;
- }
- CHttpHeader::~CHttpHeader() = default;
- void CHttpHeader::Parse(const std::string& strData)
- {
- size_t pos = 0;
- const size_t len = strData.length();
- const char* const strDataC = strData.c_str();
- // According to RFC 2616 any header line can have continuation on next line, if next line is started from whitespace char
- // This code at first checks for whitespace char at the begging of the line, and if found, then current line is appended to m_lastHeaderLine
- // If current line is NOT started from whitespace char, then previously stored (and completed) m_lastHeaderLine is parsed and current line is assigned to m_lastHeaderLine (to be parsed later)
- while (pos < len)
- {
- size_t lineEnd = strData.find('\x0a', pos); // use '\x0a' instead of '\n' to be platform independent
- if (lineEnd == std::string::npos)
- return; // error: expected only complete lines
- const size_t nextLine = lineEnd + 1;
- if (lineEnd > pos && strDataC[lineEnd - 1] == '\x0d') // use '\x0d' instead of '\r' to be platform independent
- lineEnd--;
- if (m_headerdone)
- Clear(); // clear previous header and process new one
- if (strDataC[pos] == ' ' || strDataC[pos] == '\t') // same chars as in CHttpHeader::m_whitespaceChars
- { // line is started from whitespace char: this is continuation of previous line
- pos = strData.find_first_not_of(m_whitespaceChars, pos);
- m_lastHeaderLine.push_back(' '); // replace all whitespace chars at start of the line with single space
- m_lastHeaderLine.append(strData, pos, lineEnd - pos); // append current line
- }
- else
- { // this line is NOT continuation, this line is new header line
- if (!m_lastHeaderLine.empty())
- ParseLine(m_lastHeaderLine); // process previously stored completed line (if any)
- m_lastHeaderLine.assign(strData, pos, lineEnd - pos); // store current line to (possibly) complete later. Will be parsed on next turns.
- if (pos == lineEnd)
- m_headerdone = true; // current line is bare "\r\n" (or "\n"), means end of header; no need to process current m_lastHeaderLine
- }
- pos = nextLine; // go to next line (if any)
- }
- }
- bool CHttpHeader::ParseLine(const std::string& headerLine)
- {
- const size_t valueStart = headerLine.find(':');
- if (valueStart != std::string::npos)
- {
- std::string strParam(headerLine, 0, valueStart);
- std::string strValue(headerLine, valueStart + 1);
- StringUtils::Trim(strParam, m_whitespaceChars);
- StringUtils::ToLower(strParam);
- StringUtils::Trim(strValue, m_whitespaceChars);
- if (!strParam.empty() && !strValue.empty())
- m_params.push_back(HeaderParams::value_type(strParam, strValue));
- else
- return false;
- }
- else if (m_protoLine.empty())
- m_protoLine = headerLine;
- return true;
- }
- void CHttpHeader::AddParam(const std::string& param, const std::string& value, const bool overwrite /*= false*/)
- {
- std::string paramLower(param);
- StringUtils::ToLower(paramLower);
- StringUtils::Trim(paramLower, m_whitespaceChars);
- if (paramLower.empty())
- return;
- if (overwrite)
- { // delete ALL parameters with the same name
- // note: 'GetValue' always returns last added parameter,
- // so you probably don't need to overwrite
- for (size_t i = 0; i < m_params.size();)
- {
- if (m_params[i].first == paramLower)
- m_params.erase(m_params.begin() + i);
- else
- ++i;
- }
- }
- std::string valueTrim(value);
- StringUtils::Trim(valueTrim, m_whitespaceChars);
- if (valueTrim.empty())
- return;
- m_params.push_back(HeaderParams::value_type(paramLower, valueTrim));
- }
- std::string CHttpHeader::GetValue(const std::string& strParam) const
- {
- std::string paramLower(strParam);
- StringUtils::ToLower(paramLower);
- return GetValueRaw(paramLower);
- }
- std::string CHttpHeader::GetValueRaw(const std::string& strParam) const
- {
- // look in reverse to find last parameter (probably most important)
- for (HeaderParams::const_reverse_iterator iter = m_params.rbegin(); iter != m_params.rend(); ++iter)
- {
- if (iter->first == strParam)
- return iter->second;
- }
- return "";
- }
- std::vector<std::string> CHttpHeader::GetValues(std::string strParam) const
- {
- StringUtils::ToLower(strParam);
- std::vector<std::string> values;
- for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
- {
- if (iter->first == strParam)
- values.push_back(iter->second);
- }
- return values;
- }
- std::string CHttpHeader::GetHeader(void) const
- {
- if (m_protoLine.empty() && m_params.empty())
- return "";
- std::string strHeader(m_protoLine + "\r\n");
- for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
- strHeader += ((*iter).first + ": " + (*iter).second + "\r\n");
- strHeader += "\r\n";
- return strHeader;
- }
- std::string CHttpHeader::GetMimeType(void) const
- {
- std::string strValue(GetValueRaw("content-type"));
- std::string mimeType(strValue, 0, strValue.find(';'));
- StringUtils::TrimRight(mimeType, m_whitespaceChars);
- return mimeType;
- }
- std::string CHttpHeader::GetCharset(void) const
- {
- std::string strValue(GetValueRaw("content-type"));
- if (strValue.empty())
- return strValue;
- StringUtils::ToUpper(strValue);
- const size_t len = strValue.length();
- // extract charset value from 'contenttype/contentsubtype;pram1=param1Val ; charset=XXXX\t;param2=param2Val'
- // most common form: 'text/html; charset=XXXX'
- // charset value can be in double quotes: 'text/xml; charset="XXX XX"'
- size_t pos = strValue.find(';');
- while (pos < len)
- {
- // move to the next non-whitespace character
- pos = strValue.find_first_not_of(m_whitespaceChars, pos + 1);
- if (pos != std::string::npos)
- {
- if (strValue.compare(pos, 8, "CHARSET=", 8) == 0)
- {
- pos += 8; // move position to char after 'CHARSET='
- size_t len = strValue.find(';', pos);
- if (len != std::string::npos)
- len -= pos;
- std::string charset(strValue, pos, len); // intentionally ignoring possible ';' inside quoted string
- // as we don't support any charset with ';' in name
- StringUtils::Trim(charset, m_whitespaceChars);
- if (!charset.empty())
- {
- if (charset[0] != '"')
- return charset;
- else
- { // charset contains quoted string (allowed according to RFC 2616)
- StringUtils::Replace(charset, "\\", ""); // unescape chars, ignoring possible '\"' and '\\'
- const size_t closingQ = charset.find('"', 1);
- if (closingQ == std::string::npos)
- return ""; // no closing quote
- return charset.substr(1, closingQ - 1);
- }
- }
- }
- pos = strValue.find(';', pos); // find next parameter
- }
- }
- return ""; // no charset is detected
- }
- void CHttpHeader::Clear()
- {
- m_params.clear();
- m_protoLine.clear();
- m_headerdone = false;
- m_lastHeaderLine.clear();
- }