diff --git a/frontend/XrdSsiPbRequest.h b/frontend/XrdSsiPbRequest.h
index 7fffc735b4a7fd6ce636c56c98ad59a626cb8b7c..247714a343f925e0d6c6ca17a10fa1b7c7bbe04d 100644
--- a/frontend/XrdSsiPbRequest.h
+++ b/frontend/XrdSsiPbRequest.h
@@ -32,10 +32,6 @@ public:
               std::cerr << "  Response buffer size = " << m_response_bufsize << std::endl;
               std::cerr << "  Response timeout = " << timeout << std::endl;
 
-              // Allocate response buffer
-
-              m_response_bufptr = new char[m_response_bufsize];
-
               // Set response timeout
 
               SetTimeOut(timeout);
@@ -43,8 +39,6 @@ public:
    virtual ~XrdSsiPbRequest() 
            {
               std::cerr << "Deleting XrdSsiPbRequest object" << std::endl;
-
-              delete[] m_response_bufptr;
            }
 
    // It is up to the implementation to create request data, save it in some manner, and provide it to
@@ -79,11 +73,9 @@ private:
    const char *m_request_bufptr;
    int         m_request_len;
 
-   // Pointer to the Response buffer
+   // Response buffer size
 
-   char       *m_response_bufptr;
    int         m_response_bufsize;
-   int         m_response_len;
 
    // Callbacks for each of the XRootD reply types
 
@@ -181,15 +173,17 @@ bool XrdSsiPbRequest<RequestType, ResponseType, MetadataType, AlertType>::Proces
             }
          }
 
-         // Handle messages
+         // Handle response messages
 
          if(rInfo.rType == XrdSsiRespInfo::isData)
          {
-            static const int myBSize = 1024;
+            // Allocate response buffer
 
-            char *myBuff = reinterpret_cast<char*>(malloc(myBSize));
+            char *response_bufptr = new char[m_response_bufsize];
 
-            GetResponseData(myBuff, myBSize);
+            // Read the first chunk of data into the buffer, and pass it to ProcessResponseData()
+
+            GetResponseData(response_bufptr, m_response_bufsize);
          }
    }
 
@@ -199,43 +193,53 @@ bool XrdSsiPbRequest<RequestType, ResponseType, MetadataType, AlertType>::Proces
 
 
 template<typename RequestType, typename ResponseType, typename MetadataType, typename AlertType>
-XrdSsiRequest::PRD_Xeq XrdSsiPbRequest<RequestType, ResponseType, MetadataType, AlertType>::ProcessResponseData(const XrdSsiErrInfo &eInfo, char *myBuff, int myBLen, bool isLast)
+XrdSsiRequest::PRD_Xeq XrdSsiPbRequest<RequestType, ResponseType, MetadataType, AlertType>
+             ::ProcessResponseData(const XrdSsiErrInfo &eInfo, char *response_bufptr, int response_buflen, bool is_last)
 {
    using namespace std;
 
    // If we can't handle the queue at this time, return XrdSsiRequest::PRD_Hold;
 
-   // GetResponseData() above places the data in the allocated buffer, then calls this method with
-   // the buffer type and length
+   // The buffer length can be 0 if the response is metadata only
 
-   cerr << "Called ProcessResponseData with myBLen = " << myBLen << ", isLast = " << isLast << endl;
+   if(response_buflen != 0)
+   {
+      // How do we handle message boundaries for multi-block responses?
 
-   // myBLen can be 0 if there is no relevant response or response is metadata only
+      // Deserialize the response
 
-   // Process the response data of length myBLen placed in myBuff
-   // If there is more data then get it, else free the buffer and
-   // indicate to the framework that we are done
+      const std::string response_str(response_bufptr, response_buflen);
 
-   if(!isLast)
-   {
-      static const int myBSize = 1024;
+      ResponseType response;
 
-      // Get the next chunk of data (and call this method recursively ... could this cause a stack overflow ?)
+      if(response.ParseFromString(response_str))
+      {
+         ResponseCallback(response);
+      }
+      else
+      {
+         ErrorCallback("response.ParseFromString() failed");
+      }
+   }
+
+   // If there is more data then get it, otherwise clean up
+
+   if(!is_last)
+   {
+      // Get the next chunk of data:
+      // Does this call this method recursively? Is there danger of a stack overflow?
 
-      GetResponseData(myBuff, myBSize);
+      GetResponseData(response_bufptr, m_response_bufsize);
    }
    else
    {
-      myBuff[myBLen] = 0;
-
-      cerr << "Contents of myBuff = " << myBuff << endl;
-
-      free(myBuff);
+      delete[] response_bufptr;
 
-      Finished(); // Andy says you can now do a "delete this"
+      // Note that if request objects are uniform, you may want to re-use them instead
+      // of deleting them, to avoid the overhead of repeated object creation.
 
-      delete this; // Note that if request objects are uniform, you may want to re-use them instead
-                   // of deleting them, to avoid the overhead of repeated object creation.
+      Finished();
+      delete this;
    }
 
    return XrdSsiRequest::PRD_Normal; // Indicate what type of post-processing is required (normal in this case)
diff --git a/frontend/test_client.cpp b/frontend/test_client.cpp
index 61ea6409d34f95b607422523139df82fe3ef8f45..f887db3aedfceadeffc56d936f3f38420a992486 100644
--- a/frontend/test_client.cpp
+++ b/frontend/test_client.cpp
@@ -31,6 +31,17 @@ void XrdSsiPbRequestCallback<xrdssi::test::Metadata>::operator()(const xrdssi::t
 
 
 
+// Response callback
+
+template<>
+void XrdSsiPbRequestCallback<xrdssi::test::Result>::operator()(const xrdssi::test::Result &result)
+{
+   std::cout << "ResponseCallback():" << std::endl;
+   std::cout << MessageToJsonString(result);
+}
+
+
+
 int main(int argc, char *argv[])
 {
    // Verify that the version of the Google Protocol Buffer library that we linked against is