-
Notifications
You must be signed in to change notification settings - Fork 8k
Add stream socket so_linger context option #20808
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| --TEST-- | ||
| stream_socket_server() and stream_socket_client() SO_LINGER context option test | ||
| --EXTENSIONS-- | ||
| sockets | ||
| --SKIPIF-- | ||
| <?php | ||
| if (!defined('SO_LINGER')) { | ||
| die('skip SO_LINGER not available'); | ||
| } | ||
| ?> | ||
| --FILE-- | ||
| <?php | ||
| // Test server with SO_LINGER enabled | ||
| $server_context = stream_context_create([ | ||
| 'socket' => [ | ||
| 'so_linger' => 10, | ||
| ] | ||
| ]); | ||
|
|
||
| $server = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr, | ||
| STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $server_context); | ||
|
|
||
| if (!$server) { | ||
| die('Unable to create server'); | ||
| } | ||
|
|
||
| $addr = stream_socket_get_name($server, false); | ||
| $port = (int)substr(strrchr($addr, ':'), 1); | ||
|
|
||
| // Test client with SO_LINGER enabled | ||
| $client_context = stream_context_create([ | ||
| 'socket' => [ | ||
| 'so_linger' => 8, | ||
| ] | ||
| ]); | ||
|
|
||
| $client = stream_socket_client("tcp://127.0.0.1:$port", $errno, $errstr, 30, | ||
| STREAM_CLIENT_CONNECT, $client_context); | ||
|
|
||
| if (!$client) { | ||
| die('Unable to create client'); | ||
| } | ||
|
|
||
| $accepted = stream_socket_accept($server, 1); | ||
|
|
||
| if (!$accepted) { | ||
| die('Unable to accept connection'); | ||
| } | ||
|
|
||
| $so_linger = PHP_OS_FAMILY === 'Darwin' ? SO_LINGER_SEC : SO_LINGER; | ||
|
|
||
| // Verify server side (accepted connection) | ||
| $server_sock = socket_import_stream($accepted); | ||
| $server_linger = socket_get_option($server_sock, SOL_SOCKET, $so_linger); | ||
| echo "Server SO_LINGER\n"; | ||
| var_dump($server_linger['l_onoff'] > 0); | ||
| var_dump($server_linger['l_linger']); | ||
|
|
||
| // Verify client side | ||
| $client_sock = socket_import_stream($client); | ||
| $client_linger = socket_get_option($client_sock, SOL_SOCKET, $so_linger); | ||
| echo "Client SO_LINGER\n"; | ||
| var_dump($client_linger['l_onoff'] > 0); | ||
| var_dump($client_linger['l_linger']); | ||
|
|
||
| fclose($accepted); | ||
| fclose($client); | ||
| fclose($server); | ||
|
|
||
| ?> | ||
| --EXPECT-- | ||
| Server SO_LINGER | ||
| bool(true) | ||
| int(10) | ||
| Client SO_LINGER | ||
| bool(true) | ||
| int(8) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -541,6 +541,19 @@ php_socket_t php_network_bind_socket_to_local_addr_ex(const char *host, unsigned | |
|
|
||
| /* Set socket values if provided */ | ||
| if (sockvals != NULL) { | ||
| #ifdef SO_LINGER | ||
| if (sockvals->mask & PHP_SOCKVAL_SO_LINGER) { | ||
| struct linger linger_val = { | ||
| .l_onoff = (sockvals->linger > 0), | ||
| .l_linger = (unsigned short)sockvals->linger | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the only subtlety is setting l_onoff with 1 but with l_linger with 0 is a valid use case (closing connection w/o handshake aka RST on close) but you can only carry one value per socket option right ?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. saying that, even rust expresses this in a similar way I just recalled having pushing a PR in there :) |
||
| }; | ||
| #ifdef SO_LINGER_SEC | ||
| setsockopt(sock, SOL_SOCKET, SO_LINGER_SEC, (char*)&linger_val, sizeof(linger_val)); | ||
| #else | ||
| setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_val, sizeof(linger_val)); | ||
| #endif | ||
| } | ||
| #endif | ||
| #if defined(TCP_KEEPIDLE) | ||
| if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) { | ||
| setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle)); | ||
|
|
@@ -1027,6 +1040,19 @@ php_socket_t php_network_connect_socket_to_host_ex(const char *host, unsigned sh | |
|
|
||
| /* Set socket values if provided */ | ||
| if (sockvals != NULL) { | ||
| #ifdef SO_LINGER | ||
| if (sockvals->mask & PHP_SOCKVAL_SO_LINGER) { | ||
| struct linger linger_val = { | ||
| .l_onoff = (sockvals->linger > 0), | ||
| .l_linger = (unsigned short)sockvals->linger | ||
| }; | ||
| #ifdef SO_LINGER_SEC | ||
| setsockopt(sock, SOL_SOCKET, SO_LINGER_SEC, (char*)&linger_val, sizeof(linger_val)); | ||
| #else | ||
| setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&linger_val, sizeof(linger_val)); | ||
| #endif | ||
| } | ||
| #endif | ||
| #if defined(TCP_KEEPIDLE) | ||
| if (sockvals->mask & PHP_SOCKVAL_TCP_KEEPIDLE) { | ||
| setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&sockvals->keepalive.keepidle, sizeof(sockvals->keepalive.keepidle)); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.