]> granicus.if.org Git - php/commitdiff
Implement #51879 stream context socket option tcp_nodelay
authorJoe Watkins <krakjoe@php.net>
Wed, 27 Apr 2016 21:56:15 +0000 (22:56 +0100)
committerJoe Watkins <krakjoe@php.net>
Fri, 29 Apr 2016 11:11:58 +0000 (12:11 +0100)
ext/openssl/xp_ssl.c
ext/standard/tests/streams/stream_context_tcp_nodelay.phpt [new file with mode: 0644]
ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt [new file with mode: 0644]
ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt [new file with mode: 0644]
main/network.c
main/php_network.h
main/streams/xp_socket.c

index 53536a1a77a809ed8f81b5aa311a03a9ce3fbb58..6f542323d2eac51df704a057d56ca42e590b543d 100644 (file)
@@ -2199,39 +2199,39 @@ static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_
                php_stream_xport_param *xparam STREAMS_DC)
 {
        int clisock;
-
+       zend_bool nodelay = 0;
+       zval *tmpzval = NULL;
+       
        xparam->outputs.client = NULL;
 
+       if ((tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
+               zend_is_true(tmpzval)) {
+               nodelay = 1;
+       }
+
        clisock = php_network_accept_incoming(sock->s.socket,
-                       xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
-                       xparam->want_addr ? &xparam->outputs.addr : NULL,
-                       xparam->want_addr ? &xparam->outputs.addrlen : NULL,
-                       xparam->inputs.timeout,
-                       xparam->want_errortext ? &xparam->outputs.error_text : NULL,
-                       &xparam->outputs.error_code
-                       );
+               xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+               xparam->want_addr ? &xparam->outputs.addr : NULL,
+               xparam->want_addr ? &xparam->outputs.addrlen : NULL,
+               xparam->inputs.timeout,
+               xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+               &xparam->outputs.error_code,
+               nodelay);
 
        if (clisock >= 0) {
-               php_openssl_netstream_data_t *clisockdata;
+               php_openssl_netstream_data_t *clisockdata = (php_openssl_netstream_data_t*) emalloc(sizeof(*clisockdata));
 
-               clisockdata = emalloc(sizeof(*clisockdata));
+               /* copy underlying tcp fields */
+               memset(clisockdata, 0, sizeof(*clisockdata));
+               memcpy(clisockdata, sock, sizeof(clisockdata->s));
 
-               if (clisockdata == NULL) {
-                       closesocket(clisock);
-                       /* technically a fatal error */
-               } else {
-                       /* copy underlying tcp fields */
-                       memset(clisockdata, 0, sizeof(*clisockdata));
-                       memcpy(clisockdata, sock, sizeof(clisockdata->s));
-
-                       clisockdata->s.socket = clisock;
+               clisockdata->s.socket = clisock;
 
-                       xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
-                       if (xparam->outputs.client) {
-                               xparam->outputs.client->ctx = stream->ctx;
-                               if (stream->ctx) {
-                                       GC_REFCOUNT(stream->ctx)++;
-                               }
+               xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
+               if (xparam->outputs.client) {
+                       xparam->outputs.client->ctx = stream->ctx;
+                       if (stream->ctx) {
+                               GC_REFCOUNT(stream->ctx)++;
                        }
                }
 
diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay.phpt
new file mode 100644 (file)
index 0000000..bcb7a81
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+stream context tcp_nodelay
+--SKIPIF--
+<?php if (!extension_loaded("sockets")) die("skip: need sockets") ?>
+--FILE--
+<?php
+$ctxt = stream_context_create([
+       "socket" => [
+               "tcp_nodelay" => true
+       ]
+]);
+
+$stream = stream_socket_client(
+       "tcp://www.php.net:80", $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $ctxt);
+
+$socket = 
+       socket_import_stream($stream);
+
+var_dump(socket_get_option($socket, SOL_TCP, TCP_NODELAY));
+?>
+--EXPECT--
+int(1)
diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay_fopen.phpt
new file mode 100644 (file)
index 0000000..2638935
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+stream context tcp_nodelay fopen
+--SKIPIF--
+<?php if (!extension_loaded("sockets")) die("skip: need sockets") ?>
+--FILE--
+<?php
+$ctxt = stream_context_create([
+       "socket" => [
+               "tcp_nodelay" => true
+       ]
+]);
+
+$stream = fopen("http://www.php.net", "r", false,  $ctxt);
+
+$socket = 
+       @socket_import_stream($stream);
+
+var_dump(socket_get_option($socket, STREAM_IPPROTO_TCP, TCP_NODELAY));
+?>
+--EXPECT--
+int(1)
diff --git a/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt b/ext/standard/tests/streams/stream_context_tcp_nodelay_server.phpt
new file mode 100644 (file)
index 0000000..0c4e687
--- /dev/null
@@ -0,0 +1,47 @@
+--TEST--
+stream context tcp_nodelay server
+--SKIPIF--
+<?php if (!extension_loaded("sockets")) die("skip: need sockets") ?>
+--FILE--
+<?php
+$serverCode = <<<'CODE'
+   $ctxt = stream_context_create([
+               "socket" => [
+                       "tcp_nodelay" => true
+               ]
+       ]);
+
+       $server = stream_socket_server(
+               "tcp://127.0.0.1:9099", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
+
+       $client = stream_socket_accept($server);
+
+       var_dump(socket_get_option(
+                               socket_import_stream($server), 
+                                       SOL_TCP, TCP_NODELAY));
+
+       var_dump(socket_get_option(
+                               socket_import_stream($client), 
+                                       SOL_TCP, TCP_NODELAY));
+
+       fclose($client);
+       fclose($server);
+CODE;
+
+$clientCode = <<<'CODE'
+    $test = stream_socket_client(
+               "tcp://127.0.0.1:9099", $errno, $errstr, 10);
+
+       sleep(1);
+
+       fclose($test);
+CODE;
+
+include sprintf(
+       "%s/../../../openssl/tests/ServerClientTestCase.inc", 
+       dirname(__FILE__));
+ServerClientTestCase::getInstance()->run($serverCode, $clientCode);
+?>
+--EXPECT--
+int(0)
+int(1)
index e7f1b84281fbab6844d87d8376eabaf0eb387883..e53dc66a8a1aa47aad0cdefd30f3852138f2541e 100644 (file)
@@ -487,6 +487,11 @@ php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned po
                                setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&sockoptval, sizeof(sockoptval));
                        }
 #endif
+#ifdef TCP_NODELAY
+                       if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
+                               setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sockoptval, sizeof(sockoptval));
+                       }
+#endif
 
                        n = bind(sock, sa, socklen);
 
@@ -726,7 +731,8 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
                socklen_t *addrlen,
                struct timeval *timeout,
                zend_string **error_string,
-               int *error_code
+               int *error_code,
+               int tcp_nodelay
                )
 {
        php_socket_t clisock = -1;
@@ -750,6 +756,11 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
                                        textaddr,
                                        addr, addrlen
                                        );
