blob: b9dff8b35f50f11715be503051655a21a6452be9 [file] [log] [blame]
/*
* Copyright 2015 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "basics/sockutils.h"
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include "glog/logging.h"
#include "config/heron-config.h"
#include "basics/sprcodes.h"
#include "basics/spconsts.h"
sp_int32 SockUtils::setNonBlocking(sp_int32 fd) {
sp_int32 flags;
if ((flags = ::fcntl(fd, F_GETFL, 0)) < 0) return SP_NOTOK;
if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) return SP_NOTOK;
return SP_OK;
}
sp_int32 SockUtils::getSendBufferSize(sp_int32 fd, sp_int32 &size) {
socklen_t optlen = sizeof(sp_int32);
return ::getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, &optlen);
}
sp_int32 SockUtils::setSendBufferSize(sp_int32 fd, sp_int32 size) {
return ::setsockopt(fd, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char *>(&size), sizeof(size));
}
sp_int32 SockUtils::getRecvBufferSize(sp_int32 fd, sp_int32 &size) {
socklen_t optlen = sizeof(sp_int32);
return ::getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, &optlen);
}
sp_int32 SockUtils::setRecvBufferSize(sp_int32 fd, sp_int32 size) {
return ::setsockopt(fd, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char *>(&size), sizeof(size));
}
sp_int32 SockUtils::setKeepAlive(sp_int32 fd) {
sp_int32 alive = 1;
return ::setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<char *>(&alive),
sizeof(alive));
}
sp_int32 SockUtils::setReuseAddress(sp_int32 fd) {
sp_int32 sopt = 1;
return ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&sopt), sizeof(sopt));
}
sp_int32 SockUtils::setKeepIdleTime(sp_int32 fd, sp_int32 time) {
#if defined(IS_MACOSX)
sp_int32 on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) return SP_NOTOK;
return ::setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, reinterpret_cast<char *>(&time),
sizeof(time));
#else
return ::setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, reinterpret_cast<char *>(&time), sizeof(time));
#endif
}
sp_int32 SockUtils::setKeepIdleCount(sp_int32 fd, sp_int32 count) {
#if defined(IS_MACOSX)
return ::setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, reinterpret_cast<char *>(&count),
sizeof(count));
#else
return ::setsockopt(fd, SOL_TCP, TCP_KEEPCNT, reinterpret_cast<char *>(&count), sizeof(count));
#endif
}
sp_int32 SockUtils::setKeepIdleInterval(sp_int32 fd, sp_int32 interval) {
#if defined(IS_MACOSX)
return ::setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, reinterpret_cast<char *>(&interval),
sizeof(interval));
#else
return ::setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, reinterpret_cast<char *>(&interval),
sizeof(interval));
#endif
}
sp_int32 SockUtils::setKeepIdleParams(sp_int32 fd, sp_int32 time, sp_int32 count,
sp_int32 interval) {
if (SockUtils::setKeepIdleTime(fd, time) < 0) {
PLOG(ERROR) << "unable to set keep idle time ";
return SP_NOTOK;
}
if (SockUtils::setKeepIdleCount(fd, count) < 0) {
PLOG(ERROR) << "unable to set keep idle count ";
return SP_NOTOK;
}
if (SockUtils::setKeepIdleInterval(fd, interval) < 0) {
PLOG(ERROR) << "unable to set keep idle interval ";
return SP_NOTOK;
}
return SP_OK;
}
sp_int32 SockUtils::setTcpNoDelay(sp_int32 fd) {
sp_int32 on = 1;
return ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&on), sizeof(on));
}
sp_int32 SockUtils::setSocketDefaults(sp_int32 fd) {
// set the max send buffer size so that it makes the pipe fatter
sp_int32 send_buff;
// get the send buffer size
auto res = SockUtils::getSendBufferSize(fd, send_buff);
if (res == SP_NOTOK) {
PLOG(ERROR) << "Could not get the max SO_SNDBUF size";
} else {
// Set to a dummy high value. Linux will fall back to max. OSX will throw an error.
send_buff *= 100;
res = SockUtils::setSendBufferSize(fd, send_buff);
if (res == SP_NOTOK) {
PLOG(ERROR) << "Could not set SO_SNDBUF to max " << send_buff;
}
}
// set the max recv buffer size so that it makes the pipe fatter
// get the recv buffer size
sp_int32 recv_buff;
res = SockUtils::getRecvBufferSize(fd, recv_buff);
if (res == SP_NOTOK) {
PLOG(ERROR) << "Could not get the max SO_RCVBUF size";
} else {
// Set to a dummy high value. Linux will fall back to max. OSX will throw an error.
recv_buff *= 100;
res = SockUtils::setRecvBufferSize(fd, recv_buff);
if (res == SP_NOTOK) {
PLOG(ERROR) << "Could not set SO_RCVBUF to max " << recv_buff;
}
}
// make it non blocking
if (SockUtils::setNonBlocking(fd) < 0) {
PLOG(ERROR) << "unable to make socket non blocking";
return SP_NOTOK;
}
// enable keepalive for this socket
if (SockUtils::setKeepAlive(fd) < 0) {
PLOG(ERROR) << "setsockopt for keepalive failed in server";
return SP_NOTOK;
}
// set a reasonable keepalive
sp_int32 ka_idle = constTcpKeepAliveSecs;
sp_int32 ka_interval = constTcpKeepAliveProbeInterval;
sp_int32 ka_nprobes = constTcpKeepAliveProbes;
if (SockUtils::setKeepIdleParams(fd, ka_idle, ka_nprobes, ka_interval) < 0) {
PLOG(ERROR) << "setsockopt for keepalive failed ";
return SP_NOTOK;
}
if (SockUtils::setTcpNoDelay(fd)) {
PLOG(ERROR) << "setting tcp_nodelay failed ";
return SP_NOTOK;
}
return SP_OK;
}