wpiutil: HttpServerConnection: add SendStaticResponse

This function is intended for use when the content is a static const variable.
It allows gzipped static data, but doesn't provide the functionality to ungzip
it if the client doesn't support gzip.  This is because it would add a
dependency on zlib and basically all clients support gzip.

Change extraHeader on all Send functions to include the final newline,
this makes it easier to build up extra headers incrementally.

Expose sig::Connection for messageComplete and headerConn to allow them to
be disconnected by users of the class.  This is commonly needed for things
like WebSocket upgrades.
This commit is contained in:
Peter Johnson
2018-11-18 18:28:26 -08:00
parent d65547ea74
commit 0fb24538a7
2 changed files with 75 additions and 13 deletions

View File

@@ -16,19 +16,29 @@ using namespace wpi;
HttpServerConnection::HttpServerConnection(std::shared_ptr<uv::Stream> stream)
: m_stream(*stream) {
// process HTTP messages
m_request.messageComplete.connect([this](bool keepAlive) {
m_keepAlive = keepAlive;
ProcessRequest();
m_messageCompleteConn =
m_request.messageComplete.connect_connection([this](bool keepAlive) {
m_keepAlive = keepAlive;
ProcessRequest();
});
// look for Accept-Encoding headers to determine if gzip is acceptable
m_request.messageBegin.connect([this] { m_acceptGzip = false; });
m_request.header.connect([this](StringRef name, StringRef value) {
if (name.equals_lower("accept-encoding") && value.contains("gzip")) {
m_acceptGzip = true;
}
});
// pass incoming data to HTTP parser
stream->data.connect([this](uv::Buffer& buf, size_t size) {
m_request.Execute(StringRef{buf.base, size});
if (m_request.HasError()) {
// could not parse; just close the connection
m_stream.Close();
}
});
m_headerConn =
stream->data.connect_connection([this](uv::Buffer& buf, size_t size) {
m_request.Execute(StringRef{buf.base, size});
if (m_request.HasError()) {
// could not parse; just close the connection
m_stream.Close();
}
});
// close when remote side closes
stream->end.connect([h = stream.get()] { h->Close(); });
@@ -60,7 +70,7 @@ void HttpServerConnection::BuildHeader(raw_ostream& os, int code,
os << "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: *\r\n";
SmallString<128> extraBuf;
StringRef extraStr = extra.toStringRef(extraBuf);
if (!extraStr.empty()) os << extraStr << "\r\n";
if (!extraStr.empty()) os << extraStr;
os << "\r\n"; // header ends with a blank line
}
@@ -85,6 +95,31 @@ void HttpServerConnection::SendResponse(int code, const Twine& codeText,
SendData(os.bufs(), !m_keepAlive);
}
void HttpServerConnection::SendStaticResponse(int code, const Twine& codeText,
const Twine& contentType,
StringRef content, bool gzipped,
const Twine& extraHeader) {
// TODO: handle remote side not accepting gzip (very rare)
StringRef contentEncodingHeader;
if (gzipped /* && m_acceptGzip*/)
contentEncodingHeader = "Content-Encoding: gzip\r\n";
SmallVector<uv::Buffer, 4> bufs;
raw_uv_ostream os{bufs, 4096};
BuildHeader(os, code, codeText, contentType, content.size(),
extraHeader + contentEncodingHeader);
// can send content without copying
bufs.emplace_back(content);
m_stream.Write(bufs, [ closeAfter = !m_keepAlive, stream = &m_stream ](
MutableArrayRef<uv::Buffer> bufs, uv::Error) {
// don't deallocate the static content
for (auto&& buf : bufs.drop_back()) buf.Deallocate();
if (closeAfter) stream->Close();
});
}
void HttpServerConnection::SendError(int code, const Twine& message) {
StringRef codeText, extra, baseMessage;
switch (code) {