diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2021-11-15 20:45:21 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-11-15 20:45:21 -0500 |
| commit | 914a3808ebefb0f7f0a728469a2ce6b56dfc316c (patch) | |
| tree | 9e452c21cf2a1d89d180818c65c6e7909f3328cf /source/core/slang-http.h | |
| parent | ae9df74fce7e3583effc822b3003542cf753823d (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.h | 166 |
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 |
