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)++;
}
}
--- /dev/null
+--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)
--- /dev/null
+--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)
--- /dev/null
+--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)
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);
socklen_t *addrlen,
struct timeval *timeout,
zend_string **error_string,
- int *error_code
+ int *error_code,
+ int tcp_nodelay
)
{
php_socket_t clisock = -1;
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();
}
/* }}} */
-
/* 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.
}
}
#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);
#else
# undef closesocket
# define closesocket close
+# include <netinet/tcp.h>
#endif
#ifndef HAVE_SHUTDOWN
#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) */
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,
}
#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. */
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)++;
}
}
}