36 m_sep(std::string::npos)
38 G_ASSERT( protocol.empty() || protocol.size() == 4U ) ;
39 m_http = protocol.empty() ?
"HTTP/1." : (protocol+
"/1.") ;
40 m_data.reserve( 100000U ) ;
45 m_data.append( p , n ) ;
46 if( m_data.size() > G::limits::net_file_limit )
47 throw std::runtime_error(
"receive buffer size limit exceeded" ) ;
50 if( m_sep == std::string::npos )
52 m_sep = m_data.find(
"\r\n\r\n" ) ;
53 if( m_sep != std::string::npos )
55 m_hlist = headerInfo( m_data ) ;
56 m_response_status = responseStatusInfo( m_http , m_data ) ;
61 GNet::HttpClientParser::List GNet::HttpClientParser::headerInfo(
const std::string & data )
64 typedef std::string::size_type pos_t ;
65 const pos_t npos = std::string::npos ;
66 for( pos_t pos = 0U ;; pos += 2U )
68 pos_t next_pos = data.find(
"\r\n" , pos ) ;
69 if( next_pos == npos || pos == next_pos ) break ;
70 result.push_back( HeaderInfo(data.substr(pos,next_pos-pos)) ) ;
79 m_sep = std::string::npos ;
81 m_response_status = ResponseStatusInfo() ;
84 GNet::HttpClientParser::ResponseStatusInfo GNet::HttpClientParser::responseStatusInfo(
const std::string & http ,
85 const std::string & data )
89 ResponseStatusInfo result ;
91 const size_t npos = std::string::npos ;
92 size_t pos1 = data.find(
' ') ;
93 size_t pos2 = (pos1==npos||(pos1+1U)==data.size()) ? npos : data.find(
" ",pos1+1U) ;
94 size_t pos3 = (pos2==npos||(pos2+1U)==data.size()) ? npos : data.find(
"\n",pos2+1U) ;
95 if( data.find(http) == 0U && pos1 != npos && pos2 != npos && pos3 != npos )
98 std::string s2 =
G::Str::trimmed( data.substr(pos2,pos3-pos2) ,
" \r\n" ) ;
100 result.ok = result.value >= 200 && result.value < 300 ;
101 result.unauthorised = result.value == 401 && s2 ==
"Unauthorized" ;
102 result.retry = result.value == 503 ? 1U : 0U ;
109 return m_response_status.value ;
114 return m_response_status.unauthorised ;
119 return m_response_status.ok ;
124 return m_response_status.retry > 0U ;
129 return m_sep != std::string::npos ;
134 return m_hlist.size() ;
139 const size_t npos = std::string::npos ;
140 size_t start = m_data.find(
" ") ;
141 size_t end = m_data.find_first_of(
"\n\r") ;
142 std::string s = ( start != npos && end != npos && end > start ) ? m_data.substr(start+1U,end-start) : std::string() ;
146 GNet::HttpClientParser::HeaderInfo::HeaderInfo(
const std::string & line ) :
149 size_t pos = line.find(
":" ) ;
156 bool GNet::HttpClientParser::HeaderInfo::match(
const std::string & key )
const
161 int GNet::HttpClientParser::HeaderInfo::value(
int default_ )
const
169 GNet::HttpClientParser::List::const_iterator GNet::HttpClientParser::hfind(
const List & hlist ,
170 const std::string & key )
172 for( List::const_iterator p = hlist.begin() ; p != hlist.end() ; ++p )
174 if( (*p).match(key) )
180 GNet::HttpClientParser::List::const_iterator GNet::HttpClientParser::hfind(
const List & hlist ,
size_t index )
182 if( index >= hlist.size() )
throw std::range_error(
"invalid http header index" ) ;
183 return hlist.begin() + index ;
188 std::vector<size_t> result ;
190 for( List::const_iterator p = m_hlist.begin() ; p != m_hlist.end() ; ++p , i++ )
192 if( (*p).match(key) )
193 result.push_back( i ) ;
200 List::const_iterator p = hfind( m_hlist , key ) ;
201 return p == m_hlist.end() ? default_ : (*p).m_rhs ;
206 List::const_iterator p = hfind( m_hlist , index ) ;
207 return p == m_hlist.end() ? default_ : (*p).m_rhs ;
212 List::const_iterator p = hfind( m_hlist , key ) ;
213 return p == m_hlist.end() ? default_ : (*p).value(default_) ;
218 List::const_iterator p = hfind( m_hlist , index ) ;
219 return p == m_hlist.end() ? default_ : (*p).value(default_) ;
224 return static_cast<size_t>( headerValue(
"Content-Length",0) ) ;
229 List::const_iterator p = hfind( m_hlist , key ) ;
230 return p == m_hlist.end() || (*p).m_tokens.empty() ? default_ : (*p).m_tokens.at(0U) ;
235 List::const_iterator p = hfind( m_hlist , index ) ;
236 return p == m_hlist.end() || (*p).m_tokens.empty() ? default_ : (*p).m_tokens.at(0U) ;
240 const std::string & default_ )
const
242 List::const_iterator p = hfind( m_hlist , key ) ;
243 return p == m_hlist.end() ? default_ : headerAttribute( p , attribute_key , default_ ) ;
247 const std::string & default_ )
const
249 List::const_iterator p = hfind( m_hlist , index ) ;
250 return p == m_hlist.end() ? default_ : headerAttribute( p , attribute_key , default_ ) ;
254 const std::string & default_ )
const
257 for(
size_t i = 0U ; i < attributes.size() ; i++ )
259 if( attributes[i].find(attribute_key+
"=") == 0U )
261 std::string result = attributes[i].substr(attribute_key.length()+1U) ;
262 if( result.find(
"\"") == 0U && result.length() >= 2U &&
263 (result.rfind(
"\"")+1U) == result.length() )
265 result = result.substr(1U,result.length()-2U) ;
275 return headerWord(
"Content-Type" ) ;
280 std::string content_type = headerContentType() ;
281 bool multipart = content_type.find(
"multipart") == 0U ;
285 std::string GNet::HttpClientParser::headerMultipartBoundary()
const
287 if( !headerMultipart() )
return std::string() ;
288 return headerAttribute(
"Content-Type" ,
"boundary" ) ;
294 m_sep != std::string::npos &&
295 m_data.size() >= ( m_sep + 4U + headerContentLength() ) ;
300 return std::string( bodyData() , bodySize() ) ;
305 return m_sep == std::string::npos ?
nullptr : ( m_data.data() + m_sep + 4U ) ;
310 return m_sep == std::string::npos ? 0U : ( m_data.size() - m_sep - 4U ) ;
315 PartInfo part_info = partInfo() ;
316 m_data.erase( part_info.start , part_info.headersize+part_info.bodysize ) ;
321 PartInfo part_info = partInfo() ;
322 return part_info.start != 0U && m_data.size() >= (part_info.start+part_info.headersize+part_info.bodysize) ;
325 GNet::HttpClientParser::PartInfo GNet::HttpClientParser::partInfo()
const
328 std::string::size_type npos = std::string::npos ;
329 std::string::size_type pos1 = m_data.find(
"\r\n--" + headerMultipartBoundary() +
"\r\n" ) ;
330 std::string::size_type pos2 =
G::Str::ifind( m_data ,
"\nContent-Length: " , pos1 ) ;
331 std::string::size_type pos2a =
G::Str::ifind( m_data ,
"\nContent-Type: " , pos1 ) ;
332 std::string::size_type pos3 = m_data.find(
"\r\n" , pos2 ) ;
333 std::string::size_type pos3a = m_data.find(
"\r\n" , pos2a ) ;
334 std::string::size_type pos4 = m_data.find(
"\r\n\r\n" , pos2 ) ;
335 if( pos1 != npos && pos2 != npos && pos2a != npos &&
336 pos3 != npos && pos3a != npos && pos4 != npos &&
337 pos4 >= pos3 && pos4 >= pos3a )
339 std::string cl = m_data.substr( pos2+17U , pos3-pos2-17U ) ;
342 part_info.start = pos1 + 2U ;
343 part_info.headersize = pos4 - part_info.start + 4U ;
345 part_info.type = m_data.substr( pos2a+15U , pos3a-pos2a-15U ) ;
353 PartInfo part_info = partInfo() ;
354 return m_data.data() + part_info.start + part_info.headersize ;
359 PartInfo part_info = partInfo() ;
360 return part_info.bodysize ;
365 PartInfo part_info = partInfo() ;
366 return part_info.type ;
371 GNet::HttpClientParser::ResponseStatusInfo::ResponseStatusInfo()
375 unauthorised = false ;
std::string header(const std::string &header_key, const std::string &default_=std::string()) const
Returns the value of the given header.
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
bool gotBody() const
Returns true if the body is complete.
std::vector< size_t > headers(const std::string &header_key) const
Returns the indexes for the headers with the given key.
int headerValue(const std::string &header_key, int default_=-1) const
Returns the integer value of a numeric header.
size_t headerCount() const
Returns the number of headers.
static int toInt(const std::string &s)
Converts string 's' to an int.
std::string headerContentType() const
Returns the value of the "Content-Type" header.
static bool imatch(const std::string &, const std::string &)
Returns true if the two strings are the same, ignoring case.
std::vector< std::string > StringArray
A std::vector of std::strings.
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
bool responseUnauthorised() const
Returns true for a "401 Unauthorized" response.
std::string headerWord(const std::string &header_key, const std::string &default_=std::string()) const
Returns the first part of the header with the given key.
bool gotPart() const
Returns true if a multipart part is complete.
static std::string tail(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
bool responseOk() const
Returns true for a 2xx response.
std::string partType() const
Returns the content-type of the part.
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
bool gotHeaders() const
Returns true if headers are complete.
static std::string head(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
void clear()
Clears the contents, returning the object to a newly-constructed state.
bool headerMultipart() const
Returns true if the main body is of type "multipart".
bool responseRetry() const
Returns true for a 503 response.
static std::string trimmed(const std::string &s, const std::string &ws)
Returns a trim()med version of s.
void clearPart()
Clears the current multipart body part.
size_t headerContentLength() const
Returns the value of the "Content-Length" header.
int responseValue() const
Returns the response value (eg. 200), or minus one.
size_t partSize() const
Returns the part size.
HttpClientParser(const std::string &protocol=std::string())
Constructor. The protocol defaults to "HTTP".
std::string headerAttribute(const std::string &header_key, const std::string &attribute_key, const std::string &default_=std::string()) const
Returns a named attribute of the specified header.
std::string responseSummary() const
Returns a summary of the response for debugging and error reporting.
const char * partData() const
Returns the part data.
static bool isUInt(const std::string &s)
Returns true if the string can be converted into an unsigned integer without throwing an exception...
std::string body() const
Returns the body data.
static std::string::size_type ifind(const std::string &s, const std::string &key, std::string::size_type pos=0U)
Does a case-insensitive std::string::find().
void apply(const char *p, size_t n)
Adds some data.
const char * bodyData() const
Returns the body data.
static std::string ws()
A convenience function returning standard whitespace characters.
size_t bodySize() const
Returns the body size.