Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/commands-handler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ jobs:
run: echo -e "\033[38;2;19;181;255mThis is regular commit which should be ignored.\033[0m"
- name: Checkout repository
if: steps.user-check.outputs.expected-user == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ secrets.GH_TOKEN }}
- name: Checkout release actions
if: steps.user-check.outputs.expected-user == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: pubnub/client-engineering-deployment-tools
ref: v1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ runs:
${{ inputs.os }}-cgreen-${{ inputs.version }}-
- name: Checkout Unit Test framework
if: steps.unit-test-framework.outputs.cache-hit != 'true'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: cgreen-devs/cgreen
ref: ${{ matrix.cgreen }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
release: ${{ steps.check.outputs.ready }}
steps:
- name: Checkout actions
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: pubnub/client-engineering-deployment-tools
ref: v1
Expand All @@ -34,13 +34,13 @@ jobs:
group: organization/Default
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# This should be the same as the one specified for on.pull_request.branches
ref: master
token: ${{ secrets.GH_TOKEN }}
- name: Checkout actions
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: pubnub/client-engineering-deployment-tools
ref: v1
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ jobs:
# group: organization/macos-gh
steps:
- name: Checkout project
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ secrets.GH_TOKEN }}
- name: Checkout actions
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: pubnub/client-engineering-deployment-tools
ref: v1
Expand Down Expand Up @@ -59,11 +59,11 @@ jobs:
sudo apt-get install -y ninja-build libboost-all-dev libssl-dev libgtest-dev nlohmann-json3-dev libasio-dev libtclap-dev g++ cmake
sudo gem install cucumber
- name: Checkout project
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ secrets.GH_TOKEN }}
- name: Checkout mock-server action
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: pubnub/client-engineering-deployment-tools
ref: v1
Expand All @@ -84,7 +84,7 @@ jobs:
${{ runner.os }}-cucumber-cpp-
- name: Checkout Cucumber
if: steps.cucumber-cpp.outputs.cache-hit != 'true'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: cucumber/cucumber-cpp
ref: c79100eb70fbb34f6ea10030cec051c2cc9f7961
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/run-validations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ jobs:
group: organization/Default
steps:
- name: Checkout project
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Checkout validator action
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: pubnub/client-engineering-deployment-tools
ref: v1
Expand Down
20 changes: 20 additions & 0 deletions core/pbauto_heartbeat.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ static int copy_context_settings(pubnub_t* pb_clone, pubnub_t const* pb)
pb_clone->origin = pb->origin;
}
pb_clone->options.use_http_keep_alive = pb->options.use_http_keep_alive;
pb_clone->options.tcp_keepalive.enabled = pb->options.tcp_keepalive.enabled;
pb_clone->options.tcp_keepalive.time = pb->options.tcp_keepalive.time;
pb_clone->options.tcp_keepalive.interval = pb->options.tcp_keepalive.interval;
pb_clone->options.tcp_keepalive.probes = pb->options.tcp_keepalive.probes;
#if PUBNUB_USE_IPV6 && defined(PUBNUB_CALLBACK_API)
pb_clone->options.ipv6_connectivity = pb->options.ipv6_connectivity;
#endif
Expand Down Expand Up @@ -252,6 +256,14 @@ static void auto_heartbeat_callback(pubnub_t* heartbeat_pb,
if (pubsub_keys_changed(heartbeat_pb, pb)) {
pubnub_init(
heartbeat_pb, pb->core.publish_key, pb->core.subscribe_key);
if (pbccTrue == pb->options.tcp_keepalive.enabled) {
pubnub_use_tcp_keep_alive(
heartbeat_pb,
pb->options.tcp_keepalive.time,
pb->options.tcp_keepalive.interval,
pb->options.tcp_keepalive.probes);
} else if (pbccFalse == pb->options.tcp_keepalive.enabled)
pubnub_dont_use_tcp_keep_alive(heartbeat_pb);

heartbeat_thump(pb, heartbeat_pb);
}
Expand All @@ -275,6 +287,14 @@ static pubnub_t* init_new_thumper_pb(pubnub_t* pb, unsigned i)
}
pubnub_mutex_lock(pb->monitor);
pubnub_init(pb_new, pb->core.publish_key, pb->core.subscribe_key);
if (pbccTrue == pb->options.tcp_keepalive.enabled) {
pubnub_use_tcp_keep_alive(
pb_new,
pb->options.tcp_keepalive.time,
pb->options.tcp_keepalive.interval,
pb->options.tcp_keepalive.probes);
} else if (pbccFalse == pb->options.tcp_keepalive.enabled)
pubnub_dont_use_tcp_keep_alive(pb_new);
pubnub_mutex_unlock(pb->monitor);