+                       if (tcp_nodelay) {
+#ifdef TCP_NODELAY
+                               setsockopt(clisock, IPPROTO_TCP, TCP_NODELAY, (char*)&tcp_nodelay, sizeof(tcp_nodelay));
+#endif
+                       }
                } else {
                        error = php_socket_errno();
                }
@@ -767,7 +778,6 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
 /* }}} */
 
 
-
 /* Connect to a remote host using an interruptible connect with optional timeout.
  * Optionally, the connect can be made asynchronously, which will implicitly
  * enable non-blocking mode on the socket.
@@ -902,6 +912,15 @@ skip_bind:
                                }
                        }
 #endif
+
+#ifdef TCP_NODELAY
+                       {
+                               int val = 1;
+                               if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {
+                                       setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
+                               }
+                       }
+#endif
                        n = php_network_connect_socket(sock, sa, socklen, asynchronous,
                                        timeout ? &working_timeout : NULL,
                                        error_string, error_code);
index 4ddae5cd4ce300f60e6c9615c2997ded276fe90b..2c342b4668051eacd6ea42d398461bbff7e21986 100644 (file)
@@ -28,6 +28,7 @@
 #else
 # undef closesocket
 # define closesocket close
+# include <netinet/tcp.h>
 #endif
 
 #ifndef HAVE_SHUTDOWN
@@ -115,6 +116,7 @@ typedef int php_socket_t;
 #define STREAM_SOCKOP_SO_BROADCAST        (1 << 2)
 #define STREAM_SOCKOP_IPV6_V6ONLY         (1 << 3)
 #define STREAM_SOCKOP_IPV6_V6ONLY_ENABLED (1 << 4)
+#define STREAM_SOCKOP_TCP_NODELAY         (1 << 5)
 
 
 /* uncomment this to debug poll(2) emulation on systems that have poll(2) */
