Skip to content
This repository has been archived by the owner on Dec 16, 2019. It is now read-only.

Socket error handling #819

Merged
merged 7 commits into from
Feb 9, 2018
Merged

Socket error handling #819

merged 7 commits into from
Feb 9, 2018

Conversation

sirsnyder
Copy link
Collaborator

@sirsnyder sirsnyder commented Feb 2, 2018

The return type of the following methods had to be removed from zend arg info, because these methods can return bool(false) in case of an error:

  • connect()
  • read()
  • write()
  • send()
  • select()

The two methods recvfrom() and sendto() can also return bool(false) in case of an error, but there were no explicit return types defined.

Each Socket object holds his own error state now. To query and clear the error state, the two new methods getLastError() and clearError() were added. Method getLastError() differs from the PHP equivalent socket_last_error() by one param. Param clear was added to query and clear error state in one single method call. The param is optional and false by default.

I've tried to mimic the PHP socket error behavior(without warnings) with a view to blocking/non-blocking mode as far as possible. Socket::select() should also not throw exceptions in case of an error. But this method is static and there is no global Socket error state. Therefore a optional param error was added to Socket::select() to retrieve a possible error.

Two -win32 socket tests where removed. @dktapps could you test the both socket-sentto-recvfrom- tests on windows, please?

Copy link
Contributor

@dktapps dktapps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are working fine (IPv6 needs a fix in #820 ), I haven't tested any added functionality yet.

src/socket.c Outdated
int eno = php_socket_errno(); \
(socket)->error = eno; \
PTHREADS_HANDLE_SOCKET_ERROR(eno); \
})
Copy link
Contributor

@dktapps dktapps Feb 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about other compilers, but this doesn't register as valid with MSVC (fails to compile). A do {} while(0) works.

classes/socket.h Outdated
@@ -66,22 +69,22 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_listen, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, backlog, IS_LONG, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_connect, 0, 2, _IS_BOOL, 0)
ZEND_BEGIN_ARG_INFO_EX(Socket_connect, 0, 0, 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't look like it needs changes?

classes/socket.h Outdated
@@ -111,18 +114,23 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_getHost, 0, 0, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, port, _IS_BOOL, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_select, 0, 3, IS_LONG, 0)
ZEND_BEGIN_ARG_INFO_EX(Socket_select, 0, 0, 4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the code the sec parameter still appears to be optional, so the required argcount should be 3.

classes/socket.h Outdated
@@ -215,7 +225,7 @@ PHP_METHOD(Socket, listen) {
pthreads_socket_listen(getThis(), backlog, return_value);
} /* }}} */

/* {{{ proto Socket Socket::accept([string class = self::class]) */
/* {{{ proto Socket/bool Socket::accept([string class = self::class]) */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the separator ought to be |.

src/socket.c Outdated
if(threaded->store.sock->error == SUCCESS) {
RETURN_FALSE;
}
ZVAL_LONG(return_value, threaded->store.sock->error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the RETVAL_LONG macro could be used here

src/socket.c Outdated
fd_set rfds, wfds, efds;
php_socket_t mfd = 0;
int result = SUCCESS, sets = 0;
struct timeval tv;

zval_dtor(errorno);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be zval_ptr_dtor, otherwise it will leak on cyclic references.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally this will segfault if the errorno parameter is not given.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've copied this part from

zval_dtor(port);
. Is zval_dtor in pthreads_socket_recvfrom also faulty?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks as though all three variables are not being properly freed:

pthreads/src/socket.c

Lines 773 to 775 in d32079f

zval_dtor(buffer);
zval_dtor(name);
zval_dtor(port);

They should all be using zval_ptr_dtor here (and elsewhere in pthreads_socket_recvfrom).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. zval_dtor was replaced by zval_ptr_dtor. Maybe you could have a look at the original implementation of socket_recvfrom https://github.com/php/php-src/blob/master/ext/sockets/sockets.c#L1771 which uses zval_dtor too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll have a look into this, thanks.

if (eno != SUCCESS) { \
if (estr) { \
efree(estr); \
#define PTHREADS_HANDLE_SOCKET_ERROR(eno) do { \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conventionally, parentheses should be placed around eno in the macro body.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parentheses added, should be fixed

@sirsnyder
Copy link
Collaborator Author

Some remarks where fixed, thx @dktapps and @tpunt. Please have a look at it again.

src/socket.c Outdated
@@ -947,7 +947,7 @@ void pthreads_socket_get_last_error(zval *object, zend_bool clear, zval *return_
if(threaded->store.sock->error == SUCCESS) {
RETURN_FALSE;
}
ZVAL_LONG(return_value, threaded->store.sock->error);
RETURN_LONG(threaded->store.sock->error);
Copy link
Contributor

@dktapps dktapps Feb 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will not work as expected (the block below won't be executed), did you mean RETVAL_LONG?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, you're right. RETURN_LONG contains a return;. My fault.

@dktapps
Copy link
Contributor

dktapps commented Feb 4, 2018

@sirsnyder from a non-technical perspective I don't see anything immediately wrong with it, except for Socket::select() will crash if the error reference parameter isn't specified.

@sirsnyder
Copy link
Collaborator Author

sirsnyder commented Feb 4, 2018

@dktapps Socket::select() won't crash anymore without error specified. I've added a null pointer check. @dktapps @tpunt Do you see any more issues? Otherwise I would like to merge this PR.

} \
return; \
Copy link
Collaborator

@tpunt tpunt Feb 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this return statement prevent the return_value from being set to false later on?

pthreads/src/socket.c

Lines 741 to 743 in f15c95e

PTHREADS_HANDLE_SOCKET_ERROR(eno);
RETURN_FALSE;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also in the PTHREADS_SOCKET_ERROR call sites.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of an exception, there is no need to set a return value or not?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quite true. I overlooked that part...

@tpunt
Copy link
Collaborator

tpunt commented Feb 4, 2018

I've had another look through the code, and cannot see anything immediately wrong with it (other than the couple of comments I left). Assuming @dktapps doesn't notice any further problems, then feel free to merge. Also, more tests around the sockets functionality (testing both success and error paths) would be good.

if (estr) { \
efree(estr); \
#define PTHREADS_HANDLE_SOCKET_ERROR(eno) do { \
if ((eno) != EAGAIN && (eno) != EWOULDBLOCK && (eno) != EINPROGRESS && (eno) != SOCK_EINVAL) { \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EWOULDBLOCK was written to be conditionally used lower down. Will it always be available? If not, then it should be conditionally used here too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On modern unix systems with GNU C library EWOULDBLOCK is always available with same value of EAGAIN. On modern windows systems EWOULDBLOCK is available but Microsoft do not want to guarantee EWOULDBLOCK. I've changed that. If error no EWOULDBLOCK doesn't exist on win32, it will be defined.

@sirsnyder
Copy link
Collaborator Author

@tpunt more tests will come

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants