Esempio n. 1
0
int php_mongo_io_stream_send(mongo_connection *con, mongo_server_options *options, void *data, int size, char **error_message)
{
	int retval;
	ERROR_HANDLER_DECLARATION(error_handler)
	TSRMLS_FETCH();

	php_mongo_stream_notify_io(options, MONGO_STREAM_NOTIFY_IO_WRITE, 0, size TSRMLS_CC);

	ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);
	retval = php_stream_write(con->socket, (char *) data, size);
	ERROR_HANDLER_RESTORE(error_handler);
	if (retval >= size) {
		php_mongo_stream_notify_io(options, MONGO_STREAM_NOTIFY_IO_COMPLETED, size, size TSRMLS_CC);
	}

	return retval;
}
Esempio n. 2
0
void* php_mongo_io_stream_connect(mongo_con_manager *manager, mongo_server_def *server, mongo_server_options *options, char **error_message)
{
	char *errmsg;
	int errcode;
	php_stream *stream;
	char *hash = mongo_server_create_hash(server);
	struct timeval ctimeout = {0, 0};
	char *dsn;
	int dsn_len;
	int tcp_socket = 1;
	ERROR_HANDLER_DECLARATION(error_handler)

	TSRMLS_FETCH();

	if (server->host[0] == '/') {
		dsn_len = spprintf(&dsn, 0, "unix://%s", server->host);
		tcp_socket = 0;
	} else {
		dsn_len = spprintf(&dsn, 0, "tcp://%s:%d", server->host, server->port);
	}


	/* Connection timeout behavior varies based on the following:
	 * - Negative => no timeout (i.e. block indefinitely)
	 * - Zero => not specified (PHP will use default_socket_timeout)
	 * - Positive => used specified timeout */
	if (options->connectTimeoutMS) {
		/* Convert negative value to -1 second, which implies no timeout */
		int connectTimeoutMS = options->connectTimeoutMS < 0 ? -1000 : options->connectTimeoutMS;

		ctimeout.tv_sec = connectTimeoutMS / 1000;
		ctimeout.tv_usec = (connectTimeoutMS % 1000) * 1000;
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "Connecting to %s (%s) with connection timeout: %d.%06d", dsn, hash, ctimeout.tv_sec, ctimeout.tv_usec);
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "Connecting to %s (%s) without connection timeout (default_socket_timeout will be used)", dsn, hash);
	}

	ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);
	stream = php_stream_xport_create(dsn, dsn_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, hash, options->connectTimeoutMS > 0 ? &ctimeout : NULL, (php_stream_context *)options->ctx, &errmsg, &errcode);
	ERROR_HANDLER_RESTORE(error_handler);

	efree(dsn);
	free(hash);

	if (!stream) {
		/* error_message will be free()d, but errmsg was allocated by PHP and needs efree() */
		*error_message = strdup(errmsg);
		efree(errmsg);
		return NULL;
	}

	if (tcp_socket) {
		int socket = ((php_netstream_data_t*)stream->abstract)->socket;
		int flag = 1;

		setsockopt(socket, IPPROTO_TCP,  TCP_NODELAY, (char *) &flag, sizeof(int));
	}

	if (options->ssl) {
		int crypto_enabled;

		ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);

		if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0) {
			ERROR_HANDLER_RESTORE(error_handler);
			*error_message = strdup("Cannot setup SSL, is ext/openssl loaded?");
			php_stream_close(stream);
			return NULL;
		}

		crypto_enabled = php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC);
		ERROR_HANDLER_RESTORE(error_handler);

		if (crypto_enabled < 0) {
			/* Setting up crypto failed. Thats only OK if we only preferred it */
			if (options->ssl == MONGO_SSL_PREFER) {
				/* FIXME: We can't actually get here because we reject setting
				 * this option to prefer in mcon/parse.c. This is however
				 * probably what we need to do in the future when mongod starts
				 * actually supporting this! :) */
				mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Failed establishing SSL for %s:%d", server->host, server->port);
				php_stream_xport_crypto_enable(stream, 0 TSRMLS_CC);
			} else {
				*error_message = strdup("Can't connect over SSL, is mongod running with SSL?");
				php_stream_close(stream);
				return NULL;
			}
		} else {
			mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Establish SSL for %s:%d", server->host, server->port);
		}
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Not establishing SSL for %s:%d", server->host, server->port);
	}

	/* Socket timeout behavior uses the same logic as connectTimeoutMS */
	if (options->socketTimeoutMS) {
		struct timeval rtimeout = {0, 0};

		/* Convert negative value to -1 second, which implies no timeout */
		int socketTimeoutMS = options->socketTimeoutMS < 0 ? -1000 : options->socketTimeoutMS;

		rtimeout.tv_sec = socketTimeoutMS / 1000;
		rtimeout.tv_usec = (socketTimeoutMS % 1000) * 1000;
		php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
		mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "Setting stream timeout to %d.%06d", rtimeout.tv_sec, rtimeout.tv_usec);
	}


	/* Avoid a weird leak warning in debug mode when freeing the stream */
