cataphract Tue, 22 Mar 2011 00:44:23 +0000 Revision: http://svn.php.net/viewvc?view=revision&revision=309516
Log: - Added socket_import_stream(). - Fixed socket_strerror.phpt - Made php_set_sock_blocking return FAILURE on fcntl error. - Made socket_set_block()/socket_set_nonblock() emit warning on error. Changed paths: U php/php-src/trunk/ext/sockets/php_sockets.h U php/php-src/trunk/ext/sockets/sockets.c A php/php-src/trunk/ext/sockets/tests/socket_import_stream-1.phpt A php/php-src/trunk/ext/sockets/tests/socket_import_stream-2.phpt A php/php-src/trunk/ext/sockets/tests/socket_import_stream-3.phpt A php/php-src/trunk/ext/sockets/tests/socket_import_stream-4.phpt A php/php-src/trunk/ext/sockets/tests/socket_import_stream-5.phpt U php/php-src/trunk/ext/sockets/tests/socket_strerror.phpt U php/php-src/trunk/main/network.c
Modified: php/php-src/trunk/ext/sockets/php_sockets.h =================================================================== --- php/php-src/trunk/ext/sockets/php_sockets.h 2011-03-22 00:32:33 UTC (rev 309515) +++ php/php-src/trunk/ext/sockets/php_sockets.h 2011-03-22 00:44:23 UTC (rev 309516) @@ -70,6 +70,7 @@ #endif PHP_FUNCTION(socket_last_error); PHP_FUNCTION(socket_clear_error); +PHP_FUNCTION(socket_import_stream); #ifndef PHP_WIN32 typedef int PHP_SOCKET; @@ -80,10 +81,11 @@ #endif typedef struct { - PHP_SOCKET bsd_socket; - int type; - int error; - int blocking; + PHP_SOCKET bsd_socket; + int type; + int error; + int blocking; + zval *zstream; } php_socket; #ifdef PHP_WIN32 Modified: php/php-src/trunk/ext/sockets/sockets.c =================================================================== --- php/php-src/trunk/ext/sockets/sockets.c 2011-03-22 00:32:33 UTC (rev 309515) +++ php/php-src/trunk/ext/sockets/sockets.c 2011-03-22 00:44:23 UTC (rev 309516) @@ -270,6 +270,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_clear_error, 0, 0, 0) ZEND_ARG_INFO(0, socket) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_socket_import_stream, 0, 0, 1) + ZEND_ARG_INFO(0, stream) +ZEND_END_ARG_INFO() /* }}} */ /* {{{ sockets_functions[] @@ -304,6 +308,7 @@ #endif PHP_FE(socket_last_error, arginfo_socket_last_error) PHP_FE(socket_clear_error, arginfo_socket_clear_error) + PHP_FE(socket_import_stream, arginfo_socket_import_stream) /* for downwards compatability */ PHP_FALIAS(socket_getopt, socket_get_option, arginfo_socket_get_option) @@ -344,11 +349,33 @@ } /* }}} */ +/* allocating function to make programming errors due to uninitialized fields + * less likely */ +static php_socket *php_create_socket(void) /* {{{ */ +{ + php_socket *php_sock = emalloc(sizeof *php_sock); + + php_sock->bsd_socket = -1; /* invalid socket */ + php_sock->type = PF_UNSPEC; + php_sock->error = 0; + php_sock->blocking = 1; + php_sock->zstream = NULL; + + return php_sock; +} +/* }}} */ + static void php_destroy_socket(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ { - php_socket *php_sock = (php_socket *) rsrc->ptr; + php_socket *php_sock = rsrc->ptr; - close(php_sock->bsd_socket); + if (php_sock->zstream == NULL) { + if (!IS_INVALID_SOCKET(php_sock)) { + close(php_sock->bsd_socket); + } + } else { + zval_ptr_dtor(&php_sock->zstream); + } efree(php_sock); } /* }}} */ @@ -357,7 +384,7 @@ { struct sockaddr_in la; struct hostent *hp; - php_socket *sock = (php_socket*)emalloc(sizeof(php_socket)); + php_socket *sock = php_create_socket(); *php_sock = sock; @@ -405,7 +432,7 @@ static int php_accept_connect(php_socket *in_sock, php_socket **new_sock, struct sockaddr *la, socklen_t *la_len TSRMLS_DC) /* {{{ */ { - php_socket *out_sock = (php_socket*)emalloc(sizeof(php_socket)); + php_socket *out_sock = php_create_socket(); *new_sock = out_sock; @@ -731,8 +758,6 @@ */ PHP_MINIT_FUNCTION(sockets) { - struct protoent *pe; - le_socket = zend_register_list_destructors_ex(php_destroy_socket, NULL, le_socket_name, module_number); REGISTER_LONG_CONSTANT("AF_UNIX", AF_UNIX, CONST_CS | CONST_PERSISTENT); @@ -1051,12 +1076,28 @@ } ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket); + + if (php_sock->zstream != NULL) { + php_stream *stream; + /* omit notice if resource doesn't exist anymore */ + stream = zend_fetch_resource(&php_sock->zstream TSRMLS_CC, -1, + NULL, NULL, 2, php_file_le_stream(), php_file_le_pstream()); + if (stream != NULL) { + if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 0, + NULL) != -1) { + php_sock->blocking = 1; + RETURN_TRUE; + } + } + } if (php_set_sock_blocking(php_sock->bsd_socket, 0 TSRMLS_CC) == SUCCESS) { php_sock->blocking = 0; RETURN_TRUE; + } else { + PHP_SOCKET_ERROR(php_sock, "unable to set nonblocking mode", errno); + RETURN_FALSE; } - RETURN_FALSE; } /* }}} */ @@ -1072,12 +1113,30 @@ } ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket); + + /* if socket was created from a stream, give the stream a chance to take + * care of the operation itself, thereby allowing it to update its internal + * state */ + if (php_sock->zstream != NULL) { + php_stream *stream; + stream = zend_fetch_resource(&php_sock->zstream TSRMLS_CC, -1, + NULL, NULL, 2, php_file_le_stream(), php_file_le_pstream()); + if (stream != NULL) { + if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 1, + NULL) != -1) { + php_sock->blocking = 1; + RETURN_TRUE; + } + } + } if (php_set_sock_blocking(php_sock->bsd_socket, 1 TSRMLS_CC) == SUCCESS) { php_sock->blocking = 1; RETURN_TRUE; + } else { + PHP_SOCKET_ERROR(php_sock, "unable to set blocking mode", errno); + RETURN_FALSE; } - RETURN_FALSE; } /* }}} */ @@ -1115,6 +1174,16 @@ } ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket); + if (php_sock->zstream != NULL) { + php_stream *stream = NULL; + php_stream_from_zval_no_verify(stream, &php_sock->zstream); + if (stream != NULL) { + /* close & destroy stream, incl. removing it from the rsrc list; + * resource stored in php_sock->zstream will become invalid */ + php_stream_free(stream, PHP_STREAM_FREE_CLOSE | + (stream->is_persistent?PHP_STREAM_FREE_CLOSE_PERSISTENT:0)); + } + } zend_list_delete(Z_RESVAL_P(arg1)); } /* }}} */ @@ -1372,7 +1441,7 @@ PHP_FUNCTION(socket_create) { long arg1, arg2, arg3; - php_socket *php_sock = (php_socket*)emalloc(sizeof(php_socket)); + php_socket *php_sock = php_create_socket(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &arg1, &arg2, &arg3) == FAILURE) { efree(php_sock); @@ -2244,8 +2313,8 @@ return; } - php_sock[0] = (php_socket*)emalloc(sizeof(php_socket)); - php_sock[1] = (php_socket*)emalloc(sizeof(php_socket)); + php_sock[0] = php_create_socket(); + php_sock[1] = php_create_socket(); if (domain != AF_INET #if HAVE_IPV6 @@ -2362,8 +2431,82 @@ } /* }}} */ +/* {{{ proto void socket_import_stream(resource stream) + Imports a stream that encapsulates a socket into a socket extension resource. */ +PHP_FUNCTION(socket_import_stream) +{ + zval *zstream; + php_stream *stream; + php_socket *retsock = NULL;; + PHP_SOCKET socket; /* fd */ + php_sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + int t; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream) == FAILURE) { + return; + } + php_stream_from_zval(stream, &zstream); + + if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void**)&socket, 1)) { + /* error supposedly already shown */ + RETURN_FALSE; + } + + retsock = php_create_socket(); + + retsock->bsd_socket = socket; + + /* determine family */ + if (getsockname(socket, (struct sockaddr*)&addr, &addr_len) == 0) { + retsock->type = addr.ss_family; + } else { + PHP_SOCKET_ERROR(retsock, "unable to obtain socket family", errno); + goto error; + } + + /* determine blocking mode */ +#ifndef PHP_WIN32 + t = fcntl(socket, F_GETFL); + if(t == -1) { + PHP_SOCKET_ERROR(retsock, "unable to obtain blocking state", errno); + goto error; + } else { + retsock->blocking = !(t & O_NONBLOCK); + } +#else + /* on windows, check if the stream is a socket stream and read its + * private data; otherwise assume it's in non-blocking mode */ + if (php_stream_is(stream, PHP_STREAM_IS_SOCKET)) { + retsock->blocking = + ((php_netstream_data_t)stream->abstract)->is_blocked; + } else { + retsock->blocking = 1; + } #endif + + /* hold a zval reference to the stream (holding a php_stream* directly could + * also be done, but this might be slightly better if in the future we want + * to provide a socket_export_stream) */ + MAKE_STD_ZVAL(retsock->zstream); + *retsock->zstream = *zstream; + zval_copy_ctor(retsock->zstream); + Z_UNSET_ISREF_P(retsock->zstream); + Z_SET_REFCOUNT_P(retsock->zstream, 1); + + php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, + PHP_STREAM_BUFFER_NONE, NULL); + + ZEND_REGISTER_RESOURCE(return_value, retsock, le_socket); + return; +error: + if (retsock != NULL) + efree(retsock); +} +/* }}} */ +#endif + /* * Local variables: * tab-width: 4 Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-1.phpt =================================================================== --- php/php-src/trunk/ext/sockets/tests/socket_import_stream-1.phpt (rev 0) +++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-1.phpt 2011-03-22 00:44:23 UTC (rev 309516) @@ -0,0 +1,26 @@ +--TEST-- +socket_import_stream: Basic test +--SKIPIF-- +<?php +if (!extension_loaded('sockets')) { + die('SKIP sockets extension not available.'); +} + +--FILE-- +<?php + +$domain = (strtoupper(substr(PHP_OS, 0, 3) == 'WIN') ? STREAM_PF_INET : STREAM_PF_UNIX); +$s = stream_socket_pair($domain, STREAM_SOCK_STREAM, 0); + +$s0 = reset($s); +$s1 = next($s); + +$sock = socket_import_stream($s0); +var_dump($sock); +socket_write($sock, "test message"); +socket_close($sock); + +var_dump(stream_get_contents($s1)); +--EXPECTF-- +resource(%d) of type (Socket) +string(12) "test message" Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-2.phpt =================================================================== --- php/php-src/trunk/ext/sockets/tests/socket_import_stream-2.phpt (rev 0) +++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-2.phpt 2011-03-22 00:44:23 UTC (rev 309516) @@ -0,0 +1,49 @@ +--TEST-- +socket_import_stream: Bad arguments +--SKIPIF-- +<?php +if (!extension_loaded('sockets')) { + die('SKIP sockets extension not available.'); +} + +--FILE-- +<?php + +var_dump(socket_import_stream()); +var_dump(socket_import_stream(1, 2)); +var_dump(socket_import_stream(1)); +var_dump(socket_import_stream(new stdclass)); +var_dump(socket_import_stream(fopen(__FILE__, "rb"))); +var_dump(socket_import_stream(socket_create(AF_INET, SOCK_DGRAM, SOL_UDP))); +$s = stream_socket_server("udp://127.0.0.1:58392", $errno, $errstr, STREAM_SERVER_BIND); +var_dump($s); +var_dump(fclose($s)); +var_dump(socket_import_stream($s)); + + +echo "Done."; +--EXPECTF-- +Warning: socket_import_stream() expects exactly 1 parameter, 0 given in %s on line %d +NULL + +Warning: socket_import_stream() expects exactly 1 parameter, 2 given in %s on line %d +NULL + +Warning: socket_import_stream() expects parameter 1 to be resource, integer given in %s on line %d +NULL + +Warning: socket_import_stream() expects parameter 1 to be resource, object given in %s on line %d +NULL + +Warning: socket_import_stream(): cannot represent a stream of type STDIO as a Socket Descriptor in %s on line %d +bool(false) + +Warning: socket_import_stream(): supplied resource is not a valid stream resource in %s on line %d +bool(false) +resource(%d) of type (stream) +bool(true) + +Warning: socket_import_stream(): %d is not a valid stream resource in %s on line %d +bool(false) +Done. + Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-3.phpt =================================================================== --- php/php-src/trunk/ext/sockets/tests/socket_import_stream-3.phpt (rev 0) +++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-3.phpt 2011-03-22 00:44:23 UTC (rev 309516) @@ -0,0 +1,46 @@ +--TEST-- +socket_import_stream: Test with multicasting +--SKIPIF-- +<?php +if (!extension_loaded('sockets')) { + die('SKIP sockets extension not available.'); +} +$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); +$br = socket_bind($s, '0.0.0.0', 58381); +if ($br === false) + die("SKIP IPv4/port 58381 not available"); +$so = socket_set_option($s, IPPROTO_IP, MCAST_JOIN_GROUP, array( + "group" => '224.0.0.23', + "interface" => "lo", +)); +if ($br === false) + die("SKIP joining group 224.0.0.23 on interface lo failed"); +--FILE-- +<?php + +$stream = stream_socket_server("udp://0.0.0.0:58381", $errno, $errstr, STREAM_SERVER_BIND); +$sock = socket_import_stream($stream); +var_dump($sock); +$so = socket_set_option($sock, IPPROTO_IP, MCAST_JOIN_GROUP, array( + "group" => '224.0.0.23', + "interface" => "lo", +)); +var_dump($so); + +$sendsock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); +var_dump($sendsock); +$br = socket_bind($sendsock, '127.0.0.1'); +$so = socket_sendto($sendsock, $m = "my message", strlen($m), 0, "224.0.0.23", 58381); +var_dump($so); + +stream_set_blocking($stream, 0); +var_dump(fread($stream, strlen($m))); +echo "Done.\n"; +--EXPECTF-- +resource(%d) of type (Socket) +bool(true) +resource(%d) of type (Socket) +int(10) +string(10) "my message" +Done. + Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-4.phpt =================================================================== --- php/php-src/trunk/ext/sockets/tests/socket_import_stream-4.phpt (rev 0) +++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-4.phpt 2011-03-22 00:44:23 UTC (rev 309516) @@ -0,0 +1,98 @@ +--TEST-- +socket_import_stream: effects of closing +--SKIPIF-- +<?php +if (!extension_loaded('sockets')) { + die('SKIP sockets extension not available.'); +} + +--FILE-- +<?php + +function test($stream, $sock) { + if ($stream !== null) { + echo "stream_set_blocking "; + print_r(stream_set_blocking($stream, 0)); + echo "\n"; + } + if ($sock !== null) { + echo "socket_set_block "; + print_r(socket_set_block($sock)); + echo "\n"; + echo "socket_get_option "; + print_r(socket_get_option($sock, SOL_SOCKET, SO_TYPE)); + echo "\n"; + } + echo "\n"; +} + +echo "normal\n"; +$stream0 = stream_socket_server("udp://0.0.0.0:58380", $errno, $errstr, STREAM_SERVER_BIND); +$sock0 = socket_import_stream($stream0); +test($stream0, $sock0); + +echo "\nunset stream\n"; +$stream1 = stream_socket_server("udp://0.0.0.0:58381", $errno, $errstr, STREAM_SERVER_BIND); +$sock1 = socket_import_stream($stream1); +unset($stream1); +test(null, $sock1); + +echo "\nunset socket\n"; +$stream2 = stream_socket_server("udp://0.0.0.0:58382", $errno, $errstr, STREAM_SERVER_BIND); +$sock2 = socket_import_stream($stream2); +unset($sock2); +test($stream2, null); + +echo "\nclose stream\n"; +$stream3 = stream_socket_server("udp://0.0.0.0:58383", $errno, $errstr, STREAM_SERVER_BIND); +$sock3 = socket_import_stream($stream3); +fclose($stream3); +test($stream3, $sock3); + +echo "\nclose socket\n"; +$stream4 = stream_socket_server("udp://0.0.0.0:58384", $errno, $errstr, STREAM_SERVER_BIND); +$sock4 = socket_import_stream($stream4); +socket_close($sock4); +test($stream4, $sock4); + +echo "Done.\n"; +--EXPECTF-- +normal +stream_set_blocking 1 +socket_set_block 1 +socket_get_option 2 + + +unset stream +socket_set_block 1 +socket_get_option 2 + + +unset socket +stream_set_blocking 1 + + +close stream +stream_set_blocking +Warning: stream_set_blocking(): %d is not a valid stream resource in %s on line %d + +socket_set_block +Warning: socket_set_block(): unable to set blocking mode [%d]: %s in %s on line %d + +socket_get_option +Warning: socket_get_option(): unable to retrieve socket option [%d]: %s in %s on line %d + + + +close socket +stream_set_blocking +Warning: stream_set_blocking(): %d is not a valid stream resource in %s on line %d + +socket_set_block +Warning: socket_set_block(): %d is not a valid Socket resource in %s on line %d + +socket_get_option +Warning: socket_get_option(): %d is not a valid Socket resource in %s on line %d + + +Done. Added: php/php-src/trunk/ext/sockets/tests/socket_import_stream-5.phpt =================================================================== --- php/php-src/trunk/ext/sockets/tests/socket_import_stream-5.phpt (rev 0) +++ php/php-src/trunk/ext/sockets/tests/socket_import_stream-5.phpt 2011-03-22 00:44:23 UTC (rev 309516) @@ -0,0 +1,23 @@ +--TEST-- +socket_import_stream: effects of leaked handles +--SKIPIF-- +<?php +if (!extension_loaded('sockets')) { + die('SKIP sockets extension not available.'); +} +if (!function_exists('leak_variable')) + die('SKIP only for debug builds') +--FILE-- +<?php + +$stream0 = stream_socket_server("udp://0.0.0.0:58380", $errno, $errstr, STREAM_SERVER_BIND); +$sock0 = socket_import_stream($stream0); +leak_variable($stream0, true); + +$stream1 = stream_socket_server("udp://0.0.0.0:58381", $errno, $errstr, STREAM_SERVER_BIND); +$sock1 = socket_import_stream($stream1); +leak_variable($sock1, true); + +echo "Done.\n"; +--EXPECT-- +Done. Modified: php/php-src/trunk/ext/sockets/tests/socket_strerror.phpt =================================================================== --- php/php-src/trunk/ext/sockets/tests/socket_strerror.phpt 2011-03-22 00:32:33 UTC (rev 309515) +++ php/php-src/trunk/ext/sockets/tests/socket_strerror.phpt 2011-03-22 00:44:23 UTC (rev 309516) @@ -154,4 +154,4 @@ string(27) "Key was rejected by service" string(10) "Owner died" string(21) "State not recoverable" -string(17) "Unknown error 132" +string(37) "Operation not possible due to RF-kill" Modified: php/php-src/trunk/main/network.c =================================================================== --- php/php-src/trunk/main/network.c 2011-03-22 00:32:33 UTC (rev 309515) +++ php/php-src/trunk/main/network.c 2011-03-22 00:44:23 UTC (rev 309516) @@ -1095,7 +1095,9 @@ } else { flags &= ~myflag; } - fcntl(socketd, F_SETFL, flags); + if (fcntl(socketd, F_SETFL, flags) == -1) { + ret = FAILURE; + } #endif return ret; }
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php