@@ -753,6 +753,30 @@ PHPAPI int php_network_get_sock_name(php_socket_t sock,
753753 * version of the address will be emalloc'd and returned.
754754 * */
755755
756+ /* Helper functions for timeout calculation */
757+ static struct timeval php_network_subtract_timeval (struct timeval a , struct timeval b )
758+ {
759+ struct timeval difference ;
760+ difference .tv_sec = a .tv_sec - b .tv_sec ;
761+ difference .tv_usec = a .tv_usec - b .tv_usec ;
762+ if (a .tv_usec < b .tv_usec ) {
763+ difference .tv_sec -= 1L ;
764+ difference .tv_usec += 1000000L ;
765+ }
766+ return difference ;
767+ }
768+
769+ static int php_network_compare_timeval (struct timeval a , struct timeval b )
770+ {
771+ if (a .tv_sec > b .tv_sec || (a .tv_sec == b .tv_sec && a .tv_usec > b .tv_usec )) {
772+ return 1 ;
773+ } else if (a .tv_sec == b .tv_sec && a .tv_usec == b .tv_usec ) {
774+ return 0 ;
775+ } else {
776+ return -1 ;
777+ }
778+ }
779+
756780/* {{{ php_network_accept_incoming */
757781PHPAPI php_socket_t php_network_accept_incoming (php_socket_t srvsock ,
758782 zend_string * * textaddr ,
@@ -768,33 +792,120 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
768792 int error = 0 , n ;
769793 php_sockaddr_storage sa ;
770794 socklen_t sl ;
795+ int original_flags = 0 ;
796+ int made_nonblocking = 0 ;
797+ struct timeval start_time , * current_timeout = timeout ;
798+ struct timeval remaining_timeout ;
799+ int has_timeout = 0 ;
800+
801+ /* Initialize timeout tracking if we have a timeout */
802+ if (timeout && (timeout -> tv_sec > 0 || timeout -> tv_usec > 0 )) {
803+ has_timeout = 1 ;
804+ /* gettimeofday is not monotonic; using it here is not strictly correct */
805+ gettimeofday (& start_time , NULL );
806+ remaining_timeout = * timeout ;
807+ current_timeout = & remaining_timeout ;
808+ }
771809
772- n = php_pollfd_for (srvsock , PHP_POLLREADABLE , timeout );
810+ while (1 ) {
811+ n = php_pollfd_for (srvsock , PHP_POLLREADABLE , current_timeout );
773812
774- if (n == 0 ) {
775- error = PHP_TIMEOUT_ERROR_VALUE ;
776- } else if (n == -1 ) {
777- error = php_socket_errno ();
778- } else {
779- sl = sizeof (sa );
813+ if (n == 0 ) {
814+ error = PHP_TIMEOUT_ERROR_VALUE ;
815+ break ;
816+ } else if (n == -1 ) {
817+ error = php_socket_errno ();
818+ break ;
819+ } else {
820+ sl = sizeof (sa );
780821
781- clisock = accept (srvsock , (struct sockaddr * )& sa , & sl );
822+ /* Get original socket flags */
823+ original_flags = fcntl (srvsock , F_GETFL , 0 );
824+ if (original_flags == -1 ) {
825+ error = php_socket_errno ();
826+ break ;
827+ }
828+
829+ /* Make socket non-blocking if it wasn't already */
830+ if (!(original_flags & O_NONBLOCK )) {
831+ if (fcntl (srvsock , F_SETFL , original_flags | O_NONBLOCK ) == -1 ) {
832+ error = php_socket_errno ();
833+ break ;
834+ }
835+ made_nonblocking = 1 ;
836+ }
837+
838+ clisock = accept (srvsock , (struct sockaddr * )& sa , & sl );
782839
783- if (clisock != SOCK_ERR ) {
784- php_network_populate_name_from_sockaddr ((struct sockaddr * )& sa , sl ,
785- textaddr ,
786- addr , addrlen
787- );
788- if (tcp_nodelay ) {
840+ /* Restore original blocking mode if we changed it */
841+ if (made_nonblocking ) {
842+ fcntl (srvsock , F_SETFL , original_flags );
843+ made_nonblocking = 0 ;
844+ }
845+
846+ if (clisock != SOCK_ERR ) {
847+ /* Successfully accepted connection */
848+ php_network_populate_name_from_sockaddr ((struct sockaddr * )& sa , sl ,
849+ textaddr ,
850+ addr , addrlen
851+ );
852+ if (tcp_nodelay ) {
789853#ifdef TCP_NODELAY
790- setsockopt (clisock , IPPROTO_TCP , TCP_NODELAY , (char * )& tcp_nodelay , sizeof (tcp_nodelay ));
854+ setsockopt (clisock , IPPROTO_TCP , TCP_NODELAY , (char * )& tcp_nodelay , sizeof (tcp_nodelay ));
855+ #endif
856+ }
857+ break ; /* Success - exit the loop */
858+ } else {
859+ error = php_socket_errno ();
860+
861+ /* If we got EAGAIN/EWOULDBLOCK, the connection was accepted by another process
862+ * Go back to polling with remaining timeout */
863+ #if EAGAIN == EWOULDBLOCK
864+ if (error == EAGAIN ) {
865+ #else
866+ if (error == EAGAIN || error == EWOULDBLOCK ) {
791867#endif
868+ if (has_timeout ) {
869+ struct timeval cur_time , elapsed_time ;
870+
871+ /* Calculate how much time has elapsed */
872+ gettimeofday (& cur_time , NULL );
873+ elapsed_time = php_network_subtract_timeval (cur_time , start_time );
874+
875+ /* Check if we've already exceeded the timeout */
876+ if (php_network_compare_timeval (elapsed_time , * timeout ) >= 0 ) {
877+ error = PHP_TIMEOUT_ERROR_VALUE ;
878+ break ;
879+ }
880+
881+ /* Calculate remaining timeout */
882+ remaining_timeout = php_network_subtract_timeval (* timeout , elapsed_time );
883+
884+ /* Ensure we don't have negative values */
885+ if (remaining_timeout .tv_sec < 0 ||
886+ (remaining_timeout .tv_sec == 0 && remaining_timeout .tv_usec <= 0 )) {
887+ error = PHP_TIMEOUT_ERROR_VALUE ;
888+ break ;
889+ }
890+
891+ current_timeout = & remaining_timeout ;
892+ }
893+
894+ error = 0 ; /* Reset error - this is expected behavior */
895+ continue ; /* Go back to poll with reduced timeout */
896+ } else {
897+ /* Real error occurred */
898+ break ;
899+ }
792900 }
793- } else {
794- error = php_socket_errno ();
795901 }
796902 }
797903
904+ /* Clean up if we still have non-blocking mode set (error case) */
905+ if (made_nonblocking ) {
906+ fcntl (srvsock , F_SETFL , original_flags );
907+ }
908+
798909 if (error_code ) {
799910 * error_code = error ;
800911 }
0 commit comments