//------------------------------------------------------------------------------
// File: NetworkStream.cc
// Author: Georgios Bitzes - CERN
//------------------------------------------------------------------------------
/************************************************************************
* qclient - A simple redis C++ client with support for redirects *
* Copyright (C) 2016 CERN/Switzerland *
* *
* 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, see .*
************************************************************************/
#include
#include
#include "NetworkStream.hh"
#include
#include
using namespace qclient;
static RecvStatus recvfn(int socket, char *buffer, int len, int timeout) {
int ret = ::recv(socket, buffer, len, timeout);
int err = errno;
// Case 1: EOF, no more data to read, connection is closed
if(ret == 0) {
return RecvStatus(false, 0, 0);
}
// Case 2: Non-blocking socket: no data to read, connection still alive
if(ret == -1 && (err == EWOULDBLOCK || err == EAGAIN)) {
return RecvStatus(true, err, 0);
}
// Case 3: Socket error, connection is closed
if(ret < 0) {
return RecvStatus(false, ret, 0);
}
// Case 4: We have data
return RecvStatus(true, 0, ret);
}
static LinkStatus sendfn(int socket, const char *buffer, int len, int timeout) {
return send(socket, buffer, len, timeout);
}
//------------------------------------------------------------------------------
// Create a network stream based on an existing socket fd.
//------------------------------------------------------------------------------
NetworkStream::NetworkStream(int fd_, TlsConfig tlsconfig)
: fd(fd_) {
isOk = (fd >= 0);
initializeTlsFliter(tlsconfig);
}
//------------------------------------------------------------------------------
// Initialize TlsFilter
//------------------------------------------------------------------------------
void NetworkStream::initializeTlsFliter(const TlsConfig &tlsconfig) {
if(tlsconfig.active) {
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
RecvFunction recvF = std::bind(recvfn, fd, _1, _2, _3);
SendFunction sendF = std::bind(sendfn, fd, _1, _2, 0);
tlsfilter.reset(new TlsFilter(tlsconfig, FilterType::CLIENT, recvF, sendF));
}
}
void NetworkStream::shutdown() {
if(fd < 0 || fdShutdown) return;
int ret = ::shutdown(fd, SHUT_RDWR);
fdShutdown = true;
isOk = false;
if(ret != 0 && errno != ENOTCONN) {
std::cerr << "qclient: Error during socket shutdown for fd " << fd << " towards " << host << ":" << port << ", retval: " << ret
<< ", errno: " << errno << std::endl;
}
// Don't close the socket yet! The fd might be in use (ie inside poll)
// Only close socket on object destruction.
}
void NetworkStream::close() {
int ret = ::close(fd);
if(ret != 0) {
std::cerr << "qclient: Error during socket close for fd " << fd << ", retval: " << ret
<< ", errno: " << errno << std::endl;
}
fd = -1;
}
RecvStatus NetworkStream::recv(char *buffer, int len, int timeout) {
if(tlsfilter) {
return tlsfilter->recv(buffer, len, 0);
}
return recvfn(fd, buffer, len, 0);
}
LinkStatus NetworkStream::send(const char *buff, int len) {
if(tlsfilter) {
return tlsfilter->send(buff, len);
}
return ::send(fd, buff, len, 0);
}
NetworkStream::~NetworkStream() {
tlsfilter.reset();
if(fd > 0) {
shutdown();
close();
}
}