/
session.c
132 lines (109 loc) · 4.97 KB
/
session.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/***********************************************************************************************************************************
Socket Session
***********************************************************************************************************************************/
#include "build.auto.h"
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#include "common/debug.h"
#include "common/log.h"
#include "common/io/socket/client.h"
#include "common/io/socket/common.h"
#include "common/memContext.h"
#include "common/type/object.h"
#include "common/wait.h"
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
struct SocketSession
{
MemContext *memContext; // Mem context
SocketSessionType type; // Type (server or client)
int fd; // File descriptor
String *host; // Hostname or IP address
unsigned int port; // Port to connect to host on
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
};
OBJECT_DEFINE_MOVE(SOCKET_SESSION);
OBJECT_DEFINE_GET(Fd, , SOCKET_SESSION, int, fd);
OBJECT_DEFINE_GET(Type, const, SOCKET_SESSION, SocketSessionType, type);
OBJECT_DEFINE_FREE(SOCKET_SESSION);
/***********************************************************************************************************************************
Free connection
***********************************************************************************************************************************/
OBJECT_DEFINE_FREE_RESOURCE_BEGIN(SOCKET_SESSION, LOG, logLevelTrace)
{
close(this->fd);
}
OBJECT_DEFINE_FREE_RESOURCE_END(LOG);
/**********************************************************************************************************************************/
SocketSession *
sckSessionNew(SocketSessionType type, int fd, const String *host, unsigned int port, TimeMSec timeout)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(ENUM, type);
FUNCTION_LOG_PARAM(INT, fd);
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(UINT, port);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_END();
ASSERT(fd != -1);
ASSERT(host != NULL);
SocketSession *this = NULL;
MEM_CONTEXT_NEW_BEGIN("SocketSession")
{
this = memNew(sizeof(SocketSession));
*this = (SocketSession)
{
.memContext = MEM_CONTEXT_NEW(),
.type = type,
.fd = fd,
.host = strDup(host),
.port = port,
.timeout = timeout,
};
memContextCallbackSet(this->memContext, sckSessionFreeResource, this);
}
MEM_CONTEXT_NEW_END();
FUNCTION_LOG_RETURN(SOCKET_SESSION, this);
}
/**********************************************************************************************************************************/
void
sckSessionReadWait(SocketSession *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(SOCKET_SESSION, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
ASSERT(this->fd != -1);
// Initialize the file descriptor set used for select
fd_set selectSet;
FD_ZERO(&selectSet);
// We know the socket is not negative because it passed error handling, so it is safe to cast to unsigned
FD_SET((unsigned int)this->fd, &selectSet);
// Initialize timeout struct used for select. Recreate this structure each time since Linux (at least) will modify it.
struct timeval timeoutSelect;
timeoutSelect.tv_sec = (time_t)(this->timeout / MSEC_PER_SEC);
timeoutSelect.tv_usec = (time_t)(this->timeout % MSEC_PER_SEC * 1000);
// Determine if there is data to be read
int result = select(this->fd + 1, &selectSet, NULL, NULL, &timeoutSelect);
THROW_ON_SYS_ERROR_FMT(result == -1, AssertError, "unable to select from '%s:%u'", strPtr(this->host), this->port);
// If no data available after time allotted then error
if (!result)
{
THROW_FMT(
FileReadError, "timeout after %" PRIu64 "ms waiting for read from '%s:%u'", this->timeout, strPtr(this->host),
this->port);
}
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
String *
sckSessionToLog(const SocketSession *this)
{
return strNewFmt(
"{type: %s, fd %d, host: %s, port: %u, timeout: %" PRIu64 "}", this->type == sckSessionTypeClient ? "client" : "server",
this->fd, strPtr(this->host), this->port, this->timeout);
}