pubnub_mutex_lock(pb_new->monitor);
Expand Down
3 changes: 3 additions & 0 deletions core/pbpal.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ int pbpal_close(pubnub_t *pb);
/** Sets blocking I/O option on the context for the communication */
int pbpal_set_blocking_io(pubnub_t *pb);

/** Sets user-provided TCP Keep-Alive configuration for active connection. */
void pbpal_set_tcp_keepalive(const pubnub_t *pb);

/** Frees-up any resources allocated by the PAL for the given
context. After this call, context is not safe for use by PAL any
more (it is assumed it will be freed-up by the caller).
Expand Down
24 changes: 24 additions & 0 deletions core/pubnub_internal_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,27 @@ struct pbntlm_context {

typedef struct pbntlm_context pbntlm_ctx_t;

/** TCP Keep-Alive configuration object. */
typedef struct pubnub_tcp_keepalive_ {
/** Whether TCP Keep-Alive should be used or not. */
enum pubnub_tribool enabled;

/** The time in seconds a socket needs to be @c idle before the first
keep-alive probe is sent.
*/
uint8_t time;

/** The number of seconds that should pass between sends of
keep-alive probes if the last one wasn't acknowledged.
*/
uint8_t interval;

/** The number of times a probe will be sent and not acknowledged
before the connection is deemed broken.
*/
uint8_t probes;
} pubnub_tcp_keepalive;

struct pubnub_options {
#if PUBNUB_BLOCKING_IO_SETTABLE
/** Indicates whether to use blocking I/O. Not implemented if
Expand All @@ -214,6 +235,9 @@ struct pubnub_options {
*/
bool use_http_keep_alive : 1;

/** Per-context (because of one request per-context) TCP Keep-Alive
configuration. */
pubnub_tcp_keepalive tcp_keepalive;
#if PUBNUB_USE_IPV6
/* Connectivity type(true-Ipv6/false-Ipv4) chosen on a given context */
bool ipv6_connectivity : 1;
Expand Down
5 changes: 1 addition & 4 deletions core/pubnub_netcore.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,7 @@ static enum pubnub_state close_kept_alive_connection(struct pubnub_* pb)
pbpal_forget(pb);
return PBS_IDLE;
}
else {
return PBS_KEEP_ALIVE_WAIT_CLOSE;
}
return PBS_KEEP_ALIVE_WAIT_CLOSE;
}