#if ZEND_DEBUG
	stream->__exposed = 1;
#endif

	return stream;

}
Esempio n. 3
0
/* Returns the bytes read on success
 * Returns -31 on unknown failure
 * Returns -80 on timeout
 * Returns -32 when remote server closes the connection
 */
int php_mongo_io_stream_read(mongo_connection *con, mongo_server_options *options, int timeout, void *data, int size, char **error_message)
{
	int num = 1, received = 0;
	TSRMLS_FETCH();

	int socketTimeoutMS = options->socketTimeoutMS ? options->socketTimeoutMS : FG(default_socket_timeout) * 1000;

	/* Convert negative values to -1 second, which implies no timeout */
	socketTimeoutMS = socketTimeoutMS < 0 ? -1000 : socketTimeoutMS;
	timeout = timeout < 0 ? -1000 : timeout;

	/* Socket timeout behavior varies based on the following:
	 * - Negative => no timeout (i.e. block indefinitely)
	 * - Zero => not specified (no changes to existing configuration)
	 * - Positive => used specified timeout (revert to previous value later) */
	if (timeout && timeout != socketTimeoutMS) {
		struct timeval rtimeout = {0, 0};

		rtimeout.tv_sec = timeout / 1000;
		rtimeout.tv_usec = (timeout % 1000) * 1000;

		php_stream_set_option(con->socket, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
		mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "Setting the stream timeout to %d.%06d", rtimeout.tv_sec, rtimeout.tv_usec);
	} else {
		mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "No timeout changes for %s", con->hash);
	}

	php_mongo_stream_notify_io(options, MONGO_STREAM_NOTIFY_IO_READ, 0, size TSRMLS_CC);

	/* this can return FAILED if there is just no more data from db */
	while (received < size && num > 0) {
		int len = 4096 < (size - received) ? 4096 : size - received;
		ERROR_HANDLER_DECLARATION(error_handler)

		ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);
		num = php_stream_read(con->socket, (char *) data, len);
		ERROR_HANDLER_RESTORE(error_handler);

		if (num < 0) {
			/* Doesn't look like this can happen, php_sockop_read overwrites
			 * the failure from recv() to return 0 */
			*error_message = strdup("Read from socket failed");
			return -31;
		}

		/* It *may* have failed. It also may simply have no data */
		if (num == 0) {
			zval *metadata;

			MAKE_STD_ZVAL(metadata);
			array_init(metadata);
			if (php_stream_populate_meta_data(con->socket, metadata)) {
				zval **tmp;

				if (zend_hash_find(Z_ARRVAL_P(metadata), "timed_out", sizeof("timed_out"), (void**)&tmp) == SUCCESS) {
					convert_to_boolean_ex(tmp);
					if (Z_BVAL_PP(tmp)) {
						struct timeval rtimeout = {0, 0};

						if (timeout > 0 && options->socketTimeoutMS != timeout) {
							rtimeout.tv_sec = timeout / 1000;
							rtimeout.tv_usec = (timeout % 1000) * 1000;
						} else {
							/* Convert timeout=-1 to -1second, which PHP interprets as no timeout */
							int socketTimeoutMS = options->socketTimeoutMS == -1 ? -1000 : options->socketTimeoutMS;
							rtimeout.tv_sec = socketTimeoutMS / 1000;
							rtimeout.tv_usec = (socketTimeoutMS % 1000) * 1000;
						}
						*error_message = malloc(256);
						snprintf(*error_message, 256, "Read timed out after reading %d bytes, waited for %d.%06d seconds", num, rtimeout.tv_sec, rtimeout.tv_usec);
						zval_ptr_dtor(&metadata);
						return -80;
					}
				}
				if (zend_hash_find(Z_ARRVAL_P(metadata), "eof", sizeof("eof"), (void**)&tmp) == SUCCESS) {
					convert_to_boolean_ex(tmp);
					if (Z_BVAL_PP(tmp)) {
						*error_message = strdup("Remote server has closed the connection");
						zval_ptr_dtor(&metadata);
						return -32;
					}
				}
			}
			zval_ptr_dtor(&metadata);
		}

		data = (char*)data + num;
		received += num;
	}
	/* PHP may have sent notify-progress of *more then* 'received' in some
	 * cases.
	 * PHP will read 8192 byte chunks at a time, but if we request less data
	 * then that PHP will just buffer the rest, which is fine.  It could
	 * confuse users a little, why their progress update was higher then the
	 * max-bytes-expected though... */
	php_mongo_stream_notify_io(options, MONGO_STREAM_NOTIFY_IO_COMPLETED, received, size TSRMLS_CC);

	/* If the timeout was changed, revert to the previous value now */
	if (timeout && timeout != socketTimeoutMS) {
		struct timeval rtimeout = {0, 0};

		/* If socketTimeoutMS was never specified, revert to default_socket_timeout */
		if (options->socketTimeoutMS == 0) {
			mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "Stream timeout will be reverted to default_socket_timeout (%d)", FG(default_socket_timeout));
		}

		rtimeout.tv_sec = socketTimeoutMS / 1000;
		rtimeout.tv_usec = (socketTimeoutMS % 1000) * 1000;

		php_stream_set_option(con->socket, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
		mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "Now setting stream timeout back to %d.%06d", rtimeout.tv_sec, rtimeout.tv_usec);
	}

	return received;
}
Esempio n. 4
0
/* Returns the bytes read on success
 * Returns -31 on unknown failure
 * Returns -80 on timeout
 * Returns -32 when remote server closes the connection
 */
