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:
Eric Frias 2015-07-10 10:07:14 -04:00
parent 3dd848d7eb
commit 1e6176911a
6 changed files with 241 additions and 23 deletions

View file

@ -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
View 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 )

View file

@ -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
View 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) );
}
}

View file

@ -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();

View file

@ -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;