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/sha256.cpp
|
||||||
src/crypto/sha224.cpp
|
src/crypto/sha224.cpp
|
||||||
src/crypto/sha512.cpp
|
src/crypto/sha512.cpp
|
||||||
|
src/crypto/md5.cpp
|
||||||
src/crypto/dh.cpp
|
src/crypto/dh.cpp
|
||||||
src/crypto/blowfish.cpp
|
src/crypto/blowfish.cpp
|
||||||
src/crypto/elliptic_common.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;
|
ostring pass()const;
|
||||||
opath path()const;
|
opath path()const;
|
||||||
ovariant_object args()const;
|
ovariant_object args()const;
|
||||||
|
std::string args_as_string()const;
|
||||||
fc::optional<uint16_t> port()const;
|
fc::optional<uint16_t> port()const;
|
||||||
|
|
||||||
private:
|
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/io/stdio.hpp>
|
||||||
#include <fc/network/url.hpp>
|
#include <fc/network/url.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||||
|
|
||||||
class fc::http::connection::impl
|
class fc::http::connection::impl
|
||||||
{
|
{
|
||||||
|
|
@ -67,14 +67,25 @@ class fc::http::connection::impl
|
||||||
|
|
||||||
if (is_chunked)
|
if (is_chunked)
|
||||||
{
|
{
|
||||||
// Chunked means we get a hexadecimal number of bytes on a line, followed by the content
|
do
|
||||||
s = read_until( line.data(), line.data()+line.size(), '\n' ); // DESCRIPTION
|
{
|
||||||
if (line[strlen(line.data())] == '\r')
|
// Chunked means we get a hexadecimal number of bytes on a line, followed by the content
|
||||||
line[strlen(line.data())] = 0;
|
s = read_until( line.data(), line.data()+line.size(), '\n' ); //read chunk length
|
||||||
unsigned length;
|
if (line[strlen(line.data())] == '\r')
|
||||||
if (sscanf(line.data(), "%x", &length) != 1)
|
line[strlen(line.data())] = 0;
|
||||||
FC_THROW("Invalid content length: ${length}", ("length", fc::string(line.data())));
|
unsigned length;
|
||||||
content_length = 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)
|
if (content_length)
|
||||||
|
|
@ -85,7 +96,7 @@ class fc::http::connection::impl
|
||||||
sock.read( rep.body.data(), *content_length );
|
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);
|
std::shared_ptr<char> buf(new char);
|
||||||
while (true)
|
while (true)
|
||||||
|
|
@ -140,7 +151,7 @@ http::reply connection::request( const fc::string& method,
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
fc::stringstream req;
|
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 << "Host: "<<*parsed_url.host()<<"\r\n";
|
||||||
req << "Content-Type: " << content_type << "\r\n";
|
req << "Content-Type: " << content_type << "\r\n";
|
||||||
for( auto i = he.begin(); i != he.end(); ++i )
|
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();
|
fc::string head = req.str();
|
||||||
|
|
||||||
my->sock.write( head.c_str(), head.size() );
|
my->sock.write( head.c_str(), head.size() );
|
||||||
|
//elog("Sending header ${head}", ("head", head));
|
||||||
// fc::cerr.write( head.c_str() );
|
// fc::cerr.write( head.c_str() );
|
||||||
|
|
||||||
if( body.size() ) {
|
if( body.size() ) {
|
||||||
my->sock.write( body.c_str(), body.size() );
|
my->sock.write( body.c_str(), body.size() );
|
||||||
|
//elog("Sending body ${body}", ("body", body));
|
||||||
// fc::cerr.write( body.c_str() );
|
// fc::cerr.write( body.c_str() );
|
||||||
}
|
}
|
||||||
// fc::cerr.flush();
|
// fc::cerr.flush();
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,22 @@ namespace fc
|
||||||
_path = fc::path( "/" ) / _lpath;
|
_path = fc::path( "/" ) / _lpath;
|
||||||
#endif
|
#endif
|
||||||
std::getline( ss, _largs );
|
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
|
url::operator string()const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss<<my->_proto<<"://";
|
ss << my->_proto << "://";
|
||||||
if( my->_user.valid() ) {
|
if( my->_user.valid() )
|
||||||
|
{
|
||||||
ss << *my->_user;
|
ss << *my->_user;
|
||||||
if( my->_pass.valid() ) {
|
if( my->_pass.valid() )
|
||||||
ss<<":"<<*my->_pass;
|
ss << ":" << *my->_pass;
|
||||||
}
|
ss << "@";
|
||||||
ss<<"@";
|
|
||||||
}
|
}
|
||||||
if( my->_host.valid() ) ss<<*my->_host;
|
if( my->_host.valid() )
|
||||||
if( my->_port.valid() ) ss<<":"<<*my->_port;
|
ss << *my->_host;
|
||||||
if( my->_path.valid() ) ss<<my->_path->generic_string();
|
if( my->_port.valid() )
|
||||||
// if( my->_args ) ss<<"?"<<*my->_args;
|
ss << ":" << *my->_port;
|
||||||
|
if( my->_path.valid() )
|
||||||
|
ss << my->_path->generic_string();
|
||||||
|
ss << args_as_string();
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,6 +205,21 @@ namespace fc
|
||||||
{
|
{
|
||||||
return my->_args;
|
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
|
fc::optional<uint16_t> url::port()const
|
||||||
{
|
{
|
||||||
return my->_port;
|
return my->_port;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue