Skip to content

Instantly share code, notes, and snippets.

@xeroc
Created November 10, 2017 16:08
Show Gist options
  • Save xeroc/a0046d3c61b2fcdb7f7eed5661597a78 to your computer and use it in GitHub Desktop.
Save xeroc/a0046d3c61b2fcdb7f7eed5661597a78 to your computer and use it in GitHub Desktop.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 73e6b1b..08862ab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,6 +25,12 @@ SET( DEFAULT_EXECUTABLE_INSTALL_DIR bin/ )
SET( CMAKE_DEBUG_POSTFIX _debug )
SET( BUILD_SHARED_LIBS NO )
SET( ECC_IMPL secp256k1 CACHE STRING "secp256k1 or openssl or mixed" )
+SET( FC_USE_FULL_ZLIB FALSE CACHE BOOL "TRUE to try to use full zlib for compression, FALSE to use miniz.c")
+
+if( FC_USE_FULL_ZLIB )
+ find_package( ZLIB REQUIRED )
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFC_USE_FULL_ZLIB")
+endif()
set(platformBitness 32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
@@ -142,17 +148,6 @@ find_package(OpenSSL REQUIRED)
set( CMAKE_FIND_LIBRARY_SUFFIXES ${ORIGINAL_LIB_SUFFIXES} )
-# We are now building in support for deflate compression into our websockets layer by default,
-# which requires zlib. Aside from that, all of fc compiles without zlib, so this could be
-# made optional without much effort
-# (important exception, apple: as of 10.10 yosemite, the OpenSSL static libraries shipped with
-# os x have a dependency on zlib)
-# On a side note, fc's fc::zlib_compress() function uses a separate implementation of zlib
-# from the miniz library. If we're comfortable requiring an external zlib, we can
-# reimplement fc::zlib_compress() to call the real zlib, and remove miniz.c from our
-# repository.
-find_package( ZLIB REQUIRED )
-
option( UNITY_BUILD OFF )
set( fc_sources
@@ -215,6 +210,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
@@ -249,11 +245,10 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/git_revision.cpp.in" "${CMAKE_CU
list(APPEND sources "${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp")
list(APPEND sources ${fc_headers})
-add_subdirectory( vendor/websocketpp EXCLUDE_FROM_ALL )
+add_subdirectory( vendor/websocketpp )
add_subdirectory( vendor/udt4 )
-setup_library( fc SOURCES ${sources} LIBRARY_TYPE STATIC )
-install( DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" DESTINATION include )
+setup_library( fc SOURCES ${sources} LIBRARY_TYPE STATIC DONT_INSTALL_LIBRARY )
# begin readline stuff
find_package(Curses)
@@ -317,7 +312,7 @@ target_include_directories(fc
${CMAKE_CURRENT_SOURCE_DIR}/vendor/udt4/src
${CMAKE_CURRENT_SOURCE_DIR}/vendor/websocketpp
${CMAKE_CURRENT_SOURCE_DIR}/vendor/secp256k1-zkp
- ${ZLIB_INCLUDE_DIR}
+ ${ZLIB_INCLUDE_DIRS}
)
#target_link_libraries( fc PUBLIC udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${ECC_LIB} )
@@ -486,6 +481,14 @@ if(WIN32)
endif(WIN32)
+IF(APPLE)
+ # As of 10.10 yosemite, the OpenSSL static libraries shipped with os x have a dependency
+ # on zlib, so any time you link in openssl you also need to link zlib. . We really want to detect whether openssl was configured with the --no-zlib
+ # option or not when it was built, but that's difficult to do in practice, so we
+ # just always try to link it in on mac.
+ find_package( ZLIB REQUIRED )
+ENDIF(APPLE)
+
SET(OPENSSL_CONF_TARGET )
IF(DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
SET (OPENSSL_CONF_TARGET ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
diff --git a/include/fc/asio.hpp b/include/fc/asio.hpp
index 9310774..57da7a1 100644
--- a/include/fc/asio.hpp
+++ b/include/fc/asio.hpp
@@ -39,17 +39,8 @@ namespace asio {
std::shared_ptr<const char> _buffer;
};
- //void read_write_handler( const promise<size_t>::ptr& p,
- // const boost::system::error_code& ec,
- // size_t bytes_transferred );
- void read_write_handler_ec( promise<size_t>* p,
- boost::system::error_code* oec,
- const boost::system::error_code& ec,
- size_t bytes_transferred );
void error_handler( const promise<void>::ptr& p,
- const boost::system::error_code& ec );
- void error_handler_ec( promise<boost::system::error_code>* p,
- const boost::system::error_code& ec );
+ const boost::system::error_code& ec );
template<typename C>
struct non_blocking {
@@ -57,14 +48,14 @@ namespace asio {
bool operator()( C& c, bool s ) { c.non_blocking(s); return true; }
};
- #if WIN32 // windows stream handles do not support non blocking!
- template<>
- struct non_blocking<boost::asio::windows::stream_handle> {
- typedef boost::asio::windows::stream_handle C;
- bool operator()( C& ) { return false; }
- bool operator()( C&, bool ) { return false; }
+#if WIN32 // windows stream handles do not support non blocking!
+ template<>
+ struct non_blocking<boost::asio::windows::stream_handle> {
+ typedef boost::asio::windows::stream_handle C;
+ bool operator()( C& ) { return false; }
+ bool operator()( C&, bool ) { return false; }
};
- #endif
+#endif
}
/**
* @return the default boost::asio::io_service for use with fc::asio
diff --git a/include/fc/compress/zlib.hpp b/include/fc/compress/zlib.hpp
index 10a3a0e..baf01f5 100644
--- a/include/fc/compress/zlib.hpp
+++ b/include/fc/compress/zlib.hpp
@@ -1,10 +1,16 @@
#pragma once
#include <fc/string.hpp>
+#ifdef FC_USE_FULL_ZLIB
+# include <fc/filesystem.hpp>
+#endif
namespace fc
{
string zlib_compress(const string& in);
+#ifdef FC_USE_FULL_ZLIB
+ void gzip_compress_file(const path& input_filename, const path& output_filename);
+#endif
} // namespace fc
diff --git a/include/fc/crypto/elliptic.hpp b/include/fc/crypto/elliptic.hpp
index dd60da7..99cd68a 100644
--- a/include/fc/crypto/elliptic.hpp
+++ b/include/fc/crypto/elliptic.hpp
@@ -123,7 +123,7 @@ namespace fc {
fc::sha512 get_shared_secret( const public_key& pub )const;
// signature sign( const fc::sha256& digest )const;
- compact_signature sign_compact( const fc::sha256& digest, bool require_canonical = true )const;
+ compact_signature sign_compact( const fc::sha256& digest )const;
// bool verify( const fc::sha256& digest, const signature& sig );
public_key get_public_key()const;
diff --git a/include/fc/crypto/hash_ctr_rng.hpp b/include/fc/crypto/hash_ctr_rng.hpp
deleted file mode 100644
index 878cf77..0000000
--- a/include/fc/crypto/hash_ctr_rng.hpp
+++ /dev/null
@@ -1,107 +0,0 @@
-#pragma once
-
-#include <boost/multiprecision/integer.hpp>
-
-namespace fc {
-
-/**
- * Always returns 0. Useful for testing.
- */
-class nullary_rng
-{
- public:
- nullary_rng() {}
- virtual ~nullary_rng() {}
-
- template< typename T > T operator()( T max )
- { return T(0); }
-} ;
-
-/**
- * The hash_ctr_rng generates bits using a hash function in counter (CTR)
- * mode.
- */
-template<class HashClass, int SeedLength>
-class hash_ctr_rng
-{
- public:
- hash_ctr_rng( const char* seed, uint64_t counter = 0 )
- : _counter( counter ), _current_offset( 0 )
- {
- memcpy( _seed, seed, SeedLength );
- _reset_current_value();
- return;
- }
-
- virtual ~hash_ctr_rng() {}
-
- uint64_t get_bits( uint8_t count )
- {
- uint64_t result = 0;
- uint64_t mask = 1;
- // grab the requested number of bits
- while( count > 0 )
- {
- result |=
- (
- (
- (
- _current_value.data()[ (_current_offset >> 3) & 0x1F ]
- & ( 1 << (_current_offset & 0x07) )
- )
- != 0
- ) ? mask : 0
- );
- mask += mask;
- --count;
- ++_current_offset;
- if( _current_offset == (_current_value.data_size() << 3) )
- {
- _counter++;
- _current_offset = 0;
- _reset_current_value();
- }
- }
- return result;
- }
-
- uint64_t operator()( uint64_t bound )
- {
- if( bound <= 1 )
- return 0;
- uint8_t bitcount = boost::multiprecision::detail::find_msb( bound ) + 1;
-
- // probability of loop exiting is >= 1/2, so probability of
- // running N times is bounded above by (1/2)^N
- while( true )
- {
- uint64_t result = get_bits( bitcount );
- if( result < bound )
- return result;
- }
- }
-
- // convenience method which does casting for types other than uint64_t
- template< typename T > T operator()( T bound )
- { return (T) ( (*this)(uint64_t( bound )) ); }
-
- void _reset_current_value()
- {
- // internal implementation detail, called to update
- // _current_value when _counter changes
- typename HashClass::encoder enc;
- enc.write( _seed , SeedLength );
- enc.write( (char *) &_counter, 8 );
- _current_value = enc.result();
- return;
- }
-
- uint64_t _counter;
- char _seed[ SeedLength ];
- HashClass _current_value;
- uint16_t _current_offset;
-
- static const int seed_length = SeedLength;
-};
-
-} // end namespace fc
diff --git a/include/fc/crypto/md5.hpp b/include/fc/crypto/md5.hpp
new file mode 100644
index 0000000..06141f7
--- /dev/null
+++ b/include/fc/crypto/md5.hpp
@@ -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 )
diff --git a/include/fc/exception/exception.hpp b/include/fc/exception/exception.hpp
index 028b172..35d9f27 100644
--- a/include/fc/exception/exception.hpp
+++ b/include/fc/exception/exception.hpp
@@ -449,7 +449,7 @@ namespace fc
FC_RETHROW_EXCEPTION( er, LOG_LEVEL, FORMAT, __VA_ARGS__ ); \
} catch( const std::exception& e ) { \
fc::exception fce( \
- FC_LOG_MESSAGE( LOG_LEVEL, "${what}: " FORMAT,__VA_ARGS__("what",e.what())), \
+ BOOST_PP_EXPAND(FC_LOG_MESSAGE( LOG_LEVEL, "${what}: " FORMAT,__VA_ARGS__("what",e.what()))), \
fc::std_exception_code,\
typeid(e).name(), \
e.what() ) ; throw fce;\
diff --git a/include/fc/io/raw.hpp b/include/fc/io/raw.hpp
index 46dcf4b..bc5cd22 100644
--- a/include/fc/io/raw.hpp
+++ b/include/fc/io/raw.hpp
@@ -324,13 +324,13 @@ namespace fc {
struct if_enum<fc::true_type> {
template<typename Stream, typename T>
static inline void pack( Stream& s, const T& v ) {
- fc::raw::pack(s, signed_int((int32_t)v));
+ fc::raw::pack(s, (int64_t)v);
}
template<typename Stream, typename T>
static inline void unpack( Stream& s, T& v ) {
- signed_int temp;
+ int64_t temp;
fc::raw::unpack(s, temp);
- v = (T)temp.value;
+ v = (T)temp;
}
};
diff --git a/include/fc/log/file_appender.hpp b/include/fc/log/file_appender.hpp
index a05fcd3..70a6e0a 100644
--- a/include/fc/log/file_appender.hpp
+++ b/include/fc/log/file_appender.hpp
@@ -18,6 +18,7 @@ class file_appender : public appender {
bool rotate = false;
microseconds rotation_interval;
microseconds rotation_limit;
+ bool rotation_compression = false;
};
file_appender( const variant& args );
~file_appender();
@@ -31,4 +32,4 @@ class file_appender : public appender {
#include <fc/reflect/reflect.hpp>
FC_REFLECT( fc::file_appender::config,
- (format)(filename)(flush)(rotate)(rotation_interval)(rotation_limit) )
+ (format)(filename)(flush)(rotate)(rotation_interval)(rotation_limit)(rotation_compression) )
diff --git a/include/fc/log/log_message.hpp b/include/fc/log/log_message.hpp
index 1928a98..c17c0e6 100644
--- a/include/fc/log/log_message.hpp
+++ b/include/fc/log/log_message.hpp
@@ -8,6 +8,14 @@
#include <fc/shared_ptr.hpp>
#include <memory>
+#include <boost/preprocessor/seq/for_each.hpp>
+#include <boost/preprocessor/stringize.hpp>
+#include <boost/preprocessor/control/if.hpp>
+#include <boost/preprocessor/comparison/equal.hpp>
+#include <boost/preprocessor/facilities/expand.hpp>
+#include <boost/preprocessor/variadic/size.hpp>
+#include <boost/preprocessor/seq/variadic_seq_to_seq.hpp>
+
namespace fc
{
namespace detail
@@ -157,6 +165,15 @@ FC_REFLECT_TYPENAME( fc::log_message );
* @param FORMAT A const char* string containing zero or more references to keys as "${key}"
* @param ... A set of key/value pairs denoted as ("key",val)("key2",val2)...
*/
-#define FC_LOG_MESSAGE( LOG_LEVEL, FORMAT, ... ) \
- fc::log_message( FC_LOG_CONTEXT(LOG_LEVEL), FORMAT, fc::mutable_variant_object()__VA_ARGS__ )
+#define FC_LOG_MESSAGE_GENERATE_PARAMETER_NAME(VALUE) BOOST_PP_LPAREN() BOOST_PP_STRINGIZE(VALUE), fc::variant(VALUE) BOOST_PP_RPAREN()
+#define FC_LOG_MESSAGE_DONT_GENERATE_PARAMETER_NAME(NAME, VALUE) BOOST_PP_LPAREN() NAME, fc::variant(VALUE) BOOST_PP_RPAREN()
+#define FC_LOG_MESSAGE_GENERATE_PARAMETER_NAMES_IF_NEEDED(r, data, PARAMETER_AND_MAYBE_NAME) BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE PARAMETER_AND_MAYBE_NAME,1),FC_LOG_MESSAGE_GENERATE_PARAMETER_NAME,FC_LOG_MESSAGE_DONT_GENERATE_PARAMETER_NAME)PARAMETER_AND_MAYBE_NAME
+
+#define FC_LOG_MESSAGE_STRING_ONLY(LOG_LEVEL, FORMAT) \
+ fc::log_message(FC_LOG_CONTEXT(LOG_LEVEL), FORMAT, fc::variant_object())
+#define FC_LOG_MESSAGE_WITH_SUBSTITUTIONS(LOG_LEVEL, FORMAT, ...) \
+ fc::log_message(FC_LOG_CONTEXT(LOG_LEVEL), FORMAT, fc::mutable_variant_object() BOOST_PP_SEQ_FOR_EACH(FC_LOG_MESSAGE_GENERATE_PARAMETER_NAMES_IF_NEEDED, _, BOOST_PP_VARIADIC_SEQ_TO_SEQ(__VA_ARGS__)))
+
+#define FC_LOG_MESSAGE(LOG_LEVEL, ...) \
+ BOOST_PP_EXPAND(BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),1),FC_LOG_MESSAGE_STRING_ONLY,FC_LOG_MESSAGE_WITH_SUBSTITUTIONS)(LOG_LEVEL,__VA_ARGS__))
diff --git a/include/fc/log/logger.hpp b/include/fc/log/logger.hpp
index 9005d75..8c1020d 100644
--- a/include/fc/log/logger.hpp
+++ b/include/fc/log/logger.hpp
@@ -155,6 +155,8 @@ namespace fc
#define FC_FORMAT_ARG_PARAMS( ... )\
BOOST_PP_SEQ_FOR_EACH( FC_FORMAT_ARGS, v, __VA_ARGS__ )
+#define ddump( SEQ ) \
+ dlog( FC_FORMAT(SEQ), FC_FORMAT_ARG_PARAMS(SEQ) )
#define idump( SEQ ) \
ilog( FC_FORMAT(SEQ), FC_FORMAT_ARG_PARAMS(SEQ) )
#define wdump( SEQ ) \
@@ -176,4 +178,4 @@ namespace fc
# define ilog(...) FC_MULTILINE_MACRO_BEGIN FC_MULTILINE_MACRO_END
# undef dlog
# define dlog(...) FC_MULTILINE_MACRO_BEGIN FC_MULTILINE_MACRO_END
-#endif
\ No newline at end of file
+#endif
diff --git a/include/fc/network/http/connection.hpp b/include/fc/network/http/connection.hpp
index 3c8de77..153a47e 100644
--- a/include/fc/network/http/connection.hpp
+++ b/include/fc/network/http/connection.hpp
@@ -60,7 +60,7 @@ namespace fc {
~connection();
// used for clients
void connect_to( const fc::ip::endpoint& ep );
- http::reply request( const fc::string& method, const fc::string& url, const fc::string& body = std::string(), const headers& = headers());
+ http::reply request( const fc::string& method, const fc::string& url, const fc::string& body = std::string(), const headers& = headers(), const fc::string& content_type = "application/json");
// used for servers
fc::tcp_socket& get_socket()const;
diff --git a/include/fc/network/http/websocket.hpp b/include/fc/network/http/websocket.hpp
index bcb022e..c0c7266 100644
--- a/include/fc/network/http/websocket.hpp
+++ b/include/fc/network/http/websocket.hpp
@@ -8,7 +8,8 @@
namespace fc { namespace http {
namespace detail {
- class abstract_websocket_server;
+ class websocket_server_impl;
+ class websocket_tls_server_impl;
class websocket_client_impl;
class websocket_tls_client_impl;
} // namespace detail;
@@ -41,7 +42,7 @@ namespace fc { namespace http {
class websocket_server
{
public:
- websocket_server(bool enable_permessage_deflate = true);
+ websocket_server();
~websocket_server();
void on_connection( const on_connection_handler& handler);
@@ -50,16 +51,16 @@ namespace fc { namespace http {
void start_accept();
private:
- std::unique_ptr<detail::abstract_websocket_server> my;
+ friend class detail::websocket_server_impl;
+ std::unique_ptr<detail::websocket_server_impl> my;
};
class websocket_tls_server
{
public:
- websocket_tls_server(const std::string& server_pem = std::string(),
- const std::string& ssl_password = std::string(),
- bool enable_permessage_deflate = false);
+ websocket_tls_server( const std::string& server_pem = std::string(),
+ const std::string& ssl_password = std::string());
~websocket_tls_server();
void on_connection( const on_connection_handler& handler);
@@ -68,7 +69,8 @@ namespace fc { namespace http {
void start_accept();
private:
- std::unique_ptr<detail::abstract_websocket_server> my;
+ friend class detail::websocket_tls_server_impl;
+ std::unique_ptr<detail::websocket_tls_server_impl> my;
};
class websocket_client
diff --git a/include/fc/network/url.hpp b/include/fc/network/url.hpp
index 6f8c745..4d6066c 100644
--- a/include/fc/network/url.hpp
+++ b/include/fc/network/url.hpp
@@ -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:
diff --git a/include/fc/optional.hpp b/include/fc/optional.hpp
index bb760a5..dfb725d 100644
--- a/include/fc/optional.hpp
+++ b/include/fc/optional.hpp
@@ -18,11 +18,9 @@ namespace fc {
* fc::optional adds less than 400.
*/
template<typename T>
- class optional
+ class optional
{
public:
- typedef T value_type;
-
optional():_valid(false){}
~optional(){ reset(); }
diff --git a/include/fc/variant.hpp b/include/fc/variant.hpp
index 11ab95a..3b86fcb 100644
--- a/include/fc/variant.hpp
+++ b/include/fc/variant.hpp
@@ -93,10 +93,10 @@ namespace fc
template<typename K, typename T>
void from_variant( const variant& var, fc::flat_map<K,T>& vo );
- template<typename K, typename T>
- void to_variant( const std::map<K,T>& var, variant& vo );
- template<typename K, typename T>
- void from_variant( const variant& var, std::map<K,T>& vo );
+ template<typename K, typename T, typename C>
+ void to_variant( const std::map<K,T, C>& var, variant& vo );
+ template<typename K, typename T, typename C>
+ void from_variant( const variant& var, std::map<K,T,C>& vo );
template<typename K, typename T>
void to_variant( const std::multimap<K,T>& var, variant& vo );
template<typename K, typename T>
@@ -134,6 +134,8 @@ namespace fc
#ifdef __APPLE__
void to_variant( size_t s, variant& v );
+ #elif defined(_MSC_VER)
+ void to_variant( unsigned long s, variant& v);
#elif !defined(_MSC_VER)
void to_variant( long long int s, variant& v );
void to_variant( unsigned long long int s, variant& v );
@@ -401,8 +403,8 @@ namespace fc
vo.insert( itr->as< std::pair<K,T> >() );
}
- template<typename K, typename T>
- void to_variant( const std::map<K, T>& var, variant& vo )
+ template<typename K, typename T, typename C>
+ void to_variant( const std::map<K, T, C>& var, variant& vo )
{
std::vector< variant > vars(var.size());
size_t i = 0;
@@ -410,8 +412,8 @@ namespace fc
vars[i] = fc::variant(*itr);
vo = vars;
}
- template<typename K, typename T>
- void from_variant( const variant& var, std::map<K, T>& vo )
+ template<typename K, typename T, typename C>
+ void from_variant( const variant& var, std::map<K, T, C>& vo )
{
const variants& vars = var.get_array();
vo.clear();
@@ -529,6 +531,11 @@ namespace fc
#ifdef __APPLE__
inline void to_variant( size_t s, variant& v ) { v = variant(uint64_t(s)); }
#endif
+
+ #ifdef _MSC_VER
+ inline void to_variant( unsigned long s, variant& v) { v = variant(uint64_t(s)); }
+ #endif
+
template<typename T>
void to_variant( const std::shared_ptr<T>& var, variant& vo )
{
diff --git a/include/fc/variant_object.hpp b/include/fc/variant_object.hpp
index 5a39c80..bd68791 100644
--- a/include/fc/variant_object.hpp
+++ b/include/fc/variant_object.hpp
@@ -202,7 +202,7 @@ namespace fc
mutable_variant_object( string key, T&& val )
:_key_value( new std::vector<entry>() )
{
- set( std::move(key), variant(forward<T>(val)) );
+ set( std::move(key), variant(fc::forward<T>(val)) );
}
mutable_variant_object( mutable_variant_object&& );
diff --git a/src/asio.cpp b/src/asio.cpp
index 0319eaa..7843336 100644
--- a/src/asio.cpp
+++ b/src/asio.cpp
@@ -11,11 +11,12 @@ namespace fc {
read_write_handler::read_write_handler(const promise<size_t>::ptr& completion_promise) :
_completion_promise(completion_promise)
{
- // assert(false); // to detect anywhere we're not passing in a shared buffer
+ //assert(false); // to detect anywhere we're not passing in a shared buffer
}
+
void read_write_handler::operator()(const boost::system::error_code& ec, size_t bytes_transferred)
{
- // assert(false); // to detect anywhere we're not passing in a shared buffer
+ //assert(false); // to detect anywhere we're not passing in a shared buffer
if( !ec )
_completion_promise->set_value(bytes_transferred);
else if( ec == boost::asio::error::eof )
@@ -23,11 +24,13 @@ namespace fc {
else
_completion_promise->set_exception( fc::exception_ptr( new fc::exception( FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) );
}
- read_write_handler_with_buffer::read_write_handler_with_buffer(const promise<size_t>::ptr& completion_promise,
+
+ read_write_handler_with_buffer::read_write_handler_with_buffer(const promise<size_t>::ptr& completion_promise,
const std::shared_ptr<const char>& buffer) :
_completion_promise(completion_promise),
_buffer(buffer)
{}
+
void read_write_handler_with_buffer::operator()(const boost::system::error_code& ec, size_t bytes_transferred)
{
if( !ec )
@@ -38,57 +41,53 @@ namespace fc {
_completion_promise->set_exception( fc::exception_ptr( new fc::exception( FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) );
}
- void read_write_handler_ec( promise<size_t>* p, boost::system::error_code* oec, const boost::system::error_code& ec, size_t bytes_transferred ) {
- p->set_value(bytes_transferred);
- *oec = ec;
- }
void error_handler( const promise<void>::ptr& p,
- const boost::system::error_code& ec ) {
- if( !ec )
- p->set_value();
- else
- {
- if( ec == boost::asio::error::eof )
- {
+ const boost::system::error_code& ec )
+ {
+ if( !ec )
+ p->set_value();
+ else
+ {
+ if( ec == boost::asio::error::eof )
+ {
p->set_exception( fc::exception_ptr( new fc::eof_exception(
- FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) );
- }
- else
- {
- //elog( "${message} ", ("message", boost::system::system_error(ec).what()));
+ FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) );
+ }
+ else
+ {
+ //elog( "${message} ", ("message", boost::system::system_error(ec).what()));
p->set_exception( fc::exception_ptr( new fc::exception(
- FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) );
- }
- }
+ FC_LOG_MESSAGE( error, "${message} ", ("message", boost::system::system_error(ec).what())) ) ) );
+ }
}
+ }
- void error_handler_ec( promise<boost::system::error_code>* p,
- const boost::system::error_code& ec ) {
- p->set_value(ec);
+ template<typename EndpointType, typename IteratorType>
+ void resolve_handler(const typename promise<std::vector<EndpointType> >::ptr& p,
+ const boost::system::error_code& ec,
+ IteratorType itr)
+ {
+ if( !ec )
+ {
+ std::vector<EndpointType> eps;
+ while( itr != IteratorType() )
+ {
+ eps.push_back(*itr);
+ ++itr;
+ }
+ p->set_value( eps );
}
-
- template<typename EndpointType, typename IteratorType>
- void resolve_handler(
- const typename promise<std::vector<EndpointType> >::ptr& p,
- const boost::system::error_code& ec,
- IteratorType itr) {
- if( !ec ) {
- std::vector<EndpointType> eps;
- while( itr != IteratorType() ) {
- eps.push_back(*itr);
- ++itr;
- }
- p->set_value( eps );
- } else {
- //elog( "%s", boost::system::system_error(ec).what() );
- //p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) );
+ else
+ {
+ //elog( "%s", boost::system::system_error(ec).what() );
+ //p->set_exception( fc::copy_exception( boost::system::system_error(ec) ) );
p->set_exception(
fc::exception_ptr( new fc::exception(
FC_LOG_MESSAGE( error, "process exited with: ${message} ",
- ("message", boost::system::system_error(ec).what())) ) ) );
- }
+ ("message", boost::system::system_error(ec).what())) ) ) );
}
- }
+ }
+ } // end namespace detail
struct default_io_service_scope
{
diff --git a/src/compress/zlib.cpp b/src/compress/zlib.cpp
index dd9d23b..a7099ba 100644
--- a/src/compress/zlib.cpp
+++ b/src/compress/zlib.cpp
@@ -1,9 +1,77 @@
#include <fc/compress/zlib.hpp>
-#include "miniz.c"
+#ifdef FC_USE_FULL_ZLIB
+# include <zlib.h>
+# include <memory>
+# include <fstream>
+#else
+# include "miniz.c"
+#endif
namespace fc
{
+#ifdef FC_USE_FULL_ZLIB
+ string zlib_compress(const string& in)
+ {
+ unsigned long bufferLen = compressBound(in.size());
+ std::unique_ptr<char[]> buffer(new char[bufferLen]);
+ compress((unsigned char*)buffer.get(), &bufferLen, (const unsigned char*)in.c_str(), in.size());
+ string result(buffer.get(), bufferLen);
+ return result;
+ }
+
+ void gzip_compress_file(const path& input_filename, const path& output_filename)
+ {
+ std::ifstream infile(input_filename.generic_string().c_str(), std::ios::binary);
+ std::ofstream outfile(output_filename.generic_string().c_str(), std::ios::out | std::ios::binary);
+ unsigned bufferLen = 1024 * 1024;
+ std::unique_ptr<char[]> inputBuffer(new char[bufferLen]);
+ std::unique_ptr<char[]> outputBuffer(new char[bufferLen]);
+
+ z_stream outputStream;
+ outputStream.zalloc = 0;
+ outputStream.zfree = 0;
+ outputStream.opaque = 0;
+ int windowBits = 15;
+ int GZIP_ENCODING = 16;
+
+ deflateInit2(&outputStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits | GZIP_ENCODING,
+ 8, Z_DEFAULT_STRATEGY);
+
+ if (infile)
+ {
+ do
+ {
+ infile.read(inputBuffer.get(), bufferLen);
+ int bytesRead = infile.gcount();
+ if (bytesRead == 0)
+ break;
+ outputStream.avail_in = bytesRead;
+ outputStream.next_in = (unsigned char*)inputBuffer.get();
+ do
+ {
+ outputStream.avail_out = bufferLen;
+ outputStream.next_out = (unsigned char*)outputBuffer.get();
+ deflate(&outputStream, Z_NO_FLUSH);
+ int compressedBytesGenerated = bufferLen - outputStream.avail_out;
+ outfile.write(outputBuffer.get(), compressedBytesGenerated);
+ }
+ while (outputStream.avail_out == 0);
+ }
+ while (infile);
+ }
+ do
+ {
+ outputStream.avail_out = bufferLen;
+ outputStream.next_out = (unsigned char*)outputBuffer.get();
+ deflate(&outputStream, Z_FINISH);
+ int compressedBytesGenerated = bufferLen - outputStream.avail_out;
+ outfile.write(outputBuffer.get(), compressedBytesGenerated);
+ }
+ while (outputStream.avail_out == 0);
+ deflateEnd(&outputStream);
+ }
+#else
string zlib_compress(const string& in)
{
size_t compressed_message_length;
@@ -12,4 +80,5 @@ namespace fc
free(compressed_message);
return result;
}
+#endif
}
diff --git a/src/crypto/elliptic_impl_priv.cpp b/src/crypto/elliptic_impl_priv.cpp
index 585ffde..ad79ebe 100644
--- a/src/crypto/elliptic_impl_priv.cpp
+++ b/src/crypto/elliptic_impl_priv.cpp
@@ -85,7 +85,7 @@ namespace fc { namespace ecc {
return secp256k1_nonce_function_default( nonce32, msg32, key32, *extra, nullptr );
}
- compact_signature private_key::sign_compact( const fc::sha256& digest, bool require_canonical )const
+ compact_signature private_key::sign_compact( const fc::sha256& digest )const
{
FC_ASSERT( my->_key != empty_priv );
compact_signature result;
@@ -94,7 +94,7 @@ namespace fc { namespace ecc {
do
{
FC_ASSERT( secp256k1_ecdsa_sign_compact( detail::_get_context(), (unsigned char*) digest.data(), (unsigned char*) result.begin() + 1, (unsigned char*) my->_key.data(), extended_nonce_function, &counter, &recid ));
- } while( require_canonical && !public_key::is_canonical( result ) );
+ } while( !public_key::is_canonical( result ) );
result.begin()[0] = 27 + 4 + recid;
return result;
}
diff --git a/src/crypto/md5.cpp b/src/crypto/md5.cpp
new file mode 100644
index 0000000..fd304eb
--- /dev/null
+++ b/src/crypto/md5.cpp
@@ -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) );
+ }
+}
diff --git a/src/log/file_appender.cpp b/src/log/file_appender.cpp
index 85b69c6..204686a 100644
--- a/src/log/file_appender.cpp
+++ b/src/log/file_appender.cpp
@@ -5,6 +5,9 @@
#include <fc/thread/scoped_lock.hpp>
#include <fc/thread/thread.hpp>
#include <fc/variant.hpp>
+#ifdef FC_USE_FULL_ZLIB
+# include <fc/compress/zlib.hpp>
+#endif
#include <boost/thread/mutex.hpp>
#include <iomanip>
#include <queue>
@@ -12,6 +15,8 @@
namespace fc {
+ static const string compression_extension( ".gz" );
+
class file_appender::impl : public fc::retainable
{
public:
@@ -22,6 +27,7 @@ namespace fc {
private:
future<void> _rotation_task;
time_point_sec _current_file_start_time;
+ std::unique_ptr<thread> _compression_thread;
time_point_sec get_file_start_time( const time_point_sec& timestamp, const microseconds& interval )
{
@@ -30,6 +36,28 @@ namespace fc {
return time_point_sec( (uint32_t)(file_number * interval_seconds) );
}
+ void compress_file( const fc::path& filename )
+ {
+#ifdef FC_USE_FULL_ZLIB
+ FC_ASSERT( cfg.rotate && cfg.rotation_compression );
+ FC_ASSERT( _compression_thread );
+ if( !_compression_thread->is_current() )
+ {
+ _compression_thread->async( [this, filename]() { compress_file( filename ); }, "compress_file" ).wait();
+ return;
+ }
+
+ try
+ {
+ gzip_compress_file( filename, filename.parent_path() / (filename.filename().string() + compression_extension) );
+ remove_all( filename );
+ }
+ catch( ... )
+ {
+ }
+#endif
+ }
+
public:
impl( const config& c) : cfg( c )
{
@@ -38,8 +66,10 @@ namespace fc {
FC_ASSERT( cfg.rotation_interval >= seconds( 1 ) );
FC_ASSERT( cfg.rotation_limit >= cfg.rotation_interval );
-
-
+#ifdef FC_USE_FULL_ZLIB
+ if( cfg.rotation_compression )
+ _compression_thread.reset( new thread( "compression") );
+#endif
_rotation_task = async( [this]() { rotate_files( true ); }, "rotate_files(1)" );
}
@@ -107,11 +137,16 @@ namespace fc {
fc::time_point_sec current_timestamp = fc::time_point_sec::from_iso_string( current_timestamp_str );
if( current_timestamp < start_time )
{
- if( current_timestamp < limit_time || file_size( current_filename ) <= 0 )
+ if( current_timestamp < limit_time || file_size( link_filename.parent_path() / itr->filename() ) <= 0 )
{
remove_all( *itr );
continue;
}
+ if( !cfg.rotation_compression )
+ continue;
+ if( current_filename.find( compression_extension ) != string::npos )
+ continue;
+ compress_file( *itr );
}
}
catch (const fc::canceled_exception&)
@@ -134,7 +169,8 @@ namespace fc {
format( "${timestamp} ${thread_name} ${context} ${file}:${line} ${method} ${level}] ${message}" ),
filename(p),
flush(true),
- rotate(false)
+ rotate(false),
+ rotation_compression(false)
{}
file_appender::file_appender( const variant& args ) :
@@ -161,7 +197,11 @@ namespace fc {
{
std::stringstream line;
//line << (m.get_context().get_timestamp().time_since_epoch().count() % (1000ll*1000ll*60ll*60))/1000 <<"ms ";
- line << string(m.get_context().get_timestamp()) << " ";
+ //line << string(m.get_context().get_timestamp()) << " ";
+ time_point timestamp = m.get_context().get_timestamp();
+ line << string(timestamp);
+ uint64_t milliseconds = (timestamp.time_since_epoch().count() % 1000000) / 1000;
+ line << "." << std::setw(3) << std::setfill('0') << milliseconds << std::setfill(' ') << " ";
line << std::setw( 21 ) << (m.get_context().get_thread_name().substr(0,9) + string(":") + m.get_context().get_task_name()).c_str() << " ";
string method_name = m.get_context().get_method();
diff --git a/src/network/http/http_connection.cpp b/src/network/http/http_connection.cpp
index 309866d..7a1e076 100644
--- a/src/network/http/http_connection.cpp
+++ b/src/network/http/http_connection.cpp
@@ -9,67 +9,137 @@
#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
{
public:
fc::tcp_socket sock;
fc::ip::endpoint ep;
- impl() {
- }
+ impl() {}
- int read_until( char* buffer, char* end, char c = '\n' ) {
- char* p = buffer;
- // try {
- while( p < end && 1 == sock.readsome(p,1) ) {
- if( *p == c ) {
- *p = '\0';
- return (p - buffer)-1;
- }
- ++p;
- }
- // } catch ( ... ) {
- // elog("%s", fc::current_exception().diagnostic_information().c_str() );
- //elog( "%s", fc::except_str().c_str() );
- // }
- return (p-buffer);
+ size_t read_until(std::shared_ptr<char> buffer, size_t buffer_length, char c = '\n')
+ {
+ size_t offset = 0;
+ while (offset < buffer_length &&
+ sock.readsome(buffer, 1, offset) == 1)
+ {
+ if (buffer.get()[offset] == c)
+ {
+ buffer.get()[offset] = 0;
+ return offset;
+ }
+ ++offset;
+ }
+ return offset;
}
- fc::http::reply parse_reply() {
+ fc::http::reply parse_reply()
+ {
fc::http::reply rep;
- try {
- std::vector<char> line(1024*8);
- int s = read_until( line.data(), line.data()+line.size(), ' ' ); // HTTP/1.1
- s = read_until( line.data(), line.data()+line.size(), ' ' ); // CODE
- rep.status = static_cast<int>(to_int64(fc::string(line.data())));
- s = read_until( line.data(), line.data()+line.size(), '\n' ); // DESCRIPTION
+ fc::oexception parsing_exception;
+ try
+ {
+ const size_t buffer_length = 1024 * 8;
+ std::shared_ptr<char> line(new char[buffer_length], [](char* p){ delete[] p; });
+ read_until(line, buffer_length, ' '); // HTTP/1.1
+ size_t bytes_read = read_until(line, buffer_length, ' '); // CODE
+ rep.status = static_cast<int>(to_int64(fc::string(line.get(), bytes_read)));
+ read_until(line, buffer_length, '\n'); // DESCRIPTION
- while( (s = read_until( line.data(), line.data()+line.size(), '\n' )) > 1 ) {
+ fc::optional<size_t> content_length;
+ bool is_chunked = false;
+ while( (bytes_read = read_until(line, buffer_length, '\n')) > 1 )
+ {
fc::http::header h;
- char* end = line.data();
- while( *end != ':' )++end;
- h.key = fc::string(line.data(),end);
- ++end; // skip ':'
- ++end; // skip space
- char* skey = end;
- while( *end != '\r' ) ++end;
- h.val = fc::string(skey,end);
+ std::string line_string(line.get(), bytes_read);
+ size_t colon_pos = line_string.find(": ");
+ if (colon_pos != std::string::npos)
+ {
+ h.key = line_string.substr(0, colon_pos);
+ size_t value_start_pos = colon_pos + 2;
+ size_t carriage_return_pos = line_string.find('\r', value_start_pos);
+ if (carriage_return_pos != std::string::npos)
+ h.val = line_string.substr(value_start_pos, carriage_return_pos - value_start_pos);
+ else
+ h.val = line_string.substr(value_start_pos);
+ }
rep.headers.push_back(h);
- if( boost::iequals(h.key, "Content-Length") ) {
- rep.body.resize( static_cast<size_t>(to_uint64( fc::string(h.val) ) ));
+ if( boost::iequals(h.key, "Content-Length") )
+ content_length = static_cast<size_t>(to_uint64( fc::string(h.val) ));
+ if( boost::iequals(h.key, "Transfer-Encoding") &&
+ boost::iequals(fc::string(h.val), "chunked") )
+ is_chunked = true;
+ }
+
+ if (is_chunked)
+ {
+ do
+ {
+ // Chunked means we get a hexadecimal number of bytes on a line, followed by the content
+ bytes_read = read_until(line, buffer_length, '\n'); //read chunk length
+ std::string line_string(line.get(), bytes_read);
+ if (line_string.size() > 0 && line_string[line_string.size() - 1] == '\r')
+ line_string.erase(line_string.size() - 1);
+ unsigned length;
+ if (sscanf(line_string.c_str(), "%x", &length) != 1)
+ FC_THROW("Invalid content length: ${length}", ("length", line_string));
+ content_length = length;
+ if (*content_length)
+ {
+ std::shared_ptr<char> temp_data(new char[*content_length], [](char* p){ delete[] p; });
+ sock.read(temp_data, *content_length, 0);
+ boost::push_back(rep.body, std::make_pair(temp_data.get(), temp_data.get() + *content_length));
+ read_until(line, buffer_length, '\n'); //discard cr/lf after each chunk
+ }
}
+ while (*content_length != 0);
}
- if( rep.body.size() ) {
- sock.read( rep.body.data(), rep.body.size() );
+
+ if (content_length)
+ {
+ if (*content_length)
+ {
+ std::shared_ptr<char> temp_data(new char[*content_length], [](char* p){ delete[] p; });
+ sock.read(temp_data, *content_length, 0);
+ boost::push_back(rep.body, std::make_pair(temp_data.get(), temp_data.get() + *content_length));
+ }
}
+ else //just read until closed if no content length or chunking
+ {
+ while (true)
+ {
+ try
+ {
+ sock.read(line, 1, 0);
+ rep.body.push_back(line.get()[0]);
+ }
+ catch (const fc::canceled_exception&)
+ {
+ throw;
+ }
+ catch (const fc::eof_exception&)
+ {
+ break;
+ }
+ }
+ }
+
return rep;
- } catch ( fc::exception& e ) {
- elog( "${exception}", ("exception",e.to_detail_string() ) );
- sock.close();
- rep.status = http::reply::InternalServerError;
- return rep;
}
+ catch (const fc::canceled_exception&)
+ {
+ throw;
+ }
+ catch (const fc::exception& e)
+ {
+ parsing_exception = e;
+ }
+ assert(parsing_exception); // the only way we get here is if the last catch falls through
+ elog("${exception}", ("exception", parsing_exception->to_detail_string()));
+ sock.close();
+ rep.status = http::reply::InternalServerError;
+ return rep;
}
};
@@ -77,54 +147,76 @@ class fc::http::connection::impl
namespace fc { namespace http {
- connection::connection()
- :my( new connection::impl() ){}
- connection::~connection(){}
+connection::connection() :
+ my( new connection::impl() )
+{}
+connection::~connection(){}
// used for clients
-void connection::connect_to( const fc::ip::endpoint& ep ) {
+void connection::connect_to( const fc::ip::endpoint& ep )
+{
my->sock.close();
my->sock.connect_to( my->ep = ep );
}
http::reply connection::request( const fc::string& method,
- const fc::string& url,
- const fc::string& body, const headers& he ) {
-
+ const fc::string& url,
+ const fc::string& body,
+ const headers& he,
+ const fc::string& content_type )
+{
fc::url parsed_url(url);
- if( !my->sock.is_open() ) {
+ if( !my->sock.is_open() )
+ {
wlog( "Re-open socket!" );
my->sock.connect_to( my->ep );
}
- try {
- fc::stringstream req;
- req << method <<" "<<parsed_url.path()->generic_string()<<" HTTP/1.1\r\n";
- req << "Host: "<<*parsed_url.host()<<"\r\n";
- req << "Content-Type: application/json\r\n";
- for( auto i = he.begin(); i != he.end(); ++i )
- {
- req << i->key <<": " << i->val<<"\r\n";
- }
- if( body.size() ) req << "Content-Length: "<< body.size() << "\r\n";
- req << "\r\n";
- fc::string head = req.str();
+ try
+ {
+ fc::stringstream req;
+ 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 )
+ req << i->key << ": " << i->val << "\r\n";
+ if( body.size() )
+ req << "Content-Length: "<< body.size() << "\r\n";
+ req << "\r\n";
- my->sock.write( head.c_str(), head.size() );
- // fc::cerr.write( head.c_str() );
+ {
+ fc::string head = req.str();
+ std::shared_ptr<char> write_buffer(new char[head.size()], [](char* p){ delete[] p; });
+ std::copy(head.begin(), head.end(), write_buffer.get());
+ my->sock.write(write_buffer, head.size(), 0);
+ //elog("Sending header ${head}", ("head", head));
+ // fc::cerr.write( head.c_str() );
+ }
- if( body.size() ) {
- my->sock.write( body.c_str(), body.size() );
- // fc::cerr.write( body.c_str() );
- }
- // fc::cerr.flush();
+ if( body.size() )
+ {
+ std::shared_ptr<char> write_buffer(new char[body.size()], [](char* p){ delete[] p; });
+ std::copy(body.begin(), body.end(), write_buffer.get());
+ my->sock.write(write_buffer, body.size(), 0);
+ //elog("Sending body ${body}", ("body", body));
+ //fc::cerr.write( body.c_str() );
+ }
+ // fc::cerr.flush();
- return my->parse_reply();
- } catch ( ... ) {
- my->sock.close();
- FC_THROW_EXCEPTION( exception, "Error Sending HTTP Request" ); // TODO: provide more info
- // return http::reply( http::reply::InternalServerError ); // TODO: replace with connection error
+ return my->parse_reply();
+ }
+ catch (const fc::canceled_exception&)
+ {
+ throw;
}
+ catch (...)
+ {
+ // fall through
+ }
+ // the only way we get here is if we encountered catch(...)
+ my->sock.close();
+ FC_THROW_EXCEPTION( exception, "Error Sending HTTP Request" ); // TODO: provide more info
+ // return http::reply( http::reply::InternalServerError ); // TODO: replace with connection error
}
// used for servers
@@ -132,73 +224,97 @@ fc::tcp_socket& connection::get_socket()const {
return my->sock;
}
-http::request connection::read_request()const {
+http::request connection::read_request()const {
http::request req;
- std::vector<char> line(1024*8);
- int s = my->read_until( line.data(), line.data()+line.size(), ' ' ); // METHOD
- req.method = line.data();
- s = my->read_until( line.data(), line.data()+line.size(), ' ' ); // PATH
- req.path = line.data();
- s = my->read_until( line.data(), line.data()+line.size(), '\n' ); // HTTP/1.0
+ const size_t buffer_length = 1024 * 8;
+ std::shared_ptr<char> line(new char[buffer_length], [](char* p){ delete[] p; });
+ size_t bytes_read = my->read_until(line, buffer_length, ' '); // METHOD
+ req.method = std::string(line.get(), bytes_read);
+ bytes_read = my->read_until(line, buffer_length, ' '); // PATH
+ req.path = std::string(line.get(), bytes_read);
+ bytes_read = my->read_until(line, buffer_length, '\n'); // HTTP/1.0
- while( (s = my->read_until( line.data(), line.data()+line.size(), '\n' )) > 1 ) {
+ while( (bytes_read = my->read_until(line, buffer_length, '\n')) > 1 )
+ {
fc::http::header h;
- char* end = line.data();
- while( *end != ':' )++end;
- h.key = fc::string(line.data(),end);
- ++end; // skip ':'
- ++end; // skip space
- char* skey = end;
- while( *end != '\r' ) ++end;
- h.val = fc::string(skey,end);
- req.headers.push_back(h);
- if( boost::iequals(h.key, "Content-Length")) {
- auto s = static_cast<size_t>(to_uint64( fc::string(h.val) ) );
- FC_ASSERT( s < 1024*1024 );
- req.body.resize( static_cast<size_t>(to_uint64( fc::string(h.val) ) ));
+ std::string line_string(line.get(), bytes_read);
+ size_t colon_pos = line_string.find(": ");
+ if (colon_pos != std::string::npos)
+ {
+ h.key = line_string.substr(0, colon_pos);
+ size_t value_start_pos = colon_pos + 2;
+ size_t carriage_return_pos = line_string.find('\r', value_start_pos);
+ if (carriage_return_pos != std::string::npos)
+ h.val = line_string.substr(value_start_pos, carriage_return_pos - value_start_pos);
+ else
+ h.val = line_string.substr(value_start_pos);
}
- if( boost::iequals(h.key, "Host") ) {
- req.domain = h.val;
+ req.headers.push_back(h);
+ if( boost::iequals(h.key, "Content-Length"))
+ {
+ size_t content_length = static_cast<size_t>(to_uint64( fc::string(h.val) ) );
+ FC_ASSERT(content_length < 1024*1024);
+ req.body.resize( static_cast<size_t>(to_uint64( fc::string(h.val) ) ));
}
+ if( boost::iequals(h.key, "Host") )
+ req.domain = h.val;
}
// TODO: some common servers won't give a Content-Length, they'll use
// Transfer-Encoding: chunked. handle that here.
- if( req.body.size() ) {
- my->sock.read( req.body.data(), req.body.size() );
+ if( req.body.size() )
+ {
+ std::shared_ptr<char> body_buffer(new char[req.body.size()], [](char* p){ delete[] p; });
+ my->sock.read(body_buffer, req.body.size(), 0);
+ std::copy(body_buffer.get(), body_buffer.get() + req.body.size(), req.body.data());
}
+
return req;
}
-fc::string request::get_header( const fc::string& key )const {
- for( auto itr = headers.begin(); itr != headers.end(); ++itr ) {
- if( boost::iequals(itr->key, key) ) { return itr->val; }
- }
+fc::string request::get_header( const fc::string& key )const
+{
+ for( auto itr = headers.begin(); itr != headers.end(); ++itr )
+ if( boost::iequals(itr->key, key) )
+ return itr->val;
return fc::string();
}
-std::vector<header> parse_urlencoded_params( const fc::string& f ) {
+
+std::vector<header> parse_urlencoded_params( const fc::string& f )
+{
int num_args = 0;
- for( size_t i = 0; i < f.size(); ++i ) {
- if( f[i] == '=' ) ++num_args;
- }
+ for( size_t i = 0; i < f.size(); ++i )
+ if( f[i] == '=' )
+ ++num_args;
+
std::vector<header> h(num_args);
int arg = 0;
- for( size_t i = 0; i < f.size(); ++i ) {
- while( f[i] != '=' && i < f.size() ) {
- if( f[i] == '%' ) {
+ for( size_t i = 0; i < f.size(); ++i )
+ {
+ while( f[i] != '=' && i < f.size() )
+ {
+ if( f[i] == '%' )
+ {
h[arg].key += char((fc::from_hex(f[i+1]) << 4) | fc::from_hex(f[i+2]));
i += 3;
- } else {
- h[arg].key += f[i];
- ++i;
+ }
+ else
+ {
+ h[arg].key += f[i];
+ ++i;
}
}
++i;
- while( i < f.size() && f[i] != '&' ) {
- if( f[i] == '%' ) {
+
+ while( i < f.size() && f[i] != '&' )
+ {
+ if( f[i] == '%' )
+ {
h[arg].val += char((fc::from_hex(f[i+1]) << 4) | fc::from_hex(f[i+2]));
i += 3;
- } else {
+ }
+ else
+ {
h[arg].val += f[i] == '+' ? ' ' : f[i];
++i;
}
diff --git a/src/network/http/http_server.cpp b/src/network/http/http_server.cpp
index 648fc54..0fb399d 100644
--- a/src/network/http/http_server.cpp
+++ b/src/network/http/http_server.cpp
@@ -16,24 +16,38 @@ namespace fc { namespace http {
:body_bytes_sent(0),body_length(0),con(c),handle_next_req(cont)
{}
- void send_header() {
+ void send_header()
+ {
//ilog( "sending header..." );
fc::stringstream ss;
ss << "HTTP/1.1 " << rep.status << " ";
- switch( rep.status ) {
- case fc::http::reply::OK: ss << "OK\r\n"; break;
- case fc::http::reply::RecordCreated: ss << "Record Created\r\n"; break;
- case fc::http::reply::NotFound: ss << "Not Found\r\n"; break;
- case fc::http::reply::Found: ss << "Found\r\n"; break;
- default: ss << "Internal Server Error\r\n"; break;
- }
- for( uint32_t i = 0; i < rep.headers.size(); ++i ) {
- ss << rep.headers[i].key <<": "<<rep.headers[i].val <<"\r\n";
+ switch( rep.status )
+ {
+ case fc::http::reply::OK:
+ ss << "OK\r\n";
+ break;
+ case fc::http::reply::RecordCreated:
+ ss << "Record Created\r\n";
+ break;
+ case fc::http::reply::NotFound:
+ ss << "Not Found\r\n";
+ break;
+ case fc::http::reply::Found:
+ ss << "Found\r\n";
+ break;
+ default:
+ ss << "Internal Server Error\r\n";
+ break;
}
- ss << "Content-Length: "<<body_length<<"\r\n\r\n";
- auto s = ss.str();
+ for( uint32_t i = 0; i < rep.headers.size(); ++i )
+ ss << rep.headers[i].key << ": " << rep.headers[i].val << "\r\n";
+ ss << "Content-Length: " << body_length << "\r\n\r\n";
+ std::string s = ss.str();
+ std::shared_ptr<char> write_buffer(new char[s.size()], [](char* p){ delete[] p; });
+ std::copy(s.begin(), s.end(), write_buffer.get());
+
//fc::cerr<<s<<"\n";
- con->get_socket().write( s.c_str(), s.size() );
+ con->get_socket().write(write_buffer, s.size(), 0);
}
http::reply rep;
@@ -184,7 +198,9 @@ namespace fc { namespace http {
my->send_header();
}
my->body_bytes_sent += len;
- my->con->get_socket().write( data, static_cast<size_t>(len) );
+ std::shared_ptr<char> write_buffer(new char[len], [](char* p){ delete[] p; });
+ std::copy(data, data + len, write_buffer.get());
+ my->con->get_socket().write(write_buffer, static_cast<size_t>(len), 0);
if( my->body_bytes_sent == int64_t(my->body_length) ) {
if( false || my->handle_next_req ) {
ilog( "handle next request..." );
diff --git a/src/network/http/websocket.cpp b/src/network/http/websocket.cpp
index b8a3d3c..ef880ff 100644
--- a/src/network/http/websocket.cpp
+++ b/src/network/http/websocket.cpp
@@ -1,19 +1,8 @@
#include <fc/network/http/websocket.hpp>
-
-#ifndef WIN32
-// websocket++ currently does not build correctly with permessage deflate enabled
-// since chrome does not work with websocketpp's implementation of permessage-deflate
-// yet, I'm just disabling it on windows instead of trying to fix the build error.
-# define ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE
-#endif
-
#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/config/asio.hpp>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_client.hpp>
-#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE
-# include <websocketpp/extensions/permessage_deflate/enabled.hpp>
-#endif
#include <websocketpp/client.hpp>
#include <websocketpp/logger/stub.hpp>
@@ -30,11 +19,12 @@
namespace fc { namespace http {
namespace detail {
+
struct asio_with_stub_log : public websocketpp::config::asio {
+
typedef asio_with_stub_log type;
typedef asio base;
- //// All boilerplate copying the base class's config, except as noted
typedef base::concurrency_type concurrency_type;
typedef base::request_type request_type;
@@ -43,8 +33,15 @@ namespace fc { namespace http {
typedef base::message_type message_type;
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
-
- /// Custom Logging policies, use do-nothing log::stub instead of log::basic
+
+ /// Custom Logging policies
+ /*typedef websocketpp::log::syslog<concurrency_type,
+ websocketpp::log::elevel> elog_type;
+ typedef websocketpp::log::syslog<concurrency_type,
+ websocketpp::log::alevel> alog_type;
+ */
+ //typedef base::alog_type alog_type;
+ //typedef base::elog_type elog_type;
typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;
@@ -63,16 +60,13 @@ namespace fc { namespace http {
typedef websocketpp::transport::asio::endpoint<transport_config>
transport_type;
- // override default value of 5 sec timeout
static const long timeout_open_handshake = 0;
};
+ struct asio_tls_with_stub_log : public websocketpp::config::asio_tls {
-#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE
- struct asio_with_stub_log_and_deflate : public websocketpp::config::asio {
- typedef asio_with_stub_log_and_deflate type;
- typedef asio base;
+ typedef asio_with_stub_log type;
+ typedef asio_tls base;
- //// All boilerplate copying the base class's config, except as noted
typedef base::concurrency_type concurrency_type;
typedef base::request_type request_type;
@@ -82,7 +76,14 @@ namespace fc { namespace http {
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
- /// Custom Logging policies, use do-nothing log::stub instead of log::basic
+ /// Custom Logging policies
+ /*typedef websocketpp::log::syslog<concurrency_type,
+ websocketpp::log::elevel> elog_type;
+ typedef websocketpp::log::syslog<concurrency_type,
+ websocketpp::log::alevel> alog_type;
+ */
+ //typedef base::alog_type alog_type;
+ //typedef base::elog_type elog_type;
typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;
@@ -94,97 +95,54 @@ namespace fc { namespace http {
typedef type::elog_type elog_type;
typedef type::request_type request_type;
typedef type::response_type response_type;
- typedef websocketpp::transport::asio::basic_socket::endpoint
- socket_type;
+ typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
};
typedef websocketpp::transport::asio::endpoint<transport_config>
transport_type;
- /// enable the permessage_compress extension
- struct permessage_deflate_config {};
- typedef websocketpp::extensions::permessage_deflate::enabled
- <permessage_deflate_config> permessage_deflate_type;
-
- // override default value of 5 sec timeout
static const long timeout_open_handshake = 0;
};
-#endif ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE
-
struct asio_tls_stub_log : public websocketpp::config::asio_tls {
- typedef asio_tls_stub_log type;
- typedef asio_tls base;
+ typedef asio_tls_stub_log type;
+ typedef asio_tls base;
- //// All boilerplate copying the base class's config, except as noted
- typedef base::concurrency_type concurrency_type;
+ typedef base::concurrency_type concurrency_type;
- typedef base::request_type request_type;
- typedef base::response_type response_type;
+ typedef base::request_type request_type;
+ typedef base::response_type response_type;
- typedef base::message_type message_type;
- typedef base::con_msg_manager_type con_msg_manager_type;
- typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
+ typedef base::message_type message_type;
+ typedef base::con_msg_manager_type con_msg_manager_type;
+ typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
- /// Custom Logging policies, use do-nothing log::stub instead of log::basic
- typedef websocketpp::log::stub elog_type;
- typedef websocketpp::log::stub alog_type;
+ //typedef base::alog_type alog_type;
+ //typedef base::elog_type elog_type;
+ typedef websocketpp::log::stub elog_type;
+ typedef websocketpp::log::stub alog_type;
- typedef base::rng_type rng_type;
+ typedef base::rng_type rng_type;
- struct transport_config : public base::transport_config {
- typedef type::concurrency_type concurrency_type;
- typedef type::alog_type alog_type;
- typedef type::elog_type elog_type;
- typedef type::request_type request_type;
- typedef type::response_type response_type;
- typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
- };
+ struct transport_config : public base::transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::alog_type alog_type;
+ typedef type::elog_type elog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+ typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
+ };
- typedef websocketpp::transport::asio::endpoint<transport_config>
- transport_type;
+ typedef websocketpp::transport::asio::endpoint<transport_config>
+ transport_type;
};
-#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE
- struct asio_tls_stub_log_and_deflate : public websocketpp::config::asio_tls {
- typedef asio_tls_stub_log_and_deflate type;
- typedef asio_tls base;
- //// All boilerplate copying the base class's config, except as noted
- typedef base::concurrency_type concurrency_type;
- typedef base::request_type request_type;
- typedef base::response_type response_type;
- typedef base::message_type message_type;
- typedef base::con_msg_manager_type con_msg_manager_type;
- typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
-
- /// Custom Logging policies, use do-nothing log::stub instead of log::basic
- typedef websocketpp::log::stub elog_type;
- typedef websocketpp::log::stub alog_type;
-
- typedef base::rng_type rng_type;
-
- struct transport_config : public base::transport_config {
- typedef type::concurrency_type concurrency_type;
- typedef type::alog_type alog_type;
- typedef type::elog_type elog_type;
- typedef type::request_type request_type;
- typedef type::response_type response_type;
- typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
- };
-
- typedef websocketpp::transport::asio::endpoint<transport_config>
- transport_type;
-
- /// enable the permessage_compress extension
- struct permessage_deflate_config {};
- typedef websocketpp::extensions::permessage_deflate::enabled
- <permessage_deflate_config> permessage_deflate_type;
- };
-#endif
using websocketpp::connection_hdl;
+ typedef websocketpp::server<asio_with_stub_log> websocket_server_type;
+ typedef websocketpp::server<asio_tls_stub_log> websocket_tls_server_type;
template<typename T>
class websocket_connection_impl : public websocket_connection
@@ -215,19 +173,7 @@ namespace fc { namespace http {
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
- class abstract_websocket_server
- {
- public:
- virtual ~abstract_websocket_server() {}
-
- virtual void on_connection( const on_connection_handler& handler) = 0;
- virtual void listen( uint16_t port ) = 0;
- virtual void listen( const fc::ip::endpoint& ep ) = 0;
- virtual void start_accept() = 0;
- };
-
- template <typename config>
- class websocket_server_impl : public abstract_websocket_server
+ class websocket_server_impl
{
public:
websocket_server_impl()
@@ -239,15 +185,15 @@ namespace fc { namespace http {
_server.set_reuse_addr(true);
_server.set_open_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
- websocket_connection_ptr new_con = std::make_shared<websocket_connection_impl<typename websocketpp::server<config>::connection_ptr>>( _server.get_con_from_hdl(hdl) );
+ auto new_con = std::make_shared<websocket_connection_impl<websocket_server_type::connection_ptr>>( _server.get_con_from_hdl(hdl) );
_on_connection( _connections[hdl] = new_con );
}).wait();
});
- _server.set_message_handler( [&]( connection_hdl hdl, typename websocketpp::server<config>::message_ptr msg ){
+ _server.set_message_handler( [&]( connection_hdl hdl, websocket_server_type::message_ptr msg ){
_server_thread.async( [&](){
auto current_con = _connections.find(hdl);
assert( current_con != _connections.end() );
- //wdump(("server")(msg->get_payload()));
+ idump(("server")(msg->get_payload()));
//std::cerr<<"recv: "<<msg->get_payload()<<"\n";
auto payload = msg->get_payload();
std::shared_ptr<websocket_connection> con = current_con->second;
@@ -260,20 +206,20 @@ namespace fc { namespace http {
_server.set_http_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
- auto current_con = std::make_shared<websocket_connection_impl<typename websocketpp::server<config>::connection_ptr>>( _server.get_con_from_hdl(hdl) );
+ auto current_con = std::make_shared<websocket_connection_impl<websocket_server_type::connection_ptr>>( _server.get_con_from_hdl(hdl) );
_on_connection( current_con );
auto con = _server.get_con_from_hdl(hdl);
con->defer_http_response();
std::string request_body = con->get_request_body();
- //wdump(("server")(request_body));
+ wdump(("server")(request_body));
fc::async([current_con, request_body, con] {
std::string response = current_con->on_http(request_body);
- con->set_body( response );
- con->set_status( websocketpp::http::status_code::ok );
+ con->set_body( response );
+ con->set_status( websocketpp::http::status_code::ok );
con->send_http_response();
- current_con->closed();
+ current_con->closed();
}, "call on_http");
}).wait();
});
@@ -328,62 +274,132 @@ namespace fc { namespace http {
if( _closed ) _closed->wait();
}
- void on_connection( const on_connection_handler& handler ) override
- {
- _on_connection = handler;
- }
-
- void listen( uint16_t port ) override
- {
- _server.listen(port);
- }
-
- void listen( const fc::ip::endpoint& ep ) override
- {
- _server.listen( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4(uint32_t(ep.get_address())),ep.port()) );
- }
-
- void start_accept() override
- {
- _server.start_accept();
- }
-
typedef std::map<connection_hdl, websocket_connection_ptr,std::owner_less<connection_hdl> > con_map;
con_map _connections;
fc::thread& _server_thread;
- websocketpp::server<config> _server;
+ websocket_server_type _server;
on_connection_handler _on_connection;
fc::promise<void>::ptr _closed;
uint32_t _pending_messages = 0;
};
- template <typename config>
- class websocket_tls_server_impl : public websocket_server_impl<config>
+ class websocket_tls_server_impl
{
public:
websocket_tls_server_impl( const string& server_pem, const string& ssl_password )
+ :_server_thread( fc::thread::current() )
{
- this->_server.set_tls_init_handler( [=]( websocketpp::connection_hdl hdl ) -> context_ptr {
- context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv1);
- try {
- ctx->set_options(boost::asio::ssl::context::default_workarounds |
- boost::asio::ssl::context::no_sslv2 |
- boost::asio::ssl::context::no_sslv3 |
- boost::asio::ssl::context::single_dh_use);
- ctx->set_password_callback([=](std::size_t max_length, boost::asio::ssl::context::password_purpose){ return ssl_password;});
- ctx->use_certificate_chain_file(server_pem);
- ctx->use_private_key_file(server_pem, boost::asio::ssl::context::pem);
- } catch (std::exception& e) {
- std::cout << e.what() << std::endl;
- }
- return ctx;
+ //if( server_pem.size() )
+ {
+ _server.set_tls_init_handler( [=]( websocketpp::connection_hdl hdl ) -> context_ptr {
+ context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv1);
+ try {
+ ctx->set_options(boost::asio::ssl::context::default_workarounds |
+ boost::asio::ssl::context::no_sslv2 |
+ boost::asio::ssl::context::no_sslv3 |
+ boost::asio::ssl::context::single_dh_use);
+ ctx->set_password_callback([=](std::size_t max_length, boost::asio::ssl::context::password_purpose){ return ssl_password;});
+ ctx->use_certificate_chain_file(server_pem);
+ ctx->use_private_key_file(server_pem, boost::asio::ssl::context::pem);
+ } catch (std::exception& e) {
+ std::cout << e.what() << std::endl;
+ }
+ return ctx;
+ });
+ }
+
+ _server.clear_access_channels( websocketpp::log::alevel::all );
+ _server.init_asio(&fc::asio::default_io_service());
+ _server.set_reuse_addr(true);
+ _server.set_open_handler( [&]( connection_hdl hdl ){
+ _server_thread.async( [&](){
+ auto new_con = std::make_shared<websocket_connection_impl<websocket_tls_server_type::connection_ptr>>( _server.get_con_from_hdl(hdl) );
+ _on_connection( _connections[hdl] = new_con );
+ }).wait();
+ });
+ _server.set_message_handler( [&]( connection_hdl hdl, websocket_server_type::message_ptr msg ){
+ _server_thread.async( [&](){
+ auto current_con = _connections.find(hdl);
+ assert( current_con != _connections.end() );
+ auto received = msg->get_payload();
+ std::shared_ptr<websocket_connection> con = current_con->second;
+ fc::async([con,received](){ con->on_message( received ); });
+ }).wait();
+ });
+
+ _server.set_http_handler( [&]( connection_hdl hdl ){
+ _server_thread.async( [&](){
+
+ auto current_con = std::make_shared<websocket_connection_impl<websocket_tls_server_type::connection_ptr>>( _server.get_con_from_hdl(hdl) );
+ try{
+ _on_connection( current_con );
+
+ auto con = _server.get_con_from_hdl(hdl);
+ idump(("server")(con->get_request_body()));
+ auto response = current_con->on_http( con->get_request_body() );
+
+ con->set_body( response );
+ con->set_status( websocketpp::http::status_code::ok );
+ } catch ( const fc::exception& e )
+ {
+ edump((e.to_detail_string()));
+ }
+ current_con->closed();
+
+ }).wait();
+ });
+
+ _server.set_close_handler( [&]( connection_hdl hdl ){
+ _server_thread.async( [&](){
+ _connections[hdl]->closed();
+ _connections.erase( hdl );
+ }).wait();
+ });
+
+ _server.set_fail_handler( [&]( connection_hdl hdl ){
+ if( _server.is_listening() )
+ {
+ _server_thread.async( [&](){
+ if( _connections.find(hdl) != _connections.end() )
+ {
+ _connections[hdl]->closed();
+ _connections.erase( hdl );
+ }
+ }).wait();
+ }
});
}
+ ~websocket_tls_server_impl()
+ {
+ if( _server.is_listening() )
+ _server.stop_listening();
+ auto cpy_con = _connections;
+ for( auto item : cpy_con )
+ _server.close( item.first, 0, "server exit" );
+ }
+
+ typedef std::map<connection_hdl, websocket_connection_ptr,std::owner_less<connection_hdl> > con_map;
+
+ con_map _connections;
+ fc::thread& _server_thread;
+ websocket_tls_server_type _server;
+ on_connection_handler _on_connection;
+ fc::promise<void>::ptr _closed;
};
+
+
+
+
+
+
+
+
+
+
typedef websocketpp::client<asio_with_stub_log> websocket_client_type;
typedef websocketpp::client<asio_tls_stub_log> websocket_tls_client_type;
@@ -401,7 +417,7 @@ namespace fc { namespace http {
_client.clear_access_channels( websocketpp::log::alevel::all );
_client.set_message_handler( [&]( connection_hdl hdl, message_ptr msg ){
_client_thread.async( [&](){
- wdump((msg->get_payload()));
+ idump((msg->get_payload()));
//std::cerr<<"recv: "<<msg->get_payload()<<"\n";
auto received = msg->get_payload();
fc::async( [=](){
@@ -456,7 +472,7 @@ namespace fc { namespace http {
_client.clear_access_channels( websocketpp::log::alevel::all );
_client.set_message_handler( [&]( connection_hdl hdl, message_ptr msg ){
_client_thread.async( [&](){
- wdump((msg->get_payload()));
+ idump((msg->get_payload()));
_connection->on_message( msg->get_payload() );
}).wait();
});
@@ -465,7 +481,7 @@ namespace fc { namespace http {
{
try {
_client_thread.async( [&](){
- wlog(". ${p}", ("p",uint64_t(_connection.get())));
+ ilog(". ${p}", ("p",uint64_t(_connection.get())));
if( !_shutting_down && !_closed && _connection )
_connection->closed();
_connection.reset();
@@ -509,7 +525,7 @@ namespace fc { namespace http {
{
if(_connection )
{
- wlog(".");
+ ilog(".");
_shutting_down = true;
_connection->close(0, "client closed");
_closed->wait();
@@ -526,79 +542,57 @@ namespace fc { namespace http {
} // namespace detail
- websocket_server::websocket_server(bool enable_permessage_deflate /* = true */) :
- my(
-#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE
- enable_permessage_deflate ?
- (detail::abstract_websocket_server*)new detail::websocket_server_impl<detail::asio_with_stub_log_and_deflate> :
-#endif
- (detail::abstract_websocket_server*)new detail::websocket_server_impl<detail::asio_with_stub_log> )
- {
-#ifndef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE
- if (enable_permessage_deflate)
- elog("Websocket permessage-deflate requested but not enabled during compile");
-#endif
- }
+ websocket_server::websocket_server():my( new detail::websocket_server_impl() ) {}
websocket_server::~websocket_server(){}
void websocket_server::on_connection( const on_connection_handler& handler )
{
- my->on_connection(handler);
+ my->_on_connection = handler;
}
void websocket_server::listen( uint16_t port )
{
- my->listen(port);
+ my->_server.listen(port);
}
void websocket_server::listen( const fc::ip::endpoint& ep )
{
- my->listen(ep);
+ my->_server.listen( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4(uint32_t(ep.get_address())),ep.port()) );
}
void websocket_server::start_accept() {
- my->start_accept();
+ my->_server.start_accept();
}
- websocket_tls_server::websocket_tls_server(const string& server_pem,
- const string& ssl_password,
- bool enable_permessage_deflate /* = true */) :
- my(
-#ifdef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE
- enable_permessage_deflate ?
- (detail::abstract_websocket_server*)new detail::websocket_tls_server_impl<detail::asio_tls_stub_log_and_deflate>(server_pem, ssl_password) :
-#endif
- (detail::abstract_websocket_server*)new detail::websocket_tls_server_impl<detail::asio_tls_stub_log>(server_pem, ssl_password) )
- {
-#ifndef ENABLE_WEBSOCKET_PERMESSAGE_DEFLATE
- if (enable_permessage_deflate)
- elog("Websocket permessage-deflate requested but not enabled during compile");
-#endif
- }
+ websocket_tls_server::websocket_tls_server( const string& server_pem, const string& ssl_password ):my( new detail::websocket_tls_server_impl(server_pem, ssl_password) ) {}
websocket_tls_server::~websocket_tls_server(){}
void websocket_tls_server::on_connection( const on_connection_handler& handler )
{
- my->on_connection(handler);
+ my->_on_connection = handler;
}
void websocket_tls_server::listen( uint16_t port )
{
- my->listen(port);
+ my->_server.listen(port);
}
void websocket_tls_server::listen( const fc::ip::endpoint& ep )
{
- my->listen(ep);
+ my->_server.listen( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4(uint32_t(ep.get_address())),ep.port()) );
}
- void websocket_tls_server::start_accept()
- {
- my->start_accept();
+ void websocket_tls_server::start_accept() {
+ my->_server.start_accept();
}
+ websocket_tls_client::websocket_tls_client():my( new detail::websocket_tls_client_impl() ) {}
+ websocket_tls_client::~websocket_tls_client(){ }
+
+
+
websocket_client::websocket_client():my( new detail::websocket_client_impl() ),smy(new detail::websocket_tls_client_impl()) {}
websocket_client::~websocket_client(){ }
diff --git a/src/network/url.cpp b/src/network/url.cpp
index 635dd2d..d7d6339 100644
--- a/src/network/url.cpp
+++ b/src/network/url.cpp
@@ -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;
diff --git a/tests/compress/compress.cpp b/tests/compress/compress.cpp
index a30255e..a155d54 100644
--- a/tests/compress/compress.cpp
+++ b/tests/compress/compress.cpp
@@ -38,9 +38,9 @@ BOOST_AUTO_TEST_CASE(smaz_test)
BOOST_CHECK_EQUAL( decomp, line );
}
+#ifndef FC_USE_FULL_ZLIB
extern "C" {
-
enum
{
TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
@@ -85,5 +85,6 @@ BOOST_AUTO_TEST_CASE(zlib_test)
std::string decomp = zlib_decompress( compressed );
BOOST_CHECK_EQUAL( decomp, line );
}
+#endif
BOOST_AUTO_TEST_SUITE_END()
diff --git a/vendor/websocketpp b/vendor/websocketpp
index 378437a..c5510d6 160000
--- a/vendor/websocketpp
+++ b/vendor/websocketpp
@@ -1 +1 @@
-Subproject commit 378437aecdcb1dfe62096ffd5d944bf1f640ccc3
+Subproject commit c5510d6de04917812b910a8dd44735c1f17061d9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment