BitShares-Core  5.0.0
BitShares blockchain implementation and command-line interface software
http_connection.cpp
Go to the documentation of this file.
3 #include <fc/io/sstream.hpp>
4 #include <fc/io/iostream.hpp>
6 #include <fc/network/ip.hpp>
7 #include <fc/crypto/hex.hpp>
8 #include <fc/log/logger.hpp>
9 #include <fc/io/stdio.hpp>
10 #include <fc/network/url.hpp>
11 #include <boost/algorithm/string.hpp>
12 
13 
15 {
16  public:
19  impl() {
20  }
21 
22  int read_until( char* buffer, char* end, char c = '\n' ) {
23  char* p = buffer;
24  // try {
25  while( p < end && 1 == sock.readsome(p,1) ) {
26  if( *p == c ) {
27  *p = '\0';
28  return (p - buffer)-1;
29  }
30  ++p;
31  }
32  // } catch ( ... ) {
33  // elog("%s", fc::current_exception().diagnostic_information().c_str() );
34  //elog( "%s", fc::except_str().c_str() );
35  // }
36  return (p-buffer);
37  }
38 
40  fc::http::reply rep;
41  try {
42  std::vector<char> line(1024*8);
43  int s = read_until( line.data(), line.data()+line.size(), ' ' ); // HTTP/1.1
44  s = read_until( line.data(), line.data()+line.size(), ' ' ); // CODE
45  rep.status = static_cast<int>(to_int64(std::string(line.data())));
46  s = read_until( line.data(), line.data()+line.size(), '\n' ); // DESCRIPTION
47 
48  while( (s = read_until( line.data(), line.data()+line.size(), '\n' )) > 1 ) {
50  char* end = line.data();
51  while( *end != ':' )++end;
52  h.key = std::string(line.data(),end);
53  ++end; // skip ':'
54  ++end; // skip space
55  char* skey = end;
56  while( *end != '\r' ) ++end;
57  h.val = std::string(skey,end);
58  rep.headers.push_back(h);
59  if( boost::iequals(h.key, "Content-Length") ) {
60  rep.body.resize( static_cast<size_t>(to_uint64( std::string(h.val) ) ));
61  }
62  }
63  if( rep.body.size() ) {
64  sock.read( rep.body.data(), rep.body.size() );
65  }
66  return rep;
67  } catch ( fc::exception& e ) {
68  elog( "${exception}", ("exception",e.to_detail_string() ) );
69  sock.close();
71  return rep;
72  }
73  }
74 };
75 
76 
77 
78 namespace fc { namespace http {
79 
81  :my( new connection::impl() ){}
83 
84 
85 // used for clients
87  my->sock.close();
88  my->sock.connect_to( my->ep = ep );
89 }
90 
91 http::reply connection::request( const std::string& method,
92  const std::string& url,
93  const std::string& body, const headers& he ) {
94 
95  fc::url parsed_url(url);
96  if( !my->sock.is_open() ) {
97  wlog( "Re-open socket!" );
98  my->sock.connect_to( my->ep );
99  }
100  try {
101  fc::stringstream req;
102  req << method <<" "<<parsed_url.path()->generic_string()<<" HTTP/1.1\r\n";
103  req << "Host: "<<*parsed_url.host()<<"\r\n";
104  req << "Content-Type: application/json\r\n";
105  for( auto i = he.begin(); i != he.end(); ++i )
106  {
107  req << i->key <<": " << i->val<<"\r\n";
108  }
109  if( body.size() ) req << "Content-Length: "<< body.size() << "\r\n";
110  req << "\r\n";
111  std::string head = req.str();
112 
113  my->sock.write( head.c_str(), head.size() );
114  // fc::cerr.write( head.c_str() );
115 
116  if( body.size() ) {
117  my->sock.write( body.c_str(), body.size() );
118  // fc::cerr.write( body.c_str() );
119  }
120  // fc::cerr.flush();
121 
122  return my->parse_reply();
123  } catch ( ... ) {
124  my->sock.close();
125  FC_THROW_EXCEPTION( exception, "Error Sending HTTP Request" ); // TODO: provide more info
126  // return http::reply( http::reply::InternalServerError ); // TODO: replace with connection error
127  }
128 }
129 
130 // used for servers
132  return my->sock;
133 }
134 
136  http::request req;
137  req.remote_endpoint = fc::variant(get_socket().remote_endpoint()).as_string();
138  std::vector<char> line(1024*8);
139  int s = my->read_until( line.data(), line.data()+line.size(), ' ' ); // METHOD
140  req.method = line.data();
141  s = my->read_until( line.data(), line.data()+line.size(), ' ' ); // PATH
142  req.path = line.data();
143  s = my->read_until( line.data(), line.data()+line.size(), '\n' ); // HTTP/1.0
144 
145  while( (s = my->read_until( line.data(), line.data()+line.size(), '\n' )) > 1 ) {
147  char* end = line.data();
148  while( *end != ':' )++end;
149  h.key = std::string(line.data(),end);
150  ++end; // skip ':'
151  ++end; // skip space
152  char* skey = end;
153  while( *end != '\r' ) ++end;
154  h.val = std::string(skey,end);
155  req.headers.push_back(h);
156  if( boost::iequals(h.key, "Content-Length")) {
157  auto s = static_cast<size_t>(to_uint64( std::string(h.val) ) );
158  FC_ASSERT( s < 1024*1024 );
159  req.body.resize( static_cast<size_t>(to_uint64( std::string(h.val) ) ));
160  }
161  if( boost::iequals(h.key, "Host") ) {
162  req.domain = h.val;
163  }
164  }
165  // TODO: some common servers won't give a Content-Length, they'll use
166  // Transfer-Encoding: chunked. handle that here.
167 
168  if( req.body.size() ) {
169  my->sock.read( req.body.data(), req.body.size() );
170  }
171  return req;
172 }
173 
174 std::string request::get_header( const std::string& key )const {
175  for( auto itr = headers.begin(); itr != headers.end(); ++itr ) {
176  if( boost::iequals(itr->key, key) ) { return itr->val; }
177  }
178  return std::string();
179 }
180 std::vector<header> parse_urlencoded_params( const std::string& f ) {
181  int num_args = 0;
182  for( size_t i = 0; i < f.size(); ++i ) {
183  if( f[i] == '=' ) ++num_args;
184  }
185  std::vector<header> h(num_args);
186  int arg = 0;
187  for( size_t i = 0; i < f.size(); ++i ) {
188  while( i < f.size() && f[i] != '=' ) {
189  if( f[i] == '%' ) {
190  h[arg].key += char((fc::from_hex(f[i+1]) << 4) | fc::from_hex(f[i+2]));
191  i += 3;
192  } else {
193  h[arg].key += f[i];
194  ++i;
195  }
196  }
197  ++i;
198  while( i < f.size() && f[i] != '&' ) {
199  if( f[i] == '%' ) {
200  h[arg].val += char((fc::from_hex(f[i+1]) << 4) | fc::from_hex(f[i+2]));
201  i += 3;
202  } else {
203  h[arg].val += f[i] == '+' ? ' ' : f[i];
204  ++i;
205  }
206  }
207  ++arg;
208  }
209  return h;
210 }
211 
212 } } // fc::http
std::vector< header > parse_urlencoded_params(const std::string &f)
virtual void close()
Definition: tcp_socket.cpp:103
uint8_t from_hex(char c)
Definition: hex.cpp:6
istream & read(char *buf, size_t len)
Definition: iostream.cpp:274
std::string path
Definition: connection.hpp:48
http::reply request(const std::string &method, const std::string &url, const std::string &body=std::string(), const headers &=headers())
opath path() const
Definition: url.cpp:182
std::vector< header > headers
Definition: connection.hpp:49
void connect_to(const fc::ip::endpoint &ep)
#define elog(FORMAT,...)
Definition: logger.hpp:129
http::request read_request() const
fc::tcp_socket & get_socket() const
Used to generate a useful error report when an exception is thrown.At each level in the stack where t...
Definition: exception.hpp:56
std::string to_detail_string(log_level ll=log_level::all) const
Definition: exception.cpp:183
std::string get_header(const std::string &key) const
int64_t to_int64(const std::string &)
Definition: string.cpp:34
#define wlog(FORMAT,...)
Definition: logger.hpp:123
std::string generic_string() const
Definition: filesystem.cpp:95
std::vector< char > body
Definition: connection.hpp:50
ostring host() const
Definition: url.cpp:170
std::string str()
Definition: sstream.cpp:33
int read_until(char *buffer, char *end, char c= '\n')
std::string method
Definition: connection.hpp:46
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
std::vector< char > body
Definition: connection.hpp:38
stores null, int64, uint64, double, bool, string, std::vector<variant>, and variant_object&#39;s.
Definition: variant.hpp:198
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
Definition: exception.hpp:378
virtual size_t readsome(char *buffer, size_t max)
Definition: tcp_socket.cpp:147
uint64_t to_uint64(const std::string &)
Definition: string.cpp:47
Defines exception&#39;s used by fc.
std::string remote_endpoint
Definition: connection.hpp:45
fc::http::reply parse_reply()
Definition: api.hpp:15
std::vector< header > headers
Definition: connection.hpp:21
Definition: url.hpp:22
std::string val
Definition: connection.hpp:18
std::string key
Definition: connection.hpp:17
std::string as_string() const
Definition: variant.cpp:469
std::vector< header > headers
Definition: connection.hpp:37
std::string domain
Definition: connection.hpp:47