identt
HttpClientBase.hpp
Go to the documentation of this file.
1 
33 #ifndef _IDENTT_HTTP_CLIENTBASE_HPP_
34 #define _IDENTT_HTTP_CLIENTBASE_HPP_
35 
36 #include <map>
37 #include <random>
38 #include "ClientResponse.hpp"
39 
40 namespace identt {
41 namespace http {
42 
43 template <class socket_type>
44 class ClientBase {
45 public:
47  using RespPtr = std::shared_ptr<Response>;
48 
49  RespPtr request(
50  const std::string& request_type,
51  const std::string& path="/",
52  const std::map<std::string,
53  std::string>& header=std::map<std::string, std::string>())
54  {
55  std::stringstream empty_ss;
56  return request(request_type, path, empty_ss, header);
57  }
58 
59  RespPtr request(const std::string& request_type, const std::string& path, std::ostream& content,
60  const std::map<std::string, std::string>& header=std::map<std::string, std::string>())
61  {
62  std::string corrected_path=path;
63  if(corrected_path=="") corrected_path="/";
64 
65  content.seekp(0, std::ios::end);
66  size_t content_length=content.tellp();
67  content.seekp(0, std::ios::beg);
68 
69  boost::asio::streambuf write_buffer;
70  std::ostream write_stream(&write_buffer);
71  write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n";
72  write_stream << "Host: " << host << "\r\n";
73  for(auto& h: header) {
74  write_stream << h.first << ": " << h.second << "\r\n";
75  }
76  if(content_length>0)
77  write_stream << "Content-Length: " << std::to_string(content_length) << "\r\n";
78  write_stream << "\r\n";
79  if(content_length>0)
80  write_stream << content.rdbuf();
81 
82  RespPtr response(new Response());
83 
84  try {
85  connect();
86  boost::asio::write(*socket, write_buffer);
87  size_t bytes_transferred = boost::asio::read_until(*socket, response->content_buffer, "\r\n\r\n");
88  size_t num_additional_bytes=response->content_buffer.size()-bytes_transferred;
89  parse_response_header(response, response->content);
90 
91  if(response->header.count("Content-Length")>0) {
92  boost::asio::read(*socket, response->content_buffer,
93  boost::asio::transfer_exactly(stoull(response->header["Content-Length"])-num_additional_bytes));
94  } else if(response->header.count("Transfer-Encoding")>0 && response->header["Transfer-Encoding"]=="chunked") {
95  boost::asio::streambuf streambuf;
96  std::ostream content(&streambuf);
97 
98  size_t length;
99  std::string buffer;
100  do {
101  size_t bytes_transferred = boost::asio::read_until(*socket, response->content_buffer, "\r\n");
102  std::string line;
103  getline(response->content, line);
104  bytes_transferred-=line.size()+1;
105  line.pop_back();
106  length=stoull(line, 0, 16);
107 
108  size_t num_additional_bytes=response->content_buffer.size()-bytes_transferred;
109 
110  if((2+length)>num_additional_bytes) {
111  boost::asio::read(*socket, response->content_buffer,
112  boost::asio::transfer_exactly(2+length-num_additional_bytes));
113  }
114 
115  buffer.resize(length);
116  response->content.read(&buffer[0], length);
117  content.write(&buffer[0], length);
118 
119  //Remove "\r\n"
120  response->content.get();
121  response->content.get();
122  } while(length>0);
123 
124  std::ostream response_content_output_stream(&response->content_buffer);
125  response_content_output_stream << content.rdbuf();
126  }
127  } catch(const std::exception& e) {
128  socket_error=true;
129  throw std::invalid_argument(e.what());
130  }
131 
132  return response;
133  }
134 
135 protected:
136  boost::asio::io_service asio_io_service;
137  boost::asio::ip::tcp::endpoint asio_endpoint;
138  boost::asio::ip::tcp::resolver asio_resolver;
139 
140  std::shared_ptr<socket_type> socket;
141  bool socket_error;
142 
143  std::string host;
144  unsigned short port;
145 
146  ClientBase(const std::string& host_port, unsigned short default_port) :
147  asio_resolver(asio_io_service), socket_error(false)
148  {
149  size_t host_end=host_port.find(':');
150  if(host_end==std::string::npos) {
151  host=host_port;
152  port=default_port;
153  } else {
154  host=host_port.substr(0, host_end);
155  port=(unsigned short)stoul(host_port.substr(host_end+1));
156  }
157 
158  asio_endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port);
159  }
160 
161  virtual void connect()=0;
162 
163  void parse_response_header(RespPtr response, std::istream& stream) const
164  {
165  std::string line;
166  getline(stream, line);
167  size_t version_end=line.find(' ');
168  if(version_end!=std::string::npos) {
169  response->http_version=line.substr(5, version_end-5);
170  response->status_code=line.substr(version_end+1, line.size()-version_end-2);
171 
172  getline(stream, line);
173  size_t param_end=line.find(':');
174  while(param_end!=std::string::npos) {
175  size_t value_start=param_end+1;
176  if(line[value_start]==' ')
177  value_start++;
178 
179  response->header[line.substr(0, param_end)]=line.substr(value_start, line.size()-value_start-1);
180 
181  getline(stream, line);
182  param_end=line.find(':');
183  }
184  }
185  }
186 };
187 
188 } // namespace webserver
189 } // namespace identt
190 #endif /* _IDENTT_HTTP_CLIENTBASE_HPP_ */
Definition: ClientResponse.hpp:45
Definition: CryptoBase.hpp:49
Definition: HttpClientBase.hpp:44