Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add changes to support URL redirection. #50

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
99 changes: 98 additions & 1 deletion src/nopoll_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,7 @@ void nopoll_conn_unref (noPollConn * conn)
nopoll_free (conn->handshake->expected_accept);
nopoll_free (conn->handshake->cookie);
nopoll_free (conn->handshake);
nopoll_free (conn->handshake->redirectURL);
} /* end if */

/* release connection options if defined and reuse flag is not defined */
Expand Down Expand Up @@ -2881,6 +2882,7 @@ int nopoll_conn_complete_handshake_client (noPollCtx * ctx, noPollConn * conn, c
char * header;
char * value;
int iterator;
char statuscode[4]={'\0'};

/* handle content */
if (! conn->handshake->received_101 && nopoll_ncmp (buffer, "HTTP/1.1 ", 9)) {
Expand All @@ -2889,6 +2891,16 @@ int nopoll_conn_complete_handshake_client (noPollCtx * ctx, noPollConn * conn, c
iterator++;
if (! nopoll_ncmp (buffer + iterator, "101", 3)) {
nopoll_log (ctx, NOPOLL_LEVEL_CRITICAL, "websocket server denied connection with: %s", buffer + iterator);
strncpy(statuscode, buffer + iterator, 3);
/* flag that we have received HTTP/1.1 statuscode indication */
conn->handshake->received_non_101 = nopoll_true;
if(strlen(statuscode) > 0)
{
nopoll_log (ctx, NOPOLL_LEVEL_DEBUG, "Received HTTP %d response from server", atoi(statuscode));
conn->handshake->httpStatus = atoi(statuscode);
return 1; /* continue to read next lines for error case */
}

return 0; /* do not continue */
} /* end if */

Expand Down Expand Up @@ -2926,7 +2938,15 @@ int nopoll_conn_complete_handshake_client (noPollCtx * ctx, noPollConn * conn, c
} else if (strcasecmp (header, "Connection") == 0) {
conn->handshake->connection_upgrade = 1;
nopoll_free (value);
} else {
} else if (strcasecmp (header, "Location") == 0) {
if(conn->handshake->received_non_101 && (conn->handshake->httpStatus >= 300 || conn->handshake->httpStatus <= 399))
{
conn->handshake->redirectURL = nopoll_strdup (value);
nopoll_log (ctx, NOPOLL_LEVEL_DEBUG, "nopoll_conn_complete_handshake_client: conn->handshake->redirectURL: %s",conn->handshake->redirectURL);
nopoll_free (value);
}
}
else {
/* release value, no body claimed it */
nopoll_free (value);
} /* end if */
Expand Down Expand Up @@ -4952,4 +4972,81 @@ nopoll_bool nopoll_conn_wait_until_connection_ready (noPollConn * conn,
return nopoll_conn_is_ok (conn) && nopoll_conn_is_ready (conn);
}

/**
* @brief Allows to implement a wait operation until the provided
* connection is ready or the provided timeout is reached.
*
* @param conn The connection that is being waited to be created.
*
* @param timeout The timeout operation to limit the wait
* operation. Timeout is provided in seconds.
*
* *@param status in-out parameter of integer value. The response http status code indicating
* success with 101 and failure with non 101 code. e.g. 307 redirect, 403 unauthorized etc.

* @param message in-out parameter of char pointer. The response message string description indicating
* "Success", "Failure" or "Redirect: Redirect_URL". Caller needs to free memory for this after use.
*
* @return The function returns when the timeout was reached or the
* connection is ready. In the case the connection is ready when the
* function finished nopoll_true is returned, otherwise nopoll_false.
*/
nopoll_bool nopoll_conn_wait_for_status_until_connection_ready (noPollConn * conn,
int timeout, int *status, char **message)
{
long int total_timeout = timeout * 1000000;
nopoll_bool result = nopoll_false;

/* check if the connection already finished its connection
handshake */
while (! nopoll_conn_is_ready (conn) && total_timeout > 0) {

if (conn->handshake->received_non_101) {
break;
}
/* check if the connection is ok */
if (! nopoll_conn_is_ok (conn))
return nopoll_false;

/* wait a bit 0,5ms */
nopoll_sleep (500);

/* reduce the amount of time we have to wait */
total_timeout = total_timeout - 500;
} /* end if */

result = nopoll_conn_is_ok (conn) && nopoll_conn_is_ready (conn);

if(conn->handshake->received_non_101 == nopoll_true && (conn->handshake->httpStatus >= 300 || conn->handshake->httpStatus <= 399) && (conn->handshake->redirectURL != NULL))
{
if(message != NULL)
{
*message = nopoll_strdup_printf ("Redirect:%s", conn->handshake->redirectURL);
*status = conn->handshake->httpStatus;
}

conn->handshake->received_non_101 = nopoll_false;
nopoll_log (conn->ctx, NOPOLL_LEVEL_DEBUG, "nopoll_conn_wait_for_status_until_connection_ready() response: message: %s" ,*message );
return nopoll_false; /* retry with redirection URLs */
}
else if(conn->handshake->received_non_101 == nopoll_true && conn->handshake->httpStatus !=0)
{
*status = conn->handshake->httpStatus;
nopoll_log (conn->ctx, NOPOLL_LEVEL_DEBUG, "nopoll_conn_wait_for_status_until_connection_ready() response: status: %d" ,*status );
return nopoll_false; /* retry as server returns error http code */
}
else if(result && message != NULL)
{
*message = nopoll_strdup_printf("Success");
}
else if(!result && message != NULL)
{
*message = nopoll_strdup_printf("Failure");
}
nopoll_log (conn->ctx, NOPOLL_LEVEL_DEBUG, "*****End nopoll_conn_wait_for_status_until_connection_ready ****");

/* report if the connection is ok */
return result;
}

/* @} */
3 changes: 3 additions & 0 deletions src/nopoll_conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ int __nopoll_conn_send_common (noPollConn * conn,
nopoll_bool nopoll_conn_wait_until_connection_ready (noPollConn * conn,
int timeout);

nopoll_bool nopoll_conn_wait_for_status_until_connection_ready (noPollConn * conn,
int timeout, int *status, char **message);

void nopoll_conn_connect_timeout (noPollCtx * ctx,
long microseconds_to_wait);

Expand Down
4 changes: 4 additions & 0 deletions src/nopoll_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,13 +372,17 @@ struct _noPollHandshake {
nopoll_bool upgrade_websocket;
nopoll_bool connection_upgrade;
nopoll_bool received_101;
nopoll_bool received_non_101;
int httpStatus;
char * websocket_key;
char * websocket_version;
char * websocket_accept;
char * expected_accept;

/* reference to cookie header */
char * cookie;
/* redirect Location URL */
char * redirectURL;
};

struct _noPollConnOpts {
Expand Down
142 changes: 142 additions & 0 deletions test/nopoll-regression-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,134 @@ nopoll_bool test_02b (void) {
return nopoll_true;
}

/* Test URL redirection with status code 3xx */
nopoll_bool test_url_redirection (void) {

noPollCtx * ctx;
noPollConn * conn;
int status = 0;
char *message = NULL;
char *redirect_url = NULL;
char *port = NULL;

/* create context */
ctx = create_ctx ();

/* call to create a connection */
printf ("Test url_redirection: creating connection localhost:6789 (errno=%d)\n", errno);
conn = nopoll_conn_new (ctx, "localhost", "6789", NULL, NULL, NULL, NULL);
if (! nopoll_conn_is_ok (conn)) {
printf ("ERROR: Expected to find proper client connection status, but found error.. (conn=%p, conn->session=%d, NOPOLL_INVALID_SOCKET=%d, errno=%d, strerr=%s)..\n",
conn, (int) nopoll_conn_socket (conn), (int) NOPOLL_INVALID_SOCKET, errno, strerror (errno));
return nopoll_false;
}

printf ("Test url_redirection: waiting until connection is ok (errno=%d)\n", errno);

if (! nopoll_conn_wait_for_status_until_connection_ready (conn, 5, &status , &message)) {
printf ("INFO: Failed to connect with server, received redirection URL is \"%s\" with status code %d.\n",message,status);
}

if (status != 0 && message != NULL) {
if (status >= 300 && status <= 399) {
redirect_url = strchr(message, ':');
if (redirect_url) {
redirect_url += 8;
port = strchr (redirect_url, ':');
if (port){
int url_index = port - redirect_url;
*(redirect_url + (url_index)) = '\0';
port++;
printf("INFO: Extracted received URL is %s and port is %s.\n",redirect_url, port);
}
else {
printf("ERROR: Unable to get port number.\n");
}
}
else {
printf("ERROR: Unable to get redirection URL.\n");
}
}

if (!redirect_url || !port || (status < 300 || status > 399)) {
printf ("ERROR: Received Invalid data from server, closing the connection.\n");
return nopoll_false;
}

printf("INFO: Connecting to new redirection URL.\n");
nopoll_conn_close (conn);

/* call to create a connection */

conn = nopoll_conn_new (ctx, redirect_url, port, NULL, NULL, NULL, NULL);
if (! nopoll_conn_is_ok (conn)) {
printf ("ERROR: Expected to find proper client connection status, but found error.. (conn=%p, conn->session=%d, NOPOLL_INVALID_SOCKET=%d, errno=%d, strerr=%s)..\n",
conn, (int) nopoll_conn_socket (conn), (int) NOPOLL_INVALID_SOCKET, errno, strerror (errno));
return nopoll_false;
}

printf ("Test url_redirection: waiting until connection is ok (errno=%d)\n", errno);
if (! nopoll_conn_wait_until_connection_ready (conn, 5)) {
printf ("ERROR: failed to fully establish connection nopoll_conn_wait_until_connection_ready (conn, 5) failed..\n");
}
}

else {
printf("ERROR: Expected status code is 3xx along with new redirection URL, but not received\n");
return nopoll_false;
}

/* finish connection */
nopoll_conn_close (conn);

/* finish */
nopoll_ctx_unref (ctx);

return nopoll_true;
}

/* Test non_101 & non_3xx status code */
nopoll_bool test_non_redirection_status (void) {

noPollCtx * ctx;
noPollConn * conn;
int status = 0;
char *message = NULL;

/* create context */
ctx = create_ctx ();

/* call to create a connection */
printf ("Test test_non_redirection_status: creating connection localhost:9876 (errno=%d)\n", errno);
conn = nopoll_conn_new (ctx, "localhost", "9876", NULL, NULL, NULL, NULL);
if (! nopoll_conn_is_ok (conn)) {
printf ("ERROR: Expected to find proper client connection status, but found error.. (conn=%p, conn->session=%d, NOPOLL_INVALID_SOCKET=%d, errno=%d, strerr=%s)..\n",
conn, (int) nopoll_conn_socket (conn), (int) NOPOLL_INVALID_SOCKET, errno, strerror (errno));
return nopoll_false;
}

printf ("Test test_non_redirection_status: waiting until connection is ok (errno=%d)\n", errno);

if (! nopoll_conn_wait_for_status_until_connection_ready (conn, 5, &status , &message)) {
printf ("INFO: Failed to connect with server, received reply \"%s\" with status code %d \n",message,status);
}

if (status == 500) {
printf("Test non_redirection_status: Received Expected status %d \n",status);
/* finish connection */
nopoll_conn_close (conn);

/* finish */
nopoll_ctx_unref (ctx);

return nopoll_true;
}
else {
printf("ERROR: Expected status code from server as 500, but received something else\n");
return nopoll_false;
}
}


nopoll_bool test_03 (void) {
noPollCtx * ctx;
Expand Down Expand Up @@ -2957,6 +3085,20 @@ int main (int argc, char ** argv)
return -1;
}

/* test URL redirection with status code 3xx */
if (test_url_redirection ()) {
printf ("Test url_redirection: Server URL redirection Support [ OK ]\n");
}else {
printf ("Test url_redirection: Server URL redirection Support [ FAILED ]\n");
return -1;
}

if (test_non_redirection_status ()) {
printf ("Test non_redirection_status: Server support for non_101 status [ OK ]\n");
}else {
printf ("Test non_redirection_status: Server support for non_101 status [ FAILED ]\n");
return -1;
}
/* test sending pong (without ping) */

/* test streaming api */
Expand Down
45 changes: 45 additions & 0 deletions test/nopoll-regression-listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
#include <openssl/x509v3.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <unistd.h>
#include <pthread.h>

int connection_close_count = 0;

Expand Down Expand Up @@ -375,6 +377,44 @@ noPollPtr ssl_context_creator (noPollCtx * ctx, noPollConn * conn, noPollConnOpt
printf ("RETURNING: ssl context reference %p\n", ssl_ctx);
return ssl_ctx;
}
void *start_listener(char *reply, int port) {

NOPOLL_SOCKET listener, client_sock;
struct sockaddr_in serveraddr;

listener = socket(AF_INET, SOCK_STREAM, 0);
memset( &serveraddr,0, sizeof(serveraddr));

serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htons(INADDR_ANY);
serveraddr.sin_port = htons(port);

printf("noPoll listener started at: %s:%d.\n", inet_ntoa(serveraddr.sin_addr), htons(serveraddr.sin_port));
bind(listener, (struct sockaddr *) &serveraddr, sizeof(serveraddr));

listen(listener, 1);

client_sock = accept(listener, (struct sockaddr*) NULL, NULL);
printf("INFO: Received request at %s:%d, replying to client %s",inet_ntoa(serveraddr.sin_addr), htons(serveraddr.sin_port),reply);

if(write(client_sock, reply, strlen(reply)+1)){
close(client_sock);
}
else {
printf("ERROR: Unable to write data on socket\n");
}
return NULL;
}

void *listener_for_URL_redirection (void *vargp) {
char server_response[] = "HTTP/1.1 307 Temporary Redirect\r\nLocation: http://localhost:1234\r\n\r\n";
return start_listener(server_response,6789);
}

void *listener_for_non_101_status (void *vargp) {
char server_response[] = "HTTP/1.1 500 Internal Server Error\r\n\r\n";
return start_listener(server_response,9876);
}

int main (int argc, char ** argv)
{
Expand Down Expand Up @@ -428,6 +468,11 @@ int main (int argc, char ** argv)
iterator++;
}

/* create listener for URL redirection */
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, listener_for_URL_redirection, NULL);
pthread_create(&tid2, NULL, listener_for_non_101_status, NULL);

/* call to create a listener */
listener = nopoll_listener_new (ctx, "0.0.0.0", "1234");
if (! nopoll_conn_is_ok (listener)) {
Expand Down