@@ -265,7 +267,8 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock,
                socklen_t *addrlen,
                struct timeval *timeout,
                zend_string **error_string,
-               int *error_code
+               int *error_code,
+               int tcp_nodelay
                );
 
 PHPAPI int php_network_get_sock_name(php_socket_t sock,
index 7a21fbef4458dacc1306d531df40a51f5057aade..cc76ace51047051e0807cece1e9a9e90486698b0 100644 (file)
@@ -742,6 +742,18 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_
        }
 #endif
 
+       if (stream->ops != &php_stream_udp_socket_ops /* TCP_NODELAY is only applicable for TCP */
+#ifdef AF_UNIX
+               && stream->ops != &php_stream_unix_socket_ops
+               && stream->ops != &php_stream_unixdg_socket_ops
+#endif
+               && PHP_STREAM_CONTEXT(stream)
+               && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL
+               && zend_is_true(tmpzval)
+       ) {
+               sockopts |= STREAM_SOCKOP_TCP_NODELAY;
+       }
+
        /* Note: the test here for php_stream_udp_socket_ops is important, because we
         * want the default to be TCP sockets so that the openssl extension can
         * re-use this code. */
@@ -783,36 +795,37 @@ static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t
                php_stream_xport_param *xparam STREAMS_DC)
 {
        int clisock;
+       zend_bool nodelay = 0;
+       zval *tmpzval = NULL;
 
        xparam->outputs.client = NULL;
 
+       if ((NULL != PHP_STREAM_CONTEXT(stream)) &&
+               (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL &&
+               zend_is_true(tmpzval)) {
+               nodelay = 1;
+       }
+
        clisock = php_network_accept_incoming(sock->socket,
-                       xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
-                       xparam->want_addr ? &xparam->outputs.addr : NULL,
-                       xparam->want_addr ? &xparam->outputs.addrlen : NULL,
-                       xparam->inputs.timeout,
-                       xparam->want_errortext ? &xparam->outputs.error_text : NULL,
-                       &xparam->outputs.error_code
-                       );
+               xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
+               xparam->want_addr ? &xparam->outputs.addr : NULL,
+               xparam->want_addr ? &xparam->outputs.addrlen : NULL,
+               xparam->inputs.timeout,
+               xparam->want_errortext ? &xparam->outputs.error_text : NULL,
+               &xparam->outputs.error_code,
+               nodelay);
 
        if (clisock >= 0) {
-               php_netstream_data_t *clisockdata;
-
-               clisockdata = emalloc(sizeof(*clisockdata));
-
-               if (clisockdata == NULL) {
-                       close(clisock);
-                       /* technically a fatal error */
-               } else {
-                       memcpy(clisockdata, sock, sizeof(*clisockdata));
-                       clisockdata->socket = clisock;
-
-                       xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
-                       if (xparam->outputs.client) {
-                               xparam->outputs.client->ctx = stream->ctx;
-                               if (stream->ctx) {
-                                       GC_REFCOUNT(stream->ctx)++;
-                               }
+               php_netstream_data_t *clisockdata = (php_netstream_data_t*) emalloc(sizeof(*clisockdata));
+
+               memcpy(clisockdata, sock, sizeof(*clisockdata));
+               clisockdata->socket = clisock;
+
+               xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
+               if (xparam->outputs.client) {
+                       xparam->outputs.client->ctx = stream->ctx;
+                       if (stream->ctx) {
+                               GC_REFCOUNT(stream->ctx)++;
                        }
                }
        }