diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/slang-http.cpp | 340 | ||||
| -rw-r--r-- | source/core/slang-http.h | 166 | ||||
| -rw-r--r-- | source/core/slang-stream.cpp | 81 | ||||
| -rw-r--r-- | source/core/slang-stream.h | 20 | ||||
| -rw-r--r-- | source/core/windows/slang-win-process.cpp | 73 |
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; } } |
