summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/icap/Makefile.am24
-rw-r--r--lib/icap/common.h41
-rw-r--r--lib/icap/header.cpp223
-rw-r--r--lib/icap/header.h168
-rw-r--r--lib/icap/request.cpp84
-rw-r--r--lib/icap/request.h72
-rw-r--r--lib/icap/request_header.cpp113
-rw-r--r--lib/icap/request_header.h84
-rw-r--r--lib/icap/response.cpp81
-rw-r--r--lib/icap/response.h65
-rw-r--r--lib/icap/response_header.cpp101
-rw-r--r--lib/icap/response_header.h78
-rw-r--r--lib/icap/util.cpp693
-rw-r--r--lib/icap/util.h265
-rw-r--r--lib/socket/Makefile.am4
-rw-r--r--lib/socket/socket.cpp472
-rw-r--r--lib/socket/socket.h427
18 files changed, 2998 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..bcd3933
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,3 @@
+## [bitz-server] lib/
+SUBDIRS = socket icap
+
diff --git a/lib/icap/Makefile.am b/lib/icap/Makefile.am
new file mode 100644
index 0000000..02d8bb4
--- /dev/null
+++ b/lib/icap/Makefile.am
@@ -0,0 +1,24 @@
+## [icap-library] lib/icap/
+AM_CPPFLAGS = -I$(top_srcdir)/lib
+libicapincludedir = $(includedir)/icap
+
+lib_LTLIBRARIES = libicap.la
+libicap_la_LIBADD = $(top_builddir)/lib/socket/libsocket.la
+libicap_la_LDFLAGS = -version-info @ICAP_LT_VERSION@ -no-undefined
+libicap_la_SOURCES = \
+ header.cpp \
+ request_header.cpp \
+ response_header.cpp \
+ request.cpp \
+ response.cpp \
+ util.cpp
+
+libicapinclude_HEADERS = \
+ common.h \
+ header.h \
+ request_header.h \
+ response_header.h \
+ request.h \
+ response.h \
+ util.h
+
diff --git a/lib/icap/common.h b/lib/icap/common.h
new file mode 100644
index 0000000..65c3694
--- /dev/null
+++ b/lib/icap/common.h
@@ -0,0 +1,41 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ICAP_COMMON_H
+#define ICAP_COMMON_H
+
+#include <string>
+
+
+namespace icap {
+
+ /**
+ * Payload structure common to both requests and responses.
+ */
+ struct payload_t {
+ std::string req_header; /**< request header */
+ std::string req_body; /**< request body */
+ std::string res_header; /**< response header */
+ std::string res_body; /**< response body */
+ bool ieof; /**< boolen flag to store the presence of "ieof" */
+ };
+
+} /* end of namespace icap */
+
+#endif /* !ICAP_COMMON_H */
diff --git a/lib/icap/header.cpp b/lib/icap/header.cpp
new file mode 100644
index 0000000..493e659
--- /dev/null
+++ b/lib/icap/header.cpp
@@ -0,0 +1,223 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "header.h"
+#include "util.h"
+
+#include <algorithm>
+#include <cstdlib>
+
+
+namespace icap {
+
+ Header::Header() {
+
+ // initialise defaults
+ _encapsulated["req-hdr"] = -1;
+ _encapsulated["req-body"] = -1;
+ _encapsulated["res-hdr"] = -1;
+ _encapsulated["res-body"] = -1;
+ _encapsulated["opt-body"] = -1;
+ _encapsulated["null-body"] = -1;
+
+ }
+
+ Header::~Header() {}
+
+
+ const Header::headers_t &Header::headers() const throw() {
+ return _headers;
+ }
+
+
+ const std::string Header::value( const std::string &key ) throw() {
+
+ std::string value = "";
+ Header::headers_index_t idx = _headers.find( key );
+
+ if ( idx != _headers.end() ) {
+ value = idx->second;
+ }
+
+ return value;
+
+ }
+
+
+ const int Header::encapsulated_header( const std::string &entity ) throw() {
+
+ Header::encapsulated_header_index_t idx;
+
+ idx = _encapsulated.find( entity );
+ if ( idx == _encapsulated.end() ) {
+ return -1;
+ }
+
+ return idx->second;
+
+ }
+
+
+ void Header::attach( std::string key, std::string value ) throw() {
+
+ // trim
+ key = util::trim( key );
+ value = util::trim( value );
+
+ // check for 'Encapsulated' headers
+ if ( key == "Encapsulated" ) {
+ attach_encapsulated( value );
+ } else {
+ _headers[key] = value;
+ }
+
+ }
+
+
+ bool Header::attach_encapsulated( std::string header_value ) throw() {
+
+ std::vector<std::string> list_data;
+ std::vector<std::string> entity_data;
+
+ encapsulated_header_index_t idx;
+ bool r_status = true;
+
+ // grab the entity list [ req-hdr=0, null-body=170 ]
+ list_data = util::split( util::trim( header_value ), "," );
+
+ for ( size_t i = 0; i < list_data.size(); i++ ) {
+
+ // get entity data [ req-hdr=0 ]
+ entity_data = util::split( util::trim( list_data.at( i ) ), "=" );
+
+ if ( entity_data.size() == 2 ) {
+
+ idx = _encapsulated.find( util::trim( entity_data.at( 0 ) ) );
+ if ( idx != _encapsulated.end() ) {
+ idx->second = atoi( util::trim( entity_data.at( 1 ) ).c_str() );
+ }
+
+ } else {
+ r_status = false;
+ }
+ }
+
+ return r_status;
+
+ }
+
+
+ bool Header::remove( std::string key ) throw() {
+ return ( (bool) _headers.erase( util::trim( key ) ) );
+ }
+
+
+ const std::string Header::encapsulated_header_str() throw() {
+
+ /*
+ * Encapsulated request header grammer:
+ * REQMOD request encapsulated_list: [reqhdr] reqbody
+ * REQMOD response encapsulated_list: {[reqhdr] reqbody} |
+ * {[reshdr] resbody}
+ * RESPMOD request encapsulated_list: [reqhdr] [reshdr] resbody
+ * RESPMOD response encapsulated_list: [reshdr] resbody
+ * OPTIONS response encapsulated_list: optbody
+ */
+
+ Header::encapsulated_header_index_t idx;
+ std::string encaps_header = "";
+
+ // FIXME: chances are that we will always get the correct order
+ // but should consider sorting
+ for ( idx = _encapsulated.begin(); idx != _encapsulated.end(); idx++ ) {
+
+ if ( idx->second > 0 ) {
+
+ if ( encaps_header != "" ) {
+ encaps_header.append( ", " );
+ }
+
+ encaps_header.append( idx->first ).append( "=" ).append( util::itoa( idx->second ) );
+
+ }
+
+ }
+
+ // sanity check
+ if ( encaps_header == "" ) {
+ encaps_header = "null-body=0";
+ }
+
+ return encaps_header;
+
+ }
+
+
+ void Header::update_encapsulated( const payload_t &payload ) throw() {
+
+ unsigned int data_length = 0;
+ unsigned int data_offset = 0;
+
+ // request header
+ if ( payload.req_header.size() > 0 ) {
+ _encapsulated["req-hdr"] = data_length;
+ data_offset = data_length;
+ data_length += payload.req_header.size();
+ }
+
+ // request body (POST data)
+ if ( payload.req_body.size() > 0 ) {
+ _encapsulated["req-body"] = data_length;
+ data_offset = data_length;
+ data_length += payload.req_body.size();
+ }
+
+ // response header
+ if ( payload.res_header.size() > 0 ) {
+ _encapsulated["res-hdr"] = data_length;
+ data_offset = data_length;
+ data_length += payload.res_header.size();
+ }
+
+ // response body
+ if ( payload.res_body.size() > 0 ) {
+ _encapsulated["res-body"] = data_length;
+ data_offset = data_length;
+ data_length += payload.res_body.size();
+ }
+
+ // null-body
+ if ( data_offset == 0 ) {
+ _encapsulated["null-body"] = data_length;
+ }
+
+ }
+
+
+ std::vector<Header::encapsulated_header_data_t> Header::sort_encapsulated_header() {
+
+ std::vector<Header::encapsulated_header_data_t> data( _encapsulated.begin(), _encapsulated.end() );
+ std::sort(data.begin(), data.end(), encapsulated_header_compare());
+
+ return data;
+
+ }
+
+}
+
diff --git a/lib/icap/header.h b/lib/icap/header.h
new file mode 100644
index 0000000..52a8fef
--- /dev/null
+++ b/lib/icap/header.h
@@ -0,0 +1,168 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ICAP_HEADER_H
+#define ICAP_HEADER_H
+
+#include "common.h"
+
+#include <string>
+#include <map>
+#include <vector>
+#include <functional>
+
+
+namespace icap {
+
+ class Header {
+ public:
+
+ /* headers data type */
+ typedef std::map<std::string, std::string> headers_t;
+
+ /* headers iterator type */
+ typedef headers_t::iterator headers_index_t;
+
+ /* encapsulated header type */
+ typedef std::map<std::string, int> encapsulated_header_t;
+
+ /* encapsulated header iterator type */
+ typedef encapsulated_header_t::iterator encapsulated_header_index_t;
+
+ /* encapsulated header data type */
+ typedef std::pair<std::string, int> encapsulated_header_data_t;
+
+ /**
+ * Binary compare structure to compare two encapsulated header
+ * entity (data) values. Used for sorting.
+ */
+ struct encapsulated_header_compare
+ : std::binary_function<icap::Header::encapsulated_header_data_t, icap::Header::encapsulated_header_data_t, bool> {
+ inline bool operator()( const icap::Header::encapsulated_header_data_t &lhs, const icap::Header::encapsulated_header_data_t &rhs ) {
+ return lhs.second < rhs.second;
+ }
+ };
+
+
+ Header();
+ virtual ~Header();
+
+ /**
+ * Return headers
+ * @return headers
+ */
+ const headers_t &headers() const throw();
+
+ /**
+ * Returns the header value for the given header key or an empty string
+ * if the header is not found.
+ *
+ * @param key header key
+ * @return header value
+ */
+ const std::string value( const std::string &key ) throw();
+
+ /**
+ * Return Encapsulated header value for the given entity
+ * or -1 if the given entity is invalid.
+ *
+ * @param entity encapsulated header entity
+ * @return -1 | encapsulated header value
+ */
+ const int encapsulated_header( const std::string &entity ) throw();
+
+ /**
+ * Attach header data into the header
+ *
+ * <pre>
+ * e.g.
+ * Host: icap-server.net
+ * Encapsulated: req-hdr=0, null-body=170
+ * [key]: [value]
+ * </pre>
+ *
+ * @param key header key
+ * @param value header value
+ */
+ virtual void attach( std::string key, std::string value ) throw();
+
+ /**
+ * Attach Encapsulated header data. This method should only be used
+ * when reading a raw request / response. Consider using update_encapsulated()
+ * method for other scenarios.
+ *
+ * <pre>
+ * e.g.
+ * Encapsulated: req-hdr=0, req-body=412
+ * Encapsulated: req-hdr=0, res-hdr=822, res-body=1655
+ * Encapsulated: [header_value]
+ * </pre>
+ *
+ * @param header_value Encapsulated header value
+ * @return boolean to denote success or failure
+ */
+ virtual bool attach_encapsulated( std::string header_value ) throw();
+
+
+ /**
+ * Update Encapsulated header data using the passed in (icap::payload_t) payload.
+ * When the request / response has been populated with the payload, calling this
+ * method will update the encapsulated header entities with appropriate values.
+ *
+ * This methos will always succeed.
+ *
+ * @param payload request or response payload
+ */
+ virtual void update_encapsulated( const payload_t &payload ) throw();
+
+ /**
+ * Remove header data from the header instance with the given key
+ *
+ * @param key header key
+ * @return boolean to denote success or failure
+ */
+ virtual bool remove( std::string key ) throw();
+
+ /**
+ * Return Encapsulated header as a std::string value.
+ * @return encapsulated header value (e.g. res-hdr=0, res-body=213)
+ */
+ virtual const std::string encapsulated_header_str() throw();
+
+ /**
+ * Sort the encapsulated header data and return a std::vector of
+ * encapsulated_header_data_t. The actual header data won't be changed.
+ *
+ * @return sorted encapsulated header
+ */
+ virtual std::vector<encapsulated_header_data_t> sort_encapsulated_header();
+
+
+ protected:
+ headers_t _headers;
+ encapsulated_header_t _encapsulated;
+
+ private:
+
+ };
+
+} /* end of namespace icap */
+
+#endif /* !ICAP_HEADER_H */
+
diff --git a/lib/icap/request.cpp b/lib/icap/request.cpp
new file mode 100644
index 0000000..bf80e13
--- /dev/null
+++ b/lib/icap/request.cpp
@@ -0,0 +1,84 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "request.h"
+
+#include <cstdlib>
+
+
+namespace icap {
+
+ Request::Request( RequestHeader * req_header ) {
+
+ _header = req_header;
+
+ // initialise defaults
+ _payload.req_header = "";
+ _payload.req_body = "";
+ _payload.res_header = "";
+ _payload.res_body = "";
+ _payload.ieof = false;
+
+ }
+
+
+ Request::~Request() { }
+
+
+ RequestHeader * const Request::header() const throw() {
+ return _header;
+ }
+
+
+ void Request::payload( payload_t payload ) throw() {
+
+ _payload = payload;
+
+ // update encapsulated data
+ _header->update_encapsulated( _payload );
+
+ }
+
+
+ const payload_t &Request::payload() const throw() {
+ return _payload;
+ }
+
+
+ const int Request::preview_size() throw() {
+
+ int size = -1;
+
+ // grab the size from request header
+ std::string s_size = _header->value( "Preview" );
+
+ // sanity check
+ if (! s_size.empty() ) {
+
+ // convert string to integer
+ size = atoi( s_size.c_str() );
+
+ }
+
+ return size;
+
+ }
+
+} /* end of namespace icap */
+
diff --git a/lib/icap/request.h b/lib/icap/request.h
new file mode 100644
index 0000000..d5c4f69
--- /dev/null
+++ b/lib/icap/request.h
@@ -0,0 +1,72 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ICAP_REQUEST_H
+#define ICAP_REQUEST_H
+
+#include "common.h"
+#include "request_header.h"
+
+
+namespace icap {
+
+ class Request {
+ public:
+ Request( RequestHeader * req_header );
+ virtual ~Request();
+
+ /**
+ * Return the request header
+ * @return request header
+ */
+ RequestHeader * const header() const throw();
+
+ /**
+ * Set the payload data for this response instance from a
+ * icap::payload_t data structure
+ *
+ * @param payload payload data structure
+ */
+ void payload( payload_t payload ) throw();
+
+ /**
+ * Return the payload data for this response instance
+ * @return payload data
+ */
+ const payload_t &payload() const throw();
+
+ /**
+ * Returns the number of preview bytes in the request. If the preview
+ * header is not present in the request then a minus (-1) value will be
+ * returned.
+ *
+ * @return preview bytes
+ */
+ const int preview_size() throw();
+
+ private:
+ RequestHeader * _header;
+ payload_t _payload;
+
+ };
+
+} /* end of namespace icap */
+
+#endif /* !ICAP_REQUEST_H */
+
diff --git a/lib/icap/request_header.cpp b/lib/icap/request_header.cpp
new file mode 100644
index 0000000..a9b42dc
--- /dev/null
+++ b/lib/icap/request_header.cpp
@@ -0,0 +1,113 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "request_header.h"
+#include "util.h"
+
+
+namespace icap {
+
+ /*
+ * sample icap request header:
+ * REQMOD icap://icap-server.net/server?arg=87 ICAP/1.0
+ * Host: icap-server.net
+ * Encapsulated: req-hdr=0, null-body=170
+ *
+ * [payload]
+ */
+ RequestHeader::RequestHeader( const std::string &raw_data ) : Header() {
+
+ // initialise defaults
+ _request.method = "";
+ _request.uri = "";
+ _request.protocol = "ICAP/1.0";
+
+ // read header
+ read_header( raw_data );
+
+ }
+
+
+ RequestHeader::~RequestHeader() { }
+
+
+ const std::string &RequestHeader::method() const throw() {
+ return _request.method;
+ }
+
+
+ const std::string &RequestHeader::uri() const throw() {
+ return _request.uri;
+ }
+
+
+ const std::string &RequestHeader::protocol() const throw() {
+ return _request.protocol;
+ }
+
+
+ const RequestHeader::request_t &RequestHeader::request() const throw() {
+ return _request;
+ }
+
+
+ const std::string &RequestHeader::raw_data() const throw() {
+ return _raw_data;
+ }
+
+
+ void RequestHeader::read_header( const std::string &raw_data ) throw() {
+
+ std::vector<std::string> data;
+
+ _raw_data = raw_data;
+ data = util::split( raw_data, "\r\n" );
+
+ if ( data.size() > 0 ) {
+
+ std::vector<std::string> header_data;
+ std::vector<std::string> request;
+
+ std::string request_data = data.at( 0 );
+ request = util::split( util::trim( request_data ) );
+
+ if ( request.size() == 3 ) {
+ _request.method = request.at(0);
+ _request.uri = request.at(1);
+ _request.protocol = request.at(2);
+ } else {
+ // TODO: error, invalid request format
+ }
+
+ for ( int i = 1; i < data.size(); i++ ) {
+ header_data = util::split( data.at( i ), ":" );
+
+ if ( header_data.size() == 2 ) {
+ this->attach( header_data.at( 0 ), header_data.at( 1 ) );
+ } else {
+ // TODO: error parsing header data
+ }
+ }
+
+ }
+
+ }
+
+} /* end of namespace icap */
+
diff --git a/lib/icap/request_header.h b/lib/icap/request_header.h
new file mode 100644
index 0000000..47978c1
--- /dev/null
+++ b/lib/icap/request_header.h
@@ -0,0 +1,84 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ICAP_REQUEST_HEADER_H
+#define ICAP_REQUEST_HEADER_H
+
+#include "header.h"
+
+#include <string>
+#include <vector>
+
+
+namespace icap {
+
+ class RequestHeader : public Header {
+ public:
+
+ struct request_t {
+ std::string method;
+ std::string uri;
+ std::string protocol;
+ };
+
+ RequestHeader( const std::string &raw_data );
+ virtual ~RequestHeader();
+
+ /**
+ * Return request method
+ * @return method
+ */
+ const std::string &method() const throw();
+
+ /**
+ * Return request URI
+ * @return URI
+ */
+ const std::string &uri() const throw();
+
+ /**
+ * Return request protocol
+ * @return protocol
+ */
+ const std::string &protocol() const throw();
+
+ /**
+ * Return request
+ * @return request
+ */
+ const request_t &request() const throw();
+
+ /**
+ * Return raw header data
+ * @return raw request header
+ */
+ const std::string &raw_data() const throw();
+
+ private:
+ request_t _request;
+ std::string _raw_data;
+
+ void read_header( const std::string &raw_data ) throw();
+
+ };
+
+} /* end of namespace icap */
+
+#endif /* !ICAP_REQUEST_HEADER_H */
+
diff --git a/lib/icap/response.cpp b/lib/icap/response.cpp
new file mode 100644
index 0000000..6bd730f
--- /dev/null
+++ b/lib/icap/response.cpp
@@ -0,0 +1,81 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "response.h"
+
+
+namespace icap {
+
+ Response::Response( ResponseHeader * header ) {
+
+ if ( header != NULL ) {
+ _header = header;
+ _cleanup_header = false;
+ } else {
+ _header = new ResponseHeader( ResponseHeader::SERVER_ERROR );
+ _cleanup_header = true;
+ }
+
+ // initialise defaults
+ _payload.req_header = "";
+ _payload.req_body = "";
+ _payload.res_header = "";
+ _payload.res_body = "";
+ _payload.ieof = false;
+
+ }
+
+
+ Response::Response( ResponseHeader::status_t status ) {
+ _header = new ResponseHeader( status );
+ _cleanup_header = true;
+ }
+
+
+ Response::~Response() {
+
+ // cleanup
+ if ( _cleanup_header ) {
+ delete _header;
+ }
+
+ }
+
+
+ ResponseHeader * const Response::header() const throw() {
+ return _header;
+ }
+
+
+ void Response::payload( payload_t payload ) throw() {
+
+ _payload = payload;
+
+ // update encapsulated data
+ _header->update_encapsulated( _payload );
+
+ }
+
+
+ const payload_t &Response::payload() const throw() {
+ return _payload;
+ }
+
+} /* end of namespace icap */
+
diff --git a/lib/icap/response.h b/lib/icap/response.h
new file mode 100644
index 0000000..2977c07
--- /dev/null
+++ b/lib/icap/response.h
@@ -0,0 +1,65 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ICAP_RESPONSE_H
+#define ICAP_RESPONSE_H
+
+#include "common.h"
+#include "response_header.h"
+
+
+namespace icap {
+
+ class Response {
+ public:
+ Response( ResponseHeader * response_header = NULL );
+ Response( ResponseHeader::status_t status );
+ virtual ~Response();
+
+ /**
+ * Return the response header
+ * @return response header
+ */
+ ResponseHeader * const header() const throw();
+
+ /**
+ * Set the payload data for this response instance from a
+ * icap::payload_t data structure
+ *
+ * @param payload payload data structure
+ */
+ void payload( payload_t payload ) throw();
+
+ /**
+ * Return the payload data for this response instance
+ * @return payload data
+ */
+ const payload_t &payload() const throw();
+
+ private:
+ ResponseHeader * _header;
+ payload_t _payload;
+ bool _cleanup_header;
+
+ };
+
+} /* end of namespace icap */
+
+#endif /* !ICAP_RESPONSE_H */
+
diff --git a/lib/icap/response_header.cpp b/lib/icap/response_header.cpp
new file mode 100644
index 0000000..aa29e63
--- /dev/null
+++ b/lib/icap/response_header.cpp
@@ -0,0 +1,101 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "response_header.h"
+#include "util.h"
+#include <config.h>
+
+#include <ctime>
+#include <stdlib.h>
+
+
+namespace icap {
+
+ ResponseHeader::ResponseHeader( status_t status ) : Header() {
+
+ // FIXME: protocol shouldn't be hard-coded
+ _response.protocol = "ICAP/1.0";
+ _response.status = status;
+
+ // initialise default headers
+ init_defaults();
+
+ }
+
+ ResponseHeader::~ResponseHeader() { }
+
+
+ const std::string &ResponseHeader::protocol() const throw() {
+ return _response.protocol;
+ }
+
+
+ const ResponseHeader::status_t &ResponseHeader::status() const throw() {
+ return _response.status;
+ }
+
+
+ void ResponseHeader::init_defaults() throw() {
+
+ update_timestamp();
+ generate_istag();
+ attach( "Server", PACKAGE_STRING );
+
+ // close connection header
+ if ( _response.status != ResponseHeader::CONTINUE ) {
+ attach( "Connection" , "close" );
+ }
+
+ }
+
+
+ void ResponseHeader::update_timestamp() throw() {
+
+ time_t raw_time;
+ struct tm * time_info;
+ char buffer [80];
+
+ time( &raw_time );
+ time_info = localtime( &raw_time );
+
+ strftime ( buffer, 80, "%c %Z", time_info );
+ attach( "Date", buffer );
+
+ }
+
+
+ void ResponseHeader::generate_istag() throw() {
+
+ time_t raw_time;
+ clock_t clock_time;
+ std::string istag;
+
+ time( &raw_time );
+ clock_time = clock();
+
+ istag = "BITZ-";
+ istag.append( util::itoa( raw_time ) ).append( "-" );
+ istag.append( util::itoa( clock_time) );
+
+ attach( "ISTag", istag.substr( 0, 32 ) );
+
+ }
+
+} /* end of namespace icap */
+
diff --git a/lib/icap/response_header.h b/lib/icap/response_header.h
new file mode 100644
index 0000000..48d9ddd
--- /dev/null
+++ b/lib/icap/response_header.h
@@ -0,0 +1,78 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ICAP_RESPONSE_HEADER_H
+#define ICAP_RESPONSE_HEADER_H
+
+#include "header.h"
+
+
+namespace icap {
+
+ class ResponseHeader : public Header {
+ public:
+
+ enum status_t {
+ CONTINUE = 100,
+ OK = 200,
+ NO_CONTENT = 204,
+ BAD_REQUEST = 400,
+ NOT_FOUND = 404,
+ NOT_ALLOWED = 405,
+ REQ_TIMEOUT = 408,
+ SERVER_ERROR = 500,
+ NOT_IMPLEMENTED = 501,
+ BAD_GATEWAY = 502,
+ SERVICE_OVERLOADED = 503,
+ NOT_SUPPORTED = 505
+ };
+
+ struct response_t {
+ std::string protocol;
+ status_t status;
+ };
+
+ ResponseHeader( status_t status );
+ virtual ~ResponseHeader();
+
+ /**
+ * Return the response protocol
+ * @return protocol
+ */
+ const std::string &protocol() const throw();
+
+ /**
+ * Return the response status
+ * @return status
+ */
+ const status_t &status() const throw();
+
+ private:
+ response_t _response;
+
+ void update_timestamp() throw();
+ void generate_istag() throw();
+ void init_defaults() throw();
+
+ };
+
+} /* end of namespace icap */
+
+#endif /* !ICAP_RESPONSE_HEADER_H */
+
diff --git a/lib/icap/util.cpp b/lib/icap/util.cpp
new file mode 100644
index 0000000..c383261
--- /dev/null
+++ b/lib/icap/util.cpp
@@ -0,0 +1,693 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "util.h"
+
+#include <string>
+#include <algorithm>
+#include <locale>
+#include <iostream>
+
+
+namespace icap {
+
+ namespace util {
+
+ unsigned int hextodec( const std::string &hex ) throw() {
+
+ unsigned int dec;
+ std::stringstream ss;
+
+ ss << std::hex << hex;
+ ss >> dec;
+
+ return dec;
+
+ }
+
+
+ const std::string dectohex( const unsigned int &dec ) throw() {
+
+ std::string hex;
+ std::stringstream ss;
+
+ ss << std::hex << dec;
+ ss >> hex;
+
+ return hex;
+
+ }
+
+
+ int read_line( socketlibrary::TCPSocket * socket, char * buf, int buf_length, bool incl_endl ) throw() {
+
+ int i = 0, n;
+ char c = '\0';
+
+ while ( i < ( buf_length - 1 ) ) {
+
+ n = socket->recv( &c, 1 );
+
+ if ( n > 0 ) {
+ if ( c == '\r' ) {
+
+ if ( incl_endl ) {
+ buf[i] = c;
+ i++;
+ }
+
+ // peak for \n
+ n = socket->peek( &c, 1 );
+
+ if ( ( n > 0 ) && ( c == '\n' ) ) {
+
+ n = socket->recv( &c, 1 );
+
+ if ( incl_endl ) {
+ buf[i] = c;
+ i++;
+ }
+
+ break; // end of line
+ }
+ }
+
+ buf[i] = c;
+ i++;
+ } else {
+ break; // nothing read from socket
+ }
+
+ }
+
+ buf[i] = '\0';
+ return i;
+
+ }
+
+
+ std::string read_line( socketlibrary::TCPSocket * socket, bool incl_endl ) throw() {
+
+ int n;
+ std::string line;
+ char c = '\0';
+
+ try {
+
+ while ( ( n = socket->recv( &c, 1 ) ) > 0 ) {
+
+ if ( c == '\r' ) {
+
+ if ( incl_endl ) {
+ line += c;
+ }
+
+ // peak for \n
+ n = socket->peek( &c, 1 );
+
+ if ( ( n > 0 ) && ( c == '\n' ) ) {
+
+ n = socket->recv( &c, 1 );
+
+ if ( incl_endl ) {
+ line += c;
+ }
+
+ break; // end of line
+ }
+ }
+
+ line += c;
+
+ }
+
+ } catch ( socketlibrary::SocketException &sex ) {
+ // TODO: log error?
+ line = "";
+ }
+
+ return line;
+
+ }
+
+
+ std::string read_data( socketlibrary::TCPSocket * socket, int size ) throw() {
+
+ char buffer[ICAP_BUFFER_SIZE];
+ std::string data = "";
+ int n;
+
+ // loop until we have read all the bytes
+ while ( size > 0 ) {
+
+ try {
+
+ // read from socket
+ n = socket->recv( buffer, min( size, ICAP_BUFFER_SIZE ) );
+
+ // sanity check
+ if ( n == 0 ) {
+ break;
+ }
+
+ // append to data
+ data.append( buffer, n );
+
+ // update size with remaining bytes
+ size -= n;
+
+ } catch ( socketlibrary::SocketException &sex ) {
+ // TODO: log errors ??
+ }
+
+ }
+
+ return data;
+
+ }
+
+
+ unsigned int read_chunk_size( socketlibrary::TCPSocket * socket ) throw() {
+
+ std::string line;
+ std::vector<std::string> chunk_header;
+
+ line = read_line( socket );
+ chunk_header = split( line, ";" );
+
+ return hextodec( chunk_header.at( 0 ) );
+
+ }
+
+
+ void read_chunk_header( socketlibrary::TCPSocket * socket, chunk_t &chunk ) throw() {
+
+ std::string line;
+ std::vector<std::string> chunk_header;
+
+ line = read_line( socket );
+ chunk_header = split( line, ";" );
+
+ // sanity check
+ if ( chunk_header.size() > 0 ) {
+
+ // sanity check
+ if ( chunk_header.at( 0 ).size() > 0 ) {
+ chunk.size = hextodec( chunk_header.at( 0 ) );
+ }
+
+ // check for chunk-extension
+ if ( chunk_header.size() == 2 ) {
+ chunk.extention = trim( chunk_header.at( 1 ) );
+ }
+ }
+
+ return;
+
+ }
+
+
+ chunk_t read_chunk( socketlibrary::TCPSocket * socket ) throw() {
+
+ chunk_t chunk;
+ std::string line;
+ std::vector<std::string> chunk_header;
+
+ // initialise chunk
+ chunk.size = 0;
+ chunk.extention = "";
+ chunk.data = "";
+
+ // read chunk header
+ read_chunk_header( socket, chunk );
+
+ // read chunk data
+ if ( chunk.size > 0 ) {
+ chunk.data = read_data( socket, chunk.size );
+ }
+
+ // read \r\n ending for the chunk
+ read_data( socket, 2 );
+
+ return chunk;
+
+ }
+
+
+ std::string read_chunked( socketlibrary::TCPSocket * socket ) throw() {
+
+ unsigned int chunk_size = 0;
+ unsigned int offset = 0;
+ std::string chunked_data = "";
+
+ while ( ( chunk_size = read_chunk_size( socket ) ) > 0 ) {
+
+ offset = chunked_data.size();
+
+ // read chunk-data
+ chunked_data.append( read_data( socket, chunk_size ) );
+
+ // sanity check
+ if ( ( chunked_data.size() - offset ) != chunk_size ) {
+ // something went wrong
+ break;
+ }
+
+ // extra \r\n
+ read_data( socket, 2 );
+
+ }
+
+ // read until the end of chunked data
+ while ( read_line( socket, true ).size() > 2 ) ;
+
+ return chunked_data;
+
+ }
+
+
+ bool read_chunked_payload( socketlibrary::TCPSocket * socket, std::string &payload ) throw() {
+
+ chunk_t chunk;
+ bool ieof = false;
+
+ do {
+
+ // read chunk
+ chunk = read_chunk( socket );
+
+ // append to payload
+ payload.append( chunk.data );
+
+ // sanity check
+ if ( chunk.data.size() != chunk.size ) {
+ // something went wrong
+ break;
+ }
+
+ } while( chunk.size > 0 );
+
+ // check for ieof
+ if ( chunk.extention == "ieof" ) {
+ ieof = true;
+ }
+
+ return ieof;
+
+ }
+
+
+ bool send_line( const std::string &line, socketlibrary::TCPSocket * socket ) throw() {
+
+ try {
+ socket->send( line.c_str(), line.length() );
+ socket->send( "\r\n", 2 );
+ } catch ( socketlibrary::SocketException &sex ) {
+ // TODO: log errors
+ return false;
+ }
+
+ return true;
+
+ }
+
+
+ bool send_data( const std::string &data, socketlibrary::TCPSocket * socket ) throw() {
+
+ try {
+ socket->send( data.c_str(), data.size() );
+ } catch( socketlibrary::SocketException &sex ) {
+ // TODO: log errors
+ return false;
+ }
+
+ return true;
+
+ }
+
+
+ bool send_chunked( const std::string &data, socketlibrary::TCPSocket * socket ) throw() {
+
+ std::string chunked_data = "";
+ unsigned int offset = 0;
+ int chunks = 0;
+
+ // calculate the number of chunks we need
+ if ( data.size() > ICAP_BUFFER_SIZE ) {
+ chunks = ( data.size() / ICAP_BUFFER_SIZE );
+ }
+
+ try {
+
+ do {
+
+ // prepare data for this chunk
+ chunked_data = data.substr( offset, ICAP_BUFFER_SIZE );
+
+ // sanity check
+ if ( chunked_data.size() <= 0 ) {
+ // we shouldn't get here
+ break;
+ }
+
+ // update offset
+ offset += chunked_data.size();
+
+ // send chunk size
+ if (! send_line( dectohex( chunked_data.size() ), socket ) ) {
+ return false;
+ }
+
+ // send chunk
+ if (! send_data( chunked_data, socket ) ) {
+ return false;
+ }
+
+ chunks--;
+
+ } while ( chunks > 0 );
+
+ // end of chunk
+ if (! send_data( "\r\n0\r\n\r\n", socket ) ) {
+ return false;
+ }
+
+ } catch ( socketlibrary::SocketException &sex ) {
+ // TODO: log errors ??
+ return false;
+ }
+
+ return true;
+
+ }
+
+
+ std::vector<std::string> split( const std::string &str, const std::string &delimiter ) throw() {
+
+ std::vector<std::string> result;
+ size_t current;
+ size_t next = -1;
+
+ do {
+ current = next + 1;
+ next = str.find_first_of( delimiter, current );
+ result.push_back( str.substr( current, ( next - current ) ) );
+ } while ( next != std::string::npos );
+
+ return result;
+
+ }
+
+
+ std::string &ltrim( std::string &str ) throw() {
+ str.erase( str.begin(), std::find_if( str.begin(), str.end(), std::not1( std::ptr_fun<int, int>( std::isspace ) ) ) );
+ return str;
+ }
+
+
+ std::string &rtrim( std::string &str ) throw() {
+ str.erase( std::find_if( str.rbegin(), str.rend(), std::not1( std::ptr_fun<int, int>( std::isspace ) ) ).base(), str.end() );
+ return str;
+ }
+
+
+ std::string &trim( std::string &str ) throw() {
+ return ltrim( rtrim( str ) );
+ }
+
+
+ icap::RequestHeader * read_req_header( socketlibrary::TCPSocket * socket ) throw() {
+
+ char buffer[ICAP_BUFFER_SIZE];
+ int n = 0;
+ std::string data = "";
+
+ while ( ( n = read_line( socket, buffer, ICAP_BUFFER_SIZE, true ) ) > 2 ) {
+ data.append( buffer );
+ }
+
+ icap::RequestHeader * req_header = new icap::RequestHeader( data );
+ return req_header;
+
+ }
+
+
+ bool read_req_data( icap::Request * request, socketlibrary::TCPSocket * socket ) throw() {
+
+ int data_offset = 0;
+ int data_length = 0;
+ int data_read = 0;
+ std::vector<icap::Header::encapsulated_header_data_t> sorted_encaps_header;
+ std::vector<icap::Header::encapsulated_header_data_t>::iterator sorted_idx;
+
+ // payload
+ icap::payload_t payload;
+ payload.req_header = "";
+ payload.req_body = "";
+ payload.res_header = "";
+ payload.res_body = "";
+ payload.ieof = false;
+
+ // header
+ icap::Header * header = request->header();
+ sorted_encaps_header = header->sort_encapsulated_header();
+
+ // loop through the sorted header
+ for ( sorted_idx = sorted_encaps_header.begin(); sorted_idx != sorted_encaps_header.end(); sorted_idx++ ) {
+
+ // don't want to read negative headers
+ if ( sorted_idx->second < 0 ) {
+ continue;
+ }
+
+ // if this is the last header entity then check for chunked content
+ if ( sorted_idx == ( sorted_encaps_header.end() - 1 ) ) {
+
+ if ( sorted_idx->first == "req-body" ) {
+ payload.ieof = read_chunked_payload( socket, payload.req_body );
+ } else if ( sorted_idx->first == "res-body" ) {
+ payload.ieof = read_chunked_payload( socket, payload.res_body );
+ } else {
+ /*
+ * null-body is the only other legal possibility here
+ * we take that into account in the previous iterations
+ */
+ break;
+ }
+
+ } else {
+
+ data_offset = sorted_idx->second;
+ data_length = ( ( sorted_idx + 1 )->second - data_offset );
+
+
+ /* read request data */
+
+ // is there anything to read?
+ if ( data_length > 0 ) {
+
+ // update payload
+ if ( sorted_idx->first == "req-hdr" ) {
+ payload.req_header = read_data( socket, data_length );
+ } else if ( sorted_idx->first == "req-body" ) {
+ payload.req_body = read_data( socket, data_length );
+ } else if ( sorted_idx->first == "res-hdr" ) {
+ payload.res_header = read_data( socket, data_length );
+ } else if ( sorted_idx->first == "res-body" ) {
+ payload.res_body = read_data( socket, data_length );
+ } else {
+ // TODO: error?
+ }
+
+ }
+
+ }
+
+ }
+
+ // update request
+ request->payload( payload );
+
+ return true;
+
+ }
+
+
+ bool read_req_continue_data( icap::Request * request, socketlibrary::TCPSocket * socket ) throw() {
+
+ std::vector<icap::Header::encapsulated_header_data_t> sorted_encaps_header;
+ icap::Header::encapsulated_header_data_t header_idx;
+
+ // copy the payload from request so we can append to it
+ icap::payload_t payload;
+ payload.req_header = request->payload().req_header;
+ payload.req_body = request->payload().req_body;
+ payload.res_header = request->payload().res_header;
+ payload.res_body = request->payload().res_body;
+ payload.ieof = request->payload().ieof;
+
+ // header
+ icap::Header * header = request->header();
+ sorted_encaps_header = header->sort_encapsulated_header();
+
+ // sanity check
+ if ( sorted_encaps_header.size() > 0 ) {
+
+ // we are only interested in the last header entity
+ header_idx = sorted_encaps_header.back();
+
+ // read payload data
+ if ( header_idx.first == "req-body" ) {
+ payload.ieof = read_chunked_payload( socket, payload.req_body );
+ } else if ( header_idx.first == "res-body" ) {
+ payload.ieof = read_chunked_payload( socket, payload.res_body );
+ }
+
+ } else {
+
+ // something isn't quite right
+ return false;
+
+ }
+
+ // update request
+ request->payload( payload );
+
+ return true;
+
+ }
+
+
+ bool send_headers( icap::Header * header, socketlibrary::TCPSocket * socket ) throw() {
+
+ std::string line;
+ icap::Header::headers_index_t i;
+ icap::ResponseHeader::headers_t headers;
+
+ // headers
+ headers = header->headers();
+
+ for ( i = headers.begin(); i != headers.end(); i++ ) {
+
+ line = i->first;
+ line.append( ": " );
+ line.append( i->second );
+
+ if (! send_line( line, socket ) ) {
+ return false;
+ }
+
+ }
+
+ // send encapsulated header
+ line = "Encapsulated: ";
+ line.append( header->encapsulated_header_str() );
+ if (! send_line( line, socket ) ) {
+ return false;
+ }
+
+ // end of header
+ if (! send_data( "\r\n", socket ) ) {
+ return false;
+ }
+
+ return true;
+
+ }
+
+
+ bool send_response( icap::Response * response, socketlibrary::TCPSocket * socket ) throw() {
+
+ bool r_success = true;
+
+ icap::ResponseHeader * header;
+
+ // grab the response header
+ header = response->header();
+
+ // response status
+ std::string line = header->protocol();
+ line.append( " " );
+ line.append( itoa( header->status() ) );
+ line.append( " " );
+ line.append( response_status( header->status() ) );
+
+ r_success = send_line( line, socket );
+
+ // response headers
+ if ( r_success ) {
+ r_success = send_headers( header, socket );
+ }
+
+ // response content (if there are any)
+ if ( r_success ) {
+
+ // req-hdr
+ if ( response->payload().req_header.size() > 0 ) {
+ send_data( response->payload().req_header, socket );
+ }
+
+ // red-body
+ if ( response->payload().req_body.size() > 0 ) {
+ send_chunked( response->payload().req_body, socket );
+ }
+
+ // res-hdr
+ if ( response->payload().res_header.size() > 0 ) {
+ send_data( response->payload().res_header, socket );
+ }
+
+ // res-body
+ if ( response->payload().res_body.size() > 0 ) {
+ send_chunked( response->payload().res_body, socket );
+ }
+
+ }
+
+ return r_success;
+
+ }
+
+
+ const std::string response_status( const ResponseHeader::status_t &status ) throw() {
+
+ // FIXME: probably there's a better way of mapping this
+ std::map<ResponseHeader::status_t, std::string> status_text;
+
+ status_text[ResponseHeader::CONTINUE] = "Continue";
+ status_text[ResponseHeader::OK] = "OK";
+ status_text[ResponseHeader::NO_CONTENT] = "No modifications needed";
+ status_text[ResponseHeader::BAD_REQUEST] = "Bad request";
+ status_text[ResponseHeader::NOT_FOUND] = "ICAP Service not found";
+ status_text[ResponseHeader::NOT_ALLOWED] = "Method not allowed for service";
+ status_text[ResponseHeader::REQ_TIMEOUT] = "Request timeout";
+ status_text[ResponseHeader::SERVER_ERROR] = "Server error";
+ status_text[ResponseHeader::NOT_IMPLEMENTED] = "Method not implemented";
+ status_text[ResponseHeader::BAD_GATEWAY] = "Bad gateway";
+ status_text[ResponseHeader::SERVICE_OVERLOADED] = "Service overloaded";
+ status_text[ResponseHeader::NOT_SUPPORTED] = "ICAP version not supported by server";
+
+ return status_text[status];
+
+ }
+
+ } /* end of namespace util */
+
+} /* end of namespace icap */
+
diff --git a/lib/icap/util.h b/lib/icap/util.h
new file mode 100644
index 0000000..366bb9c
--- /dev/null
+++ b/lib/icap/util.h
@@ -0,0 +1,265 @@
+/*
+ * C++ ICAP library
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ICAP_UTIL_H
+#define ICAP_UTIL_H
+
+#include <sstream>
+
+#include <socket/socket.h>
+#include "request.h"
+#include "response.h"
+
+#ifndef ICAP_BUFFER_SIZE
+#define ICAP_BUFFER_SIZE 1024
+#endif
+
+
+namespace icap {
+
+ namespace util {
+
+ struct chunk_t {
+ unsigned int size;
+ std::string extention;
+ std::string data;
+ };
+
+ /**
+ * Convert a number into a string
+ *
+ * @param number number to be converted
+ * @return converted string
+ */
+ template <typename T> std::string itoa( T number ) {
+ std::ostringstream ss;
+ ss << number;
+
+ return ss.str();
+ }
+
+ /**
+ * Convert a hexadecimal number to a decimal number.
+ *
+ * @param hex hex to convert to
+ * @return converted decimal value
+ */
+ unsigned int hextodec( const std::string &hex ) throw();
+
+ /**
+ * Convert a decimal number to its hexadecimal value
+ *
+ * @param dec decimal number
+ * @return converted hex value
+ */
+ const std::string dectohex( const unsigned int &dec ) throw();
+
+ /**
+ * Read a line (ending with \r\n) from the socket
+ *
+ * @param socket socket to read from
+ * @param buf buffer to read the data into
+ * @param buf_length length / size of the buffer data is read into
+ * @param incl_endl (optional) switch to control whether to include \r\n in the read line,
+ * default is false.
+ * @return number of bytes read
+ */
+ int read_line( socketlibrary::TCPSocket * socket, char * buf, int buf_length, bool incl_endl = false ) throw();
+
+ /**
+ * Read a line (ending with \r\n) from the socket
+ *
+ * @param socket socket instance to read data from
+ * @param incl_endl (optional) switch to control whether to include \r\n in the read line,
+ * default is false.
+ * @return read data
+ */
+ std::string read_line( socketlibrary::TCPSocket * socket, bool incl_endl = false ) throw();
+
+ /**
+ * Read data from the socket
+ *
+ * @param socket socket instance to read data from
+ * @param size size / length of data to be read
+ * @return read data
+ */
+ std::string read_data( socketlibrary::TCPSocket * socket, int size ) throw();
+
+ /**
+ * Read chunk size. This is a helper method used by read_chunked().
+ *
+ * @param socket socket instance to read from
+ * @return chunk size
+ */
+ unsigned int read_chunk_size( socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Read chunk header from the given socket.
+ *
+ * @param socket socket instance to read data from
+ * @param chunk chunk data structure to store header data
+ */
+ void read_chunk_header( socketlibrary::TCPSocket * socket, chunk_t &chunk ) throw();
+
+ /**
+ * Read a data chunk from a HTTP chunked transfer encoded data stream.
+ *
+ * @param socket socket instance to read data from
+ * @return chunk data structure
+ */
+ chunk_t read_chunk( socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Read chunked data from the given socket
+ *
+ * @param socket socket instance to read data from
+ * @return read data (without the control characters)
+ */
+ std::string read_chunked( socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Read chunked payload data from the given socket
+ *
+ * @param socket socket instance to read data from
+ * @param payload payload to read data into
+ * @return boolean flag to denote the presence of "ieof"
+ */
+ bool read_chunked_payload( socketlibrary::TCPSocket * socket, std::string &payload ) throw();
+
+ /**
+ * Send / write a line (ending with \r\n) to the socket
+ *
+ * @param line line to send through the socket without the line-ending chars
+ * @param socket socket object to write the data to
+ * @return boolean to denote success or failure
+ */
+ bool send_line( const std::string &line, socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Send / write data to the socket.
+ * If chunked is set to true then data will be transferred using
+ * "chunked" transfer-encoding.
+ *
+ * @param data data to be sent
+ * @param socket socket instance to write to
+ * @return boolean to denote success or failure
+ */
+ bool send_data( const std::string &data, socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Send / write data to the socket using chunked transfer encoding
+ *
+ * @param data data to be sent
+ * @param socket socket instance to write to
+ * @return boolean to denote success or failure
+ */
+ bool send_chunked( const std::string &data, socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * split a string into a vector by the given delimiter
+ *
+ * @param str input string
+ * @param delimiter (optional) delimiter, defaults to " "
+ */
+ std::vector<std::string> split( const std::string &str, const std::string &delimiter = " " ) throw();
+
+ /**
+ * Left trim (trim from start) a passed in string
+ *
+ * @param str string to trim
+ * @return trimmed string
+ */
+ std::string &ltrim( std::string &str ) throw();
+
+ /**
+ * Right trim (trim from end) a passed in string
+ *
+ * @param str string to trim
+ * @return trimmed string
+ */
+ std::string &rtrim( std::string &str ) throw();
+
+ /**
+ * Trim (trim from both ends) a passed in string
+ *
+ * @param str string to trim
+ * @return trimmed string
+ */
+ std::string &trim( std::string &str ) throw();
+
+ /**
+ * Read icap request header from the socket passes in
+ *
+ * @param socket socket object to read data from
+ * @return icap request header object
+ */
+ icap::RequestHeader * read_req_header( socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Read icap request into the icap::Request instance
+ * using the socket passed in
+ *
+ * @param request request object to read data into
+ * @param socket socket object to read data from
+ * @return boolean to denote success or failure
+ */
+ bool read_req_data( icap::Request * request, socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Read icap request data after a '100 Continue' response. This will not look for any
+ * additional headers and will treat any data coming through the socket as payload data.
+ *
+ * @param request request object to read data into
+ * @param socket socket object to read data from
+ * @return boolean to denote success or failure
+ */
+ bool read_req_continue_data( icap::Request * request, socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Send / write header data to a socket
+ *
+ * @param headers headers to be sent
+ * @param socket socket object to write the data to
+ * @return boolean to denote success or failure
+ */
+ bool send_headers( icap::Header::headers_t headers, socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Output a response using the passed in icap::Response class to the
+ * passed in socket
+ *
+ * @param response response object to get the response data from
+ * @param socket socket object to send the data to
+ * @return boolean to denote success or failure
+ */
+ bool send_response( icap::Response * response, socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Returns the response status text for the given status
+ *
+ * @param status status to get the text for
+ */
+ const std::string response_status( const ResponseHeader::status_t &status ) throw();
+
+ } /* end of namespace util */
+
+} /* end of namespace icap */
+
+#endif /* !ICAP_UTIL_H */
+
diff --git a/lib/socket/Makefile.am b/lib/socket/Makefile.am
new file mode 100644
index 0000000..93f98a5
--- /dev/null
+++ b/lib/socket/Makefile.am
@@ -0,0 +1,4 @@
+## [socket-library] lib/
+noinst_LTLIBRARIES = libsocket.la
+libsocket_la_SOURCES = socket.h socket.cpp
+
diff --git a/lib/socket/socket.cpp b/lib/socket/socket.cpp
new file mode 100644
index 0000000..a086d95
--- /dev/null
+++ b/lib/socket/socket.cpp
@@ -0,0 +1,472 @@
+/*
+ * C++ sockets on Unix and Windows
+ * Copyright (C) 2002 <unknown>
+ * Copyright (C) 2010 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <socket.h>
+
+#ifndef WIN32
+ #include <sys/types.h> // For data types
+ #include <sys/socket.h> // For socket(), connect(), send(), and recv()
+ #include <netdb.h> // For gethostbyname()
+ #include <arpa/inet.h> // For inet_addr()
+ #include <unistd.h> // For close()
+#endif
+
+#include <cerrno> // For errno
+#include <cstring> // For strerror
+#include <cstdlib> // For atoi()
+
+using namespace std;
+
+namespace socketlibrary {
+
+#ifdef WIN32
+ static bool initialized = false;
+#endif
+
+// SocketException Code
+
+SocketException::SocketException(const string &message, bool inclSysMsg) throw() : userMessage(message) {
+ if (inclSysMsg) {
+ userMessage.append(": ");
+ userMessage.append(strerror(errno));
+ }
+}
+
+SocketException::~SocketException() throw() {
+}
+
+const char *SocketException::what() const throw() {
+ return userMessage.c_str();
+}
+
+// Function to fill in address structure given an address and port
+void fillAddr(const string &address, unsigned short port, sockaddr_in &addr) {
+
+ memset(&addr, 0, sizeof(addr)); // Zero out address structure
+ addr.sin_family = AF_INET; // Internet address
+
+ hostent *host; // Resolve name
+ if ((host = gethostbyname(address.c_str())) == NULL) {
+ // strerror() will not work for gethostbyname() and hstrerror()
+ // is supposedly obsolete
+ throw SocketException("Failed to resolve name (gethostbyname())");
+ }
+
+ addr.sin_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
+ addr.sin_port = htons(port); // Assign port in network byte order
+
+}
+
+// Socket Code
+
+Socket::Socket(int type, int protocol) throw(SocketException) {
+#ifdef WIN32
+ if (!initialized) {
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 0); // Request WinSock v2.0
+ if (WSAStartup(wVersionRequested, &wsaData) != 0) { // Load WinSock DLL
+ throw SocketException("Unable to load WinSock DLL");
+ }
+ initialized = true;
+ }
+#endif
+
+ // Make a new socket
+ if ((sock = socket(PF_INET, type, protocol)) < 0) {
+ throw SocketException("Socket creation failed (socket())", true);
+ }
+}
+
+Socket::Socket(int sock) {
+ this->sock = sock;
+}
+
+Socket::~Socket() {
+
+#ifdef WIN32
+ ::closesocket(sock);
+#else
+ ::close(sock);
+#endif
+ sock = -1;
+}
+
+string Socket::getLocalAddress() throw(SocketException) {
+ sockaddr_in addr;
+ unsigned int addr_len = sizeof(addr);
+
+ if (getsockname(sock, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
+ throw SocketException("Fetch of local address failed (getsockname())", true);
+ }
+ return inet_ntoa(addr.sin_addr);
+}
+
+unsigned short Socket::getLocalPort() throw(SocketException) {
+ sockaddr_in addr;
+ unsigned int addr_len = sizeof(addr);
+
+ if (getsockname(sock, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
+ throw SocketException("Fetch of local port failed (getsockname())", true);
+ }
+ return ntohs(addr.sin_port);
+}
+
+void Socket::setLocalPort(unsigned short localPort) throw(SocketException) {
+ // Bind the socket to its port
+ sockaddr_in localAddr;
+ memset(&localAddr, 0, sizeof(localAddr));
+ localAddr.sin_family = AF_INET;
+ localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ localAddr.sin_port = htons(localPort);
+
+ if (bind(sock, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) {
+ throw SocketException("Set of local port failed (bind())", true);
+ }
+}
+
+void Socket::setLocalAddressAndPort(const string &localAddress, unsigned short localPort) throw(SocketException) {
+ // Get the address of the requested host
+ sockaddr_in localAddr;
+ fillAddr(localAddress, localPort, localAddr);
+
+ if (bind(sock, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) {
+ throw SocketException("Set of local address and port failed (bind())", true);
+ }
+}
+
+void Socket::cleanUp() throw(SocketException) {
+#ifdef WIN32
+ if (WSACleanup() != 0) {
+ throw SocketException("WSACleanup() failed");
+ }
+#endif
+}
+
+unsigned short Socket::resolveService(const string &service, const string &protocol) {
+
+ struct servent *serv; /* Structure containing service information */
+
+ if ((serv = getservbyname(service.c_str(), protocol.c_str())) == NULL)
+ return atoi(service.c_str()); /* Service is port number */
+ else
+ return ntohs(serv->s_port); /* Found port (network byte order) by name */
+
+}
+
+// CommunicatingSocket Code
+
+CommunicatingSocket::CommunicatingSocket(int type, int protocol) throw(SocketException) : Socket(type, protocol) {
+}
+
+CommunicatingSocket::CommunicatingSocket(int newSD) : Socket(newSD) {
+}
+
+void CommunicatingSocket::connect(const string &foreignAddress, unsigned short foreignPort) throw(SocketException) {
+ // Get the address of the requested host
+ sockaddr_in destAddr;
+ fillAddr(foreignAddress, foreignPort, destAddr);
+
+ // Try to connect to the given port
+ if (::connect(sock, (sockaddr *) &destAddr, sizeof(destAddr)) < 0) {
+ throw SocketException("Connect failed (connect())", true);
+ }
+}
+
+void CommunicatingSocket::send(const void *buffer, int bufferLen) throw(SocketException) {
+ if (::send(sock, (raw_type *) buffer, bufferLen, 0) < 0) {
+ throw SocketException("Send failed (send())", true);
+ }
+}
+
+int CommunicatingSocket::recv(void *buffer, int bufferLen) throw(SocketException) {
+ int rtn;
+ if ((rtn = ::recv(sock, (raw_type *) buffer, bufferLen, 0)) < 0) {
+ throw SocketException("Received failed (recv()) for socket ", true);
+ }
+ return rtn;
+}
+
+int CommunicatingSocket::peek(void *buffer, int bufferLen) throw(SocketException) {
+ int rtn;
+ if ((rtn = ::recv(sock, (raw_type *) buffer, bufferLen, MSG_PEEK)) < 0) {
+ throw SocketException("Received failed (recv()) for socket ", true);
+ }
+ return rtn;
+}
+
+int CommunicatingSocket::readLine(char *buffer, int bufferLen, const char delimiter) throw(SocketException) {
+
+ int n, rc;
+ char c;
+
+ for (n = 1; n < bufferLen; n++) {
+ if ((rc = recv(&c, 1)) == 1) {
+ *buffer++ = c;
+ if (c == delimiter) {
+ break;
+ }
+ } else if (rc == 0) {
+ if (n == 1) {
+ return 0; // EOF, no data read
+ } else {
+ break; // EOF, read some data
+ }
+ } else {
+ throw SocketException("Failed to read data from socket ", true);
+ }
+ }
+
+ *buffer = '\0';
+ return n;
+
+}
+
+string CommunicatingSocket::getForeignAddress() throw(SocketException) {
+ sockaddr_in addr;
+ unsigned int addr_len = sizeof(addr);
+
+ if (getpeername(sock, (sockaddr *) &addr,(socklen_t *) &addr_len) < 0) {
+ throw SocketException("Fetch of foreign address failed (getpeername())", true);
+ }
+ return inet_ntoa(addr.sin_addr);
+}
+
+unsigned short CommunicatingSocket::getForeignPort() throw(SocketException) {
+ sockaddr_in addr;
+ unsigned int addr_len = sizeof(addr);
+
+ if (getpeername(sock, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
+ throw SocketException("Fetch of foreign port failed (getpeername())", true);
+ }
+ return ntohs(addr.sin_port);
+}
+
+// TCPSocket Code
+
+TCPSocket::TCPSocket() throw(SocketException) : CommunicatingSocket(SOCK_STREAM, IPPROTO_TCP) {
+}
+
+TCPSocket::TCPSocket(const string &foreignAddress, unsigned short foreignPort) throw(SocketException) : CommunicatingSocket(SOCK_STREAM, IPPROTO_TCP) {
+ connect(foreignAddress, foreignPort);
+}
+
+TCPSocket::TCPSocket(int newSD) : CommunicatingSocket(newSD) {
+}
+
+// TCPServerSocket Code
+
+TCPServerSocket::TCPServerSocket(unsigned short localPort, int queueLen) throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) {
+ setLocalPort(localPort);
+ setListen(queueLen);
+}
+
+TCPServerSocket::TCPServerSocket(const string &localAddress, unsigned short localPort, int queueLen) throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) {
+ setLocalAddressAndPort(localAddress, localPort);
+ setListen(queueLen);
+}
+
+TCPSocket *TCPServerSocket::accept() throw(SocketException) {
+ int newSD;
+ if ((newSD = ::accept(sock, NULL, 0)) < 0) {
+ throw SocketException("Accept failed (accept())", true);
+ }
+ return new TCPSocket(newSD);
+}
+
+void TCPServerSocket::setListen(int queueLen) throw(SocketException) {
+ if (listen(sock, queueLen) < 0) {
+ throw SocketException("Set listening socket failed (listen())", true);
+ }
+}
+
+// TCPServerSocketM Code
+
+TCPServerSocketM::TCPServerSocketM(unsigned short localPort, int queueLen) throw(SocketException) : TCPServerSocket(localPort, queueLen) {
+
+ FD_ZERO(&fds_master); // clear the master and temp sets
+
+ FD_SET(sock, &fds_master); // Add ourself to the master set
+ fdmax = sock; // keep track of the biggest file descriptor, only ourself for the moment
+
+}
+
+TCPSocket *TCPServerSocketM::accept() throw(SocketException) {
+
+ TCPSocket *newClient = TCPServerSocket::accept();
+ int newSD = newClient->sock;
+
+ FD_SET(newSD, &fds_master); // add to master set
+ if (newSD > fdmax) { // keep track of the max
+ fdmax = newSD;
+ }
+
+ v_clients.push_back(newClient);
+ return newClient;
+
+}
+
+bool TCPServerSocketM::pendingConnections() throw (SocketException) {
+
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+
+ read_fds = fds_master;
+ if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
+ throw SocketException("Failed to select read fds (select())", true);
+ }
+
+ if (FD_ISSET(sock, &read_fds)) {
+ return true;
+ } else {
+ return false;
+ }
+
+}
+
+int TCPServerSocketM::getWaitingClients(vector<TCPSocket *> &clients) throw (SocketException) {
+
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+
+ int cl_count = 0;
+
+ read_fds = fds_master;
+ if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
+ throw SocketException("Failed to select read fds (select())", true);
+ }
+
+ // resize the vector so it doesn't contain any old information
+ clients.resize(0);
+
+ for(unsigned int i=0; i<v_clients.size(); i++) {
+ if (FD_ISSET(v_clients.at(i)->sock, &read_fds)) { // we got one!!
+ clients.push_back(v_clients.at(i));
+ cl_count++;
+ }
+ }
+
+ return cl_count;
+}
+
+void TCPServerSocketM::closeClientConnection(TCPSocket * client) {
+
+ for(unsigned int i=0; i<v_clients.size(); i++) {
+ if (client->sock == v_clients.at(i)->sock) {
+ FD_CLR(client->sock, &fds_master);
+ v_clients.erase(v_clients.begin() + i);
+ delete client;
+ break;
+ }
+ }
+
+}
+
+
+// UDPSocket Code
+
+UDPSocket::UDPSocket() throw(SocketException) : CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) {
+ setBroadcast();
+}
+
+UDPSocket::UDPSocket(unsigned short localPort) throw(SocketException) : CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) {
+ setLocalPort(localPort);
+ setBroadcast();
+}
+
+UDPSocket::UDPSocket(const string &localAddress, unsigned short localPort) throw(SocketException) : CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) {
+ setLocalAddressAndPort(localAddress, localPort);
+ setBroadcast();
+}
+
+void UDPSocket::setBroadcast() {
+ // If this fails, we'll hear about it when we try to send. This will allow
+ // system that cannot broadcast to continue if they don't plan to broadcast
+ int broadcastPermission = 1;
+ setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (raw_type *) &broadcastPermission, sizeof(broadcastPermission));
+}
+
+void UDPSocket::disconnect() throw(SocketException) {
+ sockaddr_in nullAddr;
+ memset(&nullAddr, 0, sizeof(nullAddr));
+ nullAddr.sin_family = AF_UNSPEC;
+
+ // Try to disconnect
+ if (::connect(sock, (sockaddr *) &nullAddr, sizeof(nullAddr)) < 0) {
+#ifdef WIN32
+ if (errno != WSAEAFNOSUPPORT) {
+#else
+ if (errno != EAFNOSUPPORT) {
+#endif
+ throw SocketException("Disconnect failed (connect())", true);
+ }
+ }
+}
+
+void UDPSocket::sendTo(const void *buffer, int bufferLen, const string &foreignAddress, unsigned short foreignPort) throw(SocketException) {
+ sockaddr_in destAddr;
+ fillAddr(foreignAddress, foreignPort, destAddr);
+
+ // Write out the whole buffer as a single message.
+ if (sendto(sock, (raw_type *) buffer, bufferLen, 0, (sockaddr *) &destAddr, sizeof(destAddr)) != bufferLen) {
+ throw SocketException("Send failed (sendto())", true);
+ }
+}
+
+int UDPSocket::recvFrom(void *buffer, int bufferLen, string &sourceAddress, unsigned short &sourcePort) throw(SocketException) {
+ sockaddr_in clntAddr;
+ socklen_t addrLen = sizeof(clntAddr);
+ int rtn;
+ if ((rtn = recvfrom(sock, (raw_type *) buffer, bufferLen, 0, (sockaddr *) &clntAddr, (socklen_t *) &addrLen)) < 0) {
+ throw SocketException("Receive failed (recvfrom())", true);
+ }
+ sourceAddress = inet_ntoa(clntAddr.sin_addr);
+ sourcePort = ntohs(clntAddr.sin_port);
+ return rtn;
+}
+
+void UDPSocket::setMulticastTTL(unsigned char multicastTTL) throw(SocketException) {
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (raw_type *) &multicastTTL, sizeof(multicastTTL)) < 0) {
+ throw SocketException("Multicast TTL set failed (setsockopt())", true);
+ }
+}
+
+void UDPSocket::joinGroup(const string &multicastGroup) throw(SocketException) {
+ struct ip_mreq multicastRequest;
+
+ multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str());
+ multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (raw_type *) &multicastRequest, sizeof(multicastRequest)) < 0) {
+ throw SocketException("Multicast group join failed (setsockopt())", true);
+ }
+}
+
+void UDPSocket::leaveGroup(const string &multicastGroup) throw(SocketException) {
+ struct ip_mreq multicastRequest;
+
+ multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str());
+ multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (raw_type *) &multicastRequest, sizeof(multicastRequest)) < 0) {
+ throw SocketException("Multicast group leave failed (setsockopt())", true);
+ }
+}
+
+} // End of namespace SocketLibrary
diff --git a/lib/socket/socket.h b/lib/socket/socket.h
new file mode 100644
index 0000000..2c4d777
--- /dev/null
+++ b/lib/socket/socket.h
@@ -0,0 +1,427 @@
+/*
+ * C++ sockets on Unix and Windows
+ * Copyright (C) 2002 <unknown>
+ * Copyright (C) 2010 Uditha Atukorala
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SL_SOCKET_H
+#define SL_SOCKET_H
+
+#include <string> // For string
+#include <exception> // For exception class
+#include <vector> // For vectors
+
+#ifdef WIN32
+ #include <winsock.h> // For socket(), connect(), send(), and recv()
+ typedef int socklen_t;
+ typedef char raw_type; // Type used for raw data on this platform
+#else
+ #include <netinet/in.h> // For sockaddr_in
+ typedef void raw_type; // Type used for raw data on this platform
+#endif
+
+using namespace std;
+
+namespace socketlibrary {
+
+/**
+ * Function to fill in address structure given an address and port
+ */
+void fillAddr(const string &address, unsigned short port, sockaddr_in &addr);
+
+/**
+ * Signals a problem with the execution of a socket call.
+ */
+class SocketException : public exception {
+public:
+ /**
+ * Construct a SocketException with a explanatory message.
+ * @param message explanatory message
+ * @param incSysMsg true if system message (from strerror(errno))
+ * should be postfixed to the user provided message
+ */
+ SocketException(const string &message, bool inclSysMsg = false) throw();
+
+ /**
+ * Provided just to guarantee that no exceptions are thrown.
+ */
+ ~SocketException() throw();
+
+ /**
+ * Get the exception message
+ * @return exception message
+ */
+ const char *what() const throw();
+
+private:
+ string userMessage; // Exception message
+};
+
+
+/**
+ * Base class representing basic communication endpoint
+ */
+class Socket {
+public:
+ /**
+ * Close and deallocate this socket
+ */
+ ~Socket();
+
+ /**
+ * Get the local address
+ * @return local address of socket
+ * @exception SocketException thrown if fetch fails
+ */
+ string getLocalAddress() throw(SocketException);
+
+ /**
+ * Get the local port
+ * @return local port of socket
+ * @exception SocketException thrown if fetch fails
+ */
+ unsigned short getLocalPort() throw(SocketException);
+
+ /**
+ * Set the local port to the specified port and the local address
+ * to any interface
+ * @param localPort local port
+ * @exception SocketException thrown if setting local port fails
+ */
+ void setLocalPort(unsigned short localPort) throw(SocketException);
+
+ /**
+ * Set the local port to the specified port and the local address
+ * to the specified address. If you omit the port, a random port
+ * will be selected.
+ * @param localAddress local address
+ * @param localPort local port
+ * @exception SocketException thrown if setting local port or address fails
+ */
+ void setLocalAddressAndPort(const string &localAddress, unsigned short localPort = 0) throw(SocketException);
+
+ /**
+ * If WinSock, unload the WinSock DLLs; otherwise do nothing. We ignore
+ * this in our sample client code but include it in the library for
+ * completeness. If you are running on Windows and you are concerned
+ * about DLL resource consumption, call this after you are done with all
+ * Socket instances. If you execute this on Windows while some instance of
+ * Socket exists, you are toast. For portability of client code, this is
+ * an empty function on non-Windows platforms so you can always include it.
+ * @param buffer buffer to receive the data
+ * @param bufferLen maximum number of bytes to read into buffer
+ * @return number of bytes read, 0 for EOF, and -1 for error
+ * @exception SocketException thrown WinSock clean up fails
+ */
+ static void cleanUp() throw(SocketException);
+
+ /**
+ * Resolve the specified service for the specified protocol to the
+ * corresponding port number in host byte order
+ * @param service service to resolve (e.g., "http")
+ * @param protocol protocol of service to resolve. Default is "tcp".
+ */
+ static unsigned short resolveService(const string &service, const string &protocol = "tcp");
+
+private:
+ // Prevent the user from trying to use value semantics on this object
+ Socket(const Socket &sock);
+ void operator=(const Socket &sock);
+
+protected:
+ int sock; // Socket descriptor
+ Socket(int type, int protocol) throw(SocketException);
+ Socket(int sock);
+};
+
+/**
+ * Socket which is able to connect, send, and receive
+ */
+class CommunicatingSocket : public Socket {
+public:
+ /**
+ * Establish a socket connection with the given foreign
+ * address and port
+ * @param foreignAddress foreign address (IP address or name)
+ * @param foreignPort foreign port
+ * @exception SocketException thrown if unable to establish connection
+ */
+ void connect(const string &foreignAddress, unsigned short foreignPort) throw(SocketException);
+
+ /**
+ * Write the given buffer to this socket. Call connect() before
+ * calling send()
+ * @param buffer buffer to be written
+ * @param bufferLen number of bytes from buffer to be written
+ * @exception SocketException thrown if unable to send data
+ */
+ void send(const void *buffer, int bufferLen) throw(SocketException);
+
+ /**
+ * Read into the given buffer up to bufferLen bytes data from this
+ * socket. Call connect() before calling recv()
+ * @param buffer buffer to receive the data
+ * @param bufferLen maximum number of bytes to read into buffer
+ * @return number of bytes read, 0 for EOF, and -1 for error
+ * @exception SocketException thrown if unable to receive data
+ */
+ int recv(void *buffer, int bufferLen) throw(SocketException);
+
+ /**
+ * Read into the given buffer up to bufferLen bytes data from this
+ * socket but don't remove the read bytes from the socket read buffer.
+ * Call connect() before calling peek()
+ *
+ * @param buffer buffer to receive the data
+ * @param bufferLen maximum number of bytes to read into buffer
+ * @return number of bytes read, 0 for EOF, and -1 for error
+ * @exception SocketException thrown if unable to receive data
+ */
+ int peek(void *buffer, int bufferLen) throw(SocketException);
+
+ /**
+ * Read a line into the given buffer from this socket.
+ * Call connect() before calling recv()
+ *
+ * @param buffer buffer to receive the data
+ * @param bufferLen maximum number of bytes to read into buffer
+ * @param delimiter (optional) end of line delimiter
+ * @return number of bytes read, 0 for EOF, and -1 for error
+ * @exception SocketException thrown if unable to receive data
+ */
+ int readLine(char *buffer, int bufferLen, const char delimiter = '\n') throw(SocketException);
+
+ /**
+ * Get the foreign address. Call connect() before calling recv()
+ * @return foreign address
+ * @exception SocketException thrown if unable to fetch foreign address
+ */
+ string getForeignAddress() throw(SocketException);
+
+ /**
+ * Get the foreign port. Call connect() before calling recv()
+ * @return foreign port
+ * @exception SocketException thrown if unable to fetch foreign port
+ */
+ unsigned short getForeignPort() throw(SocketException);
+
+protected:
+ CommunicatingSocket(int type, int protocol) throw(SocketException);
+ CommunicatingSocket(int newSD);
+};
+
+/**
+ * TCP socket for communication with other TCP sockets
+ */
+class TCPSocket : public CommunicatingSocket {
+public:
+ /**
+ * Construct a TCP socket with no connection
+ * @exception SocketException thrown if unable to create TCP socket
+ */
+ TCPSocket() throw(SocketException);
+
+ /**
+ * Construct a TCP socket with a connection to the given foreign address
+ * and port
+ * @param foreignAddress foreign address (IP address or name)
+ * @param foreignPort foreign port
+ * @exception SocketException thrown if unable to create TCP socket
+ */
+ TCPSocket(const string &foreignAddress, unsigned short foreignPort) throw(SocketException);
+
+private:
+ friend class TCPServerSocket; // Access for TCPServerSocket::accept() connection creation
+ friend class TCPServerSocketM; // Access for TCPServerSocketM::accept() connection creation
+ TCPSocket(int newSD);
+};
+
+/**
+ * TCP socket class for servers
+ */
+class TCPServerSocket : public Socket {
+public:
+ /**
+ * Construct a TCP socket for use with a server, accepting connections
+ * on the specified port on any interface
+ * @param localPort local port of server socket, a value of zero will
+ * give a system-assigned unused port
+ * @param queueLen maximum queue length for outstanding
+ * connection requests (default 5)
+ * @exception SocketException thrown if unable to create TCP server socket
+ */
+ TCPServerSocket(unsigned short localPort, int queueLen = 5) throw(SocketException);
+
+ /**
+ * Construct a TCP socket for use with a server, accepting connections
+ * on the specified port on the interface specified by the given address
+ * @param localAddress local interface (address) of server socket
+ * @param localPort local port of server socket
+ * @param queueLen maximum queue length for outstanding
+ * connection requests (default 5)
+ * @exception SocketException thrown if unable to create TCP server socket
+ */
+ TCPServerSocket(const string &localAddress, unsigned short localPort, int queueLen = 5) throw(SocketException);
+
+ /**
+ * Blocks until a new connection is established on this socket or error
+ * @return new connection socket
+ * @exception SocketException thrown if attempt to accept a new connection fails
+ */
+ TCPSocket *accept() throw(SocketException);
+
+private:
+ void setListen(int queueLen) throw(SocketException);
+};
+
+/**
+ * TCP socket class for multi-client servers
+ */
+class TCPServerSocketM : public TCPServerSocket {
+public:
+
+ /**
+ * Construct a TCP socket for use with a server, accepting connections
+ * on the specified port on any interface
+ * @param localPort local port of server socket, a value of zero will
+ * give a system-assigned unused port
+ * @param queueLen maximum queue length for outstanding
+ * connection requests (default 5)
+ * @exception SocketException thrown if unable to create TCP server socket
+ */
+ TCPServerSocketM(unsigned short localPort, int queueLen = 5) throw(SocketException);
+
+ /**
+ * Blocks until a new connection is established on this socket or error
+ * @return new connection socket
+ * @exception SocketException thrown if attempt to accept a new connection fails
+ */
+ TCPSocket *accept() throw(SocketException);
+
+ /**
+ * Checks for incoming connections or error
+ * @return boolean (true if incoming connections are present)
+ * @exception SocketException thrown if attempt to check for new connections fail
+ */
+ bool pendingConnections() throw(SocketException);
+
+ /**
+ * Checks for read waiting clients
+ * @param clients a vector of TCPSocket pointers to get the waiting clients
+ * @return number of clients waiting
+ * @exception SocketException thrown if attempt to check for waiting clients fail
+ */
+ int getWaitingClients(vector<TCPSocket *> &clients) throw (SocketException);
+
+ /**
+ * Closes the connection to a client
+ * @param client the client TCPSocket to close
+ */
+ void closeClientConnection(TCPSocket *client);
+
+private:
+ fd_set fds_master; // master file descriptor list
+ int fdmax; // maximum file descriptor number
+
+ vector<TCPSocket *> v_clients;
+};
+
+/**
+ * UDP socket class
+ */
+class UDPSocket : public CommunicatingSocket {
+public:
+ /**
+ * Construct a UDP socket
+ * @exception SocketException thrown if unable to create UDP socket
+ */
+ UDPSocket() throw(SocketException);
+
+ /**
+ * Construct a UDP socket with the given local port
+ * @param localPort local port
+ * @exception SocketException thrown if unable to create UDP socket
+ */
+ UDPSocket(unsigned short localPort) throw(SocketException);
+
+ /**
+ * Construct a UDP socket with the given local port and address
+ * @param localAddress local address
+ * @param localPort local port
+ * @exception SocketException thrown if unable to create UDP socket
+ */
+ UDPSocket(const string &localAddress, unsigned short localPort) throw(SocketException);
+
+ /**
+ * Unset foreign address and port
+ * @return true if disassociation is successful
+ * @exception SocketException thrown if unable to disconnect UDP socket
+ */
+ void disconnect() throw(SocketException);
+
+ /**
+ * Send the given buffer as a UDP datagram to the
+ * specified address/port
+ * @param buffer buffer to be written
+ * @param bufferLen number of bytes to write
+ * @param foreignAddress address (IP address or name) to send to
+ * @param foreignPort port number to send to
+ * @return true if send is successful
+ * @exception SocketException thrown if unable to send datagram
+ */
+ void sendTo(const void *buffer, int bufferLen, const string &foreignAddress, unsigned short foreignPort) throw(SocketException);
+
+ /**
+ * Read read up to bufferLen bytes data from this socket. The given buffer
+ * is where the data will be placed
+ * @param buffer buffer to receive data
+ * @param bufferLen maximum number of bytes to receive
+ * @param sourceAddress address of datagram source
+ * @param sourcePort port of data source
+ * @return number of bytes received and -1 for error
+ * @exception SocketException thrown if unable to receive datagram
+ */
+ int recvFrom(void *buffer, int bufferLen, string &sourceAddress, unsigned short &sourcePort) throw(SocketException);
+
+ /**
+ * Set the multicast TTL
+ * @param multicastTTL multicast TTL
+ * @exception SocketException thrown if unable to set TTL
+ */
+ void setMulticastTTL(unsigned char multicastTTL) throw(SocketException);
+
+ /**
+ * Join the specified multicast group
+ * @param multicastGroup multicast group address to join
+ * @exception SocketException thrown if unable to join group
+ */
+ void joinGroup(const string &multicastGroup) throw(SocketException);
+
+ /**
+ * Leave the specified multicast group
+ * @param multicastGroup multicast group address to leave
+ * @exception SocketException thrown if unable to leave group
+ */
+ void leaveGroup(const string &multicastGroup) throw(SocketException);
+
+private:
+ void setBroadcast();
+};
+
+} // End of namespace SocketLibrary
+
+#endif /* !SL_SOCKET_H */