summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-http.cpp340
-rw-r--r--source/core/slang-http.h166
-rw-r--r--source/core/slang-stream.cpp81
-rw-r--r--source/core/slang-stream.h20
-rw-r--r--source/core/windows/slang-win-process.cpp73
5 files changed, 634 insertions, 46 deletions
diff --git a/source/core/slang-http.cpp b/source/core/slang-http.cpp
new file mode 100644
index 000000000..ea76d82a9
--- /dev/null
+++ b/source/core/slang-http.cpp
@@ -0,0 +1,340 @@
+#include "slang-http.h"
+
+#include "slang-string-util.h"
+
+#include "slang-process.h"
+
+namespace Slang {
+
+static const UnownedStringSlice g_headerEnd = UnownedStringSlice::fromLiteral("\r\n\r\n");
+static const UnownedStringSlice g_contentLength = UnownedStringSlice::fromLiteral("Content-Length");
+static const UnownedStringSlice g_contentType = UnownedStringSlice::fromLiteral("Content-Type");
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! HTTPHeader !!!!!!!!!!!!!!!!!!!!!!! */
+
+void HTTPHeader::reset()
+{
+ const UnownedStringSlice empty;
+
+ m_contentLength = 0;
+ m_mimeType = empty;
+ m_encoding = empty;
+ m_valuePairs.clear();
+ m_header = empty;
+
+ m_arena.deallocateAll();
+}
+
+/* static */SlangResult HTTPHeader::readHeaderText(BufferedReadStream* stream, Index& outEndIndex)
+{
+ // https://microsoft.github.io/language-server-protocol/specifications/specification-current/
+
+ while(true)
+ {
+ SLANG_RETURN_ON_FAIL(stream->update());
+
+ const Index index = findHeaderEnd(stream);
+ if (index >= 0)
+ {
+ outEndIndex = index;
+ return SLANG_OK;
+ }
+
+ if (stream->isEnd())
+ {
+ return SLANG_FAIL;
+ }
+
+ Process::sleepCurrentThread(0);
+ }
+}
+
+/* static */Index HTTPHeader::findHeaderEnd(BufferedReadStream* stream)
+{
+ // This could be more efficient - it just searches until there are enough bytes to have termination
+ auto bytes = stream->getView();
+ UnownedStringSlice input((const char*)bytes.begin(), (const char*)bytes.end());
+
+ const Index index = input.indexOf(g_headerEnd);
+ return (index >= 0) ? (index + g_headerEnd.getLength()) : index;
+}
+
+/* static */SlangResult HTTPHeader::parse(const UnownedStringSlice& inSlice, HTTPHeader& out)
+{
+ out.reset();
+
+ {
+ auto slice = inSlice;
+ // If has termination at end, remove so we don't have empty lines
+ if (slice.endsWith(g_headerEnd))
+ {
+ slice = slice.head(slice.getLength() - g_headerEnd.getLength());
+ }
+ // Allocate on on the arena, so when we reference other slices, they are part of this allocation.
+ out.m_header = UnownedStringSlice(out.m_arena.allocateString(slice.begin(), slice.getLength()), slice.getLength());
+ }
+
+ // Okay, we need to split into lines, and then examine the contents
+ for (auto line : LineParser(out.m_header))
+ {
+ // Examine the line for :
+ Index index = line.indexOf(':');
+ if (index < 0)
+ {
+ return SLANG_FAIL;
+ }
+
+ const UnownedStringSlice key = line.head(index).trim();
+ const UnownedStringSlice value = line.tail(index + 1).trim();
+
+ // Add the pair
+ Pair pair{ key, value };
+
+ // We could check if key is already used. Some values can be repeated I believe.
+ // So we just allow for now.
+
+ out.m_valuePairs.add(pair);
+
+ if (key == g_contentLength)
+ {
+ Index length;
+ SLANG_RETURN_ON_FAIL(StringUtil::parseInt(value, length) || length < 0);
+
+ out.m_contentLength = length;
+ }
+ else if (key == g_contentType)
+ {
+ List<UnownedStringSlice> slices;
+
+ // text/html; charset=UTF-8
+ StringUtil::split(value, ';', slices);
+
+ if (slices.getCount() < 1)
+ {
+ return SLANG_FAIL;
+ }
+ // set the mime type
+ out.m_mimeType = slices[0].trim();
+
+ // Look for other parameters, in particular charset
+ for (Index i = 1; i < slices.getCount(); ++i)
+ {
+ auto slice = slices[i];
+ Index equalIndex = slice.indexOf('=');
+ if (equalIndex >= 0)
+ {
+ auto paramName = slice.head(equalIndex).trim();
+ auto paramValue = slice.tail(equalIndex + 1).trim();
+
+ if (paramName == UnownedStringSlice::fromLiteral("charset"))
+ {
+ out.m_encoding = paramValue;
+ }
+ }
+ }
+ }
+ }
+
+ return SLANG_OK;
+}
+
+/* static */SlangResult HTTPHeader::read(BufferedReadStream* stream, HTTPHeader& out)
+{
+ Index endIndex;
+ SLANG_RETURN_ON_FAIL(readHeaderText(stream, endIndex));
+
+ // Get header into a slice
+ UnownedStringSlice headerText((const char*)stream->getBuffer(), endIndex);
+
+ // Parse the slice into the out HttpHeader
+ SLANG_RETURN_ON_FAIL(parse(headerText, out));
+
+ // Can consume these bytes from the stream.
+ stream->consume(endIndex);
+
+ return SLANG_OK;
+}
+
+void HTTPHeader::append(StringBuilder& out) const
+{
+ // Output the content length
+ out << g_contentLength << ": " << m_contentLength << "\r\n";
+
+ // If either is set construct a content type
+ if (m_mimeType.getLength() || m_encoding.getLength())
+ {
+ out << g_contentType << ": ";
+
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
+
+ auto mimeType = m_mimeType.getLength() ? m_mimeType : UnownedStringSlice::fromLiteral("text/plain");
+ auto encoding = m_encoding.getLength() ? m_encoding : UnownedStringSlice::fromLiteral("UTF-8");
+
+ out << "mimeType" << "; ";
+ out << "charset=" << encoding;
+
+ out << "\r\n";
+ }
+
+ // Output any other data
+ for (auto pair : m_valuePairs)
+ {
+ auto key = pair.key;
+ // Ignore these types, as already output from data we already have
+ if (key == g_contentType || key == g_contentLength)
+ {
+ continue;
+ }
+
+ out << key << ": " << pair.value << "\r\n";
+ }
+
+ // Add termination
+ out << "\r\n";
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! HTTPPacketConnection !!!!!!!!!!!!!!!!!!!!!!! */
+
+HTTPPacketConnection::HTTPPacketConnection(BufferedReadStream* readStream, Stream* writeStream) :
+ m_readStream(readStream),
+ m_writeStream(writeStream),
+ m_readState(ReadState::Header),
+ m_readResult(SLANG_OK)
+{
+}
+
+SlangResult HTTPPacketConnection::_handleHeader()
+{
+ SLANG_ASSERT(m_readState == ReadState::Header);
+
+ const Index index = HTTPHeader::findHeaderEnd(m_readStream);
+ if (index < 0)
+ {
+ // Don't have the full header yet
+ return SLANG_OK;
+ }
+
+ // Okay we can parse the header
+ UnownedStringSlice slice((const char*)m_readStream->getBuffer(), size_t(index));
+ SLANG_RETURN_ON_FAIL(_updateReadResult(HTTPHeader::parse(slice, m_readHeader)));
+
+ // Consume the header
+ m_readStream->consume(index);
+
+ // We are now consuming content
+ m_readState = ReadState::Content;
+ return SLANG_OK;
+}
+
+SlangResult HTTPPacketConnection::_handleContent()
+{
+ SLANG_ASSERT(m_readState == ReadState::Content);
+ // Do we have enough content, mark as done
+ if (m_readStream->getCount() >= m_readHeader.m_contentLength)
+ {
+ m_readState = ReadState::Done;
+ }
+ return SLANG_OK;
+}
+
+SlangResult HTTPPacketConnection::update()
+{
+ switch (m_readState)
+ {
+ case ReadState::Closed: return SLANG_OK;
+ case ReadState::Error: return m_readResult;
+ default: break;
+ }
+
+ SLANG_RETURN_ON_FAIL(_updateReadResult(m_readStream->update()));
+
+ // Note will only indicate end if the buffer *and* backing stream are end/empty
+ if (m_readStream->isEnd())
+ {
+ if (m_readState == ReadState::Header)
+ {
+ m_readState = ReadState::Closed;
+ }
+ else
+ {
+ // Closed without completing
+ m_readState = ReadState::Error;
+ m_readResult = SLANG_FAIL;
+ }
+ return SLANG_OK;
+ }
+
+ switch (m_readState)
+ {
+ case ReadState::Header:
+ {
+ SLANG_RETURN_ON_FAIL(_handleHeader());
+ // We might be able to progress through content, if we have the header
+ if (m_readState == ReadState::Content)
+ {
+ _handleContent();
+ }
+ break;
+ }
+ case ReadState::Content:
+ {
+ _handleContent();
+ break;
+ }
+ default: break;
+ }
+
+ return m_readResult;
+}
+
+SlangResult HTTPPacketConnection::waitForContent()
+{
+ while (m_readState == ReadState::Header ||
+ m_readState == ReadState::Content)
+ {
+ const auto prevCount = m_readStream->getCount();
+
+ SLANG_RETURN_ON_FAIL(update());
+
+ if (prevCount == m_readStream->getCount())
+ {
+ // Yield if it appears nothing was read.
+ Process::sleepCurrentThread(0);
+ }
+ }
+
+ return m_readResult;
+}
+
+void HTTPPacketConnection::consumeContent()
+{
+ SLANG_ASSERT(m_readState == ReadState::Done);
+ if (m_readState == ReadState::Done)
+ {
+ // Consume the content
+ m_readStream->consume(Index(m_readHeader.m_contentLength));
+ // Back looking for the header again
+ m_readState = ReadState::Header;
+ }
+}
+
+SlangResult HTTPPacketConnection::write(const void* content, size_t sizeInBytes)
+{
+ // Write the header
+ {
+ HTTPHeader header;
+ header.m_contentLength = sizeInBytes;
+
+ StringBuilder buf;
+ header.append(buf);
+
+ SLANG_RETURN_ON_FAIL(m_writeStream->write(buf.getBuffer(), buf.getLength()));
+ }
+
+ // Write the content
+ SLANG_RETURN_ON_FAIL(m_writeStream->write(content, sizeInBytes));
+
+ return SLANG_OK;
+}
+
+} // namespace Slang
diff --git a/source/core/slang-http.h b/source/core/slang-http.h
new file mode 100644
index 000000000..a7c6f6628
--- /dev/null
+++ b/source/core/slang-http.h
@@ -0,0 +1,166 @@
+#ifndef SLANG_CORE_HTTP_H
+#define SLANG_CORE_HTTP_H
+
+#include "../../slang.h"
+
+#include "slang-string.h"
+#include "slang-list.h"
+
+#include "slang-memory-arena.h"
+
+#include "slang-stream.h"
+
+#include "../../slang-com-helper.h"
+#include "../../slang-com-ptr.h"
+
+namespace Slang {
+
+/// All of the contained UnownedStringSlice can be stored in m_header. This can be checked via testing if
+/// the memory overlaps.
+///
+/// The m_arena can be used to store slices in an ad-hoc manner to keep in scope with the Header.
+struct HTTPHeader
+{
+ struct Pair
+ {
+ UnownedStringSlice key;
+ UnownedStringSlice value;
+ };
+
+ /// Append the header (including termination) to out
+ void append(StringBuilder& out) const;
+
+ /// Reset the contents
+ void reset();
+
+ SLANG_INLINE Index indexOfKey(const UnownedStringSlice& slice) const;
+
+ /// Ctor
+ HTTPHeader() :
+ m_arena(1024)
+ {
+ }
+
+ /// Reads from stream until the buffer contains all of the header. The outEndIndex will point
+ /// past the header termination.
+ static SlangResult readHeaderText(BufferedReadStream* stream, Index& outEndIndex);
+
+ /// Returns the index of the end of the header (index of first byte *after* the header), or < if doesn't have an end
+ static Index findHeaderEnd(BufferedReadStream* stream);
+
+ /// Parse the slice (holding a header) into out.
+ /// Will allocate the slice on the array and store in m_header.
+ /// Slices will reference sections of m_header, that may be useful in some scenarios.
+ static SlangResult parse(const UnownedStringSlice& slice, HTTPHeader& out);
+
+ /// Read from buffered stream header, and place parsed header into out
+ static SlangResult read(BufferedReadStream* stream, HTTPHeader& out);
+
+ size_t m_contentLength; ///< Content length in bytes
+
+ UnownedStringSlice m_mimeType; ///< The mime type
+ UnownedStringSlice m_encoding; ///< The character encoding
+
+ UnownedStringSlice m_header; ///< Optionally holds the whole of the header
+
+ List<Pair> m_valuePairs; /// All of the value pairs
+
+ MemoryArena m_arena; ///< Used to store backing memory
+
+private:
+ // Disable
+ HTTPHeader(const HTTPHeader&) = delete;
+ void operator=(const HTTPHeader&) = delete;
+};
+
+// -----------------------------------------------------------------
+Index HTTPHeader::indexOfKey(const UnownedStringSlice& slice) const
+{
+ return m_valuePairs.findFirstIndex([&](const HTTPHeader::Pair& pair) -> bool { return pair.key == slice; });
+}
+
+/// Implements a way to communicate over Streams via the HTTP *protocol*.
+///
+/// Allows for reading without blocking, via calls to 'update'. When a complete
+/// HTTP 'packet' (combination of header and content) is available, the ReadState will
+/// become 'Done'. For this to work without blocking it relies on the stream backing the BufferedReadStream
+/// to be non blocking.
+///
+/// If it is only necessary to respond on complete packets 'waitForContent' can be used.
+/// If this returns and ReadState is Done, then getHeader holds the current header, and getContent
+/// holds the content of the 'packet'.
+///
+/// Once the packet has been processed 'consumeContent' can be used. Once consumeContent is called
+/// both contents of getContent and getReadHeader will no longer be valid.
+///
+/// Ie using the slice returned from getContent *after* consumeContent is called is *undefined behavior*.
+///
+/// NOTE! that this does not implement HTTP over TCP/IP.
+/// That said it could be used to communicate via the HTTP protocol over TCP/IP
+/// if the Streams supplied were TCP/IP sockets.
+class HTTPPacketConnection : public RefObject
+{
+public:
+
+ enum class ReadState
+ {
+ Header, ///< Reading reader
+ Content, ///< Reading content (ie header is read)
+ Done, ///< The content is read
+ Closed, ///< The read stream is closed - no further packets can be read
+ Error, ///< In an error state - no further packets can be read
+ };
+
+ /// Update state
+ SlangResult update();
+ /// Get the current read staet
+ ReadState getReadState() const { return m_readState; }
+ /// Get the read header
+ const HTTPHeader& getReadHeader() const { SLANG_ASSERT(hasHeader()); return m_readHeader; }
+ /// Get the content
+ ConstArrayView<Byte> getContent() const { SLANG_ASSERT(m_readState == ReadState::Done); return ConstArrayView<Byte>((const Byte*)m_readStream->getBuffer(), m_readHeader.m_contentLength); }
+
+ /// Write. Will potentially block if write stream is blocking.
+ SlangResult write(const void* content, size_t sizeInBytes);
+
+ /// Blocks until full read packet is available or the stream is appropriately closed
+ SlangResult waitForContent();
+ /// Consume the content - so can read next content
+ void consumeContent();
+
+ /// True if connection is active.
+ bool isActive() const { return m_readState != ReadState::Error && m_readState != ReadState::Closed; }
+
+ bool hasHeader() const { return m_readState == ReadState::Content || m_readState == ReadState::Done; }
+ /// True if has content (implies has header)
+ bool hasContent() const { return m_readState == ReadState::Done; }
+
+ /// Ctor
+ HTTPPacketConnection(BufferedReadStream* readStream, Stream* writeStream);
+
+protected:
+ SlangResult _updateReadResult(SlangResult res)
+ {
+ if (SLANG_FAILED(res) && SLANG_SUCCEEDED(m_readResult))
+ {
+ m_readState = ReadState::Error;
+ m_readResult = res;
+ }
+ return res;
+ }
+
+ SlangResult _handleHeader();
+ SlangResult _handleContent();
+
+ SlangResult m_readResult;
+ HTTPHeader m_readHeader;
+
+ ReadState m_readState;
+
+ RefPtr<BufferedReadStream> m_readStream;
+ RefPtr<Stream> m_writeStream;
+};
+
+} // namespace Slang
+
+#endif // SLANG_CORE_HTTP_H
diff --git a/source/core/slang-stream.cpp b/source/core/slang-stream.cpp
index 042b1d898..7d4fab09e 100644
--- a/source/core/slang-stream.cpp
+++ b/source/core/slang-stream.cpp
@@ -3,6 +3,7 @@
#include <share.h>
#endif
#include "slang-io.h"
+#include "slang-process.h"
namespace Slang
{
@@ -374,13 +375,13 @@ SlangResult OwnedMemoryStream::write(const void * buffer, size_t length)
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! BufferedReadStream !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-void BufferedReadStream::_advanceStartIndex(Index byteCount)
+void BufferedReadStream::consume(Index byteCount)
{
SLANG_ASSERT(Index(getCount()) >= byteCount && byteCount >= 0);
m_startIndex += byteCount;
if (getCount() == 0)
{
- m_startIndex = 0;
+ _resetBuffer();
}
}
@@ -402,14 +403,13 @@ SlangResult BufferedReadStream::seek(SeekOrigin origin, Int64 offset)
if (origin == SeekOrigin::End || origin == SeekOrigin::Start || offset < 0 || offset >= Int64(getCount()))
{
// Empty the buffer
- m_startIndex = 0;
- m_buffer.setCount(0);
+ _resetBuffer();
// Seek on underlying stream
return m_stream->seek(origin, offset);
}
// We can just seek on the buffered data
- _advanceStartIndex(Index(offset));
+ consume(Index(offset));
return SLANG_OK;
}
@@ -439,7 +439,7 @@ SlangResult BufferedReadStream::read(void* inBuffer, size_t length, size_t& outR
::memcpy(buffer, getBuffer(), readCount);
- _advanceStartIndex(Index(readCount));
+ consume(Index(readCount));
buffer += readCount;
length -= readCount;
@@ -510,35 +510,37 @@ SlangResult BufferedReadStream::update()
return SLANG_OK;
}
+ // Repeat until we have enough space
+ for (;;)
{
// How much buffer space do we have. We need at least m_defaultReadSize
const size_t remainingCount = size_t(m_buffer.getCapacity() - m_buffer.getCount());
- // Repeat until we have enough space
- while (remainingCount < m_defaultReadSize)
+ if (remainingCount >= m_defaultReadSize)
{
- // If there is anything in the buffer shift it all down
- if (m_startIndex > 0)
- {
- Byte* buffer = m_buffer.getBuffer();
- const Index count = getCount();
- if (count > 0)
- {
- ::memmove(buffer, buffer + m_startIndex, count);
- }
-
- m_buffer.setCount(count);
- m_startIndex = 0;
- }
- else
+ break;
+ }
+
+ // If there is anything in the buffer shift it all down
+ if (m_startIndex > 0)
+ {
+ Byte* buffer = m_buffer.getBuffer();
+ const Index count = getCount();
+ if (count > 0)
{
- // Make sure we have the space
- const Index prevCount = m_buffer.getCount();
- m_buffer.setCount(prevCount + m_defaultReadSize);
- m_buffer.setCount(prevCount);
+ ::memmove(buffer, buffer + m_startIndex, count);
}
+
+ m_buffer.setCount(count);
+ m_startIndex = 0;
+ }
+ else
+ {
+ // Make sure we have the space
+ const Index prevCount = m_buffer.getCount();
+ m_buffer.setCount(prevCount + m_defaultReadSize);
+ m_buffer.setCount(prevCount);
}
- SLANG_ASSERT(size_t(m_buffer.getCapacity() - m_buffer.getCount()) >= m_defaultReadSize);
}
{
@@ -547,7 +549,7 @@ SlangResult BufferedReadStream::update()
size_t readBytes = 0;
- const SlangResult res = m_stream->read(m_buffer.getBuffer() + prevCount, m_defaultReadSize, readBytes);
+ const SlangResult res = m_stream->read(m_buffer.getBuffer() + prevCount, m_defaultReadSize, readBytes);
m_buffer.setCount(prevCount + Index(readBytes));
@@ -555,6 +557,29 @@ SlangResult BufferedReadStream::update()
}
}
+SlangResult BufferedReadStream::readUntilContains(size_t size)
+{
+ while (true)
+ {
+ if (size_t(getCount()) >= size)
+ {
+ return SLANG_OK;
+ }
+
+ const size_t preCount = size_t(getCount());
+
+ // Update buffer
+ SLANG_RETURN_ON_FAIL(update());
+
+ // If nothing was read yield
+ if (preCount == getCount())
+ {
+ Process::sleepCurrentThread(0);
+ }
+ }
+}
+
+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! StreamUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/* static */SlangResult StreamUtil::readAll(Stream* stream, size_t readSize, List<Byte>& ioBytes)
diff --git a/source/core/slang-stream.h b/source/core/slang-stream.h
index f6c9f54a3..a163a0c5d 100644
--- a/source/core/slang-stream.h
+++ b/source/core/slang-stream.h
@@ -201,16 +201,34 @@ public:
/// Will read assuming backing stream is
SlangResult update();
+ /// Consume bytes in the buffer.
+ void consume(Index byteCount);
+
Byte* getBuffer() { return m_buffer.getBuffer() + m_startIndex; }
const Byte* getBuffer() const { return m_buffer.getBuffer() + m_startIndex; }
size_t getCount() const { return m_buffer.getCount() - m_startIndex; }
+ /// Read until the buffer contains the specified amount of bytes
+ SlangResult readUntilContains(size_t size);
+
ConstArrayView<Byte> getView() const { return ConstArrayView<Byte>(getBuffer(), Index(getCount())); }
ArrayView<Byte> getView() { return ArrayView<Byte>(getBuffer(), Index(getCount())); }
+ BufferedReadStream(Stream* stream) :
+ m_stream(stream),
+ m_startIndex(0)
+ {
+
+ }
+
protected:
- void _advanceStartIndex(Index byteCount);
+
+ void _resetBuffer()
+ {
+ m_startIndex = 0;
+ m_buffer.setCount(0);
+ }
size_t m_defaultReadSize = 1024; ///< When initiating a read the default read size
List<Byte> m_buffer; ///< Holds the characters
diff --git a/source/core/windows/slang-win-process.cpp b/source/core/windows/slang-win-process.cpp
index 57eb209c3..c37936a30 100644
--- a/source/core/windows/slang-win-process.cpp
+++ b/source/core/windows/slang-win-process.cpp
@@ -103,6 +103,7 @@ protected:
FileAccess m_access = FileAccess::None;
WinHandle m_streamHandle;
bool m_isOwned;
+ bool m_isPipe;
};
@@ -135,10 +136,17 @@ WinPipeStream::WinPipeStream(HANDLE handle, FileAccess access, bool isOwned) :
m_access(access),
m_isOwned(isOwned)
{
- // It might be handy to get information about the handle
- // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipeinfo
+ // On Win32 a HANDLE has to be handled differently if it's a PIPE or FILE, so first determine
+ // if it really is a pipe.
+ // http://msdn.microsoft.com/en-us/library/aa364960(VS.85).aspx
+ m_isPipe = ::GetFileType(handle) == FILE_TYPE_PIPE;
+
+ if (m_isPipe)
{
+ // It might be handy to get information about the handle
+ // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipeinfo
+
DWORD flags, outBufferSize, inBufferSize, maxInstances;
// It appears that by default windows pipe buffer size is 4k.
if (GetNamedPipeInfo(handle, &flags, &outBufferSize, &inBufferSize, &maxInstances))
@@ -181,31 +189,40 @@ SlangResult WinPipeStream::read(void* buffer, size_t length, size_t& outReadByte
return SLANG_OK;
}
+ DWORD bytesRead = 0;
+
// Check if there is any data, so won't block
+ if (m_isPipe)
{
- DWORD bytesRead = 0;
- DWORD totalBytes = 0;
- DWORD remainingBytes = 0;
+ DWORD pipeBytesRead = 0;
+ DWORD pipeTotalBytesAvailable = 0;
+ DWORD pipeRemainingBytes = 0;
// Works on anonymous pipes too
// https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
- SLANG_RETURN_ON_FAIL(_updateState(PeekNamedPipe(m_streamHandle, nullptr, DWORD(0), &bytesRead, &totalBytes, &remainingBytes)));
+ SLANG_RETURN_ON_FAIL(_updateState(::PeekNamedPipe(m_streamHandle, nullptr, DWORD(0), &pipeBytesRead, &pipeTotalBytesAvailable, &pipeRemainingBytes)));
// If there is nothing to read we are done
// If we don't do this ReadFile will *block* if there is nothing available
- if (totalBytes == 0)
+ if (pipeTotalBytesAvailable == 0)
{
return SLANG_OK;
}
- }
+ SLANG_RETURN_ON_FAIL(_updateState(::ReadFile(m_streamHandle, buffer, DWORD(length), &bytesRead, nullptr)));
+ }
+ else
{
- DWORD bytesRead = 0;
- SLANG_RETURN_ON_FAIL(_updateState(ReadFile(m_streamHandle, buffer, DWORD(length), &bytesRead, nullptr)));
+ SLANG_RETURN_ON_FAIL(_updateState(::ReadFile(m_streamHandle, buffer, DWORD(length), &bytesRead, nullptr)));
- outReadBytes = size_t(bytesRead);
+ // If it's not a pipe, and there is nothing left, then we are done.
+ if (length > 0 && bytesRead == 0)
+ {
+ close();
+ }
}
+ outReadBytes = size_t(bytesRead);
return SLANG_OK;
}
@@ -223,9 +240,23 @@ SlangResult WinPipeStream::write(const void* buffer, size_t length)
}
DWORD numWritten = 0;
- BOOL writeResult = WriteFile(m_streamHandle, buffer, DWORD(length), &numWritten, nullptr);
+ BOOL writeResult = ::WriteFile(m_streamHandle, buffer, DWORD(length), &numWritten, nullptr);
+
+ if (!writeResult)
+ {
+ auto err = ::GetLastError();
+
+ if (err == ERROR_BROKEN_PIPE)
+ {
+ close();
+ return SLANG_FAIL;
+ }
+
+ SLANG_UNUSED(err);
+ return SLANG_FAIL;
+ }
- if (!writeResult || numWritten != length)
+ if (numWritten != length)
{
return SLANG_FAIL;
}
@@ -251,8 +282,7 @@ SlangResult WinPipeStream::flush()
}
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers
-
- if (!FlushFileBuffers(m_streamHandle))
+ if (!::FlushFileBuffers(m_streamHandle))
{
auto err = GetLastError();
SLANG_UNUSED(err);
@@ -327,8 +357,17 @@ bool WinProcess::isTerminated()
{
case StreamType::StdIn:
{
- const HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
- out = new WinPipeStream(stdinHandle, FileAccess::Read, false);
+ out = new WinPipeStream(GetStdHandle(STD_INPUT_HANDLE), FileAccess::Read, false);
+ return SLANG_OK;
+ }
+ case StreamType::StdOut:
+ {
+ out = new WinPipeStream(GetStdHandle(STD_OUTPUT_HANDLE), FileAccess::Write, false);
+ return SLANG_OK;
+ }
+ case StreamType::ErrorOut:
+ {
+ out = new WinPipeStream(GetStdHandle(STD_ERROR_HANDLE), FileAccess::Write, false);
return SLANG_OK;
}
}