From f8320a22d54e51052478ce6421a9cfc454a4255e Mon Sep 17 00:00:00 2001
From: Kumar Kaushik <kaushikk@vmware.com>
Date: Tue, 20 Mar 2018 16:36:00 -0700
Subject: [PATCH] Support persistent connection
Change-Id: I05d60c56f5f9e62e5c7b0bd6c10b7fad9e0e329a
---
common/sockinterface.c | 55 +++++---
include/vmrestcommon.h | 7 +
include/vmsock.h | 4 +-
server/restengine/httpProtocolHead.c | 145 +++++++++++----------
server/restengine/libmain.c | 2 +-
server/restengine/prototype.h | 5 -
test/scripts/BUGS_TC/BUG-2076723/README | 26 ++++
.../BUGS_TC/BUG-2076723/persistentConnection.c | 121 +++++++++++++++++
transport/api/api.c | 4 +-
transport/posix/prototypes.h | 2 +-
transport/posix/socket.c | 27 +++-
11 files changed, 288 insertions(+), 110 deletions(-)
create mode 100644 test/scripts/BUGS_TC/BUG-2076723/README
create mode 100644 test/scripts/BUGS_TC/BUG-2076723/persistentConnection.c
diff --git a/common/sockinterface.c b/common/sockinterface.c
index 197dd48..103c69c 100644
--- a/common/sockinterface.c
+++ b/common/sockinterface.c
@@ -476,6 +476,7 @@ VmRESTTcpReceiveNewData(
uint32_t nProcessed = 0;
uint32_t nBufLen = 0;
BOOLEAN bNextIO = FALSE;
+ BOOLEAN bKeepConnOpen = FALSE;
if (!pSocket || !pRESTHandle || !pQueue)
{
@@ -543,6 +544,16 @@ VmRESTTcpReceiveNewData(
{
pSetReq = pRequest;
}
+ else
+ {
+ /**** Verify and act on persistent connection requests ****/
+ dwError = VmRESTEntertainPersistentConn(
+ pRESTHandle,
+ pRequest,
+ &bKeepConnOpen
+ );
+ BAIL_ON_VMREST_ERROR(dwError);
+ }
/**** Save state of request processing in socket context ****/
dwError = VmwSockSetRequestHandle(
@@ -550,31 +561,35 @@ VmRESTTcpReceiveNewData(
pSocket,
pSetReq,
nProcessed,
- pQueue
+ bKeepConnOpen
);
BAIL_ON_VMREST_ERROR(dwError);
cleanup:
- if (!bNextIO && dwError != REST_ENGINE_ERROR_DOUBLE_FAILURE)
- {
- VMREST_LOG_DEBUG(pRESTHandle,"%s","Calling closed connection....");
- /**** Close connection ****/
- VmRESTDisconnectClient(
- pRESTHandle,
- pSocket
- );
-
- /**** free request object memory ****/
- if (pRequest)
- {
- VmRESTFreeRequestHandle(
- pRESTHandle,
- pRequest
- );
- pRequest = NULL;
- }
- }
+ if (!bNextIO && dwError != REST_ENGINE_ERROR_DOUBLE_FAILURE)
+ {
+
+ if (!bKeepConnOpen)
+ {
+ VMREST_LOG_DEBUG(pRESTHandle,"%s","Calling closed connection....");
+ /**** Close connection ****/
+ VmRESTDisconnectClient(
+ pRESTHandle,
+ pSocket
+ );
+ }
+
+ /**** free request object memory ****/
+ if (pRequest)
+ {
+ VmRESTFreeRequestHandle(
+ pRESTHandle,
+ pRequest
+ );
+ pRequest = NULL;
+ }
+ }
return dwError;
diff --git a/include/vmrestcommon.h b/include/vmrestcommon.h
index 3b7f17c..16f90ad 100644
--- a/include/vmrestcommon.h
+++ b/include/vmrestcommon.h
@@ -350,6 +350,13 @@ VmRESTProcessBuffer(
uint32_t* nProcessed
);
+uint32_t
+VmRESTEntertainPersistentConn(
+ PVMREST_HANDLE pRESTHandle,
+ PREST_REQUEST pRequest,
+ BOOLEAN* bKeepOpen
+ );
+
uint32_t
VmRESTSendFailureResponse(
PVMREST_HANDLE pRESTHandle,
diff --git a/include/vmsock.h b/include/vmsock.h
index 5075aa7..5213e67 100644
--- a/include/vmsock.h
+++ b/include/vmsock.h
@@ -265,7 +265,7 @@ VmwSockSetRequestHandle(
PVM_SOCKET pSocket,
PREST_REQUEST pRequest,
uint32_t nProcessed,
- PVM_SOCK_EVENT_QUEUE pQueue
+ BOOLEAN bPersistentConn
);
DWORD
@@ -368,7 +368,7 @@ typedef DWORD(*PFN_SET_REQUEST_HANDLE)(
PVM_SOCKET pSocket,
PREST_REQUEST pRequest,
uint32_t nProcessed,
- PVM_SOCK_EVENT_QUEUE pQueue
+ BOOLEAN bPersistentConn
);
typedef DWORD(*PFN_GET_PEER_INFO)(
diff --git a/server/restengine/httpProtocolHead.c b/server/restengine/httpProtocolHead.c
index c5b9f3c..be3ff09 100644
--- a/server/restengine/httpProtocolHead.c
+++ b/server/restengine/httpProtocolHead.c
@@ -783,77 +783,6 @@ VmRESTSendHeaderAndPayload(
goto cleanup;
}
-uint32_t
-VmRESTCloseClient(
- PVM_REST_HTTP_RESPONSE_PACKET pResPacket
- )
-{
- uint32_t dwError = REST_ENGINE_SUCCESS;
- char* connection = NULL;
- uint32_t closeSocket = 1;
- char* statusStartChar = NULL;
-
- if (!pResPacket)
- {
- dwError = VMREST_HTTP_INVALID_PARAMS;
- }
- BAIL_ON_VMREST_ERROR(dwError);
-
- /**** Look for response status ****/
- statusStartChar = pResPacket->statusLine->statusCode;
-
- if(statusStartChar != NULL && (*statusStartChar == '4' || *statusStartChar == '5'))
- {
- /**** Failure response sent, must close client ****/
- closeSocket = 1;
- }
- else
- {
- /**** Non failure response sent, respect client say on connection close ****/
- if (pResPacket->requestPacket != NULL)
- {
- dwError = VmRESTGetHttpHeader(
- pResPacket->requestPacket,
- "Connection",
- &connection
- );
- }
-
- if ((connection != NULL) && (strcmp(connection, " keep-alive") == 0))
- {
- closeSocket = 0;
- }
- }
- if (closeSocket == 1)
- {
- /**** TODO: Inform transport to close connection else MUST NOT close it****/
- }
-
- /**** Free all associcated request and response object memory ****/
- if (pResPacket->requestPacket)
- {
- VmRESTFreeHTTPRequestPacket(
- &(pResPacket->requestPacket)
- );
- pResPacket->requestPacket = NULL;
- }
-
- VmRESTFreeHTTPResponsePacket(
- &pResPacket
- );
- BAIL_ON_VMREST_ERROR(dwError);
-
-cleanup:
- if (connection != NULL)
- {
- VmRESTFreeMemory(connection);
- connection = NULL;
- }
- return dwError;
-error:
- goto cleanup;
-}
-
uint32_t
VmRESTGetRequestHandle(
PVMREST_HANDLE pRESTHandle,
@@ -1360,7 +1289,6 @@ VmRESTProcessBuffer(
}
BAIL_ON_VMREST_ERROR(dwError);
-
/**** Get the request processing state ****/
currState = pRequest->state;
*nBytesProcessed = 0;
@@ -1692,3 +1620,76 @@ VmRESTSetHttpPayloadZeroCopy(
VMREST_LOG_ERROR(pRESTHandle,"%s","Set Zero copy payload Failed");
goto cleanup;
}
+
+uint32_t
+VmRESTEntertainPersistentConn(
+ PVMREST_HANDLE pRESTHandle,
+ PREST_REQUEST pRequest,
+ BOOLEAN* bKeepOpen
+ )
+{
+ uint32_t dwError = REST_ENGINE_SUCCESS;
+ char* pszKeepAliveRequest = NULL;
+ char* pszKeepAliveResponse = NULL;
+ BOOLEAN bKeepConnOpen = FALSE;
+
+ if (!pRESTHandle || !pRequest || !bKeepOpen || !pRequest->pResponse)
+ {
+ VMREST_LOG_ERROR(pRESTHandle,"%s","Invalid params");
+ dwError = VMREST_HTTP_INVALID_PARAMS;
+ }
+
+ /**** Get client's say on persistent connection ****/
+ dwError = VmRESTGetHttpHeader(
+ pRequest,
+ "Connection",
+ &pszKeepAliveRequest
+ );
+ BAIL_ON_VMREST_ERROR(dwError);
+
+ if ((pszKeepAliveRequest != NULL) && (strncmp(pszKeepAliveRequest, "keep-alive", strlen("keep-alive")) == 0))
+ {
+ bKeepConnOpen = TRUE;
+ }
+
+ /**** Inspect application response on connection (set from application callback) ****/
+ if (bKeepConnOpen)
+ {
+ dwError = VmRESTGetHttpResponseHeader(
+ pRequest->pResponse,
+ "Connection",
+ &pszKeepAliveResponse
+ );
+ BAIL_ON_VMREST_ERROR(dwError);
+
+ if (!((pszKeepAliveResponse != NULL) && (strncmp(pszKeepAliveResponse, "keep-alive", strlen("keep-alive")) == 0)))
+ {
+ VMREST_LOG_WARNING(pRESTHandle,"%s","Client's request for persistent connection not entertained by server");
+ bKeepConnOpen = FALSE;
+ }
+ }
+
+ *bKeepOpen = bKeepConnOpen;
+
+cleanup:
+
+ if (pszKeepAliveRequest)
+ {
+ VmRESTFreeMemory(
+ pszKeepAliveRequest
+ );
+ pszKeepAliveRequest = NULL;
+ }
+
+ return dwError;
+
+error:
+
+ if (bKeepOpen)
+ {
+ *bKeepOpen = FALSE;
+ }
+
+ goto cleanup;
+}
+
diff --git a/server/restengine/libmain.c b/server/restengine/libmain.c
index 3992b76..e227a76 100644
--- a/server/restengine/libmain.c
+++ b/server/restengine/libmain.c
@@ -612,7 +612,7 @@ VmRESTSetSuccessResponse(
);
BAIL_ON_VMREST_ERROR(dwError);
- if ((connection != NULL) && (strcmp(connection, " keep-alive") == 0))
+ if ((connection != NULL) && ((strncmp(connection, "keep-alive", strlen("keep-alive")) == 0)))
{
dwError = VmRESTSetHttpHeader(
ppResponse,
diff --git a/server/restengine/prototype.h b/server/restengine/prototype.h
index dfb8bda..d4dcf51 100644
--- a/server/restengine/prototype.h
+++ b/server/restengine/prototype.h
@@ -116,11 +116,6 @@ VmRESTTriggerAppCb(
PVM_REST_HTTP_RESPONSE_PACKET* ppResponse
);
-uint32_t
-VmRESTCloseClient(
- PVM_REST_HTTP_RESPONSE_PACKET pResPacket
- );
-
uint32_t
VmRESTSetHttpPayloadZeroCopy(
PVMREST_HANDLE pRESTHandle,
diff --git a/test/scripts/BUGS_TC/BUG-2076723/README b/test/scripts/BUGS_TC/BUG-2076723/README
new file mode 100644
index 0000000..79cf1a2
--- /dev/null
+++ b/test/scripts/BUGS_TC/BUG-2076723/README
@@ -0,0 +1,26 @@
+Bugzilla Id: 2076723
+
+Category: New feature/Critical
+
+Description:
+In secure version (HTTPS) of c-rest-engine, all connection requests are transactional.
+This means each request is served via new connection. This introduces steps involving
+openssl handshake which is known to cause significant performance delays.
+
+Requirements:
+1. Re-use TCP connections originating from same host and port to server.
+2. Support HTTP header "connection: keep-alive"
+
+Feature Testing:
+Use available libcurl based client to open one single connection and send multiple
+HTTP(S) request.
+
+Steps to test.
+1. Compile the persistentConnection.c file with the following command.
+ "gcc -o persistentConnection persistentConnection.c -lcurl"
+2. Run the test with following command.
+ "./persistentConnection https://<SERVER_IP>:<PORT>/v1/pkg?x=y 2>/dev/null"
+
+ Example:
+ "./persistentConnection https://172.16.127.131:81/v1/pkg?x=y 2>/dev/null"
+
diff --git a/test/scripts/BUGS_TC/BUG-2076723/persistentConnection.c b/test/scripts/BUGS_TC/BUG-2076723/persistentConnection.c
new file mode 100644
index 0000000..e0f83e6
--- /dev/null
+++ b/test/scripts/BUGS_TC/BUG-2076723/persistentConnection.c
@@ -0,0 +1,121 @@
+/**************************************************************************
+* This test client uses libcurl to open connection to server and re-uses
+* the same connection to send multiple HTTPS requests.
+**************************************************************************/
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <curl/curl.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+struct string
+{
+ char *ptr;
+ size_t len;
+};
+
+void init_string(struct string *s)
+{
+ s->len = 0;
+ s->ptr = malloc(s->len+1);
+ if (s->ptr == NULL)
+ {
+ fprintf(stderr, "malloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ s->ptr[0] = '\0';
+}
+
+size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s)
+{
+ size_t new_len = s->len + size*nmemb;
+ s->ptr = realloc(s->ptr, new_len+1);
+ if (s->ptr == NULL)
+ {
+ fprintf(stderr, "realloc() failed\n");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(s->ptr+s->len, ptr, size*nmemb);
+ s->ptr[new_len] = '\0';
+ s->len = new_len;
+ return size*nmemb;
+}
+
+int main (int argc, char *argv[])
+{
+ CURL* curl = NULL;
+ CURLcode res = 0;
+ int ct = 0;
+ char expected[512] = {0};
+
+ if (argc != 2) {
+ printf("Wrong number of arguments supplied to test program\n");
+ exit(1);
+ }
+
+ struct string s;
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ curl = curl_easy_init();
+ if(curl)
+ {
+try:
+ init_string(&s);
+ struct curl_slist *chunk = NULL;
+
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+ curl_easy_setopt(curl, CURLOPT_HEADER, 1L);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
+ curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
+
+ if (ct == 500)
+ {
+ chunk = curl_slist_append(chunk, "Connection:close");
+ strcpy(expected,"HTTP/1.1 200 OK\r\nConnection:close\r\nContent-Length:13\r\n\r\nKumar Kaushik");
+ }
+ else
+ {
+ chunk = curl_slist_append(chunk, "Connection:keep-alive");
+ strcpy(expected,"HTTP/1.1 200 OK\r\nConnection:keep-alive\r\nContent-Length:13\r\n\r\nKumar Kaushik");
+ }
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
+
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "Kumar Kaushik");
+ curl_easy_setopt(curl, CURLOPT_URL, argv[1]);
+
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
+ res = curl_easy_perform(curl);
+ if(res != CURLE_OK)
+ {
+ fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
+ }
+
+ if (strcmp(expected, s.ptr) == 0)
+ {
+ printf("Test passed, iteration %d\n", ct);
+ }
+ else
+ {
+ printf("Test failed, iteration %d\n", ct);
+ }
+ //printf("\n===============\n%s\n==========\n", s.ptr);
+ free(s.ptr);
+
+ ct++;
+
+ if (ct <= 500)
+ {
+ goto try;
+ }
+
+ curl_easy_cleanup(curl);
+ }
+
+ return 0;
+}
+
diff --git a/transport/api/api.c b/transport/api/api.c
index cc2c930..5389ff0 100644
--- a/transport/api/api.c
+++ b/transport/api/api.c
@@ -268,12 +268,12 @@ VmwSockSetRequestHandle(
PVM_SOCKET pSocket,
PREST_REQUEST pRequest,
uint32_t nProcessed,
- PVM_SOCK_EVENT_QUEUE pQueue
+ BOOLEAN bPersistentConn
)
{
DWORD dwError = REST_ENGINE_SUCCESS;
- dwError = pRESTHandle->pPackage->pfnSetRequestHandle(pRESTHandle,pSocket,pRequest, nProcessed, pQueue);
+ dwError = pRESTHandle->pPackage->pfnSetRequestHandle(pRESTHandle,pSocket,pRequest, nProcessed, bPersistentConn);
return dwError;
}
diff --git a/transport/posix/prototypes.h b/transport/posix/prototypes.h
index d60e5e2..b1e4b57 100644
--- a/transport/posix/prototypes.h
+++ b/transport/posix/prototypes.h
@@ -101,7 +101,7 @@ VmSockPosixSetRequestHandle(
PVM_SOCKET pSocket,
PREST_REQUEST pRequest,
uint32_t nProcessed,
- PVM_SOCK_EVENT_QUEUE pQueue
+ BOOLEAN bKeepAlive
);
DWORD
diff --git a/transport/posix/socket.c b/transport/posix/socket.c
index 982ae5c..f50356d 100644
--- a/transport/posix/socket.c
+++ b/transport/posix/socket.c
@@ -1592,7 +1592,7 @@ VmSockPosixSetRequestHandle(
PVM_SOCKET pSocket,
PREST_REQUEST pRequest,
uint32_t nProcessed,
- PVM_SOCK_EVENT_QUEUE pQueue
+ BOOLEAN bPersistentConn
)
{
uint32_t dwError = REST_ENGINE_SUCCESS;
@@ -1600,7 +1600,7 @@ VmSockPosixSetRequestHandle(
BOOLEAN bCompleted = FALSE;
struct epoll_event event = {0};
- if (!pSocket || !pRESTHandle || !pQueue)
+ if (!pSocket || !pRESTHandle || !pRESTHandle->pSockContext || !pRESTHandle->pSockContext->pEventQueue)
{
VMREST_LOG_ERROR(pRESTHandle, "%s", "Invalid params ...");
dwError = ERROR_INVALID_PARAMETER;
@@ -1615,14 +1615,28 @@ VmSockPosixSetRequestHandle(
if (pRequest)
{
pSocket->pRequest = pRequest;
+ pSocket->nProcessed = nProcessed;
}
else
{
pSocket->pRequest = NULL;
- bCompleted = TRUE;
- }
- pSocket->nProcessed = nProcessed;
+ if (bPersistentConn)
+ {
+ /**** reset the socket object for new request *****/
+ if (pSocket->pszBuffer)
+ {
+ VmRESTFreeMemory(pSocket->pszBuffer);
+ pSocket->pszBuffer = NULL;
+ }
+ pSocket->nProcessed = 0;
+ pSocket->nBufData = 0;
+ }
+ else
+ {
+ bCompleted = TRUE;
+ }
+ }
if (!bCompleted)
{
@@ -1639,7 +1653,7 @@ VmSockPosixSetRequestHandle(
event.events = event.events | EPOLLONESHOT;
- if (epoll_ctl(pQueue->epollFd, EPOLL_CTL_MOD, pSocket->fd, &event) < 0)
+ if (epoll_ctl(pRESTHandle->pSockContext->pEventQueue->epollFd, EPOLL_CTL_MOD, pSocket->fd, &event) < 0)
{
dwError = VM_SOCK_POSIX_ERROR_SYS_CALL_FAILED;
}
@@ -1659,7 +1673,6 @@ VmSockPosixSetRequestHandle(
goto cleanup;
-
}