@@ -2484,21 +2484,96 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val
2484
2484
/* the poll() call was skipped if the socket is non-blocking (or MSG_DONTWAIT is available) and if the timeout is zero */
2485
2485
/* additionally, we don't use this optimization if SSL is active because in that case, we're not using MSG_DONTWAIT */
2486
2486
if (sslsock -> ssl_active ) {
2487
- int n = SSL_peek (sslsock -> ssl_handle , & buf , sizeof (buf ));
2488
- if (n <= 0 ) {
2489
- int err = SSL_get_error (sslsock -> ssl_handle , n );
2490
- switch (err ) {
2491
- case SSL_ERROR_SYSCALL :
2492
- alive = php_socket_errno () == EAGAIN ;
2493
- break ;
2494
- case SSL_ERROR_WANT_READ :
2495
- case SSL_ERROR_WANT_WRITE :
2496
- alive = 1 ;
2487
+ int retry = 1 ;
2488
+ struct timeval start_time ;
2489
+ struct timeval * timeout = NULL ;
2490
+ int began_blocked = sslsock -> s .is_blocked ;
2491
+ int has_timeout = 0 ;
2492
+
2493
+ /* never use a timeout with non-blocking sockets */
2494
+ if (began_blocked ) {
2495
+ timeout = & tv ;
2496
+ }
2497
+
2498
+ if (timeout && php_set_sock_blocking (sslsock -> s .socket , 0 ) == SUCCESS ) {
2499
+ sslsock -> s .is_blocked = 0 ;
2500
+ }
2501
+
2502
+ if (!sslsock -> s .is_blocked && timeout && (timeout -> tv_sec > 0 || (timeout -> tv_sec == 0 && timeout -> tv_usec ))) {
2503
+ has_timeout = 1 ;
2504
+ /* gettimeofday is not monotonic; using it here is not strictly correct */
2505
+ gettimeofday (& start_time , NULL );
2506
+ }
2507
+
2508
+ /* Main IO loop. */
2509
+ do {
2510
+ struct timeval cur_time , elapsed_time , left_time ;
2511
+
2512
+ /* If we have a timeout to check, figure out how much time has elapsed since we started. */
2513
+ if (has_timeout ) {
2514
+ gettimeofday (& cur_time , NULL );
2515
+
2516
+ /* Determine how much time we've taken so far. */
2517
+ elapsed_time = php_openssl_subtract_timeval (cur_time , start_time );
2518
+
2519
+ /* and return an error if we've taken too long. */
2520
+ if (php_openssl_compare_timeval (elapsed_time , * timeout ) > 0 ) {
2521
+ /* If the socket was originally blocking, set it back. */
2522
+ if (began_blocked ) {
2523
+ php_set_sock_blocking (sslsock -> s .socket , 1 );
2524
+ sslsock -> s .is_blocked = 1 ;
2525
+ }
2526
+ sslsock -> s .timeout_event = 1 ;
2527
+ return PHP_STREAM_OPTION_RETURN_ERR ;
2528
+ }
2529
+ }
2530
+
2531
+ int n = SSL_peek (sslsock -> ssl_handle , & buf , sizeof (buf ));
2532
+ /* If we didn't do anything on the last loop (or an error) check to see if we should retry or exit. */
2533
+ if (n <= 0 ) {
2534
+ /* Now, do the IO operation. Don't block if we can't complete... */
2535
+ int err = SSL_get_error (sslsock -> ssl_handle , n );
2536
+ switch (err ) {
2537
+ case SSL_ERROR_SYSCALL :
2538
+ retry = php_socket_errno () == EAGAIN ;
2539
+ break ;
2540
+ case SSL_ERROR_WANT_READ :
2541
+ case SSL_ERROR_WANT_WRITE :
2542
+ retry = 1 ;
2543
+ break ;
2544
+ default :
2545
+ /* any other problem is a fatal error */
2546
+ retry = 0 ;
2547
+ }
2548
+
2549
+ /* Don't loop indefinitely in non-blocking mode if no data is available */
2550
+ if (began_blocked == 0 || !has_timeout ) {
2551
+ alive = retry ;
2497
2552
break ;
2498
- default :
2499
- /* any other problem is a fatal error */
2500
- alive = 0 ;
2553
+ }
2554
+
2555
+ /* Now, if we have to wait some time, and we're supposed to be blocking, wait for the socket to become
2556
+ * available. Now, php_pollfd_for uses select to wait up to our time_left value only...
2557
+ */
2558
+ if (retry ) {
2559
+ /* Now, how much time until we time out? */
2560
+ left_time = php_openssl_subtract_timeval (* timeout , elapsed_time );
2561
+ if (php_pollfd_for (sslsock -> s .socket , PHP_POLLREADABLE |POLLPRI |POLLOUT , has_timeout ? & left_time : NULL ) <= 0 ) {
2562
+ retry = 0 ;
2563
+ alive = 0 ;
2564
+ };
2565
+ }
2566
+ } else {
2567
+ retry = 0 ;
2568
+ alive = 1 ;
2501
2569
}
2570
+ /* Finally, we keep going until there are any data or there is no time to wait. */
2571
+ } while (retry );
2572
+
2573
+ if (began_blocked && !sslsock -> s .is_blocked ) {
2574
+ // Set it back to blocking
2575
+ php_set_sock_blocking (sslsock -> s .socket , 1 );
2576
+ sslsock -> s .is_blocked = 1 ;
2502
2577
}
2503
2578
} else if (0 == recv (sslsock -> s .socket , & buf , sizeof (buf ), MSG_PEEK |MSG_DONTWAIT ) && php_socket_errno () != EAGAIN ) {
2504
2579
alive = 0 ;
0 commit comments