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