2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "HttpHeader.h"
22 #include "utils/StringUtils.h"
24 // header white space characters according to RFC 2616
25 const char* const CHttpHeader::m_whitespaceChars = " \t";
28 CHttpHeader::CHttpHeader()
33 CHttpHeader::~CHttpHeader()
37 void CHttpHeader::Parse(const std::string& strData)
40 const size_t len = strData.length();
41 const char* const strDataC = strData.c_str();
43 // According to RFC 2616 any header line can have continuation on next line, if next line is started from whitespace char
44 // 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
45 // 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)
48 const size_t lineEnd = strData.find("\x0d\x0a", pos); // use "\x0d\x0a" instead of "\r\n" to be platform independent
50 if (lineEnd == std::string::npos)
51 return; // error: expected only complete lines
54 Clear(); // clear previous header and process new one
56 if (strDataC[pos] == ' ' || strDataC[pos] == '\t') // same chars as in CHttpHeader::m_whitespaceChars
57 { // line is started from whitespace char: this is continuation of previous line
58 pos = strData.find_first_not_of(m_whitespaceChars);
60 m_lastHeaderLine.push_back(' '); // replace all whitespace chars at start of the line with single space
61 m_lastHeaderLine.append(strData, pos, lineEnd - pos); // append current line
64 { // this line is NOT continuation, this line is new header line
65 if (!m_lastHeaderLine.empty())
66 ParseLine(m_lastHeaderLine); // process previously stored completed line (if any)
68 m_lastHeaderLine.assign(strData, pos, lineEnd - pos); // store current line to (possibly) complete later. Will be parsed on next turns.
71 m_headerdone = true; // current line is bare "\r\n", means end of header; no need to process current m_lastHeaderLine
74 pos = lineEnd + 2; // '+2' for "\r\n": go to next line (if any)
78 bool CHttpHeader::ParseLine(const std::string& headerLine)
80 const size_t valueStart = headerLine.find(':');
82 if (valueStart != std::string::npos)
84 std::string strParam(headerLine, 0, valueStart);
85 std::string strValue(headerLine, valueStart + 1);
87 StringUtils::Trim(strParam, m_whitespaceChars);
88 StringUtils::ToLower(strParam);
90 StringUtils::Trim(strValue, m_whitespaceChars);
92 if (!strParam.empty() && !strValue.empty())
93 m_params.push_back(HeaderParams::value_type(strParam, strValue));
97 else if (m_protoLine.empty())
98 m_protoLine = headerLine;
103 void CHttpHeader::AddParam(const std::string& param, const std::string& value, const bool overwrite /*= false*/)
105 if (param.empty() || value.empty())
108 std::string paramLower(param);
110 { // delete ALL parameters with the same name
111 // note: 'GetValue' always returns last added parameter,
112 // so you probably don't need to overwrite
113 for (size_t i = 0; i < m_params.size();)
115 if (m_params[i].first == param)
116 m_params.erase(m_params.begin() + i);
122 StringUtils::ToLower(paramLower);
124 m_params.push_back(HeaderParams::value_type(paramLower, value));
127 std::string CHttpHeader::GetValue(const std::string& strParam) const
129 std::string paramLower(strParam);
130 StringUtils::ToLower(paramLower);
132 return GetValueRaw(paramLower);
135 std::string CHttpHeader::GetValueRaw(const std::string& strParam) const
137 // look in reverse to find last parameter (probably most important)
138 for (HeaderParams::const_reverse_iterator iter = m_params.rbegin(); iter != m_params.rend(); ++iter)
140 if (iter->first == strParam)
147 std::vector<std::string> CHttpHeader::GetValues(std::string strParam) const
149 StringUtils::ToLower(strParam);
150 std::vector<std::string> values;
152 for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
154 if (iter->first == strParam)
155 values.push_back(iter->second);
161 std::string CHttpHeader::GetHeader(void) const
163 std::string strHeader(m_protoLine + '\n');
165 for (HeaderParams::const_iterator iter = m_params.begin(); iter != m_params.end(); ++iter)
166 strHeader += ((*iter).first + ": " + (*iter).second + "\n");
172 std::string CHttpHeader::GetMimeType(void) const
174 std::string strValue(GetValueRaw("content-type"));
176 std::string mimeType(strValue, 0, strValue.find(';'));
177 StringUtils::TrimRight(mimeType, m_whitespaceChars);
182 std::string CHttpHeader::GetCharset(void) const
184 std::string strValue(GetValueRaw("content-type"));
185 if (strValue.empty())
188 StringUtils::ToUpper(strValue);
189 const size_t len = strValue.length();
191 // extract charset value from 'contenttype/contentsubtype;pram1=param1Val ; charset=XXXX\t;param2=param2Val'
192 // most common form: 'text/html; charset=XXXX'
193 // charset value can be in double quotes: 'text/xml; charset="XXX XX"'
195 size_t pos = strValue.find(';');
198 // move to the next non-whitespace character
199 pos = strValue.find_first_not_of(m_whitespaceChars, pos + 1);
201 if (pos != std::string::npos)
203 if (strValue.compare(pos, 8, "CHARSET=", 8) == 0)
205 pos += 8; // move position to char after 'CHARSET='
206 std::string charset(strValue, pos, strValue.find(';', pos)); // intentionally ignoring possible ';' inside quoted string
207 // as we don't support any charset with ';' in name
208 StringUtils::Trim(charset, m_whitespaceChars);
209 if (!charset.empty())
211 if (charset[0] != '"')
214 { // charset contains quoted string (allowed according to RFC 2616)
215 StringUtils::Replace(charset, "\\", ""); // unescape chars, ignoring possible '\"' and '\\'
216 const size_t closingQ = charset.find('"', 1);
217 if (closingQ == std::string::npos)
218 return ""; // no closing quote
220 return charset.substr(1, closingQ - 1);
224 pos = strValue.find(';', pos); // find next parameter
228 return ""; // no charset is detected
231 void CHttpHeader::Clear()
235 m_headerdone = false;
236 m_lastHeaderLine.clear();