PageRenderTime 56ms CodeModel.GetById 2ms app.highlight 49ms RepoModel.GetById 2ms app.codeStats 0ms

/xbmc/utils/HttpHeader.cpp

http://github.com/xbmc/xbmc
C++ | 239 lines | 175 code | 45 blank | 19 comment | 49 complexity | 3ae257b0e416b4ae2302d8eb596ee486 MD5 | raw file
  1/*
  2 *  Copyright (C) 2005-2018 Team Kodi
  3 *  This file is part of Kodi - https://kodi.tv
  4 *
  5 *  SPDX-License-Identifier: GPL-2.0-or-later
  6 *  See LICENSES/README.md for more information.
  7 */
  8
  9#include "HttpHeader.h"
 10
 11#include "utils/StringUtils.h"
 12
 13// header white space characters according to RFC 2616
 14const char* const CHttpHeader::m_whitespaceChars = " \t";
 15
 16
 17CHttpHeader::CHttpHeader()
 18{
 19  m_headerdone = false;
 20}
 21
 22CHttpHeader::~CHttpHeader() = default;
 23
 24void CHttpHeader::Parse(const std::string& strData)
 25{
 26  size_t pos = 0;
 27  const size_t len = strData.length();
 28  const char* const strDataC = strData.c_str();
 29
 30  // According to RFC 2616 any header line can have continuation on next line, if next line is started from whitespace char
 31  // 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
 32  // 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)
 33  while (pos < len)
 34  {
 35    size_t lineEnd = strData.find('\x0a', pos); // use '\x0a' instead of '\n' to be platform independent
 36
 37    if (lineEnd == std::string::npos)
 38      return; // error: expected only complete lines
 39
 40    const size_t nextLine = lineEnd + 1;
 41    if (lineEnd > pos && strDataC[lineEnd - 1] == '\x0d') // use '\x0d' instead of '\r' to be platform independent
 42      lineEnd--;
 43
 44    if (m_headerdone)
 45      Clear(); // clear previous header and process new one
 46
 47    if (strDataC[pos] == ' ' || strDataC[pos] == '\t') // same chars as in CHttpHeader::m_whitespaceChars
 48    { // line is started from whitespace char: this is continuation of previous line
 49      pos = strData.find_first_not_of(m_whitespaceChars, pos);
 50
 51      m_lastHeaderLine.push_back(' '); // replace all whitespace chars at start of the line with single space
 52      m_lastHeaderLine.append(strData, pos, lineEnd - pos); // append current line
 53    }
 54    else
 55    { // this line is NOT continuation, this line is new header line
 56      if (!m_lastHeaderLine.empty())
 57        ParseLine(m_lastHeaderLine); // process previously stored completed line (if any)
 58
 59      m_lastHeaderLine.assign(strData, pos, lineEnd - pos); // store current line to (possibly) complete later. Will be parsed on next turns.
 60
 61      if (pos == lineEnd)
 62        m_headerdone = true; // current line is bare "\r\n" (or "\n"), means end of header; no need to process current m_lastHeaderLine
 63    }
 64
 65    pos = nextLine; // go to next line (if any)
 66  }
 67}
 68
 69bool CHttpHeader::ParseLine(const std::string& headerLine)
 70{
 71  const size_t valueStart = headerLine.find(':');
 72
 73  if (valueStart != std::string::npos)
 74  {
 75    std::string strParam(headerLine, 0, valueStart);
 76    std::string strValue(headerLine, valueStart + 1);
 77
 78    StringUtils::Trim(strParam, m_whitespaceChars);
 79    StringUtils::ToLower(strParam);
 80
 81    StringUtils::Trim(strValue, m_whitespaceChars);
 82
 83    if (!strParam.empty() && !strValue.empty())
 84      m_params.push_back(HeaderParams::value_type(strParam, strValue));
 85    else
 86      return false;
 87  }
 88  else if (m_protoLine.empty())
 89    m_protoLine = headerLine;
 90
 91  return true;
 92}
 93
 94void CHttpHeader::AddParam(const std::string& param, const std::string& value, const bool overwrite /*= false*/)
 95{
 96  std::string paramLower(param);
 97  StringUtils::ToLower(paramLower);
 98  StringUtils::Trim(paramLower, m_whitespaceChars);
 99  if (paramLower.empty())
100    return;
101
102  if (overwrite)
103  { // delete ALL parameters with the same name
104    // note: 'GetValue' always returns last added parameter,
105    //       so you probably don't need to overwrite
106    for (size_t i = 0; i < m_params.size();)
107    {
108      if (m_params[i].first == paramLower)
109        m_params.erase(m_params.begin() + i);
110      else
111        ++i;
112    }
113  }
114
115  std::string valueTrim(value);
116  StringUtils::Trim(valueTrim, m_whitespaceChars);
117  if (valueTrim.empty())
118    return;
119
120  m_params.push_back(HeaderParams::value_type(paramLower, valueTrim));
121}
122
123std::string CHttpHeader::GetValue(const std::string& strParam) const
124{
125  std::string paramLower(strParam);
126  StringUtils::ToLower(paramLower);
127
128  return GetValueRaw(paramLower);
129}
130
131std::string CHttpHeader::GetValueRaw(const std::string& strParam) const
132{
133  // look in reverse to find last parameter (probably most important)
134  for (HeaderParams::const_reverse_iterator iter = m_params.rbegin(); iter != m_params.rend(); ++iter)
135  {
136    if (iter->first == strParam)
137      return iter->second;
138  }
139
140  return "";
141}
142
143std::vector<std::string> CHttpHeader::GetValues(std::string strParam) const
144{
145  StringUtils::ToLower(strParam);
146  std::vector<std::string> values;
147
148  for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
149  {
150    if (iter->first == strParam)
151      values.push_back(iter->second);
152  }
153
154  return values;
155}
156
157std::string CHttpHeader::GetHeader(void) const
158{
159  if (m_protoLine.empty() && m_params.empty())
160    return "";
161
162  std::string strHeader(m_protoLine + "\r\n");
163
164  for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
165    strHeader += ((*iter).first + ": " + (*iter).second + "\r\n");
166
167  strHeader += "\r\n";
168  return strHeader;
169}
170
171std::string CHttpHeader::GetMimeType(void) const
172{
173  std::string strValue(GetValueRaw("content-type"));
174
175  std::string mimeType(strValue, 0, strValue.find(';'));
176  StringUtils::TrimRight(mimeType, m_whitespaceChars);
177
178  return mimeType;
179}
180
181std::string CHttpHeader::GetCharset(void) const
182{
183  std::string strValue(GetValueRaw("content-type"));
184  if (strValue.empty())
185    return strValue;
186
187  StringUtils::ToUpper(strValue);
188  const size_t len = strValue.length();
189
190  // extract charset value from 'contenttype/contentsubtype;pram1=param1Val ; charset=XXXX\t;param2=param2Val'
191  // most common form: 'text/html; charset=XXXX'
192  // charset value can be in double quotes: 'text/xml; charset="XXX XX"'
193
194  size_t pos = strValue.find(';');
195  while (pos < len)
196  {
197    // move to the next non-whitespace character
198    pos = strValue.find_first_not_of(m_whitespaceChars, pos + 1);
199
200    if (pos != std::string::npos)
201    {
202      if (strValue.compare(pos, 8, "CHARSET=", 8) == 0)
203      {
204        pos += 8; // move position to char after 'CHARSET='
205        size_t len = strValue.find(';', pos);
206        if (len != std::string::npos)
207          len -= pos;
208        std::string charset(strValue, pos, len);  // intentionally ignoring possible ';' inside quoted string
209                                                  // as we don't support any charset with ';' in name
210        StringUtils::Trim(charset, m_whitespaceChars);
211        if (!charset.empty())
212        {
213          if (charset[0] != '"')
214            return charset;
215          else
216          { // charset contains quoted string (allowed according to RFC 2616)
217            StringUtils::Replace(charset, "\\", ""); // unescape chars, ignoring possible '\"' and '\\'
218            const size_t closingQ = charset.find('"', 1);
219            if (closingQ == std::string::npos)
220              return ""; // no closing quote
221
222            return charset.substr(1, closingQ - 1);
223          }
224        }
225      }
226      pos = strValue.find(';', pos); // find next parameter
227    }
228  }
229
230  return ""; // no charset is detected
231}
232
233void CHttpHeader::Clear()
234{
235  m_params.clear();
236  m_protoLine.clear();
237  m_headerdone = false;
238  m_lastHeaderLine.clear();
239}