From bb9bc9051629c3319c56785c2f4ae0e605d76329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sat, 21 Nov 2015 14:51:17 +0100 Subject: Initial import of bitz-server version 0.1.6-1 --- lib/Makefile.am | 3 + lib/icap/Makefile.am | 24 ++ lib/icap/common.h | 41 +++ lib/icap/header.cpp | 223 ++++++++++++++ lib/icap/header.h | 168 +++++++++++ lib/icap/request.cpp | 84 ++++++ lib/icap/request.h | 72 +++++ lib/icap/request_header.cpp | 113 +++++++ lib/icap/request_header.h | 84 ++++++ lib/icap/response.cpp | 81 +++++ lib/icap/response.h | 65 ++++ lib/icap/response_header.cpp | 101 +++++++ lib/icap/response_header.h | 78 +++++ lib/icap/util.cpp | 693 +++++++++++++++++++++++++++++++++++++++++++ lib/icap/util.h | 265 +++++++++++++++++ lib/socket/Makefile.am | 4 + lib/socket/socket.cpp | 472 +++++++++++++++++++++++++++++ lib/socket/socket.h | 427 ++++++++++++++++++++++++++ 18 files changed, 2998 insertions(+) create mode 100644 lib/Makefile.am create mode 100644 lib/icap/Makefile.am create mode 100644 lib/icap/common.h create mode 100644 lib/icap/header.cpp create mode 100644 lib/icap/header.h create mode 100644 lib/icap/request.cpp create mode 100644 lib/icap/request.h create mode 100644 lib/icap/request_header.cpp create mode 100644 lib/icap/request_header.h create mode 100644 lib/icap/response.cpp create mode 100644 lib/icap/response.h create mode 100644 lib/icap/response_header.cpp create mode 100644 lib/icap/response_header.h create mode 100644 lib/icap/util.cpp create mode 100644 lib/icap/util.h create mode 100644 lib/socket/Makefile.am create mode 100644 lib/socket/socket.cpp create mode 100644 lib/socket/socket.h (limited to 'lib') 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 + + +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 +#include + + +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 list_data; + std::vector 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::sort_encapsulated_header() { + + std::vector 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 +#include +#include +#include + + +namespace icap { + + class Header { + public: + + /* headers data type */ + typedef std::map headers_t; + + /* headers iterator type */ + typedef headers_t::iterator headers_index_t; + + /* encapsulated header type */ + typedef std::map encapsulated_header_t; + + /* encapsulated header iterator type */ + typedef encapsulated_header_t::iterator encapsulated_header_index_t; + + /* encapsulated header data type */ + typedef std::pair 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 { + 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 + * + *
+		*   e.g.
+		*   Host: icap-server.net
+		*   Encapsulated: req-hdr=0, null-body=170
+		*   [key]: [value]
+		*   
+ * + * @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. + * + *
+		*   e.g.
+		*   Encapsulated: req-hdr=0, req-body=412
+		*   Encapsulated: req-hdr=0, res-hdr=822, res-body=1655
+		*   Encapsulated: [header_value]
+		*   
+ * + * @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 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 + + +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 data; + + _raw_data = raw_data; + data = util::split( raw_data, "\r\n" ); + + if ( data.size() > 0 ) { + + std::vector header_data; + std::vector 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 +#include + + +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 + +#include +#include + + +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 +#include +#include +#include + + +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 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 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 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 split( const std::string &str, const std::string &delimiter ) throw() { + + std::vector 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 <rim( std::string &str ) throw() { + str.erase( str.begin(), std::find_if( str.begin(), str.end(), std::not1( std::ptr_fun( 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( 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 sorted_encaps_header; + std::vector::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 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 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 + +#include +#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 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 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 <rim( 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 + * 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 + +#ifndef WIN32 + #include // For data types + #include // For socket(), connect(), send(), and recv() + #include // For gethostbyname() + #include // For inet_addr() + #include // For close() +#endif + +#include // For errno +#include // For strerror +#include // 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 &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; isock, &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; isock == 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 + * 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 // For string +#include // For exception class +#include // For vectors + +#ifdef WIN32 + #include // For socket(), connect(), send(), and recv() + typedef int socklen_t; + typedef char raw_type; // Type used for raw data on this platform +#else + #include // 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 &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 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 */ -- cgit v1.2.3