diff --git a/src/nopoll_conn.c b/src/nopoll_conn.c index 14b6ec8..ef51c49 100644 --- a/src/nopoll_conn.c +++ b/src/nopoll_conn.c @@ -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 */ @@ -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)) { @@ -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 */ @@ -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 */ @@ -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; +} + /* @} */ diff --git a/src/nopoll_conn.h b/src/nopoll_conn.h index ce8d61c..688580c 100644 --- a/src/nopoll_conn.h +++ b/src/nopoll_conn.h @@ -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); diff --git a/src/nopoll_private.h b/src/nopoll_private.h index 021870e..d4da42a 100644 --- a/src/nopoll_private.h +++ b/src/nopoll_private.h @@ -372,6 +372,8 @@ 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; @@ -379,6 +381,8 @@ struct _noPollHandshake { /* reference to cookie header */ char * cookie; + /* redirect Location URL */ + char * redirectURL; }; struct _noPollConnOpts { diff --git a/test/nopoll-regression-client.c b/test/nopoll-regression-client.c index aa81997..1e9bdfe 100644 --- a/test/nopoll-regression-client.c +++ b/test/nopoll-regression-client.c @@ -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; @@ -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 */ diff --git a/test/nopoll-regression-listener.c b/test/nopoll-regression-listener.c index 43b4c38..2209f3b 100644 --- a/test/nopoll-regression-listener.c +++ b/test/nopoll-regression-listener.c @@ -46,6 +46,8 @@ #include #include #include +#include +#include int connection_close_count = 0; @@ -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) { @@ -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)) {