コード例 #1
0
ファイル: QoreException.cpp プロジェクト: temnoregg/qore
// static member function
void ExceptionSink::defaultWarningHandler(QoreException *e) {
   ExceptionSink xsink;

   while (e) {
      printe("warning encountered ");

      if (!e->file.empty()) {
         printe("at %s:", e->file.c_str());
	 if (e->start_line == e->end_line) {
	    if (!e->start_line) {
	       printe("<init>");
               if (!e->source.empty())
                  printe(" (source %s)", e->source.c_str());
	    }
	    else {
	       printe("%d", e->start_line);
	       if (!e->source.empty())
	          printe(" (source %s:%d)", e->source.c_str(), e->start_line + e->offset);
	    }
	 }
	 else {
	    printe("%d-%d", e->start_line, e->end_line);
            if (!e->source.empty())
               printe(" (source %s:%d-%d)", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset);
	 }
      }
      else if (e->start_line) {
	 if (e->start_line == e->end_line) {
	    if (!e->start_line)
	       printe("at <init>");
	    else
	       printe("on line %d", e->start_line);
	 }
	 else
	    printe("on line %d-%d", e->start_line, e->end_line);
      }
      printe("\n");

      QoreStringNode* err  = reinterpret_cast<QoreStringNode*>(e->err);
      QoreStringNode* desc = reinterpret_cast<QoreStringNode*>(e->desc);

      printe("%s: %s\n", err->getBuffer(), desc->getBuffer());

      e = e->next;
      if (e)
	 printe("next warning:\n");
   }
}
コード例 #2
0
AbstractQoreNode *javaToQore(java::lang::String *jstr) {
   if (!jstr)
      return 0;

   QoreStringNode *str = new QoreStringNode(QCS_UTF8);

   int size = JvGetStringUTFLength(jstr);
   
   str->allocate(size + 1);
   JvGetStringUTFRegion(jstr, 0, jstr->length(), (char *)str->getBuffer());
   str->terminate(size);
   return str;
}
コード例 #3
0
ファイル: ExceptionSink.cpp プロジェクト: qorelanguage/qore
AbstractQoreNode* ExceptionSink::raiseException(const char *err, const char *fmt, ...) {
   QoreStringNode *desc = new QoreStringNode;
   
   va_list args;
   
   while (true) {
      va_start(args, fmt);
      int rc = desc->vsprintf(fmt, args);
      va_end(args);
      if (!rc)
	 break;
   }
   printd(5, "ExceptionSink::raiseException(%s, %s)\n", err, desc->getBuffer());
   priv->insert(new QoreException(err, desc));
   return 0;
}
コード例 #4
0
ファイル: QoreException.cpp プロジェクト: temnoregg/qore
// static member function
void ExceptionSink::defaultExceptionHandler(QoreException* e) {
   ExceptionSink xsink;

   QoreString nstr;
   {
      DateTime now;
      now.setNow();
      now.format(nstr, "YYYY-MM-DD HH:mm:SS.xx Dy Z (z)");
   }

   unsigned ecnt = 0;

   while (e) {
      //printd(5, "ExceptionSink::defaultExceptionHandler() cs size=%d\n", cs->size());
      printe("unhandled QORE %s exception thrown in TID %d at %s", e->type == ET_USER ? "User" : "System", gettid(), nstr.getBuffer());

      QoreListNode *cs = e->callStack;
      bool found = false;
      if (cs->size()) {
	 // find first non-rethrow element
	 unsigned i = 0;
	 
	 QoreHashNode *h;
	 while (true) {
	    h = reinterpret_cast<QoreHashNode *>(cs->retrieve_entry(i));
	    assert(h);	    
	    if ((reinterpret_cast<QoreBigIntNode *>(h->getKeyValue("typecode")))->val != CT_RETHROW)
	       break;
	    i++;
	    if (i == cs->size())
	       break;
	 }

	 if (i < cs->size()) {
	    found = true;
	    QoreStringNode *func = reinterpret_cast<QoreStringNode *>(h->getKeyValue("function"));
	    QoreStringNode *type = reinterpret_cast<QoreStringNode *>(h->getKeyValue("type"));

	    printe(" in %s() (%s:%d", func->getBuffer(), e->file.c_str(), e->start_line);

	    if (e->start_line == e->end_line) {
	       if (!e->source.empty())
	          printe(", source %s:%d", e->source.c_str(), e->start_line + e->offset);
	    }
	    else {
               printe("-%d", e->end_line);
	       if (!e->source.empty())
                  printe(", source %s:%d-%d", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset);
	    }
	    printe(", %s code)\n", type->getBuffer());
	 }
      }

      if (!found) {
	 if (!e->file.empty()) {
	    printe(" at %s:", e->file.c_str());
	    if (e->start_line == e->end_line) {
	       if (!e->start_line) {
		  printe("<init>");
		  if (!e->source.empty())
		     printe(" (source %s)", e->source.c_str());
	       }
	       else {
		  printe("%d", e->start_line);
                  if (!e->source.empty())
                     printe(" (source %s:%d)", e->source.c_str(), e->start_line + e->offset);
	       }
	    }
	    else {
	       printe("%d-%d", e->start_line, e->end_line);
               if (!e->source.empty())
                  printe(" (source %s:%d-%d)", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset);
	    }
	 }
	 else if (e->start_line) {
	    if (e->start_line == e->end_line) {
	       if (!e->start_line)
		  printe(" at <init>");
	       else
		  printe(" on line %d", e->start_line);
	    }
	    else
	       printe(" on lines %d through %d", e->start_line, e->end_line);
	 }
	 printe("\n");
      }
      
      if (e->type == ET_SYSTEM) {
	 QoreStringNode* err = reinterpret_cast<QoreStringNode *>(e->err);
	 QoreStringNode* desc = reinterpret_cast<QoreStringNode *>(e->desc);
	 printe("%s: %s\n", err->getBuffer(), desc->getBuffer());
      }
      else {
	 bool hdr = false;

	 if (e->err) {
	    if (e->err->getType() == NT_STRING) {
	       QoreStringNode *err = reinterpret_cast<QoreStringNode *>(e->err);
	       printe("%s", err->getBuffer());
	    }
	    else {
	       QoreNodeAsStringHelper str(e->err, FMT_NORMAL, &xsink);
	       printe("EXCEPTION: %s", str->getBuffer());
	       hdr = true;
	    }
	 }
	 else
	    printe("EXCEPTION");
	 
	 if (e->desc) {
	    if (e->desc->getType() == NT_STRING) {
	       QoreStringNode *desc = reinterpret_cast<QoreStringNode *>(e->desc);
	       printe("%s%s", hdr ? ", desc: " : ": ", desc->getBuffer());
	    }
	    else {
	       QoreNodeAsStringHelper str(e->desc, FMT_NORMAL, &xsink);
	       printe(", desc: %s", str->getBuffer());
	       hdr = true;
	    }
	 }
	 
	 if (e->arg) {
	    if (e->arg->getType() == NT_STRING) {
	       QoreStringNode *arg = reinterpret_cast<QoreStringNode *>(e->arg);
	       printe("%s%s", hdr ? ", arg: " : "", arg->getBuffer());
	    }
	    else {
	       QoreNodeAsStringHelper str (e->arg, FMT_NORMAL, &xsink);
	       printe(", arg: %s", str->getBuffer());
	    }
	 }
	 printe("\n");
      }

      if (cs->size()) {
	 printe("call stack:\n");
	 for (unsigned i = 0; i < cs->size(); i++) {
	    int pos = cs->size() - i;
	    QoreHashNode* h = reinterpret_cast<QoreHashNode*>(cs->retrieve_entry(i));
	    QoreStringNode* strtype = reinterpret_cast<QoreStringNode*>(h->getKeyValue("type"));
	    const char* type = strtype->getBuffer();
	    int typecode = (int)reinterpret_cast<QoreBigIntNode*>(h->getKeyValue("typecode"))->val;
	    if (!strcmp(type, "new-thread"))
	       printe(" %2d: *thread start*\n", pos);
	    else {
	       QoreStringNode* fn = reinterpret_cast<QoreStringNode*>(h->getKeyValue("file"));
	       const char* fns = fn && !fn->empty() ? fn->getBuffer() : 0;
	       int start_line = (int)reinterpret_cast<QoreBigIntNode*>(h->getKeyValue("line"))->val;
	       int end_line = (int)reinterpret_cast<QoreBigIntNode*>(h->getKeyValue("endline"))->val;

	       QoreStringNode* src = reinterpret_cast<QoreStringNode*>(h->getKeyValue("source"));
	       const char* srcs = src && !src->empty() ? src->getBuffer() : 0;
	       int offset = (int)reinterpret_cast<QoreBigIntNode*>(h->getKeyValue("offset"))->val;

	       printe(" %2d: ", pos);

	       if (typecode == CT_RETHROW) {
	          printe("RETHROW at ");
	          if (fn) {
	             printe("%s:", fn->getBuffer());
	          }
	          else
	             printe("line");
	          printe("%d", start_line);
                  if (srcs)
                     printe(" (source %s:%d)", srcs, offset + start_line);
	       }
	       else {
	          QoreStringNode* fs = reinterpret_cast<QoreStringNode *>(h->getKeyValue("function"));
		  printe("%s() (", fs->getBuffer());
		  if (fns) {
		     if (start_line == end_line) {
			if (!start_line)
			   printe("%s:<init>", fns);
			else {
                           printe("%s:%d", fns, start_line);
			   if (srcs)
			      printe(" (source %s:%d)", srcs, start_line + offset);
			}
		     }
		     else {
			printe("%s:%d-%d", fns, start_line, end_line);
                        if (srcs)
                           printe(" (source %s:%d-%d)", srcs, start_line + offset, end_line + offset);
		     }
		  }
		  else {
		     if (start_line == end_line) {
			if (!start_line)
			   printe("<init>");
			else
			   printe("line %d", start_line);
		     }
		     else
			printe("line %d - %d", start_line, end_line);
		  }
		  printe(", %s code)", type);
	       }
	       printe("\n");
	    }
	 }
      }
      e = e->next;
      if (e) {
	 ++ecnt;
	 if (ecnt == Q_MAX_EXCEPTIONS) {
	    printe("*** maximum exception count reached (%d); supressing further output\n", ecnt);
	    break;
	 }
	 printe("chained exception:\n");
      }
   }
}
コード例 #5
0
QoreHashNode* qore_httpclient_priv::send_internal(ExceptionSink* xsink, const char* mname, const char* meth, const char* mpath, const QoreHashNode* headers, const void* data, unsigned size, const ResolvedCallReferenceNode* send_callback, bool getbody, QoreHashNode* info, int timeout_ms, const ResolvedCallReferenceNode* recv_callback, QoreObject* obj) {
   assert(!(data && send_callback));

   // check if method is valid
   method_map_t::const_iterator i = method_map.find(meth);
   if (i == method_map.end()) {
       i = additional_methods_map.find(meth);
       if (i == additional_methods_map.end()) {
          xsink->raiseException("HTTP-CLIENT-METHOD-ERROR", "HTTP method (%s) not recognized.", meth);
          return 0;
       }
   }

   // make sure the capitalized version is used
   meth = i->first.c_str();
   bool bodyp = i->second;

   // use the default timeout value if a zero value is given in the call
   if (!timeout_ms)
      timeout_ms = timeout;

   SafeLocker sl(msock->m);
   Queue* cb_queue = msock->socket->getQueue();

   ReferenceHolder<QoreHashNode> nh(new QoreHashNode, xsink);
   bool keep_alive = true;

   bool transfer_encoding = false;

   if (headers) {
      ConstHashIterator hi(headers);
      while (hi.next()) {
	 // if one of the mandatory headers is found, then ignore it
	 strcase_set_t::iterator si = header_ignore.find(hi.getKey());
	 if (si != header_ignore.end())
	    continue;

	 // otherwise set the value in the hash
	 const AbstractQoreNode* n = hi.getValue();
	 if (!is_nothing(n)) {
	    if (!strcasecmp(hi.getKey(), "transfer-encoding"))
	       transfer_encoding = true;

	    nh->setKeyValue(hi.getKey(), n->refSelf(), xsink);

	    if (!strcasecmp(hi.getKey(), "connection") || (proxy_connection.has_url() && !strcasecmp(hi.getKey(), "proxy-connection"))) {
	       const char* conn = get_string_header(xsink, **nh, hi.getKey(), true);
	       if (*xsink) {
		  disconnect_unlocked();
		  return 0;
	       }
	       if (conn && !strcasecmp(conn, "close"))
		  keep_alive = false;
	    }
	 }
      }
   }

   // add default headers if they weren't overridden
   for (header_map_t::const_iterator hdri = default_headers.begin(), e = default_headers.end(); hdri != e; ++hdri) {
      // look in original headers to see if the key was already given
      if (headers) {
	 bool skip = false;
	 ConstHashIterator hi(headers);
	 while (hi.next()) {
	    if (!strcasecmp(hi.getKey(), hdri->first.c_str())) {
	       skip = true;
	       break;
	    }
	 }
	 if (skip)
	    continue;
      }
      // if there is no message body then do not send the "content-type" header
      if (!data && !send_callback && !strcmp(hdri->first.c_str(), "Content-Type"))
	 continue;
      nh->setKeyValue(hdri->first.c_str(), new QoreStringNode(hdri->second.c_str()), xsink);
   }

   // set Transfer-Encoding: chunked if used with a send callback
   if (send_callback && !transfer_encoding)
      nh->setKeyValue("Transfer-Encoding", new QoreStringNode("chunked"), xsink);

   if (!connection.username.empty()) {
      // check for "Authorization" header
      bool auth_found = false;
      if (headers) {
	 ConstHashIterator hi(headers);
	 while (hi.next()) {
	    if (!strcasecmp(hi.getKey(), "Authorization")) {
	       auth_found = true;
	       break;
	    }
	 }
      }

      if (!auth_found) {
	 QoreString tmp;
	 tmp.sprintf("%s:%s", connection.username.c_str(), connection.password.c_str());
	 QoreStringNode* auth_str = new QoreStringNode("Basic ");
	 auth_str->concatBase64(&tmp);
	 nh->setKeyValue("Authorization", auth_str, xsink);
      }
   }

   // save original HTTP method in case we have to issue a CONNECT request to a proxy for an HTTPS connection
   const char* meth_orig = meth;

   bool use_proxy_connect = false;
   const char* proxy_path = 0;
   ReferenceHolder<QoreHashNode> proxy_headers(xsink);
   QoreString hostport;
   if (!proxy_connected && proxy_connection.has_url()) {
      // use CONNECT if we need to make an HTTPS connection from the proxy
      if (!proxy_connection.ssl && connection.ssl) {
	 meth = "CONNECT";
	 use_proxy_connect = true;
	 hostport.concat(connection.host);
	 if (connection.port)
	    hostport.sprintf(":%d", connection.port);
	 proxy_path = hostport.getBuffer();
	 proxy_headers = new QoreHashNode;
	 proxy_headers->setKeyValue("Host", new QoreStringNode(hostport), xsink);

	 addProxyAuthorization(headers, **proxy_headers, xsink);
      }
      else
	 addProxyAuthorization(headers, **nh, xsink);
   }

   bool host_override = headers ? (bool)headers->getKeyValue("Host") : false;

   int code;
   ReferenceHolder<QoreHashNode> ans(xsink);
   int redirect_count = 0;
   const char* location = 0;

   // flag for aborted chunked sends
   bool send_aborted = false;

   while (true) {
      // set host field automatically if not overridden
      if (!host_override)
	 nh->setKeyValue("Host", getHostHeaderValue(), xsink);

      if (info) {
	 info->setKeyValue("headers", nh->copy(), xsink);
	 if (*xsink)
	    return 0;
      }

      //printd(5, "qore_httpclient_priv::send_internal() meth=%s proxy_path=%s mpath=%s upc=%d\n", meth, proxy_path ? proxy_path : "n/a", mpath, use_proxy_connect);
      // send HTTP message and get response header
      if (use_proxy_connect)
	 ans = sendMessageAndGetResponse(meth, proxy_path, *(*proxy_headers), 0, 0, 0, info, true, timeout_ms, code, send_aborted, xsink);
      else
	 ans = sendMessageAndGetResponse(meth, mpath, *(*nh), data, size, send_callback, info, false, timeout_ms, code, send_aborted, xsink);
      if (!ans)
	 return 0;

      if (info) {
	 info->setKeyValue("response-headers", ans->refSelf(), xsink);
	 if (*xsink)
	    return 0;
      }

      if (code >= 300 && code < 400) {
	 disconnect_unlocked();

	 host_override = false;
	 const QoreStringNode* mess = reinterpret_cast<QoreStringNode*>(ans->getKeyValue("status_message"));

	 const QoreStringNode* loc = get_string_header_node(xsink, **ans, "location");
	 if (*xsink)
	    return 0;
	 const char* location = loc && !loc->empty() ? loc->getBuffer() : 0;
	 if (!location) {
	    sl.unlock();
	    const char* msg = mess ? mess->getBuffer() : "<no message>";
	    xsink->raiseException("HTTP-CLIENT-REDIRECT-ERROR", "no redirect location given for status code %d: message: '%s'", code, msg);
	    return 0;
	 }

	 if (cb_queue)
	    do_redirect_event(cb_queue, msock->socket->getObjectIDForEvents(), loc, mess);

	 if (++redirect_count > max_redirects)
	    break;

	 if (set_url_unlocked(location, xsink)) {
	    sl.unlock();
	    const char* msg = mess ? mess->getBuffer() : "<no message>";
	    xsink->raiseException("HTTP-CLIENT-REDIRECT-ERROR", "exception occurred while setting URL for new location '%s' (code %d: message: '%s')", location, code, msg);
	    return 0;
	 }

	 // set redirect info in info hash if present
	 if (info) {
	    QoreString tmp;
	    tmp.sprintf("redirect-%d", redirect_count);
	    info->setKeyValue(tmp.getBuffer(), loc->refSelf(), xsink);
	    if (*xsink)
	       return 0;

	    tmp.clear();
	    tmp.sprintf("redirect-message-%d", redirect_count);
	    info->setKeyValue(tmp.getBuffer(), mess ? mess->refSelf() : 0, xsink);
	 }

	 // FIXME: reset send callback and send_aborted here

	 // set mpath to NULL so that the new path will be taken
	 mpath = 0;
	 continue;
      }
      else if (use_proxy_connect) {
	 meth = meth_orig;
	 use_proxy_connect = false;
	 proxy_path = 0;
	 if (msock->socket->upgradeClientToSSL(0, 0, xsink)) {
	    disconnect_unlocked();
	    return 0;
	 }
	 proxy_connected = true;

	 // remove "Proxy-Authorization" header
	 nh->removeKey("Proxy-Authorization", xsink);
	 if (*xsink)
	    return 0;

	 // try again as if we are talking directly to the client
	 continue;
      }

      break;
   }

   if (code >= 300 && code < 400) {
      sl.unlock();
      const char* mess = get_string_header(xsink, **ans, "status_message");
      if (!mess)
	 mess = "<no message>";
      if (!location)
	 location = "<no location>";
      xsink->raiseException("HTTP-CLIENT-MAXIMUM-REDIRECTS-EXCEEDED", "maximum redirections (%d) exceeded; redirect code %d to '%s' ignored (message: '%s')", max_redirects, code, location, mess);
      return 0;
   }

   // process content-type
   const QoreStringNode* v = get_string_header_node(xsink, **ans, "content-type");
   if (*xsink) {
      disconnect_unlocked();
      return 0;
   }
   //ans->getKeyValue("content-type");   

   // see if there is a character set specification in the content-type header
   if (v) {
      // save original content-type header before processing
      ans->setKeyValue("_qore_orig_content_type", v->refSelf(), xsink);

      const char* str = v->getBuffer();
      const char* p = strstr(str, "charset=");
      if (p && (p == str || *(p - 1) == ';' || *(p - 1) == ' ')) {
	 // move p to start of encoding
	 const char* c = p + 8;
	 char quote = '\0';
	 if (*c == '\'' || *c == '"') {
	    quote = *c;
	    ++c;
	 }
	 QoreString enc;
	 while (*c && *c != ';' && *c != ' ' && *c != quote)
	    enc.concat(*(c++));
	 
	 if (quote && *c == quote)
	    ++c;

	 printd(5, "QoreHttpClientObject::send_intern() setting encoding to '%s' from content-type header: '%s' (cs=%p c=%p %d)\n", enc.getBuffer(), str, p + 8, c);

	 // set new encoding
	 msock->socket->setEncoding(QEM.findCreate(&enc));
	 // strip from content-type
	 QoreStringNode* nc = new QoreStringNode();
	 // skip any spaces before the charset=
	 while (p != str && (*(p - 1) == ' ' || *(p - 1) == ';'))
	    p--;
	 if (p != str)
	    nc->concat(str, p - str);
	 if (*c)
	    nc->concat(c);
	 ans->setKeyValue("content-type", nc, xsink);
	 str = nc->getBuffer();
      }
      // split into a list if ";" characters are present
      p = strchr(str, ';');
      if (p) {
	 bool multipart = false;
	 QoreListNode* l = new QoreListNode();
	 do {
	    // skip whitespace
	    while (*str == ' ') str++;
	    if (str != p) {
	       int len = p - str;
	       check_headers(str, len, multipart, *(*ans), msock->socket->getEncoding(), xsink);
	       l->push(new QoreStringNode(str, len, msock->socket->getEncoding()));
	    }
	    str = p + 1;
	 } while ((p = strchr(str, ';')));
	 // skip whitespace
	 while (*str == ' ') str++;
	 // add last field
	 if (*str) {
	    check_headers(str, strlen(str), multipart, *(*ans), msock->socket->getEncoding(), xsink);
	    l->push(new QoreStringNode(str, msock->socket->getEncoding()));
	 }
	 ans->setKeyValue("content-type", l, xsink);
      }
   }

   // send headers to recv_callback
   if (recv_callback && msock->socket->priv->runHeaderCallback(xsink, mname, *recv_callback, &msock->m, *ans, send_aborted, obj))
      return 0;

   AbstractQoreNode* body = 0;
   const char* content_encoding = 0;

   // do not read any message body for messages that cannot have one
   // rfc 2616 4.4 p1 (http://tools.ietf.org/html/rfc2616#section-4.4)
   /*
     1.Any response message which "MUST NOT" include a message-body (such
     as the 1xx, 204, and 304 responses and any response to a HEAD
     request) is always terminated by the first empty line after the
     header fields, regardless of the entity-header fields present in
     the message.
    */
   //printd(5, "qore_httpclient_priv::send_internal() this: %p bodyp: %d code: %d\n", this, bodyp, code);

   qore_uncompress_to_string_t dec = 0;

   // code >= 300 && < 400 is already handled above
   if (bodyp && (code < 100 || code >= 200) && code != 204) {
      // see if we should do a binary or string read
      content_encoding = get_string_header(xsink, **ans, "content-encoding");
      if (*xsink) {
	 disconnect_unlocked();
	 return 0;
      }

      if (content_encoding) {
	 // check for misuse (? not sure: check RFCs again) of this field by including a character encoding value
	 if (!strncasecmp(content_encoding, "iso", 3) || !strncasecmp(content_encoding, "utf-", 4)) {
	    msock->socket->setEncoding(QEM.findCreate(content_encoding));
	    content_encoding = 0;
	 }
	 else if (!recv_callback) {
	    // only decode message bodies automatically if there is no receive callback
	    if (!strcasecmp(content_encoding, "deflate") || !strcasecmp(content_encoding, "x-deflate"))
	       dec = qore_inflate_to_string;
	    else if (!strcasecmp(content_encoding, "gzip") || !strcasecmp(content_encoding, "x-gzip"))
	       dec = qore_gunzip_to_string;
	    else if (!strcasecmp(content_encoding, "bzip2") || !strcasecmp(content_encoding, "x-bzip2"))
	       dec = qore_bunzip2_to_string;
	 }
      }

      const char* te = get_string_header(xsink, **ans, "transfer-encoding");
      if (*xsink) {
	 disconnect_unlocked();
	 return 0;
      }
   
      // get response body, if any
      const char* cl = get_string_header(xsink, **ans, "content-length");
      if (*xsink) {
	 disconnect_unlocked();
	 return 0;
      }
      int len = cl ? atoi(cl) : 0;
      
      if (cl && cb_queue)
	 do_content_length_event(cb_queue, msock->socket->getObjectIDForEvents(), len);

      if (te && !strcasecmp(te, "chunked")) { // check for chunked response body
	 if (cb_queue)
	    do_event(cb_queue, msock->socket->getObjectIDForEvents(), QORE_EVENT_HTTP_CHUNKED_START);
	 ReferenceHolder<QoreHashNode> nah(xsink);
	 if (recv_callback) {
	    if (content_encoding)
	       msock->socket->priv->readHttpChunkedBodyBinary(timeout_ms, xsink, QORE_SOURCE_HTTPCLIENT, recv_callback, &msock->m, obj);
	    else
	       msock->socket->priv->readHttpChunkedBody(timeout_ms, xsink, QORE_SOURCE_HTTPCLIENT, recv_callback, &msock->m, obj);
	 }
	 else {
	    if (content_encoding)
	       nah = msock->socket->priv->readHttpChunkedBodyBinary(timeout_ms, xsink, QORE_SOURCE_HTTPCLIENT);
	    else
	       nah = msock->socket->priv->readHttpChunkedBody(timeout_ms, xsink, QORE_SOURCE_HTTPCLIENT);
	 }
	 if (cb_queue)
	    do_event(cb_queue, msock->socket->getObjectIDForEvents(), QORE_EVENT_HTTP_CHUNKED_END);
	 
	 if (!nah && !recv_callback) {
	    if (!msock->socket->isOpen())
	       disconnect_unlocked();
	    return 0;
	 }

	 if (info) {
	    info->setKeyValue("chunked", &True, xsink);
	    if (*xsink)
	       return 0;
	 }
	 
	 if (!recv_callback) {
	    body = nah->takeKeyValue("body");
	    ans->merge(*nah, xsink);
	 }
      }
      else if (getbody || len) {
	 if (content_encoding) {
	    SimpleRefHolder<BinaryNode> bobj(msock->socket->recvBinary(len, timeout_ms, xsink));
	    if (!(*xsink) && bobj)
	       body = bobj.release();
	 }
	 else {
	    QoreStringNodeHolder bstr(msock->socket->recv(len, timeout_ms, xsink));
	    if (!(*xsink) && bstr)
	       body = bstr.release();
	 }
	 
	 if (*xsink && !msock->socket->isOpen())
	    disconnect_unlocked();
	 //printf("body=%p\n", body);
      }  
   }

   // check for connection: close header
   if (!keep_alive)
      disconnect_unlocked();
   else {
      const char* conn = get_string_header(xsink, **ans, "connection", true);
      if (*xsink) {
	 disconnect_unlocked();
	 return 0;
      }
      if (conn && !strcasecmp(conn, "close"))
	 disconnect_unlocked();
   }

   sl.unlock();

   // for content-encoding processing we can run unlocked

   // add body to result hash and process content encoding if necessary
   if (body) {
      if (content_encoding) {
	 if (!dec) {
	    if (!recv_callback) {
	       xsink->raiseException("HTTP-CLIENT-RECEIVE-ERROR", "don't know how to handle content-encoding '%s'", content_encoding);
	       ans = 0;
	    }
	 }
	 else {
	    BinaryNode* bobj = reinterpret_cast<BinaryNode*>(body);
	    QoreStringNode* str = dec(bobj, msock->socket->getEncoding(), xsink);
	    bobj->deref();
	    body = str;
	 }
      }

      if (body) {
	 // send data to recv_callback (already unlocked)
	 if (recv_callback) {
	    ReferenceHolder<> bh(body, xsink);
	    if (msock->socket->priv->runDataCallback(xsink, mname, *recv_callback, 0, body, false)
		|| msock->socket->priv->runHeaderCallback(xsink, mname, *recv_callback, 0, 0, send_aborted, obj))
	       return 0;
	 }
	 else
	    ans->setKeyValue("body", body, xsink);
      }
   }

   // do not throw an exception if a receive callback is used
   if (!recv_callback && !*xsink && (code < 100 || code >= 300)) {
      const char* mess = get_string_header(xsink, **ans, "status_message");
      if (!mess)
	 mess = "<no message>";
      assert(!*xsink);

      xsink->raiseExceptionArg("HTTP-CLIENT-RECEIVE-ERROR", ans.release(), "HTTP status code %d received: message: %s", code, mess);
      return 0;
   }

   return *xsink || recv_callback ? 0 : ans.release();
}