2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "HttpHeader.h"
11 #include "utils/StringUtils.h"
13 // header white space characters according to RFC 2616
14 const char* const CHttpHeader::m_whitespaceChars
= " \t";
17 CHttpHeader::CHttpHeader()
22 CHttpHeader::~CHttpHeader() = default;
24 void CHttpHeader::Parse(const std::string
& strData
)
27 const size_t len
= strData
.length();
28 const char* const strDataC
= strData
.c_str();
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)
35 size_t lineEnd
= strData
.find('\x0a', pos
); // use '\x0a' instead of '\n' to be platform independent
37 if (lineEnd
== std::string::npos
)
38 return; // error: expected only complete lines
40 const size_t nextLine
= lineEnd
+ 1;
41 if (lineEnd
> pos
&& strDataC
[lineEnd
- 1] == '\x0d') // use '\x0d' instead of '\r' to be platform independent
45 Clear(); // clear previous header and process new one
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
);
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
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)
59 m_lastHeaderLine
.assign(strData
, pos
, lineEnd
- pos
); // store current line to (possibly) complete later. Will be parsed on next turns.
62 m_headerdone
= true; // current line is bare "\r\n" (or "\n"), means end of header; no need to process current m_lastHeaderLine
65 pos
= nextLine
; // go to next line (if any)
69 bool CHttpHeader::ParseLine(const std::string
& headerLine
)
71 const size_t valueStart
= headerLine
.find(':');
73 if (valueStart
!= std::string::npos
)
75 std::string
strParam(headerLine
, 0, valueStart
);
76 std::string
strValue(headerLine
, valueStart
+ 1);
78 StringUtils::Trim(strParam
, m_whitespaceChars
);
79 StringUtils::ToLower(strParam
);
81 StringUtils::Trim(strValue
, m_whitespaceChars
);
83 if (!strParam
.empty() && !strValue
.empty())
84 m_params
.push_back(HeaderParams::value_type(strParam
, strValue
));
88 else if (m_protoLine
.empty())
89 m_protoLine
= headerLine
;
94 void CHttpHeader::AddParam(const std::string
& param
, const std::string
& value
, const bool overwrite
/*= false*/)
96 std::string
paramLower(param
);
97 StringUtils::ToLower(paramLower
);
98 StringUtils::Trim(paramLower
, m_whitespaceChars
);
99 if (paramLower
.empty())
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();)
108 if (m_params
[i
].first
== paramLower
)
109 m_params
.erase(m_params
.begin() + i
);
115 std::string
valueTrim(value
);
116 StringUtils::Trim(valueTrim
, m_whitespaceChars
);
117 if (valueTrim
.empty())
120 m_params
.push_back(HeaderParams::value_type(paramLower
, valueTrim
));
123 std::string
CHttpHeader::GetValue(const std::string
& strParam
) const
125 std::string
paramLower(strParam
);
126 StringUtils::ToLower(paramLower
);
128 return GetValueRaw(paramLower
);
131 std::string
CHttpHeader::GetValueRaw(const std::string
& strParam
) const
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
)
136 if (iter
->first
== strParam
)
143 std::vector
<std::string
> CHttpHeader::GetValues(std::string strParam
) const
145 StringUtils::ToLower(strParam
);
146 std::vector
<std::string
> values
;
148 for (HeaderParams::const_iterator iter
= m_params
.begin(); iter
!= m_params
.end(); ++iter
)
150 if (iter
->first
== strParam
)
151 values
.push_back(iter
->second
);
157 std::string
CHttpHeader::GetHeader(void) const
159 if (m_protoLine
.empty() && m_params
.empty())
162 std::string
strHeader(m_protoLine
+ "\r\n");
164 for (HeaderParams::const_iterator iter
= m_params
.begin(); iter
!= m_params
.end(); ++iter
)
165 strHeader
+= ((*iter
).first
+ ": " + (*iter
).second
+ "\r\n");
171 std::string
CHttpHeader::GetMimeType(void) const
173 std::string
strValue(GetValueRaw("content-type"));
175 std::string
mimeType(strValue
, 0, strValue
.find(';'));
176 StringUtils::TrimRight(mimeType
, m_whitespaceChars
);
181 std::string
CHttpHeader::GetCharset(void) const
183 std::string
strValue(GetValueRaw("content-type"));
184 if (strValue
.empty())
187 StringUtils::ToUpper(strValue
);
188 const size_t len
= strValue
.length();
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"'
194 size_t pos
= strValue
.find(';');
197 // move to the next non-whitespace character
198 pos
= strValue
.find_first_not_of(m_whitespaceChars
, pos
+ 1);
200 if (pos
!= std::string::npos
)
202 if (strValue
.compare(pos
, 8, "CHARSET=", 8) == 0)
204 pos
+= 8; // move position to char after 'CHARSET='
205 size_t len
= strValue
.find(';', pos
);
206 if (len
!= std::string::npos
)
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())
213 if (charset
[0] != '"')
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
222 return charset
.substr(1, closingQ
- 1);
226 pos
= strValue
.find(';', pos
); // find next parameter
230 return ""; // no charset is detected
233 void CHttpHeader::Clear()
237 m_headerdone
= false;
238 m_lastHeaderLine
.clear();