Expand Down Expand Up @@ -1085,7 +1083,6 @@ int pbnc_fsm(struct pubnub_* pb)
pb->state = PBS_RX_HEADERS;
goto next_state;
case PNR_CONNECTION_TIMEOUT:
case PNR_TIMEOUT:
case PNR_IO_ERROR:
if (pb->flags.started_while_kept_alive) {
pb->state = close_kept_alive_connection(pb);
Expand Down
23 changes: 23 additions & 0 deletions core/pubnub_pubsubapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ pubnub_t* pubnub_init(pubnub_t* p, const char* publish_key, const char* subscrib
p->state = PBS_IDLE;
p->trans = PBTT_NONE;
p->options.use_http_keep_alive = true;

// Setting default TCP keep-alive options.
p->options.tcp_keepalive.enabled = pbccTrue;
p->options.tcp_keepalive.time = 60;
p->options.tcp_keepalive.interval = 20;
p->options.tcp_keepalive.probes = 3;
#if !defined(PUBNUB_CALLBACK_API) || defined(PUBNUB_NTF_RUNTIME_SELECTION)
p->should_stop_await = false;
#endif
Expand Down Expand Up @@ -540,3 +546,20 @@ void pubnub_dont_use_http_keep_alive(pubnub_t* p)
{
p->options.use_http_keep_alive = 0;
}

void pubnub_use_tcp_keep_alive(
pubnub_t* pb,
const uint8_t time,
const uint8_t interval,
const uint8_t probes)
{
pb->options.tcp_keepalive.enabled = pbccTrue;
pb->options.tcp_keepalive.time = time;
pb->options.tcp_keepalive.interval = interval;
pb->options.tcp_keepalive.probes = probes;
}

void pubnub_dont_use_tcp_keep_alive(pubnub_t* pb)
{
pb->options.tcp_keepalive.enabled = pbccFalse;
}
34 changes: 34 additions & 0 deletions core/pubnub_pubsubapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,5 +394,39 @@ PUBNUB_EXTERN void pubnub_use_http_keep_alive(pubnub_t* p);
*/
PUBNUB_EXTERN void pubnub_dont_use_http_keep_alive(pubnub_t* p);

/** Enable the use of TCP Keep-Alive ("probes") on the context @p pb .
*
* @b Defaults:
* - @c time: @b 60 seconds
* - @c interval: @b 20 seconds
* - @c probes: @b 3
*
* @b Important: this option works well @b only together with HTTP Keep-Alive, which
* is managed by @c pubnub_use_http_keep_alive and
* @c pubnub_dont_use_http_keep_alive.
*
* @param pb Pointer to the PubNub context which TCP KA should be enabled.
* @param time The time in seconds a socket needs to be @c idle before the
* first keep-alive probe is sent.
* @param interval The number of seconds that should pass between sends of
* keep-alive probes if the last one wasn't acknowledged.
* @param probes The number of times a probe will be sent and not acknowledged
* before the connection is deemed broken.
*/
PUBNUB_EXTERN void pubnub_use_tcp_keep_alive(
pubnub_t* pb,
uint8_t time,
uint8_t interval,
uint8_t probes);

/** Disables the use of TCP Keep-Alive ("probes") on the context @p pb .
*
* @b Important: this option works @b only together with HTTP Keep-Alive, which
* is managed by @c pubnub_use_http_keep_alive and
* @c pubnub_dont_use_http_keep_alive.
*
* @param pb Pointer to the PubNub context which TCP KA should be disabled.
*/
PUBNUB_EXTERN void pubnub_dont_use_tcp_keep_alive(pubnub_t* pb);

#endif /* !defined INC_PUBNUB_PUBSUBAPI */
17 changes: 17 additions & 0 deletions cpp/pubnub_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1933,6 +1933,23 @@ class context {
pubnub_dont_use_http_keep_alive(d_pb);
}

/// Use of TCP Keep-Alive ("probes") on the context.
/// @see pubnub_use_tcp_keep_alive
void use_tcp_keep_alive(
uint8_t time,
uint8_t interval,
uint8_t probes)
{
pubnub_use_tcp_keep_alive(d_pb, time, interval, probes);
}

/// Don't use of TCP Keep-Alive ("probes") on the context.
/// @see pubnub_dont_use_tcp_keep_alive
void dont_use_tcp_keep_alive()
{
pubnub_dont_use_tcp_keep_alive(d_pb);
}

#if PUBNUB_PROXY_API
/// Manually set a proxy to use
/// @see pubnub_set_proxy_manual
Expand Down
42 changes: 40 additions & 2 deletions freertos/pbpal_resolv_and_connect_freertos_tcp.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
#include "lwip/dns.h"
#include "lwip/inet.h"
#include "lwip/sockets.h"
#include "lwip/ip_addr.h"
#include "lwip/netdb.h"
#include "lwip/inet.h"
#include "lwip/dns.h"
#include "pbpal.h"

#include "pubnub_internal.h"
Expand Down Expand Up @@ -44,6 +45,7 @@ enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t *pb)
if (pb->pal.socket == SOCKET_INVALID) {
return pbpal_connect_resource_failure;
}
pbpal_set_tcp_keepalive(pb);
if (connect(pb->pal.socket, (const struct sockaddr*) &addr, sizeof addr) != 0) {
closesocket(pb->pal.socket);
pb->pal.socket = SOCKET_INVALID;
Expand All @@ -61,6 +63,7 @@ enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t *pb)
if (pb->pal.socket == SOCKET_INVALID) {
return pbpal_connect_resource_failure;
}
pbpal_set_tcp_keepalive(pb);
if (connect(pb->pal.socket, &addr, sizeof addr) != 0) {
closesocket(pb->pal.socket);
pb->pal.socket = SOCKET_INVALID;
Expand Down Expand Up @@ -101,3 +104,38 @@ int pbpal_dns_rotate_server(pubnub_t *pb)
}
#endif /* PUBNUB_CHANGE_DNS_SERVERS */
#endif /* defined(PUBNUB_CALLBACK_API) */

void pbpal_set_tcp_keepalive(const pubnub_t *pb)
{
if (pb->pal.socket == SOCKET_INVALID) return;
const pubnub_tcp_keepalive keepalive = pb->options.tcp_keepalive;
const pb_socket_t skt = pb->pal.socket;

const int enabled = pbccTrue == keepalive.enabled ? 1 : 0;
(void)setsockopt(skt, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled));

if (pbccTrue != keepalive.enabled ||
(0 == keepalive.time && 0 == keepalive.interval)) return;

const int time = keepalive.time;

if (time > 0) {
#if defined(TCP_KEEPIDLE)
(void)setsockopt(skt, IPPROTO_TCP, TCP_KEEPIDLE, &time, sizeof(time));
#elif defined(TCP_KEEPALIVE)
(void)setsockopt(skt, IPPROTO_TCP, TCP_KEEPALIVE, &time, sizeof(time));
#endif
}

#if defined(TCP_KEEPINTVL)
const int interval = keepalive.interval;
if (interval > 0)
(void)setsockopt(skt, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
#endif

#if defined(TCP_KEEPCNT)
const int probes = keepalive.probes;
if (probes > 0)
(void)setsockopt(skt, IPPROTO_TCP, TCP_KEEPCNT, &probes, sizeof(probes));
#endif
}
13 changes: 12 additions & 1 deletion lib/sockets/pbpal_handle_socket_error.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,20 @@ enum pubnub_res pbpal_handle_socket_error(int socket_result,
return PNR_IN_PROGRESS;
}
else {
// Whether socket already in use for data sending / receiving or not.
const bool handles_data = STATE_READ == pb->sock_state ||
STATE_NEWDATA_EXHAUSTED == pb->sock_state ||
STATE_READ_LINE == pb->sock_state ||
STATE_SENDING_DATA == pb->sock_state;
const bool timed_out = socket_timed_out();
pb->sock_state = STATE_NONE;
pbpal_report_error_from_environment(pb, file, line);
return socket_timed_out() ? PNR_CONNECTION_TIMEOUT : PNR_IO_ERROR;

// Report data sending / receive timeout, which happened in the
// middle of the operation.
if (timed_out && handles_data) return PNR_TIMEOUT;

return timed_out ? PNR_CONNECTION_TIMEOUT : PNR_IO_ERROR;
}
}
else if (0 == socket_result) {
Expand Down
Loading
Loading