Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Integrated hessian_ipc

  • Loading branch information...
commit ffe3c852136f2a29435d70e5d60085bb00dd48de 1 parent 447a64c
Alexander Stigsen authored
View
1  .gitignore
@@ -54,6 +54,7 @@ external/pcre/
external/tinyxml/
external/wxwidgets/
external/webkit/
+external/boost/
# Ignore licensing files copied over to enable tests
e.cfg
View
29 src/eApp.cpp
@@ -35,6 +35,9 @@
#include "EditorCtrl.h"
#include "eDocumentPath.h"
#include "AppVersion.h"
+#include "IIpcServer.h"
+#include "IConnection.h"
+#include "eIpcThread.h"
#ifdef __WXMSW__
#include <wx/msw/registry.h>
@@ -111,6 +114,7 @@ BEGIN_EVENT_TABLE(eApp, wxApp)
EVT_MENU(ID_UPDATES_AVAILABLE, eApp::OnUpdatesAvailable)
EVT_MENU(ID_UPDATES_CHECKED, eApp::OnUpdatesChecked)
EVT_IDLE(eApp::OnIdle)
+ EVT_COMMAND(wxID_ANY, wxEVT_IPC_CALL, eApp::OnIpcCall)
END_EVENT_TABLE()
bool eApp::OnInit() {
@@ -119,6 +123,7 @@ bool eApp::OnInit() {
m_catalyst = NULL;
m_pSyntaxHandler = NULL;
m_checker = NULL;
+ m_ipcThread = NULL;
#ifdef __WXGTK__
m_server = NULL;
@@ -282,6 +287,9 @@ bool eApp::OnInit() {
CheckForUpdates(m_settings, GetAppVersion());
}
+ // Start the scripting server
+ m_ipcThread = new eIpcThread(*this);
+
return true;
}
@@ -685,6 +693,7 @@ int eApp::OnExit() {
#ifndef __WXMSW__
if (m_server) delete m_server;
#endif
+ if (m_ipcThread) m_ipcThread->stop();
if (m_catalyst) delete m_catalyst;
if (m_pCatalyst) delete m_pCatalyst;
if (m_checker) delete m_checker;
@@ -812,3 +821,23 @@ int eApp::DaysLeftOfTrial() const {return m_pCatalyst->DaysLeftOfTrial();}
int eApp::TotalDays() const {return m_pCatalyst->DaysLeftOfTrial();}
const wxString& eApp::RegisteredUserName() const {return m_pCatalyst->RegisteredUserName();}
const wxString& eApp::RegisteredUserEmail() const {return m_pCatalyst->RegisteredUserEmail();}
+
+void eApp::OnIpcCall(wxCommandEvent& event) {
+ IConnection* conn = (IConnection*)event.GetClientData();
+ if (!conn) return;
+
+ const hessian_ipc::Call* call = conn->get_call();
+ if (!call) return;
+
+ const string& m = call->GetMethod();
+ const wxString method(m.c_str(), wxConvUTF8, m.size());
+
+ wxLogDebug(wxT("IPC: %s"), method);
+
+ // Write the reply
+ hessian_ipc::Writer& writer = conn->get_reply_writer();
+ writer.write_reply(true);
+
+ // Notify connection that it can send the reply (threadsafe)
+ conn->reply_done();
+}
View
3  src/eApp.h
@@ -36,6 +36,7 @@ class TmSyntaxHandler;
class PListHandler;
class EditorFrame;
class AppVersion;
+class eIpcThread;
class eApp : public wxApp,
public IAppPaths,
@@ -110,6 +111,7 @@ class eApp : public wxApp,
void OnUpdatesAvailable(wxCommandEvent& event);
void OnUpdatesChecked(wxCommandEvent& event);
void OnIdle(wxIdleEvent& event);
+ void OnIpcCall(wxCommandEvent& event);
DECLARE_EVENT_TABLE();
// Member variables
@@ -122,6 +124,7 @@ class eApp : public wxApp,
wxString m_appPath;
wxString m_appDataPath;
wxArrayString m_openStack;
+ eIpcThread* m_ipcThread;
#ifndef __WXMSW__
eServer* m_server;
View
26 src/eConnection.cpp
@@ -0,0 +1,26 @@
+#include "eConnection.h"
+#include "IIpcHandler.h"
+
+eConnection::eConnection(boost::asio::io_service& io_service, hessian_ipc::connection_manager& manager, IIpcHandler& handler)
+: hessian_ipc::connection(io_service, manager), m_handler(handler)
+{
+}
+
+eConnection::~eConnection() {
+}
+
+void eConnection::invoke_method() {
+ m_handler.handle_call(*this);
+}
+
+const hessian_ipc::Call* eConnection::get_call() {
+ return request_;
+}
+
+hessian_ipc::Writer& eConnection::get_reply_writer() {
+ return writer_;
+}
+
+void eConnection::reply_done() {
+ connection::reply_done();
+}
View
42 src/eConnection.h
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ *
+ * Copyright (C) 2010, Alexander Stigsen, e-texteditor.com
+ *
+ * This software is licensed under the Open Company License as described
+ * in the file license.txt, which you should have received as part of this
+ * distribution. The terms are also available at http://opencompany.org/license.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ******************************************************************************/
+
+#ifndef __ECONNECTION_H__
+#define __ECONNECTION_H__
+
+#include "hessian_ipc/connection.h"
+#include "IConnection.h"
+
+// Pre-definitions
+class IIpcHandler;
+
+class eConnection : public hessian_ipc::connection, public IConnection {
+public:
+ explicit eConnection(boost::asio::io_service& io_service, hessian_ipc::connection_manager& manager, IIpcHandler& handler);
+ virtual ~eConnection();
+
+ // Method handling
+ void invoke_method();
+
+ // Interface methods
+ virtual const hessian_ipc::Call* get_call(); // The request recieved (may be NULL)
+ virtual hessian_ipc::Writer& get_reply_writer();
+ virtual void reply_done(); // notify connection that it can send the reply (threadsafe)
+
+private:
+ // Member variables
+ IIpcHandler& m_handler;
+};
+
+
+#endif //__ECONNECTION_H__
View
32 src/eIpcThread.cpp
@@ -0,0 +1,32 @@
+#include "eIpcThread.h"
+#include "eApp.h"
+#include "IConnection.h"
+#include "IIpcServer.h"
+
+DEFINE_EVENT_TYPE(wxEVT_IPC_CALL)
+
+eIpcThread::eIpcThread(eApp& app) : m_ipcServer(NULL), m_app(app) {
+ Create();
+ Run();
+}
+
+void* eIpcThread::Entry() {
+ m_ipcServer = NewIpcServer(*this);
+ m_ipcServer->run();
+
+ delete m_ipcServer;
+ return NULL;
+}
+
+void eIpcThread::stop() {
+ // Threadsafe stop of server
+ if (m_ipcServer) m_ipcServer->stop();
+}
+
+void eIpcThread::handle_call(IConnection& conn) {
+ wxCommandEvent event(wxEVT_IPC_CALL, wxID_ANY);
+ event.SetClientData(&conn);
+
+ // Notify app that there is a new call (threadsafe)
+ m_app.AddPendingEvent(event);
+}
View
45 src/eIpcThread.h
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ *
+ * Copyright (C) 2010, Alexander Stigsen, e-texteditor.com
+ *
+ * This software is licensed under the Open Company License as described
+ * in the file license.txt, which you should have received as part of this
+ * distribution. The terms are also available at http://opencompany.org/license.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ******************************************************************************/
+
+#ifndef __EIPCTHREAD_H__
+#define __EIPCTHREAD_H__
+
+#include "wx/wxprec.h"
+#ifndef WX_PRECOMP
+ #include <wx/wx.h>
+#endif
+
+#include "IIpcHandler.h"
+
+// Pre-definitions
+class eApp;
+class IConnections;
+class IIpcServer;
+
+DECLARE_EVENT_TYPE(wxEVT_IPC_CALL, -1)
+
+class eIpcThread : public wxThread, public IIpcHandler {
+public:
+ eIpcThread(eApp& app);
+ virtual void* Entry();
+
+ void stop(); // Threadsafe stop of server
+
+ void handle_call(IConnection& conn);
+
+private:
+ IIpcServer* m_ipcServer;
+ eApp& m_app;
+};
+
+#endif //__EIPCTHREAD_H__
View
23 src/eServer.cpp
@@ -0,0 +1,23 @@
+#include "eServer.h"
+#include "eConnection.h"
+
+eServer::eServer(IIpcHandler& handler)
+: hessian_ipc::server("localhost", "9000"), m_handler(handler)
+{
+}
+
+void eServer::run() {
+ server::run();
+}
+
+void eServer::stop() {
+ server::stop();
+}
+
+hessian_ipc::connection* eServer::new_connection() {
+ return new eConnection(io_service_, connection_manager_, m_handler);
+}
+
+IIpcServer* NewIpcServer(IIpcHandler& handler) {
+ return new eServer(handler);
+}
View
24 src/eServer.h
@@ -0,0 +1,24 @@
+#ifndef __ESERVER_H__
+#define __ESERVER_H__
+
+#include "IIpcServer.h"
+#include "hessian_ipc/server.h"
+
+// Pre-definitions
+class IIpcHandler;
+
+class eServer : public hessian_ipc::server, public IIpcServer {
+public:
+ explicit eServer(IIpcHandler& handler);
+ virtual ~eServer() {};
+
+ virtual void run();
+ virtual void stop();
+
+ hessian_ipc::connection* new_connection();
+
+private:
+ IIpcHandler& m_handler;
+};
+
+#endif // __ESERVER_H__
View
146 src/hessian_ipc/connection.cpp
@@ -0,0 +1,146 @@
+#include <boost/bind.hpp>
+#include "connection.h"
+#include "connection_manager.h"
+
+
+namespace hessian_ipc {
+
+connection::connection(boost::asio::io_service& io_service, connection_manager& manager)
+ : io_service_(io_service), socket_(io_service), connection_manager_(manager), request_(NULL)
+{
+}
+
+connection::~connection() {
+}
+
+boost::asio::ip::tcp::socket& connection::socket() {
+ return socket_;
+}
+
+void connection::start() {
+ buffer_.resize(8192); // initial buffer size
+
+ // Read first request
+ socket_.async_read_some(boost::asio::buffer(buffer_),
+ boost::bind(&connection::handle_read, shared_from_this(),
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+}
+
+void connection::stop() {
+ socket_.close();
+}
+
+void connection::invoke_method() {
+ throw hessian_ipc::value_exception("Unknown method");
+}
+
+void connection::handle_read(const boost::system::error_code& e, size_t bytes_transferred) {
+ if (!e) {
+ try {
+ vector<unsigned char>::const_iterator begin = buffer_.begin();
+ vector<unsigned char>::const_iterator end = buffer_.begin() + bytes_transferred;
+
+ // Parse the request
+ if (reader_.Parse(begin, end)) {
+ // Invoke handler
+ request_ = reader_.GetResultCall();
+ invoke_method();
+ }
+
+ socket_.async_read_some(boost::asio::buffer(buffer_),
+ boost::bind(&connection::handle_read, shared_from_this(),
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+ }
+ catch (hessian_ipc::value_exception& e) {
+ protocol_error(e.what()); // Send fault
+ }
+ catch (exception& e) {
+ method_error(e.what()); // Send fault
+ }
+ }
+ else if (e != boost::asio::error::operation_aborted) {
+ connection_manager_.stop(shared_from_this());
+ }
+}
+
+void connection::reply_done() {
+ // notify connection that it can send the reply (threadsafe)
+ io_service_.post(boost::bind(&connection::send_reply, this));
+}
+
+void connection::send_reply() {
+ // Prepare for reading next request
+ reader_.Reset();
+ request_ = NULL;
+
+ // Send the reply
+ const vector<unsigned char>& reply = writer_.GetOutput();
+ boost::asio::async_write(socket_, boost::asio::buffer(reply),
+ boost::bind(&connection::handle_write, shared_from_this(),
+ boost::asio::placeholders::error));
+}
+
+void connection::protocol_error(const string& msg) {
+ writer_.write_fault(hessian_ipc::ProtocolException, msg);
+ const vector<unsigned char>& reply = writer_.GetOutput();
+
+ // Send reply and close
+ boost::asio::async_write(socket_, boost::asio::buffer(reply),
+ boost::bind(&connection::handle_write_and_close, shared_from_this(),
+ boost::asio::placeholders::error));
+}
+
+void connection::method_error(const string& msg) {
+ writer_.write_fault(hessian_ipc::NoSuchMethodException, msg);
+ const vector<unsigned char>& reply = writer_.GetOutput();
+
+ // Send reply
+ boost::asio::async_write(socket_, boost::asio::buffer(reply),
+ boost::bind(&connection::handle_write, shared_from_this(),
+ boost::asio::placeholders::error));
+}
+
+void connection::service_error(const string& msg) {
+ writer_.write_fault(hessian_ipc::ServiceException, msg);
+ const vector<unsigned char>& reply = writer_.GetOutput();
+
+ // Send reply
+ boost::asio::async_write(socket_, boost::asio::buffer(reply),
+ boost::bind(&connection::handle_write, shared_from_this(),
+ boost::asio::placeholders::error));
+}
+
+void connection::handle_write(const boost::system::error_code& e) {
+ if (!e) {
+ // Read next request
+ socket_.async_read_some(boost::asio::buffer(buffer_),
+ boost::bind(&connection::handle_read, shared_from_this(),
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+ }
+ else if (e != boost::asio::error::operation_aborted) {
+ connection_manager_.stop(shared_from_this());
+ }
+}
+
+void connection::handle_write_and_close(const boost::system::error_code& e) {
+ if (!e) {
+ // Initiate graceful connection closure.
+ boost::system::error_code ignored_ec;
+ socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
+ }
+
+ if (e != boost::asio::error::operation_aborted) {
+ connection_manager_.stop(shared_from_this());
+ }
+}
+
+int connection::get_parameter_int(size_t pos) {
+ if (pos >= request_->GetParameterCount()) throw hessian_ipc::value_exception("Wrong number of arguments.");
+ const hessian_ipc::Value& arg = request_->GetParameter(pos);
+ return arg.GetInt();
+}
+
+} // namespace hessian_ipc
View
72 src/hessian_ipc/connection.h
@@ -0,0 +1,72 @@
+#ifndef HESSIAN_IPC_CONNECTION_H
+#define HESSIAN_IPC_CONNECTION_H
+
+#include <boost/asio.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <vector>
+#include "hessian_reader.h"
+
+namespace hessian_ipc {
+
+class connection_manager;
+
+/// Represents a single connection from a client.
+class connection
+ : public boost::enable_shared_from_this<connection>,
+ private boost::noncopyable
+{
+public:
+ // Construct a connection with the given io_service.
+ explicit connection(boost::asio::io_service& io_service, connection_manager& manager);
+ virtual ~connection();
+
+ // Get the socket associated with the connection.
+ boost::asio::ip::tcp::socket& socket();
+
+ void start(); // Start the first asynchronous operation for the connection.
+ void stop(); // Stop all asynchronous operations associated with the connection.
+
+ void reply_done();
+
+protected:
+ // Method handling
+ virtual void invoke_method();
+
+ // Functions used by method handlers
+ int get_parameter_int(size_t pos);
+ template<class T> void reply(const T& reply) {
+ writer_.write_reply(reply);
+ }
+
+ // Message handling
+ const hessian_ipc::Call* request_; // The request recieved (may be NULL)
+ hessian_ipc::Reader reader_;
+ hessian_ipc::Writer writer_;
+
+private:
+ // Handle completion of read operations
+ void handle_read(const boost::system::error_code& e, size_t bytes_transferred);
+ void send_reply();
+
+ // Handle completion of write operations
+ void handle_write(const boost::system::error_code& e);
+ void handle_write_and_close(const boost::system::error_code& e);
+
+ // Handle errors
+ void protocol_error(const string& msg);
+ void method_error(const string& msg);
+ void service_error(const string& msg);
+
+ // Member variables
+ boost::asio::io_service& io_service_;
+ boost::asio::ip::tcp::socket socket_; // Socket for the connection.
+ connection_manager& connection_manager_; // The manager for this connection.
+ std::vector<unsigned char> buffer_; // Buffer for incoming data.
+};
+
+typedef boost::shared_ptr<connection> connection_ptr;
+
+} // namespace hessian_ipc
+
+#endif // HESSIAN_IPC_CONNECTION_H
View
22 src/hessian_ipc/connection_manager.cpp
@@ -0,0 +1,22 @@
+#include "connection_manager.h"
+#include <algorithm>
+#include <boost/bind.hpp>
+
+namespace hessian_ipc {
+
+void connection_manager::start(connection_ptr c) {
+ connections_.insert(c);
+ c->start();
+}
+
+void connection_manager::stop(connection_ptr c) {
+ connections_.erase(c);
+ c->stop();
+}
+
+void connection_manager::stop_all() {
+ std::for_each(connections_.begin(), connections_.end(), boost::bind(&connection::stop, _1));
+ connections_.clear();
+}
+
+} // namespace hessian_ipc
View
27 src/hessian_ipc/connection_manager.h
@@ -0,0 +1,27 @@
+#ifndef HESSIAN_IPC_CONNECTION_MANAGER_H
+#define HESSIAN_IPC_CONNECTION_MANAGER_H
+
+#include <set>
+#include <boost/noncopyable.hpp>
+#include "connection.h"
+
+namespace hessian_ipc {
+
+/// Manages open connections so that they may be cleanly stopped when the server
+/// needs to shut down.
+class connection_manager
+ : private boost::noncopyable
+{
+public:
+ void start(connection_ptr c); // Add the specified connection to the manager and start it.
+ void stop(connection_ptr c); // Stop the specified connection.
+ void stop_all(); // Stop all connections.
+
+private:
+ // Member variables
+ std::set<connection_ptr> connections_; // The managed connections.
+};
+
+} // namespace hessian_ipc
+
+#endif // HESSIAN_IPC_CONNECTION_MANAGER_HPP
View
421 src/hessian_ipc/hessian_reader.cpp
@@ -0,0 +1,421 @@
+#include "hessian_reader.h"
+
+namespace hessian_ipc {
+
+Reader::Reader() : m_state(start), m_len_left(0)
+{
+}
+
+void Reader::Reset() {
+ m_state = start;
+ m_result.reset();
+ m_len_left = 0;
+ m_stateStack.clear();
+}
+
+bool Reader::Parse(vector<unsigned char>::const_iterator begin, vector<unsigned char>::const_iterator end) {
+ if (begin == end) return false; // need more input
+ m_pos = begin;
+ m_end = end;
+
+ while (m_pos != m_end) {
+ if (m_state >= value_start && m_state < value_last) {
+ const bool result = ParseValue();
+ if (!result) return false; // need more input
+
+ if (m_result->IsList()) {
+ m_stateStack.push_back(new savedstate(call_method, 0, m_result));
+ m_state = value_start;
+ continue;
+ }
+
+ if (m_stateStack.empty()) return true;
+ savedstate& s = m_stateStack.back();
+
+ switch (s.state) {
+ case call_method:
+ s.value->AsCall().SetMethod(m_result);
+ s.state = call_len;
+ m_state = value_start;
+ continue; //ParseValue();
+ case call_len:
+ s.len_left = m_result->GetInt();
+ s.state = call_args;
+ m_state = value_start;
+ continue; //ParseValue();
+ case call_args:
+ s.value->AsCall().AddParameter(m_result);
+ m_state = value_start;
+ if (--s.len_left) break; //ParseValue();
+ else {
+ m_result = s.value;
+ return true; // call parsed
+ }
+ default:
+ throw value_exception("invalid state");
+ }
+ }
+
+ switch (m_state) {
+ case start:
+ {
+ const char c = *m_pos;
+ ++m_pos;
+
+ switch (c) {
+ case 'H':
+ //m_state = header1;
+ break;
+ case 'C':
+ m_stateStack.push_back(new savedstate(call_method, 0, auto_ptr<Value>(new Call())));
+ m_state = value_start;
+ break; //ParseValue();
+ case 'c':
+ m_state = call10_v1;
+ case 'R':
+ m_state = value_start;
+ break; //ParseValue();
+ case 'F':
+ break;
+ default:
+ throw value_exception("invalid message tag");
+ }
+ }
+ break;
+ /*case call10_v1:
+ m_state = call10_v2;
+ break;
+ case call10_m:
+ if (c != 'm') throw value_exception("expected m");
+ m_state = call10_len1;
+ break;
+ case call10_len1;
+ m_len_left = ((int)c) << 8;
+ m_state = call10_len2;
+ break;*/
+ default:
+ throw value_exception("invalid state");
+ }
+ }
+
+ return false; // need more input
+}
+
+bool Reader::ParseValue() {
+ while (m_pos != m_end) {
+ const unsigned char c = *m_pos;
+ ++m_pos;
+
+ switch (m_state) {
+ case value_start:
+ switch (c) {
+ // Null
+ case 'N':
+ m_result.reset(new Null());
+ return true;
+
+ // Boolean
+ case 'T':
+ case 'F':
+ m_result.reset(new Boolean(c == 'T'));
+ return true;
+
+ // Integer (packed in one octet)
+ case 0x80: case 0x81: case 0x82: case 0x83:
+ case 0x84: case 0x85: case 0x86: case 0x87:
+ case 0x88: case 0x89: case 0x8a: case 0x8b:
+ case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+ case 0x90: case 0x91: case 0x92: case 0x93:
+ case 0x94: case 0x95: case 0x96: case 0x97:
+ case 0x98: case 0x99: case 0x9a: case 0x9b:
+ case 0x9c: case 0x9d: case 0x9e: case 0x9f:
+ case 0xa0: case 0xa1: case 0xa2: case 0xa3:
+ case 0xa4: case 0xa5: case 0xa6: case 0xa7:
+ case 0xa8: case 0xa9: case 0xaa: case 0xab:
+ case 0xac: case 0xad: case 0xae: case 0xaf:
+ case 0xb0: case 0xb1: case 0xb2: case 0xb3:
+ case 0xb4: case 0xb5: case 0xb6: case 0xb7:
+ case 0xb8: case 0xb9: case 0xba: case 0xbb:
+ case 0xbc: case 0xbd: case 0xbe: case 0xbf:
+ m_result.reset(new Integer(c - 0x90));
+ return true;
+
+ // Integer (packed in two octets)
+ case 0xc0: case 0xc1: case 0xc2: case 0xc3:
+ case 0xc4: case 0xc5: case 0xc6: case 0xc7:
+ case 0xc8: case 0xc9: case 0xca: case 0xcb:
+ case 0xcc: case 0xcd: case 0xce: case 0xcf:
+ m_result.reset(new Integer((c - 0xc8) << 8));
+ m_state = integer_4;
+ break;
+
+ // Integer (packed in three octets)
+ case 0xd0: case 0xd1: case 0xd2: case 0xd3:
+ case 0xd4: case 0xd5: case 0xd6: case 0xd7:
+ m_result.reset(new Integer((c - 0xd4) << 16));
+ m_state = integer_3;
+ break;
+
+ // Integer (tag + 4 octets)
+ case 'I':
+ m_result.reset(new Integer());
+ m_state = integer_1;
+ break;
+
+ // 64bit Long (packed in one octet)
+ case 0xd8: case 0xd9: case 0xda: case 0xdb:
+ case 0xdc: case 0xdd: case 0xde: case 0xdf:
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3:
+ case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb:
+ case 0xec: case 0xed: case 0xee: case 0xef:
+ m_result.reset(new Long(c - 0xe0));
+ return true;
+
+ // 64bit Long (packed in two octets)
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3:
+ case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb:
+ case 0xfc: case 0xfd: case 0xfe: case 0xff:
+ m_result.reset(new Long((c - 0xf8) << 8));
+ m_state = long_8;
+ break;
+
+ // 64bit Long (packed in three octets)
+ case 0x38: case 0x39: case 0x3a: case 0x3b:
+ case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ m_result.reset(new Long((c - 0x3c) << 16));
+ m_state = long_7;
+ break;
+
+ // 64bit Long (tag + 4 octets)
+ case 0x59:
+ m_result.reset(new Long());
+ m_state = long_5;
+ break;
+
+ // 64bit Long (tag + 8 octets)
+ case 'L':
+ m_result.reset(new Long());
+ m_state = long_1;
+ break;
+
+ // 64-bit millisecond UTC date
+ case 0x4a:
+ m_result.reset(new Date());
+ m_state = long_1;
+ break;
+
+ // 64-bit minute UTC date
+ case 0x4b:
+ m_result.reset(new Date());
+ m_state = date_1;
+ break;
+
+ // Empty string
+ case '\0':
+ m_result.reset(new String());
+ return true;
+
+ // String (length packed in one octet)
+ case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ case 0x10: case 0x11: case 0x12: case 0x13:
+ case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1a: case 0x1b:
+ case 0x1c: case 0x1d: case 0x1e: case 0x1f:
+ {
+ if (m_result.get() && m_result->IsStringChunk()) {
+ m_result->AsString().SetChunk(false);
+ }
+ else m_result.reset(new String());
+
+ m_state = string_data;
+ m_len_left = c;
+
+ String& s = m_result->AsString();
+ s.reserve(s.size() + m_len_left); // we need at least this
+ }
+ break;
+
+ // String (length packed in two octets)
+ case 0x30: case 0x31: case 0x32: case 0x33:
+ if (m_result.get() && m_result->IsStringChunk()) {
+ m_result->AsString().SetChunk(false); // final
+ }
+ else m_result.reset(new String());
+
+ m_state = string_len2;
+ m_len_left = ((int)c - 0x30);
+ break;
+
+ case 'S': // String (tag + len in 2 octets)
+ if (m_result.get() && m_result->IsStringChunk()) {
+ m_result->AsString().SetChunk(false); // final
+ }
+ else m_result.reset(new String());
+ m_state = string_len1;
+ break;
+
+ case 'R': // String chunk (tag + len in 2 octets)
+ if (m_result.get() == NULL || !m_result->IsStringChunk()) {
+ m_result.reset(new String());
+ m_result->AsString().SetChunk();
+ }
+ m_state = string_len1;
+ break;
+
+
+ // Variable-length list
+ case 0x55:
+ break;
+
+ // Variable-length untyped list
+ case 0x57:
+ break;
+
+ // Fixed-length list
+ case 'V':
+ break;
+
+ // Fixed-length untyped list
+ case 0x58:
+ break;
+
+ // Compact fixed list
+ case 0x70: case 0x71: case 0x72: case 0x73:
+ case 0x74: case 0x75: case 0x76: case 0x77:
+ break;
+
+ // Compact fixed untyped list
+ case 0x78: case 0x79: case 0x7a: case 0x7b:
+ case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+ break;
+
+
+
+
+ default:
+ throw value_exception("invalid tag");
+ }
+ break;
+
+ case integer_1:
+ m_result->AsInteger() += ((int)c << 24);
+ m_state = integer_2;
+ break;
+ case integer_2:
+ m_result->AsInteger() += ((int)c << 16);
+ m_state = integer_3;
+ break;
+ case integer_3:
+ m_result->AsInteger() += ((int)c << 8);
+ m_state = integer_4;
+ break;
+ case integer_4:
+ m_result->AsInteger() += c;
+ return true;
+
+ case long_1:
+ m_result->AsLong() += ((long long)c << 56);
+ m_state = long_2;
+ break;
+ case long_2:
+ m_result->AsLong() += ((long long)c << 48);
+ m_state = long_3;
+ break;
+ case long_3:
+ m_result->AsLong() += ((long long)c << 40);
+ m_state = long_4;
+ break;
+ case long_4:
+ m_result->AsLong() += ((long long)c << 32);
+ m_state = long_5;
+ break;
+ case long_5:
+ m_result->AsLong() += ((long long)c << 24);
+ m_state = long_6;
+ break;
+ case long_6:
+ m_result->AsLong() += ((long long)c << 16);
+ m_state = long_7;
+ break;
+ case long_7:
+ m_result->AsLong() += ((long long)c << 8);
+ m_state = long_8;
+ break;
+ case long_8:
+ m_result->AsLong() += c;
+ return true;
+
+ case date_1:
+ m_result->AsLong() += ((long long)c << 24);
+ m_state = long_6;
+ break;
+ case date_2:
+ m_result->AsLong() += ((long long)c << 16);
+ m_state = long_7;
+ break;
+ case date_3:
+ m_result->AsLong() += ((long long)c << 8);
+ m_state = long_8;
+ break;
+ case date_4:
+ m_result->AsLong() += c;
+ m_result->AsDate().AdjustToMinutes();
+ return true;
+
+ case string_len1:
+ m_len_left += ((int)c << 8);
+ m_state = string_len2;
+ break;
+ case string_len2:
+ {
+ m_len_left += c;
+ String& s = m_result->AsString();
+ s.reserve(s.size() + m_len_left); // we need at least this
+ m_state = string_data;
+ }
+ break;
+ case string_data:
+ m_result->AsString() += c;
+ if (--m_len_left) break;
+ else if (m_result->IsStringChunk()) {
+ m_state = string_next;
+ break;
+ }
+ else return true;
+
+ case string_next:
+ switch (c) {
+ // String (length packed in one octet)
+ case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ case 0x10: case 0x11: case 0x12: case 0x13:
+ case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1a: case 0x1b:
+ case 0x1c: case 0x1d: case 0x1e: case 0x1f:
+ // String (length packed in two octets)
+ case 0x30: case 0x31: case 0x32: case 0x33:
+ case 'S': // String (tag + len in 2 octets)
+ case 'R': // String chunk (tag + len in 2 octets)
+ m_state = value_start;
+ --m_pos;
+ break;
+ default:
+ throw value_exception("chunked string not completed");
+ }
+ break;
+ default:
+ throw value_exception("invalid state");
+ }
+ }
+
+ return false; // need more input
+}
+
+} // namespace hessian
View
86 src/hessian_ipc/hessian_reader.h
@@ -0,0 +1,86 @@
+#ifndef HESSIAN_READER_H
+#define HESSIAN_READER_H
+
+#include "hessian_values.h"
+#include <memory>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+using namespace std;
+
+namespace hessian_ipc {
+
+ class Reader {
+ public:
+ Reader();
+
+ void Reset();
+ bool Parse(vector<unsigned char>::const_iterator begin, vector<unsigned char>::const_iterator end);
+
+ const Value* GetResultValue() {return m_result.get();};
+ const Call* GetResultCall() {return &m_result->AsCall();};
+ vector<unsigned char>::const_iterator GetEndPos() {return m_pos;};
+
+ private:
+ bool ParseValue();
+
+ enum State {
+ start,
+ call_method,
+ call_len,
+ call_args,
+ string_chunk,
+
+ call10_v1,
+ call10_v2,
+ call10_m,
+ call10_len1,
+ call10_len2,
+ call10_method,
+ call10_args,
+
+ // value states
+ value_start,
+ integer_1,
+ integer_2,
+ integer_3,
+ integer_4,
+ long_1,
+ long_2,
+ long_3,
+ long_4,
+ long_5,
+ long_6,
+ long_7,
+ long_8,
+ date_1,
+ date_2,
+ date_3,
+ date_4,
+ string_len1,
+ string_len2,
+ string_data,
+ string_next,
+ value_last
+ };
+
+ class savedstate {
+ public:
+ savedstate(State s, size_t ll, auto_ptr<Value> v)
+ : state(s), len_left(ll), value(v) {};
+ State state;
+ size_t len_left;
+ auto_ptr<Value> value;
+ };
+
+ // Member variables
+ vector<unsigned char>::const_iterator m_pos;
+ vector<unsigned char>::const_iterator m_end;
+ State m_state;
+ size_t m_len_left;
+ auto_ptr<Value> m_result;
+ boost::ptr_vector<savedstate> m_stateStack;
+ };
+
+} // namespace hessian
+
+#endif //HESSIAN_READER_H
View
409 src/hessian_ipc/hessian_values.cpp
@@ -0,0 +1,409 @@
+#include "hessian_values.h"
+
+namespace hessian_ipc {
+
+// calculate numbers of characters in string
+size_t utf8_len(const std::string& str) {
+ const size_t src_len = str.length();
+ size_t len = 0;
+ size_t pos = 0;
+
+ while (pos < src_len) {
+ const unsigned char c = str[pos];
+ if ((c & 0x80) == 0x00) ++pos;
+ else if ((c & 0xE0) == 0xC0) pos += 2;
+ else if ((c & 0xF0) == 0xE0) pos += 3;
+ else if ((c & 0xF8) == 0xF0) pos += 4;
+ else throw value_exception("invalid utf8 in string");
+ ++len;
+ }
+ if (pos != src_len) throw value_exception("invalid utf8 in string");
+
+ return len;
+}
+
+// find byte position of specific character in string
+size_t utf8_pos(const std::string& str, size_t pos, size_t offset=0) {
+ const size_t src_len = str.length() - offset;
+ size_t len = 0;
+ size_t p = offset;
+
+ while (len < pos && p < src_len) {
+ const unsigned char c = str[p];
+ if ((c & 0x80) == 0x00) ++p;
+ else if ((c & 0xE0) == 0xC0) p += 2;
+ else if ((c & 0xF0) == 0xE0) p += 3;
+ else if ((c & 0xF8) == 0xF0) p += 4;
+ else throw value_exception("invalid utf8 in string");
+ ++len;
+ }
+ if (len != pos) throw value_exception("invalid utf8 in string");
+
+ return p - offset;
+}
+
+void Writer::Reset() {
+ out.clear();
+ objectMap.clear();
+}
+
+void Writer::write_null() {
+ out.push_back('N');
+}
+
+// write boolean
+void Writer::write(bool value) {
+ out.push_back(value ? 'T' : 'F');
+}
+
+// write integer
+void Writer::write(int value) {
+ if (-0x10 <= value && value <= 0x2f) {
+ // pack in single octet
+ const unsigned char b8 = 0x90 + (unsigned char)value;
+ out.push_back(b8);
+ }
+ else if (-0x800 <= value && value <= 0x7ff) {
+ // pack in two octets
+ const unsigned char b16 = 0xc8 + ((value >> 8) & 0x000000FF);
+ const unsigned char b8 = value & 0x000000FF;
+
+ out.push_back(b16);
+ out.push_back(b8);
+ }
+ else if (-0x40000 <= value && value <= 0x3ffff) {
+ // pack in three octets
+ const unsigned char b24 = 0xd4 + ((value >> 16) & 0x000000FF);
+ const unsigned char b16 = (value >> 8) & 0x000000FF;
+ const unsigned char b8 = value & 0x000000FF;
+
+ out.push_back(b24);
+ out.push_back(b16);
+ out.push_back(b8);
+ }
+ else {
+ const unsigned char b32 = value >> 24;
+ const unsigned char b24 = (value >> 16) & 0x000000FF;
+ const unsigned char b16 = (value >> 8) & 0x000000FF;
+ const unsigned char b8 = value & 0x000000FF;
+
+ out.push_back('I');
+ out.push_back(b32);
+ out.push_back(b24);
+ out.push_back(b16);
+ out.push_back(b8);
+ }
+}
+
+// write unsigned integer
+void Writer::write(unsigned int value) {
+ write((int)value);
+}
+
+// write 64bit integer
+void Writer::write(long long value) {
+ if (-0x08 <= value && value <= 0x0f) {
+ // pack in single octet
+ const unsigned char b8 = 0xe0 + (unsigned char)value;
+ out.push_back(b8);
+ }
+ else if (-0x800 <= value && value <= 0x7ff) {
+ // pack in two octets
+ const unsigned char b16 = 0xf8 + ((value >> 8) & 0xFF);
+ const unsigned char b8 = value & 0xFF;
+
+ out.push_back(b16);
+ out.push_back(b8);
+ }
+ else if (-0x40000 <= value && value <= 0x3ffff) {
+ // pack in three octets
+ const unsigned char b24 = 0x3c + ((value >> 16) & 0xFF);
+ const unsigned char b16 = (value >> 8) & 0xFF;
+ const unsigned char b8 = value & 0xFF;
+
+ out.push_back(b24);
+ out.push_back(b16);
+ out.push_back(b8);
+ }
+ else if (-0x80000000LL <= value && value <= 0x7fffffffLL) {
+ // tag + four octets
+ const unsigned char b32 = (value >> 24) & 0xFF;
+ const unsigned char b24 = (value >> 16) & 0xFF;
+ const unsigned char b16 = (value >> 8) & 0xFF;
+ const unsigned char b8 = value & 0xFF;
+
+ out.push_back(0x59); // tag
+ out.push_back(b32);
+ out.push_back(b24);
+ out.push_back(b16);
+ out.push_back(b8);
+ }
+ else {
+ // tag + eight octets
+ const unsigned char b64 = (value >> 56) & 0xFF;
+ const unsigned char b56 = (value >> 48) & 0xFF;
+ const unsigned char b48 = (value >> 40) & 0xFF;
+ const unsigned char b40 = (value >> 32) & 0xFF;
+ const unsigned char b32 = (value >> 24) & 0xFF;
+ const unsigned char b24 = (value >> 16) & 0xFF;
+ const unsigned char b16 = (value >> 8) & 0xFF;
+ const unsigned char b8 = value & 0xFF;
+
+ out.push_back('L'); // tag
+ out.push_back(b64);
+ out.push_back(b56);
+ out.push_back(b48);
+ out.push_back(b40);
+ out.push_back(b32);
+ out.push_back(b24);
+ out.push_back(b16);
+ out.push_back(b8);
+ }
+}
+
+// write date
+void Writer::write_date(time_t value) {
+ // Check if we can encode it as 32-bit minute UTC date
+ if (value % 60000LL == 0) {
+ const time_t minutes = value / 60000LL;
+
+ if ((minutes >> 31) == 0 || (minutes >> 31) == -1) {
+ // tag + four octets
+ const unsigned char b32 = (minutes >> 24) & 0xFF;;
+ const unsigned char b24 = (minutes >> 16) & 0xFF;
+ const unsigned char b16 = (minutes >> 8) & 0xFF;
+ const unsigned char b8 = minutes & 0xFF;
+
+ out.push_back(0x4b); // tag
+ out.push_back(b32);
+ out.push_back(b24);
+ out.push_back(b16);
+ out.push_back(b8);
+ return;
+ }
+ }
+
+ // Encode as 64-bit millisecond UTC date
+ // tag + eight octets
+ const unsigned char b64 = (value >> 56) & 0xFF;
+ const unsigned char b56 = (value >> 48) & 0xFF;
+ const unsigned char b48 = (value >> 40) & 0xFF;
+ const unsigned char b40 = (value >> 32) & 0xFF;
+ const unsigned char b32 = (value >> 24) & 0xFF;
+ const unsigned char b24 = (value >> 16) & 0xFF;
+ const unsigned char b16 = (value >> 8) & 0xFF;
+ const unsigned char b8 = value & 0xFF;
+
+ out.push_back(0x4a); // tag
+ out.push_back(b64);
+ out.push_back(b56);
+ out.push_back(b48);
+ out.push_back(b40);
+ out.push_back(b32);
+ out.push_back(b24);
+ out.push_back(b16);
+ out.push_back(b8);
+}
+
+// write string
+void Writer::write(const string& value) {
+ size_t len = utf8_len(value);
+ const char* str = value.c_str();
+
+ if (len == 0) out.push_back(0x00);
+ else {
+ size_t offset = 0;
+
+ // split in chunks
+ while (len > 0x8000) {
+ size_t sublen = 0x8000;
+ size_t bytelen = utf8_pos(value, 0x8000, offset);
+
+ // chunk can't end in high surrogate
+ const unsigned char tail_16 = value[offset + bytelen - 2];
+ const unsigned char tail_8 = value[offset + bytelen - 1];
+ const int tail = (tail_16 << 8) + tail_8;
+ if (0xd800 <= tail && tail <= 0xdbff) {
+ sublen -= 1;
+ bytelen -= 2;
+ }
+
+ const unsigned char b16 = ((sublen >> 8) & 0x000000FF);
+ const unsigned char b8 = sublen & 0x000000FF;
+ out.push_back('R');
+ out.push_back(b16);
+ out.push_back(b8);
+ out.insert(out.end(), str + offset, str + offset + bytelen);
+
+ len -= sublen;
+ offset += bytelen;
+ }
+
+ if (len <= 0x1f) {
+ out.push_back((unsigned char)len); // single octet length
+ out.insert(out.end(), str + offset, str + offset + len);
+ }
+ else if (len <= 0x3ff) {
+ // pack in two octets
+ const unsigned char b16 = 0x30 + ((len >> 8) & 0x000000FF);
+ const unsigned char b8 = len & 0x000000FF;
+ out.push_back(b16);
+ out.push_back(b8);
+ out.insert(out.end(), str + offset, str + offset + len);
+ }
+ else {
+ // tag + double octets
+ const unsigned char b16 = (len >> 8) & 0x000000FF;
+ const unsigned char b8 = len & 0x000000FF;
+ out.push_back('S');
+ out.push_back(b16);
+ out.push_back(b8);
+ out.insert(out.end(), str + offset, str + offset + len);
+ }
+ }
+}
+
+// write binary
+void Writer::write_binary(const unsigned char* value, size_t len) {
+ size_t offset = 0;
+
+ // split in chunks
+ while (len > 0x8000) {
+ out.push_back('A');
+ out.push_back(0x80);
+ out.push_back(0x00);
+ out.insert(out.end(), value + offset, value + offset + 0x8000);
+
+ len -= 0x8000;
+ offset += 0x8000;
+ }
+
+ if (len <= 0x0f) {
+ out.push_back(0x20 + (unsigned char)len); // single octet length
+ }
+ else if (len <= 0x3ff) {
+ // pack in two octets
+ const unsigned char b16 = 0x34 + ((len >> 8) & 0xFF);
+ const unsigned char b8 = len & 0xFF;
+ out.push_back(b16);
+ out.push_back(b8);
+ }
+ else {
+ // tag + double octets
+ const unsigned char b16 = (len >> 8) & 0xFF;
+ const unsigned char b8 = len & 0xFF;
+ out.push_back('B');
+ out.push_back(b16);
+ out.push_back(b8);
+ }
+ out.insert(out.end(), value + offset, value + offset + len);
+}
+
+void Writer::write_direct(unsigned char c) {
+ out.push_back(c);
+}
+
+void Writer::write(const Value& value) {
+ value.Write(*this);
+}
+
+void Writer::write(const ObjectMixin& value) {
+ const string& name = value.GetObjectName();
+
+ // Get object definition
+ size_t object_ref;
+ map<string,size_t>::const_iterator p = objectMap.find(name);
+ if (p == objectMap.end()) {
+ // Write object definition
+ out.push_back('C');
+ value.WriteObjectFieldNames(*this);
+
+ // Add to map for later reference
+ object_ref = objectMap.size();
+ objectMap[name] = object_ref;
+ }
+ else object_ref = p->second;
+
+ // Write object header
+ if (object_ref <= 0x0f) {
+ // packed in one octet
+ const unsigned char b8 = 0x60 + (object_ref & 0xFF);
+ out.push_back(b8);
+ }
+ else {
+ // tag + integer
+ out.push_back('O');
+ write(object_ref);
+ }
+
+ // Write values
+ value.WriteObjectValues(*this);
+}
+
+
+void Writer::call(const string& method) {
+ Reset();
+
+ out.push_back('C');
+ write(method);
+ write(0); // no args
+}
+
+void Writer::write_fault(fault_type type, const string& msg) {
+ Reset();
+
+ out.push_back('F');
+
+ write("code");
+ switch (type) {
+ case ProtocolException:
+ write("ProtocolException");
+ break;
+ case NoSuchObjectException:
+ write("NoSuchObjectException");
+ break;
+ case NoSuchMethodException:
+ write("NoSuchMethodException");
+ break;
+ case RequireHeaderException:
+ write("RequireHeaderException");
+ break;
+ case ServiceException:
+ write("ServiceException");
+ break;
+ default:
+ throw value_exception("invalid fault type");
+ }
+
+ if (!msg.empty()) {
+ write("message");
+ write(msg);
+ }
+
+ out.push_back('Z'); // complete
+}
+
+
+void Call::Print(std::string& out) const {
+ out += m_method;
+ out.push_back('(');
+
+ for (values::const_iterator p = m_parameters.begin(); p != m_parameters.end(); ++p) {
+ if (p != m_parameters.begin()) out += ", ";
+ p->Print(out);
+ }
+
+ out += ")\n";
+}
+
+void Call::Write(Writer& writer) const {
+ writer.write_direct('C');
+ writer.write(m_method);
+ writer.write(m_parameters.size());
+
+ for (values::const_iterator p = m_parameters.begin(); p != m_parameters.end(); ++p) {
+ writer.write(*p);
+ }
+}
+
+} // namespace hessian_ipc
View
297 src/hessian_ipc/hessian_values.h
@@ -0,0 +1,297 @@
+#ifndef HESSIAN_VALUES_H
+#define HESSIAN_VALUES_H
+
+#include <exception>
+#include <string>
+#include <boost/lexical_cast.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <memory>
+#include <map>
+
+using namespace std;
+
+namespace hessian_ipc {
+ // Pre-declarations
+ class Value;
+ class Integer;
+ class Long;
+ class Date;
+ class String;
+ class Call;
+ class Writer;
+
+ enum fault_type {
+ ProtocolException,
+ NoSuchObjectException,
+ NoSuchMethodException,
+ RequireHeaderException,
+ ServiceException
+ };
+
+ // Exception
+ class value_exception : public exception {
+ public:
+ explicit value_exception(const string& what): _message(what) {}
+ virtual ~value_exception() throw() {}
+
+ virtual const char* what() const throw() {return _message.c_str();}; //Get error message.
+ virtual void raise() const {throw *this;}; //Re-throw.
+ private:
+ string _message;
+ };
+
+ class ObjectMixin {
+ public:
+ virtual const string& GetObjectName() const = 0;
+ virtual void WriteObjectFieldNames(Writer& writer) const = 0;
+ virtual void WriteObjectValues(Writer& writer) const = 0;
+ };
+
+ class Writer {
+ public:
+ Writer() {};
+
+ void Reset();
+ const vector<unsigned char>& GetOutput() const {return out;};
+
+ // Calls
+ void call(const string& method);
+ template<class T> void call(const string& method, const T& arg);
+ template<class T1, class T2> void call(const string& method, const T1& arg1, const T2& arg2);
+
+ template<class T> void write_reply(const T& value);
+ void write_fault(fault_type type, const string& msg);
+
+ // Variable writers
+ void write(bool value);
+ void write(int value);
+ void write(unsigned int value);
+ void write(long long value);
+ void write(const string& value);
+ void write(const Value& value);
+ void write(const ObjectMixin& value);
+ template<class T> void write(const vector<T>& value);
+ template<class T1, class T2> void write(const map<T1,T2>& value);
+
+ void write_null();
+ void write_date(time_t value);
+ void write_binary(const unsigned char* value, size_t len);
+ void write_direct(unsigned char c);
+
+ private:
+ vector<unsigned char> out;
+ map<string,size_t> objectMap;
+ };
+
+ // abstract base class for all values
+ class Value {
+ public:
+ // get the value type
+ virtual bool IsNull() const {return false;};
+ virtual bool IsBinary() const {return false;};
+ virtual bool IsBoolean() const {return false;};
+ virtual bool IsInt() const {return false;};
+ virtual bool IsLong() const {return false;};
+ virtual bool IsDouble() const {return false;};
+ virtual bool IsString() const {return false;};
+ virtual bool IsDate() const {return false;};
+ virtual bool IsList() const {return false;};
+ virtual bool IsMap() const {return false;};
+ virtual bool IsObject() const {return false;};
+ virtual bool IsCall() const {return false;};
+ virtual bool IsStringChunk() const {return false;};
+
+ // get the value
+ virtual int GetInt() const {throw value_exception("invalid value access");};
+ virtual long long GetLong() const {throw value_exception("invalid value access");};
+ virtual time_t GetDate() const {throw value_exception("invalid value access");};
+ virtual bool GetBoolean() const {throw value_exception("invalid value access");};
+ virtual const string& GetString() const {throw value_exception("invalid value access");};
+
+ // get the real type
+ virtual Integer& AsInteger() {throw value_exception("invalid value type");};
+ virtual Long& AsLong() {throw value_exception("invalid value type");};
+ virtual Date& AsDate() {throw value_exception("invalid value type");};
+ virtual String& AsString() {throw value_exception("invalid value type");};
+ virtual Call& AsCall() {throw value_exception("invalid value type");};
+
+ // dump
+ virtual void Print(std::string& out) const = 0;
+ virtual void Write(Writer& writer) const = 0;
+ };
+
+ class Null : public Value {
+ public:
+ Null() {};
+
+ // get the value type
+ bool IsNull() const {return true;};
+
+ // dump
+ void Print(string& out) const {out += "null";};
+ void Write(Writer& writer) const {writer.write_null();};
+ };
+
+ class Boolean : public Value {
+ public:
+ Boolean(bool value) : m_value(value) {};
+
+ // get the value type
+ bool IsBoolean() const {return true;};
+
+ // get the value
+ bool GetBoolean() const {return m_value;};
+
+ // dump
+ void Print(string& out) const {out += m_value ? "true" : "false";};
+ void Write(Writer& writer) const {writer.write(m_value);};
+
+ private:
+ bool m_value;
+ };
+
+ class Integer : public Value {
+ public:
+ Integer() : m_value(0) {};
+ Integer(int value) : m_value(value) {};
+
+ // get the value type
+ bool IsInteger() const {return true;};
+
+ // get the value
+ int GetInt() const {return m_value;};
+ Integer& AsInteger() {return *this;};
+
+ // set value
+ int operator+=(int v) {m_value += v; return m_value;}
+
+ // dump
+ void Print(string& out) const {out += boost::lexical_cast<std::string>(m_value);};
+ void Write(Writer& writer) const {writer.write(m_value);};
+
+ private:
+ int m_value;
+ };
+
+ class Long : public Value {
+ public:
+ Long() : m_value(0) {};
+ Long(long long value) : m_value(value) {};
+
+ // get the value type
+ bool IsLong() const {return true;};
+
+ // get the value
+ long long GetLong() const {return m_value;};
+ Long& AsLong() {return *this;};
+
+ // set value
+ long long operator+=(long long v) {m_value += v; return m_value;}
+
+ // dump
+ void Print(string& out) const {out += boost::lexical_cast<std::string>(m_value);};
+ void Write(Writer& writer) const {writer.write(m_value);};
+
+ protected:
+ long long m_value;
+ };
+
+ class Date : public Long {
+ public:
+ Date() {};
+ Date(time_t value) : Long(value) {};
+
+ // get the value type
+ bool IsDate() const {return true;};
+
+ // get the value
+ Date& AsDate() {return *this;};
+
+ // set value
+ long long operator+=(long long v) {m_value += v; return m_value;}
+ void AdjustToMinutes() {m_value *= 60000LL;}
+
+ // dump
+ void Print(string& out) const {out += boost::lexical_cast<std::string>(m_value);};
+ void Write(Writer& writer) const {writer.write_date(m_value);};
+ };
+
+ class String : public Value {
+ public:
+ String() : m_isChunk(false) {};
+ String(const string& value) : m_value(value), m_isChunk(false) {};
+
+ // get the value type
+ bool IsString() const {return true;};
+ bool IsStringChunk() const {return m_isChunk;};
+
+ // get the value
+ const string& GetString() const {return m_value;};
+ String& AsString() {return *this;};
+ size_t size() const {return m_value.size();};
+
+ // set value
+ string& operator+=(char c) {m_value += c; return m_value;};
+ void reserve(size_t len) {m_value.reserve(len);};
+ void SetChunk(bool chunk=true) {m_isChunk = chunk;};
+
+ // dump
+ void Print(string& out) const {out += '"'; out += m_value; out += '"';};
+ void Write(Writer& writer) const {writer.write(m_value);};
+
+ private:
+ string m_value; // utf-8 encoded
+ bool m_isChunk;
+ };
+
+ class List : public Value {
+ public:
+ List() {};
+
+ // get the value type
+ bool IsList() const {return true;};
+
+ // get the value
+ List& AsList() {return *this;};
+
+ // set value
+ void Add(auto_ptr<Value> v) {m_values.push_back(v);};
+
+ private:
+ boost::ptr_vector<Value> m_values;
+
+ };
+
+ class Call : public Value {
+ public:
+ Call() {};
+ Call(const string& method) {m_method = method;};
+
+ // get the value type
+ bool IsCall() const {return true;};
+ Call& AsCall() {return *this;};
+
+ // get the value
+ const string& GetMethod() const {return m_method;};
+ size_t GetParameterCount() const {return m_parameters.size();};
+ const Value& GetParameter(size_t index) const {return m_parameters[index];};
+
+ // set value
+ void SetMethod(auto_ptr<Value> value) {m_method = value->GetString();};
+ void AddParameter(auto_ptr<Value> value) {m_parameters.push_back(value);};
+
+ // dump
+ void Print(string& out) const;
+ void Write(Writer& writer) const;
+
+ private:
+ string m_method; // utf-8 encoded
+ typedef boost::ptr_vector<Value> values;
+ values m_parameters;
+ };
+
+} // namespace hessian_ipc
+
+#include "hessian_writers.h"
+
+#endif //HESSIAN_VALUES_H
View
63 src/hessian_ipc/hessian_writers.h
@@ -0,0 +1,63 @@
+#ifndef HESSIAN_WRITERS_H
+#define HESSIAN_WRITERS_H
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "hessian_values.h"
+
+namespace hessian_ipc {
+
+// template implementations
+
+template<class T> void Writer::call(const string& method, const T& arg) {
+ Reset();
+
+ out.push_back('C');
+ write(method);
+ write(1);
+ write(arg);
+}
+
+template<class T1, class T2> void Writer::call(const string& method, const T1& arg1, const T2& arg2) {
+ Reset();
+
+ out.push_back('C');
+ write(method);
+ write(2);
+ write(arg1);
+ write(arg2);
+}
+
+template<class T> void Writer::write_reply(const T& value) {
+ Reset();
+
+ out.push_back('R');
+ write(value);
+}
+
+template<class T> void Writer::write(const vector<T>& value) {
+ const size_t len = value.size();
+
+ out += x57; // tag for variable-length untyped list
+ for (vector<T>::const_iterator p = value.begin(); p != value.end(); ++p) {
+ write(*p);
+ }
+ out.push_back('Z');
+}
+
+template<class T1, class T2> void Writer::write(const map<T1,T2>& value) {
+ const size_t len = value.size();
+
+ out.push_back('H'); // tag for untyped map
+ for (map<T1,T2>::const_iterator p = value.begin(); p != value.end(); ++p) {
+ write(*(p->first));
+ write(*(p->second));
+ }
+ out.push_back('Z');
+}
+
+} // namespace hessian_ipc
+
+#endif //HESSIAN_WRITERS_H
View
47 src/hessian_ipc/proxy.cpp
@@ -0,0 +1,47 @@
+#include "proxy.h"
+
+namespace hessian_ipc {
+
+proxy::proxy() : m_io_service(), m_socket(m_io_service), m_reply(8192) {
+ // Get a list of endpoints corresponding to the server name.
+ tcp::resolver resolver(m_io_service);
+ tcp::resolver::query query("localhost", "9000");
+ tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
+ tcp::resolver::iterator end;
+
+ // Try each endpoint until we successfully establish a connection.
+ boost::system::error_code error = boost::asio::error::host_not_found;
+ while (error && endpoint_iterator != end)
+ {
+ m_socket.close();
+ m_socket.connect(*endpoint_iterator++, error);
+ }
+ if (error) throw boost::system::system_error(error);
+}
+
+const Value& proxy::do_hessian_call(const vector<unsigned char>& call) {
+ // Send the request.
+ boost::asio::write(m_socket, boost::asio::buffer(call));
+
+ // Read the response
+ m_reader.Reset();
+ for (;;) {
+ const size_t n = m_socket.read_some(boost::asio::buffer(m_reply));
+
+ vector<unsigned char>::const_iterator begin = m_reply.begin();
+ vector<unsigned char>::const_iterator end = m_reply.begin() + n;
+
+ if (m_reader.Parse(begin, end)) break; // request complete
+ }
+
+ return *m_reader.GetResultValue();
+}
+
+const Value& proxy::call(const string& method) {
+ m_writer.call(method);
+ const vector<unsigned char>& out = m_writer.GetOutput();
+
+ return do_hessian_call(out);
+}
+
+} //namespace hessian_ipc
View
59 src/hessian_ipc/proxy.h
@@ -0,0 +1,59 @@
+#ifndef HESSIAN_IPC_PROXY_H
+#define HESSIAN_IPC_PROXY_H
+
+#include <boost/asio.hpp>
+#include "hessian_reader.h"
+
+using boost::asio::ip::tcp;
+
+namespace hessian_ipc {
+
+class proxy {
+public:
+ proxy();
+
+ const Value& call(const string& method);
+ template<class T> const Value& call(const string& method, const T& arg);
+
+ template<class T> int call_int(const string& method, const T& arg);
+ template<class T> long long call_long(const string& method, const T& arg);
+ template<class T> std::string call_string(const string& method, const T& arg);
+
+private:
+ const Value& do_hessian_call(const vector<unsigned char>& call);
+
+ // Member variables
+ boost::asio::io_service m_io_service;
+ tcp::socket m_socket;
+ std::vector<unsigned char> m_reply;
+ Reader m_reader;
+ Writer m_writer;
+};
+
+// template implementations
+
+template<class T> const Value& proxy::call(const string& method, const T& arg) {
+ m_writer.call(method, arg);
+ const string& out = m_writer.GetOutput();
+
+ return do_hessian_call(out);
+}
+
+template<class T> int proxy::call_int(const string& method, const T& arg) {
+ const Value& response = call(method, arg);
+ return response.GetInt();
+}
+
+template<class T> long long proxy::call_long(const string& method, const T& arg) {
+ const Value& response = call(method, arg);
+ return response.GetLong();
+}
+
+template<class T> std::string proxy::call_string(const string& method, const T& arg) {
+ const Value& response = call(method, arg);
+ return response.GetString();
+}
+
+} // namespace hessian_ipc
+
+#endif //HESSIAN_IPC_PROXY_H
View
70 src/hessian_ipc/server.cpp
@@ -0,0 +1,70 @@
+#include "server.h"
+#include <boost/bind.hpp>
+
+namespace hessian_ipc {
+
+server::server(const std::string& address, const std::string& port)
+ : io_service_(),
+ acceptor_(io_service_),
+ connection_manager_(),
+ new_connection_()
+{
+ // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
+ boost::asio::ip::tcp::resolver resolver(io_service_);
+ boost::asio::ip::tcp::resolver::query query(address, port);
+ boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
+ acceptor_.open(endpoint.protocol());
+ acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+ acceptor_.bind(endpoint);
+ acceptor_.listen();
+}
+
+connection* server::new_connection() {
+ return new connection(io_service_, connection_manager_);
+}
+
+void server::run()
+{
+ // Create initial connection
+ new_connection_.reset(new_connection());
+ acceptor_.async_accept(new_connection_->socket(),
+ boost::bind(&server::handle_accept, this,
+ boost::asio::placeholders::error));
+
+ // The io_service::run() call will block until all asynchronous operations
+ // have finished. While the server is running, there is always at least one
+ // asynchronous operation outstanding: the asynchronous accept call waiting
+ // for new incoming connections.
+ io_service_.run();
+}
+
+void server::stop()
+{
+ // Post a call to the stop function so that server::stop() is safe to call
+ // from any thread.
+ io_service_.post(boost::bind(&server::handle_stop, this));
+}
+
+void server::handle_accept(const boost::system::error_code& e)
+{
+ if (!e)
+ {
+ connection_manager_.start(new_connection_);
+ new_connection_.reset(new_connection());
+ acceptor_.async_accept(new_connection_->socket(),
+ boost::bind(&server::handle_accept, this,
+ boost::asio::placeholders::error));
+ }
+}
+
+void server::handle_stop()
+{
+ // The server is stopped by cancelling all outstanding asynchronous
+ // operations. Once all operations have finished the io_service::run() call
+ // will exit.
+ acceptor_.close();
+ connection_manager_.stop_all();
+}
+
+} // namespace hessian_ipc
+
View
45 src/hessian_ipc/server.h
@@ -0,0 +1,45 @@
+#ifndef HESSIAN_IPC_SERVER_HPP
+#define HESSIAN_IPC_SERVER_HPP
+
+#include <boost/asio.hpp>
+#include <string>
+#include <boost/noncopyable.hpp>
+#include "connection.h"
+#include "connection_manager.h"
+
+namespace hessian_ipc {
+
+// The top-level class of the ipc server.
+class server
+: private boost::noncopyable
+{
+public:
+ // Construct the server to listen on the specified TCP address and port
+ explicit server(const std::string& address, const std::string& port);
+ virtual ~server() {};
+
+ void run(); // Run the server's io_service loop.
+ virtual void stop(); // Stop the server.
+
+ virtual connection* new_connection(); // create new connection
+
+protected:
+ // Member variables
+ boost::asio::io_service io_service_; // The io_service used to perform asynchronous operations.
+ connection_manager connection_manager_; // The connection manager which owns all live connections.
+
+private:
+ // Handle completion of an asynchronous accept operation.
+ void handle_accept(const boost::system::error_code& e);
+
+ // Handle a request to stop the server.
+ void handle_stop();
+
+ // Member variables
+ boost::asio::ip::tcp::acceptor acceptor_; // Acceptor used to listen for incoming connections.
+ connection_ptr new_connection_; // The next connection to be accepted.
+};
+
+} // namespace hessian_ipc
+
+#endif // HESSIAN_IPC_SERVER_HPP
Please sign in to comment.
Something went wrong with that request. Please try again.