int php_mongo_io_stream_read(mongo_connection *con, mongo_server_options *options, int timeout, void *data, int size, char **error_message)
{
	int num = 1, received = 0;
	TSRMLS_FETCH();

	if (timeout > 0 && options->socketTimeoutMS != timeout) {
		struct timeval rtimeout = {0, 0};
		rtimeout.tv_sec = timeout / 1000;
		rtimeout.tv_usec = (timeout % 1000) * 1000;

		php_stream_set_option(con->socket, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
	}

	php_mongo_stream_notify_io(options, MONGO_STREAM_NOTIFY_IO_READ, 0, size TSRMLS_CC);

	/* this can return FAILED if there is just no more data from db */
	while (received < size && num > 0) {
		int len = 4096 < (size - received) ? 4096 : size - received;
		ERROR_HANDLER_DECLARATION(error_handler)

		ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);
		num = php_stream_read(con->socket, (char *) data, len);
		ERROR_HANDLER_RESTORE(error_handler);

		if (num < 0) {
			/* Doesn't look like this can happen, php_sockop_read overwrites
			 * the failure from recv() to return 0 */
			*error_message = strdup("Read from socket failed");
			return -31;
		}

		/* It *may* have failed. It also may simply have no data */
		if (num == 0) {
			zval *metadata;

			MAKE_STD_ZVAL(metadata);
			array_init(metadata);
			if (php_stream_populate_meta_data(con->socket, metadata)) {
				zval **tmp;

				if (zend_hash_find(Z_ARRVAL_P(metadata), "timed_out", sizeof("timed_out"), (void**)&tmp) == SUCCESS) {
					convert_to_boolean_ex(tmp);
					if (Z_BVAL_PP(tmp)) {
						struct timeval rtimeout = {0, 0};

						if (timeout > 0 && options->socketTimeoutMS != timeout) {
							rtimeout.tv_sec = timeout / 1000;
							rtimeout.tv_usec = (timeout % 1000) * 1000;
						} else {
							rtimeout.tv_sec = options->socketTimeoutMS / 1000;
							rtimeout.tv_usec = (options->socketTimeoutMS % 1000) * 1000;
						}
						*error_message = malloc(256);
						snprintf(*error_message, 256, "Read timed out after reading %d bytes, waited for %d.%06d seconds", num, rtimeout.tv_sec, rtimeout.tv_usec);
						zval_ptr_dtor(&metadata);
						return -80;
					}
				}
				if (zend_hash_find(Z_ARRVAL_P(metadata), "eof", sizeof("eof"), (void**)&tmp) == SUCCESS) {
					convert_to_boolean_ex(tmp);
					if (Z_BVAL_PP(tmp)) {
						*error_message = strdup("Remote server has closed the connection");
						zval_ptr_dtor(&metadata);
						return -32;
					}
				}
			}
			zval_ptr_dtor(&metadata);
		}

		data = (char*)data + num;
		received += num;
	}
	/* PHP may have sent notify-progress of *more then* 'received' in some
	 * cases.
	 * PHP will read 8192 byte chunks at a time, but if we request less data
	 * then that PHP will just buffer the rest, which is fine.  It could
	 * confuse users a little, why their progress update was higher then the
	 * max-bytes-expected though... */
	php_mongo_stream_notify_io(options, MONGO_STREAM_NOTIFY_IO_COMPLETED, received, size TSRMLS_CC);

	if (timeout > 0 && options->socketTimeoutMS != timeout) {
		struct timeval rtimeout = {0, 0};
		rtimeout.tv_sec = options->socketTimeoutMS / 1000;
		rtimeout.tv_usec = (options->socketTimeoutMS % 1000) * 1000;

		php_stream_set_option(con->socket, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
	}

	return received;
}