summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main/network.c3
-rwxr-xr-xmain/php_streams.h1
-rwxr-xr-xmain/streams.c139
3 files changed, 90 insertions, 53 deletions
diff --git a/main/network.c b/main/network.c
index 484550ac5d..fad7e40ff5 100644
--- a/main/network.c
+++ b/main/network.c
@@ -923,7 +923,8 @@ php_stream_ops php_stream_socket_ops = {
NULL, /* seek */
php_sockop_cast,
php_sockop_stat,
- php_sockop_set_option
+ php_sockop_set_option,
+ 1
};
diff --git a/main/php_streams.h b/main/php_streams.h
index 3ae9455bc2..421dbf2535 100755
--- a/main/php_streams.h
+++ b/main/php_streams.h
@@ -153,6 +153,7 @@ typedef struct _php_stream_ops {
int (*cast)(php_stream *stream, int castas, void **ret TSRMLS_DC);
int (*stat)(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
int (*set_option)(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
+ int dont_block;
} php_stream_ops;
typedef struct _php_stream_wrapper_ops {
diff --git a/main/streams.c b/main/streams.c
index 5d2188f746..2da448aa64 100755
--- a/main/streams.c
+++ b/main/streams.c
@@ -494,7 +494,11 @@ static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_D
if (justread <= 0)
break;
+
stream->writepos += justread;
+
+ if (stream->ops->dont_block)
+ break;
}
}
@@ -615,74 +619,105 @@ PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_D
return stream->ops->stat(stream, ssb TSRMLS_CC);
}
-PHPAPI char *_php_stream_gets(php_stream *stream, char *buf, size_t maxlen TSRMLS_DC)
+static char *php_stream_locate_eol(php_stream *stream TSRMLS_DC)
{
+ size_t avail;
char *cr, *lf, *eol = NULL;
- size_t toread = 0, didread = 0, justread = 0, avail = 0;
char *readptr;
+ readptr = stream->readbuf + stream->readpos;
+ avail = stream->writepos - stream->readpos;
+
+ /* Look for EOL */
+ if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) {
+ cr = memchr(readptr, '\r', avail);
+ lf = memchr(readptr, '\n', avail);
+
+ if (cr && lf != cr + 1) {
+ /* mac */
+ stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
+ stream->flags |= PHP_STREAM_FLAG_EOL_MAC;
+ eol = cr;
+ } else if ((cr && lf && cr == lf - 1) || (lf)) {
+ /* dos or unix endings */
+ stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
+ eol = lf;
+ }
+ } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
+ eol = memchr(readptr, '\r', avail);
+ } else {
+ /* unix (and dos) line endings */
+ eol = memchr(readptr, '\n', avail);
+ }
+
+ return eol;
+}
+
+PHPAPI char *_php_stream_gets(php_stream *stream, char *buf, size_t maxlen TSRMLS_DC)
+{
+ size_t avail = 0;
+
if (maxlen == 0)
return NULL;
-
- while (didread < maxlen - 1) {
- toread = maxlen - 1;
- if (toread > stream->chunk_size)
- toread = stream->chunk_size;
- php_stream_fill_read_buffer(stream, toread TSRMLS_CC);
+ /*
+ * If the underlying stream operations block when no new data is readable,
+ * we need to take extra precautions.
+ *
+ * If there is buffered data available, we check for a EOL. If it exists,
+ * we pass the data immediately back to the caller. This saves a call
+ * to the read implementation and will not block where blocking
+ * is not necessary at all.
+ *
+ * If the stream buffer contains more data than the caller requested,
+ * we can also avoid that costly step and simply return that data.
+ */
- readptr = stream->readbuf + stream->readpos;
+ for (;;) {
avail = stream->writepos - stream->readpos;
- if (avail == 0)
- break;
-
- /* Look for EOL */
- if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) {
- cr = memchr(readptr, '\r', avail);
- lf = memchr(readptr, '\n', avail);
-
- if (cr && lf != cr + 1) {
- /* mac */
- stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
- stream->flags |= PHP_STREAM_FLAG_EOL_MAC;
- eol = cr;
- } else if ((cr && lf && cr == lf - 1) || (lf)) {
- /* dos or unix endings */
- stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
- eol = lf;
+ if (avail > 0) {
+ size_t cpysz = 0;
+ char *readptr;
+ char *eol;
+ int done = 0;
+
+ readptr = stream->readbuf + stream->readpos;
+ eol = php_stream_locate_eol(stream TSRMLS_CC);
+
+ if (eol) {
+ cpysz = eol - readptr + 1;
+ done = 1;
+ } else {
+ cpysz = avail;
}
- } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
- eol = memchr(readptr, '\r', avail);
- } else {
- /* unix (and dos) line endings */
- eol = memchr(readptr, '\n', avail);
- }
- if (eol && (size_t)((ptrdiff_t)eol + 1 - (ptrdiff_t)readptr) <= maxlen - 1) {
- justread = eol + 1 - readptr;
- } else {
- eol = NULL;
- justread = toread;
- if (justread > avail)
- justread = avail;
- }
+ if (cpysz >= maxlen - 1) {
+ cpysz = maxlen - 1;
+ done = 1;
+ }
- memcpy(buf, readptr, justread);
- didread += justread;
- buf += justread;
- stream->readpos += justread;
+ memcpy(buf, readptr, cpysz);
- if (eol)
- break;
- }
+ stream->position += cpysz;
+ stream->readpos += cpysz;
+ buf += cpysz;
+ maxlen -= cpysz;
- if (didread == 0)
- return NULL;
+ if (done) {
+ break;
+ }
+ } else {
+ size_t toread = maxlen - 1;
+ if (toread > stream->chunk_size)
+ toread = stream->chunk_size;
+
+ /* XXX: Should not the loop end, if the stream op fails? */
+ php_stream_fill_read_buffer(stream, toread TSRMLS_CC);
+ }
+ }
- /* terminate the buffer */
- *buf = '\0';
- stream->position += didread;
+ buf[0] = '\0';
return buf;
}