Fix bugs in http request when using chunked content encoding.
Add missing support for parsing/reconstructing URLs with query strings to fc::url. Add md5 hash algorithm.
This commit is contained in:
parent
3dd848d7eb
commit
1e6176911a
6 changed files with 241 additions and 23 deletions
|
|
@ -206,6 +206,7 @@ set( fc_sources
|
|||
src/crypto/sha256.cpp
|
||||
src/crypto/sha224.cpp
|
||||
src/crypto/sha512.cpp
|
||||
src/crypto/md5.cpp
|
||||
src/crypto/dh.cpp
|
||||
src/crypto/blowfish.cpp
|
||||
src/crypto/elliptic_common.cpp
|
||||
|
|
|
|||
76
include/fc/crypto/md5.hpp
Normal file
76
include/fc/crypto/md5.hpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
#include <fc/fwd.hpp>
|
||||
#include <fc/string.hpp>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
|
||||
class md5
|
||||
{
|
||||
public:
|
||||
md5();
|
||||
explicit md5( const string& hex_str );
|
||||
|
||||
string str()const;
|
||||
operator string()const;
|
||||
|
||||
char* data()const;
|
||||
size_t data_size()const { return 128 / 8; }
|
||||
|
||||
static md5 hash( const char* d, uint32_t dlen );
|
||||
static md5 hash( const string& );
|
||||
|
||||
template<typename T>
|
||||
static md5 hash( const T& t )
|
||||
{
|
||||
md5::encoder e;
|
||||
e << t;
|
||||
return e.result();
|
||||
}
|
||||
|
||||
class encoder
|
||||
{
|
||||
public:
|
||||
encoder();
|
||||
~encoder();
|
||||
|
||||
void write( const char* d, uint32_t dlen );
|
||||
void put( char c ) { write( &c, 1 ); }
|
||||
void reset();
|
||||
md5 result();
|
||||
|
||||
private:
|
||||
struct impl;
|
||||
fc::fwd<impl,216> my;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline friend T& operator<<( T& ds, const md5& ep ) {
|
||||
ds.write( ep.data(), sizeof(ep) );
|
||||
return ds;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline friend T& operator>>( T& ds, md5& ep ) {
|
||||
ds.read( ep.data(), sizeof(ep) );
|
||||
return ds;
|
||||
}
|
||||
friend md5 operator << ( const md5& h1, uint32_t i );
|
||||
friend bool operator == ( const md5& h1, const md5& h2 );
|
||||
friend bool operator != ( const md5& h1, const md5& h2 );
|
||||
friend md5 operator ^ ( const md5& h1, const md5& h2 );
|
||||
friend bool operator >= ( const md5& h1, const md5& h2 );
|
||||
friend bool operator > ( const md5& h1, const md5& h2 );
|
||||
friend bool operator < ( const md5& h1, const md5& h2 );
|
||||
|
||||
uint64_t _hash[2];
|
||||
};
|
||||
|
||||
class variant;
|
||||
void to_variant( const md5& bi, variant& v );
|
||||
void from_variant( const variant& v, md5& bi );
|
||||
|
||||
} // fc
|
||||
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
FC_REFLECT_TYPENAME( fc::md5 )
|
||||
|
|
@ -48,6 +48,7 @@ namespace fc {
|
|||
ostring pass()const;
|
||||
opath path()const;
|
||||
ovariant_object args()const;
|
||||
std::string args_as_string()const;
|
||||
fc::optional<uint16_t> port()const;
|
||||
|
||||
private:
|
||||
|
|
|
|||
96
src/crypto/md5.cpp
Normal file
96
src/crypto/md5.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/fwd_impl.hpp>
|
||||
#include <openssl/md5.h>
|
||||
#include <string.h>
|
||||
#include <fc/crypto/md5.hpp>
|
||||
#include <fc/variant.hpp>
|
||||
|
||||
namespace fc {
|
||||
|
||||
md5::md5() { memset( _hash, 0, sizeof(_hash) ); }
|
||||
md5::md5( const string& hex_str ) {
|
||||
fc::from_hex( hex_str, (char*)_hash, sizeof(_hash) );
|
||||
}
|
||||
|
||||
string md5::str()const {
|
||||
return fc::to_hex( (char*)_hash, sizeof(_hash) );
|
||||
}
|
||||
md5::operator string()const { return str(); }
|
||||
|
||||
char* md5::data()const { return (char*)&_hash[0]; }
|
||||
|
||||
|
||||
struct md5::encoder::impl {
|
||||
MD5_CTX ctx;
|
||||
};
|
||||
|
||||
md5::encoder::~encoder() {}
|
||||
md5::encoder::encoder() {
|
||||
reset();
|
||||
}
|
||||
|
||||
md5 md5::hash( const char* d, uint32_t dlen ) {
|
||||
encoder e;
|
||||
e.write(d,dlen);
|
||||
return e.result();
|
||||
}
|
||||
md5 md5::hash( const string& s ) {
|
||||
return hash( s.c_str(), s.size() );
|
||||
}
|
||||
|
||||
void md5::encoder::write( const char* d, uint32_t dlen ) {
|
||||
MD5_Update( &my->ctx, d, dlen);
|
||||
}
|
||||
md5 md5::encoder::result() {
|
||||
md5 h;
|
||||
MD5_Final((uint8_t*)h.data(), &my->ctx );
|
||||
return h;
|
||||
}
|
||||
void md5::encoder::reset() {
|
||||
MD5_Init( &my->ctx);
|
||||
}
|
||||
|
||||
md5 operator << ( const md5& h1, uint32_t i ) {
|
||||
md5 result;
|
||||
uint8_t* r = (uint8_t*)result._hash;
|
||||
uint8_t* s = (uint8_t*)h1._hash;
|
||||
for( uint32_t p = 0; p < sizeof(h1._hash)-1; ++p )
|
||||
r[p] = s[p] << i | (s[p+1]>>(8-i));
|
||||
r[63] = s[63] << i;
|
||||
return result;
|
||||
}
|
||||
md5 operator ^ ( const md5& h1, const md5& h2 ) {
|
||||
md5 result;
|
||||
result._hash[0] = h1._hash[0] ^ h2._hash[0];
|
||||
result._hash[1] = h1._hash[1] ^ h2._hash[1];
|
||||
return result;
|
||||
}
|
||||
bool operator >= ( const md5& h1, const md5& h2 ) {
|
||||
return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) >= 0;
|
||||
}
|
||||
bool operator > ( const md5& h1, const md5& h2 ) {
|
||||
return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) > 0;
|
||||
}
|
||||
bool operator < ( const md5& h1, const md5& h2 ) {
|
||||
return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) < 0;
|
||||
}
|
||||
bool operator != ( const md5& h1, const md5& h2 ) {
|
||||
return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) != 0;
|
||||
}
|
||||
bool operator == ( const md5& h1, const md5& h2 ) {
|
||||
return memcmp( h1._hash, h2._hash, sizeof(h1._hash) ) == 0;
|
||||
}
|
||||
|
||||
void to_variant( const md5& bi, variant& v )
|
||||
{
|
||||
v = std::vector<char>( (const char*)&bi, ((const char*)&bi) + sizeof(bi) );
|
||||
}
|
||||
void from_variant( const variant& v, md5& bi )
|
||||
{
|
||||
std::vector<char> ve = v.as< std::vector<char> >();
|
||||
if( ve.size() )
|
||||
memcpy(&bi, ve.data(), fc::min<size_t>(ve.size(),sizeof(bi)) );
|
||||
else
|
||||
memset( &bi, char(0), sizeof(bi) );
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
#include <fc/io/stdio.hpp>
|
||||
#include <fc/network/url.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
|
||||
class fc::http::connection::impl
|
||||
{
|
||||
|
|
@ -67,14 +67,25 @@ class fc::http::connection::impl
|
|||
|
||||
if (is_chunked)
|
||||
{
|
||||
// Chunked means we get a hexadecimal number of bytes on a line, followed by the content
|
||||
s = read_until( line.data(), line.data()+line.size(), '\n' ); // DESCRIPTION
|
||||
if (line[strlen(line.data())] == '\r')
|
||||
line[strlen(line.data())] = 0;
|
||||
unsigned length;
|
||||
if (sscanf(line.data(), "%x", &length) != 1)
|
||||
FC_THROW("Invalid content length: ${length}", ("length", fc::string(line.data())));
|
||||
content_length = length;
|
||||
do
|
||||
{
|
||||
// Chunked means we get a hexadecimal number of bytes on a line, followed by the content
|
||||
s = read_until( line.data(), line.data()+line.size(), '\n' ); //read chunk length
|
||||
if (line[strlen(line.data())] == '\r')
|
||||
line[strlen(line.data())] = 0;
|
||||
unsigned length;
|
||||
if (sscanf(line.data(), "%x", &length) != 1)
|
||||
FC_THROW("Invalid content length: ${length}", ("length", fc::string(line.data())));
|
||||
content_length = length;
|
||||
if (*content_length)
|
||||
{
|
||||
std::vector<char> temp_data(*content_length);
|
||||
sock.read( temp_data.data(), *content_length );
|
||||
boost::push_back(rep.body, temp_data);
|
||||
read_until( line.data(), line.data()+line.size(), '\n' ); //discard cr/lf after each chunk
|
||||
}
|
||||
}
|
||||
while (*content_length != 0);
|
||||
}
|
||||
|
||||
if (content_length)
|
||||
|
|
@ -85,7 +96,7 @@ class fc::http::connection::impl
|
|||
sock.read( rep.body.data(), *content_length );
|
||||
}
|
||||
}
|
||||
else
|
||||
else //just read until closed if no content length or chunking
|
||||
{
|
||||
std::shared_ptr<char> buf(new char);
|
||||
while (true)
|
||||
|
|
@ -140,7 +151,7 @@ http::reply connection::request( const fc::string& method,
|
|||
}
|
||||
try {
|
||||
fc::stringstream req;
|
||||
req << method <<" "<<parsed_url.path()->generic_string()<<" HTTP/1.1\r\n";
|
||||
req << method << " " << parsed_url.path()->generic_string() << parsed_url.args_as_string() << " HTTP/1.1\r\n";
|
||||
req << "Host: "<<*parsed_url.host()<<"\r\n";
|
||||
req << "Content-Type: " << content_type << "\r\n";
|
||||
for( auto i = he.begin(); i != he.end(); ++i )
|
||||
|
|
@ -152,10 +163,12 @@ http::reply connection::request( const fc::string& method,
|
|||
fc::string head = req.str();
|
||||
|
||||
my->sock.write( head.c_str(), head.size() );
|
||||
//elog("Sending header ${head}", ("head", head));
|
||||
// fc::cerr.write( head.c_str() );
|
||||
|
||||
if( body.size() ) {
|
||||
my->sock.write( body.c_str(), body.size() );
|
||||
//elog("Sending body ${body}", ("body", body));
|
||||
// fc::cerr.write( body.c_str() );
|
||||
}
|
||||
// fc::cerr.flush();
|
||||
|
|
|
|||
|
|
@ -60,9 +60,22 @@ namespace fc
|
|||
_path = fc::path( "/" ) / _lpath;
|
||||
#endif
|
||||
std::getline( ss, _largs );
|
||||
if( _args.valid() && _args->size() )
|
||||
if( _largs.size() )
|
||||
{
|
||||
// TODO: args = fc::move(_args);
|
||||
mutable_variant_object new_args;
|
||||
std::istringstream args_stream(_largs);
|
||||
std::string _larg;
|
||||
while (std::getline(args_stream, _larg, '&'))
|
||||
{
|
||||
std::string::size_type equals_pos = _larg.find('=');
|
||||
if (equals_pos != std::string::npos)
|
||||
{
|
||||
std::string key = _larg.substr(0, equals_pos);
|
||||
std::string value = _larg.substr(equals_pos + 1);
|
||||
new_args[key] = value;
|
||||
}
|
||||
}
|
||||
_args = new_args;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,18 +101,21 @@ namespace fc
|
|||
url::operator string()const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss<<my->_proto<<"://";
|
||||
if( my->_user.valid() ) {
|
||||
ss << my->_proto << "://";
|
||||
if( my->_user.valid() )
|
||||
{
|
||||
ss << *my->_user;
|
||||
if( my->_pass.valid() ) {
|
||||
ss<<":"<<*my->_pass;
|
||||
}
|
||||
ss<<"@";
|
||||
if( my->_pass.valid() )
|
||||
ss << ":" << *my->_pass;
|
||||
ss << "@";
|
||||
}
|
||||
if( my->_host.valid() ) ss<<*my->_host;
|
||||
if( my->_port.valid() ) ss<<":"<<*my->_port;
|
||||
if( my->_path.valid() ) ss<<my->_path->generic_string();
|
||||
// if( my->_args ) ss<<"?"<<*my->_args;
|
||||
if( my->_host.valid() )
|
||||
ss << *my->_host;
|
||||
if( my->_port.valid() )
|
||||
ss << ":" << *my->_port;
|
||||
if( my->_path.valid() )
|
||||
ss << my->_path->generic_string();
|
||||
ss << args_as_string();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
|
@ -189,6 +205,21 @@ namespace fc
|
|||
{
|
||||
return my->_args;
|
||||
}
|
||||
std::string url::args_as_string()const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if( my->_args )
|
||||
{
|
||||
bool first = true;
|
||||
for (auto iter = my->_args->begin(); iter != my->_args->end(); ++iter)
|
||||
{
|
||||
ss << (first ? "?" : "&");
|
||||
first = false;
|
||||
ss << iter->key() << "=" << iter->value().as_string();
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
fc::optional<uint16_t> url::port()const
|
||||
{
|
||||
return my->_port;
|
||||
|
|
|
|||
Loading…
Reference in a new issue