summaryrefslogtreecommitdiff
path: root/source/core/slang-http.h
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-11-15 20:45:21 -0500
committerGitHub <noreply@github.com>2021-11-15 20:45:21 -0500
commit914a3808ebefb0f7f0a728469a2ce6b56dfc316c (patch)
tree9e452c21cf2a1d89d180818c65c6e7909f3328cf /source/core/slang-http.h
parentae9df74fce7e3583effc822b3003542cf753823d (diff)
Http protocol support (#2012)
* #include an absolute path didn't work - because paths were taken to always be relative. * Use 'Process' to communicate with an command line tool. * Remove slang-win-stream * Tidy up windows ProcessUtil. * First version of BufferedReadStream. * Windows working IPC for steams. * Test proxy count option. * Split Process/ProcessUtil. Process is platform dependant. ProcessUtil are functions that are platform independent. * First implementation of Unix Process interface. * Unix process compiles on cygwin. * Fix typo in unix process. * Separate unix pipe stream error of invalid access, from pipe availability. * Fix in standard line extraction. * Make fd non blocking. * Fix issues with Windows Process streams. * Added UnixPipe. * Some fixes around UnixPipeStream. * Make a unix stream closed explicit. * Hack to debug linux process/stream. * Revert to old linux pipe handling. * Pass executable path for unit tests. Split out CommandLine into own source. * Small improvements in process/command line. * Check process behavior with crash. * Make stderr and stdout unbuffered for crash testing. * Only turn disable buffering in crash test. * Disable crash test on CI. * Fix crash on clang/linux. * Enable crash test. Remove _appendBuffer as can use StreamUtil functionality. * Added inital processing for http headers. * Small improvements to HttpHeader. * First pass HTTPPacketConnection working on windows. * Enable other Process communication tests. * Update comments.
Diffstat (limited to 'source/core/slang-http.h')
-rw-r--r--source/core/slang-http.h166
1 files changed, 166 insertions, 0 deletions
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