VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ghttpclientprotocol.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // ghttpclientprotocol.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "ghttpclientprotocol.h"
24 #include "gstr.h"
25 #include "gurl.h"
26 #include "gbase64.h"
27 #include "gmd5.h"
28 #include "ghexdump.h"
29 #include "glog.h"
30 
32  m_callback(callback) ,
33  m_url(url) ,
34  m_tried_auth(false)
35 {
36 }
37 
39 {
40  sendGet() ;
41 }
42 
43 void GNet::HttpClientProtocol::apply( const char * p , std::string::size_type n )
44 {
45  G_DEBUG( "HttpClientProtocol::apply: " << n << " bytes\n" << G::hexdump<16>(p,p+n) ) ;
46  m_parser.apply( p , n ) ;
47  processData() ;
48 }
49 
50 void GNet::HttpClientProtocol::processData()
51 {
52  G_DEBUG( "HttpClientProtocol::apply: "
53  << "have=" << m_parser.bodySize() << " "
54  << "want=" << m_parser.headerContentLength() << " "
55  << "multipart=" << m_parser.headerMultipart() << " "
56  << "ok=" << m_parser.responseOk() << " "
57  << "got-headers=" << m_parser.gotHeaders() << " "
58  << "got-body=" << m_parser.gotBody() << " "
59  ) ;
60 
61  if( !m_parser.gotHeaders() )
62  {
63  ; // wait for more
64  }
65  else if( m_parser.responseUnauthorised() && m_url.authorisation().empty() )
66  {
67  throw Auth( "authorisation required" ) ;
68  }
69  else if( m_parser.responseUnauthorised() && m_parser.gotBody() && m_tried_auth )
70  {
71  throw Fail( "authorisation failed" ) ;
72  }
73  else if( m_parser.responseUnauthorised() && m_parser.gotBody() )
74  {
75  m_tried_auth = true ;
76  sendGetWithAuth() ;
77  m_parser.clear() ;
78  }
79  else if( m_parser.responseRetry() )
80  {
81  throw Retry( "service unavailable" ) ; // 503
82  }
83  else if( m_parser.responseOk() && m_parser.headerMultipart() && m_parser.gotPart() )
84  {
85  processPart() ;
86  m_parser.clearPart() ;
87  }
88  else if( m_parser.responseOk() && m_parser.headerMultipart() )
89  {
90  ; // wait for more
91  }
92  else if( m_parser.responseOk() && m_parser.gotBody() )
93  {
94  processBody() ;
95  m_parser.clear() ;
96  }
97  else if( m_parser.responseOk() )
98  {
99  ; // wait for more
100  }
101  else
102  {
103  throw Fail( "unexpected http response: [" + G::Str::printable(m_parser.responseSummary()) + "]" ) ;
104  }
105 }
106 
107 void GNet::HttpClientProtocol::sendGet()
108 {
109  std::string get = "GET " + m_url.request() + " HTTP/1.1\r\n" + "\r\n" ;
110  G_LOG( "GNet::HttpClientProtocol::sendGet: sending [" << G::Str::printable(get) << "]" ) ;
111  m_callback.sendHttpRequest( get ) ;
112 }
113 
115  const G::Url & url , std::string command , bool long_uri )
116 {
117  if( command.empty() ) command = "GET" ;
118 
119  std::string auth = url.authorisation() ;
120  std::string::size_type pos = auth.find(":") ;
121  std::string user = G::Str::head( auth , pos ) ;
122  std::string pwd = G::Str::tail( auth , pos ) ;
123 
124  std::string result ;
125  typedef std::vector<size_t> List ;
126  List hlist = parser.headers( "WWW-Authenticate" ) ;
127  for( List::iterator p = hlist.begin() ; p != hlist.end() ; ++p )
128  {
129  size_t index = *p ;
130  std::string auth_type = parser.headerWord( index ) ;
131  std::string realm = parser.headerAttribute( index , "realm" ) ;
132  std::string nonce = parser.headerAttribute( index , "nonce" ) ;
133  std::string opaque = parser.headerAttribute( index , "opaque" ) ;
134  if( auth_type == "Basic" )
135  {
136  result = "Authorization: Basic " + G::Base64::encode(user+":"+pwd,"") ;
137  break ;
138  }
139  else if( auth_type == "Digest" )
140  {
141  // RFC-2069
142  std::string uri = long_uri ? (url.protocol()+"://"+url.address()+url.path()) : url.path() ;
143  std::string ha1 = hash( user + ":" + realm + ":" + pwd ) ;
144  std::string ha2 = hash( command + ":" + uri ) ;
145  std::string auth_response = hash( ha1 + ":" + nonce + ":" + ha2 ) ;
146  result = std::string() +
147  "Authorization: Digest " +
148  "username=\"" + user + "\", " +
149  "realm=\"" + realm + "\", " +
150  "nonce=\"" + nonce + "\", " +
151  "uri=\"" + uri + "\", " +
152  "response=\"" + auth_response + "\"" +
153  (opaque.empty()?"":", opaque=\"") + opaque + (opaque.empty()?"":"\"") ;
154  break ;
155  }
156  }
157  if( result.empty() )
158  throw Fail( "no supported authentication mechanism" ) ;
159  return result ;
160 }
161 
162 void GNet::HttpClientProtocol::sendGetWithAuth()
163 {
164  std::string get = "GET " + m_url.request() + " HTTP/1.1\r\n" +
165  authorisation(m_parser,m_url) +
166  "\r\n\r\n" ;
167 
168  G_LOG( "GNet::HttpClientProtocol::sendGetWithAuth: sending [" << G::Str::printable(get) << "]" ) ;
169  m_callback.sendHttpRequest( get ) ;
170 }
171 
172 std::string GNet::HttpClientProtocol::hash( const std::string & s )
173 {
174  return G::Md5::printable( G::Md5::digest( s ) ) ;
175 }
176 
177 void GNet::HttpClientProtocol::processBody()
178 {
179  m_callback.onHttpBody( std::string() , m_parser.header("Content-Type") , m_parser.bodyData() , m_parser.bodySize() ) ;
180 }
181 
182 void GNet::HttpClientProtocol::processPart()
183 {
184  m_callback.onHttpBody( m_parser.header("Content-Type") , m_parser.partType() , m_parser.partData() , m_parser.partSize() ) ;
185 }
186 
187 // ==
188 
190 {
191 }
192 
193 /// \file ghttpclientprotocol.cpp
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:663
void apply(const char *, size_t)
To be called on receipt of data.
std::vector< size_t > headers(const std::string &header_key) const
Returns the indexes for the headers with the given key.
HttpClientProtocol(Callback &, const G::Url &url)
Constructor.
Synopsis:
A simple parser for URLs.
Definition: gurl.h:42
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.
A callback interface for GNet::HttpClientProtocol to send data and deliver content.
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.
Definition: gstr.cpp:1051
A parser for HTTP responses received from a remote server.
std::string path() const
Returns the path part, including the leading slash.
Definition: gurl.cpp:222
void start()
Assembles a GET request (based on the constructor url) and asks the callback interface to send it...
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.
Definition: gstr.cpp:1037
std::string protocol() const
Returns the protocol part eg. "http".
Definition: gurl.cpp:181
static std::string digest(const std::string &input)
Creates an MD5 digest.
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 authorisation() const
Returns the "user:pwd" part.
Definition: gurl.cpp:227
static std::string authorisation(const GNet::HttpClientParser &, const G::Url &, std::string get=std::string(), bool=false)
Returns an "Authorization" header for adding to a "GET" request.
static std::string encode(const std::string &s, const std::string &line_break)
Encodes the given string.
Definition: gbase64.cpp:68
std::string address() const
Returns the address part, which might include the port, and which might use ipv6 square brackets...
Definition: gurl.cpp:196
static std::string printable(const std::string &input)
Converts a binary string into a printable form, using a lowercase hexadecimal encoding.