Bläddra i källkod

HPCC-25391 Streaming a PTree content to ESP response

In the existing ESP, when a PTree content is sent out as ESP
response content, the PTree has to be streamed into a file
and the file is streamed into the socket output of the ESP
Response. By this PR, ESP may support streaming a PTree content
directly into the socket.

Revised on based on review: optionally add xml header and xml stylesheet

Signed-off-by: wangkx <kevin.wang@lexisnexis.com>
wangkx 4 år sedan
förälder
incheckning
9b8bda59f1

+ 76 - 0
esp/bindings/http/platform/httptransport.cpp

@@ -341,6 +341,7 @@ CHttpMessage::~CHttpMessage()
         m_context.clear();
         m_queryparams.clear();
         m_content_stream.clear();
+        m_content_ptree.clear();
     }
     catch(...)
     {
@@ -693,6 +694,18 @@ void CHttpMessage::setContent(IFileIOStream* stream)
     }
 }
 
+void CHttpMessage::setContent(IPropertyTree* ptree, bool addXMLHeaderToContent, bool addXMLStylesheetToContent)
+{
+    if (ptree == nullptr)
+        return;
+
+    m_content_ptree.set(ptree);
+    m_content_length = 0;
+    m_addXMLHeaderToContent = addXMLHeaderToContent;
+    m_addXMLStylesheetToContent = addXMLStylesheetToContent;
+    m_content.clear();
+}
+
 /*
 void CHttpMessage::appendContent(const char* content)
 {
@@ -814,6 +827,59 @@ void CHttpMessage::logMessage(MessageLogFlag messageLogFlag, StringBuffer& conte
     return;
 }
 
+static const unsigned defaultHttpMessageFlushThreshold = 10000; 
+
+class CHttpMessageIOAdapter : public CInterfaceOf<IIOStream>
+{
+    StringBuffer out;
+    CHttpMessage* response = nullptr;
+    unsigned flushThreshold = defaultHttpMessageFlushThreshold;
+    void sendChunk();
+public:
+    CHttpMessageIOAdapter(CHttpMessage* _response, unsigned _flushThreshold, bool addXMLHeaderToContent, bool addXMLStylesheetToContent) : response(_response), flushThreshold(_flushThreshold)
+    {
+        if (addXMLHeaderToContent)
+            out.set("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+        if (addXMLStylesheetToContent)
+            out.append("<?xml-stylesheet href=\"../esp/xslt/xmlformatter.xsl\" type=\"text/xsl\"?>");
+    }
+    ~CHttpMessageIOAdapter();
+
+    virtual void flush() override {};
+    virtual size32_t read(size32_t len, void * data) override { UNIMPLEMENTED; return 0; }
+    virtual size32_t write(size32_t len, const void * data) override;
+};
+
+CHttpMessageIOAdapter::~CHttpMessageIOAdapter()
+{
+    if (!out.isEmpty())
+    {
+        sendChunk();
+        out.clear();
+    }
+    sendChunk(); //send 0\r\n\r\n
+}
+
+size32_t CHttpMessageIOAdapter::write(size32_t len, const void * data)
+{
+    out.append(len, (const char *)data);
+    if (out.length() < flushThreshold)
+        return out.length();
+
+    sendChunk();
+    out.clear();
+    return 0;
+}
+
+void CHttpMessageIOAdapter::sendChunk()
+{
+    VStringBuffer sizeStr("%x\r\n", out.length());
+    response->sendChunk(sizeStr);
+
+    out.append("\r\n");
+    response->sendChunk(out);
+}
+
 int CHttpMessage::send()
 {
     bool logMsg = getEspLogResponses();
@@ -863,6 +929,14 @@ int CHttpMessage::send()
     }
 
     // When m_content is empty but the stream was set, read content from the stream.
+    if (m_content.isEmpty() && m_content_ptree != nullptr)
+    {
+        CHttpMessageIOAdapter ioa(this, defaultHttpMessageFlushThreshold, m_addXMLHeaderToContent, m_addXMLStylesheetToContent);
+        toXML(m_content_ptree, ioa);
+        return retcode;
+    }
+
+    // When m_content is empty but the stream was set, read content from the stream.
     if((m_content_length > 0 && m_content.length() == 0) && m_content_stream.get() != NULL)
     {
         //Read the file and send out 20K at a time.
@@ -2227,6 +2301,8 @@ StringBuffer& CHttpResponse::constructHeaderBuffer(StringBuffer& headerbuf, bool
 
     if(inclLen && m_content_length > 0)
         headerbuf.append("Content-Length: ").append(m_content_length).append("\r\n");
+    else if (m_content.isEmpty() && m_content_ptree != nullptr)
+        headerbuf.append("Transfer-Encoding: chunked\r\n"); //Streaming and no 'Content-Length' header
     else
         setPersistentEligible(false);
 

+ 3 - 0
esp/bindings/http/platform/httptransport.ipp

@@ -68,6 +68,8 @@ protected:
     StringBuffer m_content;
     StringBuffer m_header;
     OwnedIFileIOStream m_content_stream;
+    Linked<IPropertyTree> m_content_ptree;
+    bool m_addXMLHeaderToContent = false, m_addXMLStylesheetToContent = false;
     StringAttr   m_version;
     StringAttr   m_host;
     int          m_port;
@@ -139,6 +141,7 @@ public:
     virtual void setownContent(char* content);
     virtual void setownContent(unsigned len, char* content);
     virtual void setContent(IFileIOStream* stream);
+    virtual void setContent(IPropertyTree* ptree, bool addXMLHeaderToContent = false, bool addXMLStylesheetToContent = false);
     //virtual void appendContent(const char* content);
     virtual StringBuffer& getContentType(StringBuffer& contenttype);
     virtual void setContentType(const char* contenttype);