Add mutex around resultset reference counter
[yaz-moved-to-github.git] / src / zoom-c.c
index 5179a84..806cac4 100644 (file)
@@ -1,5 +1,5 @@
 /* This file is part of the YAZ toolkit.
- * Copyright (C) 1995-2009 Index Data
+ * Copyright (C) 1995-2010 Index Data
  * See the file LICENSE for details.
  */
 /**
@@ -349,8 +349,6 @@ void ZOOM_connection_remove_task(ZOOM_connection c)
     }
 }
 
-static int ZOOM_connection_exec_task(ZOOM_connection c);
-
 void ZOOM_connection_remove_tasks(ZOOM_connection c)
 {
     while (c->tasks)
@@ -416,6 +414,7 @@ ZOOM_API(ZOOM_connection)
     c->m_queue_back = 0;
 
     c->sru_version = 0;
+    c->no_redirects = 0;
     return c;
 }
 
@@ -791,14 +790,21 @@ static zoom_ret do_write(ZOOM_connection c);
 ZOOM_API(void)
     ZOOM_connection_destroy(ZOOM_connection c)
 {
-    ZOOM_resultset r;
+    ZOOM_resultsets list;
     if (!c)
         return;
     yaz_log(log_api, "%p ZOOM_connection_destroy", c);
     if (c->cs)
         cs_close(c->cs);
-    for (r = c->resultsets; r; r = r->next)
-        r->connection = 0;
+
+    // Remove the connection's usage of resultsets
+    list = c->resultsets;
+    while (list) {
+        ZOOM_resultsets removed = list;
+        ZOOM_resultset_destroy(list->resultset);
+        list = list->next;
+        xfree(removed);
+    }
 
     xfree(c->buf_in);
     xfree(c->addinfo);
@@ -832,9 +838,11 @@ void ZOOM_resultset_addref(ZOOM_resultset r)
 {
     if (r)
     {
+        yaz_mutex_enter(r->mutex);
         (r->refcount)++;
         yaz_log(log_details, "%p ZOOM_resultset_addref count=%d",
                 r, r->refcount);
+        yaz_mutex_leave(r->mutex);
     }
 }
 
@@ -858,9 +866,10 @@ ZOOM_resultset ZOOM_resultset_create(void)
     r->r_sort_spec = 0;
     r->query = 0;
     r->connection = 0;
-    r->next = 0;
     r->databaseNames = 0;
     r->num_databaseNames = 0;
+    r->mutex = 0;
+    yaz_mutex_create(&r->mutex);
     return r;
 }
 
@@ -885,6 +894,7 @@ ZOOM_API(ZOOM_resultset)
     const char *cp;
     int start, count;
     const char *syntax, *elementSetName;
+    ZOOM_resultsets set;
 
     yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
     r->r_sort_spec = q->sort_spec;
@@ -913,10 +923,12 @@ ZOOM_API(ZOOM_resultset)
     
     r->connection = c;
 
-    r->next = c->resultsets;
-    c->resultsets = r;
-
-    
+    yaz_log(log_details, "%p ZOOM_connection_search: Adding new resultset (%p) to resultsets (%p) ", c, r, c->resultsets);
+    set = xmalloc(sizeof(*set));
+    ZOOM_resultset_addref(r);
+    set->resultset = r;
+    set->next = c->resultsets;
+    c->resultsets = set;
 
     if (c->host_port && c->proto == PROTO_HTTP)
     {
@@ -1011,6 +1023,20 @@ ZOOM_API(int)
     return 0;
 }
 
+static void ZOOM_record_release(ZOOM_record rec)
+{
+    if (!rec)
+        return;
+    if (rec->wrbuf)
+        wrbuf_destroy(rec->wrbuf);
+#if YAZ_HAVE_XML2
+    if (rec->xml_mem)
+        xmlFree(rec->xml_mem);
+#endif
+    if (rec->odr)
+        odr_destroy(rec->odr);
+}
+
 ZOOM_API(void)
     ZOOM_resultset_cache_reset(ZOOM_resultset r)
 {
@@ -1020,8 +1046,7 @@ ZOOM_API(void)
         ZOOM_record_cache rc;
         for (rc = r->record_hash[i]; rc; rc = rc->next)
         {
-            if (rc->rec.wrbuf)
-                wrbuf_destroy(rc->rec.wrbuf);
+            ZOOM_record_release(&rc->rec);
         }
         r->record_hash[i] = 0;
     }
@@ -1037,35 +1062,25 @@ static void resultset_destroy(ZOOM_resultset r)
 {
     if (!r)
         return;
+    yaz_mutex_enter(r->mutex);
     (r->refcount)--;
     yaz_log(log_details, "%p ZOOM_resultset_destroy r=%p count=%d",
             r, r, r->refcount);
     if (r->refcount == 0)
     {
+        yaz_mutex_leave(r->mutex);
+        yaz_log(log_details, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
         ZOOM_resultset_cache_reset(r);
-
-        if (r->connection)
-        {
-            /* remove ourselves from the resultsets in connection */
-            ZOOM_resultset *rp = &r->connection->resultsets;
-            while (1)
-            {
-                assert(*rp);   /* we must be in this list!! */
-                if (*rp == r)
-                {   /* OK, we're here - take us out of it */
-                    *rp = (*rp)->next;
-                    break;
-                }
-                rp = &(*rp)->next;
-            }
-        }
         ZOOM_query_destroy(r->query);
         ZOOM_options_destroy(r->options);
         odr_destroy(r->odr);
         xfree(r->setname);
         xfree(r->schema);
+        yaz_mutex_destroy(&r->mutex);
         xfree(r);
     }
+    else
+        yaz_mutex_leave(r->mutex);
 }
 
 ZOOM_API(size_t)
@@ -1194,15 +1209,25 @@ static void get_cert(ZOOM_connection c)
     }
 }
 
+static zoom_ret do_connect_host(ZOOM_connection c,
+                                const char *effective_host,
+                                const char *logical_url);
+
 static zoom_ret do_connect(ZOOM_connection c)
 {
-    void *add;
     const char *effective_host;
 
     if (c->proxy)
         effective_host = c->proxy;
     else
         effective_host = c->host_port;
+    return do_connect_host(c, effective_host, c->host_port);
+}
+
+static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
+    const char *logical_url)
+{
+    void *add;
 
     yaz_log(log_details, "%p do_connect effective_host=%s", c, effective_host);
 
@@ -1213,14 +1238,17 @@ static zoom_ret do_connect(ZOOM_connection c)
     if (c->cs && c->cs->protocol == PROTO_HTTP)
     {
 #if YAZ_HAVE_XML2
-        const char *db = 0;
-
-        c->proto = PROTO_HTTP;
-        cs_get_host_args(c->host_port, &db);
-        xfree(c->path);
-
-        c->path = xmalloc(strlen(db) * 3 + 2);
-        yaz_encode_sru_dbpath_buf(c->path, db);
+        if (logical_url)
+        {
+            const char *db = 0;
+            
+            c->proto = PROTO_HTTP;
+            cs_get_host_args(logical_url, &db);
+            xfree(c->path);
+            
+            c->path = xmalloc(strlen(db) * 3 + 2);
+            yaz_encode_sru_dbpath_buf(c->path, db);
+        }
 #else
         set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
         do_close(c);
@@ -1261,7 +1289,7 @@ static zoom_ret do_connect(ZOOM_connection c)
         }
     }
     c->state = STATE_IDLE;
-    set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+    set_ZOOM_error(c, ZOOM_ERROR_CONNECT, logical_url);
     return zoom_complete;
 }
 
@@ -1711,12 +1739,12 @@ static zoom_ret ZOOM_connection_send_search(ZOOM_connection c)
                result sets on the server. */
             for (ord = 1; ; ord++)
             {
-                ZOOM_resultset rp;
+                ZOOM_resultsets rsp;
                 sprintf(setname, "%d", ord);
-                for (rp = c->resultsets; rp; rp = rp->next)
-                    if (rp->setname && !strcmp(rp->setname, setname))
+                for (rsp = c->resultsets; rsp; rsp = rsp->next)
+                    if (rsp->resultset->setname && !strcmp(rsp->resultset->setname, setname))
                         break;
-                if (!rp)
+                if (!rsp)
                     break;
             }
             r->setname = xstrdup(setname);
@@ -1780,6 +1808,10 @@ ZOOM_API(ZOOM_record)
     nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
     nrec->odr = odr_createmem(ODR_DECODE);
     nrec->wrbuf = 0;
+#if YAZ_HAVE_XML2
+    nrec->xml_mem = 0;
+    nrec->xml_size = 0;
+#endif
     odr_setbuf(nrec->odr, buf, size, 0);
     z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
     
@@ -1827,11 +1859,7 @@ ZOOM_API(ZOOM_record)
 ZOOM_API(void)
     ZOOM_record_destroy(ZOOM_record rec)
 {
-    if (!rec)
-        return;
-    if (rec->wrbuf)
-        wrbuf_destroy(rec->wrbuf);
-    odr_destroy(rec->odr);
+    ZOOM_record_release(rec);
     xfree(rec);
 }
 
@@ -1848,7 +1876,7 @@ static yaz_iconv_t iconv_create_charset(const char *record_charset)
     {
         /* Use "from,to" or just "from" */
         const char *cp = strchr(record_charset, ',');
-        int clen = strlen(record_charset);
+        size_t clen = strlen(record_charset);
         if (cp && cp[1])
         {
             strncpy( to, cp+1, sizeof(to)-1);
@@ -2058,14 +2086,42 @@ ZOOM_API(int)
     return 0;
 }
 
+static const char *get_record_format(ZOOM_record rec, int *len,
+                                     Z_NamePlusRecord *npr,
+                                     int marctype, const char *charset,
+                                     const char *format)
+{
+    const char *res = return_record(rec, len, npr, marctype, charset);
+#if YAZ_HAVE_XML2
+    if (*format == '1' && len)
+    {
+        /* try to XML format res */
+        xmlDocPtr doc;
+        xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
+        doc = xmlParseMemory(res, *len);
+        if (doc)
+        {
+            if (rec->xml_mem)
+                xmlFree(rec->xml_mem);
+            xmlDocDumpFormatMemory(doc, &rec->xml_mem, &rec->xml_size, 1);
+            xmlFreeDoc(doc);
+            res = (char *) rec->xml_mem;
+            *len = rec->xml_size;
+        } 
+    }
+#endif
+    return res;
+}
+
+
 ZOOM_API(const char *)
     ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
 {
     char type[40];
     char charset[40];
-    char xpath[512];
+    char format[3];
     const char *cp;
-    int i;
+    size_t i;
     Z_NamePlusRecord *npr;
     
     if (len)
@@ -2078,41 +2134,43 @@ ZOOM_API(const char *)
         return 0;
 
     cp = type_spec;
-    for (i = 0; cp[i] && i < sizeof(type)-1; i++)
-    {
-        if (cp[i] == ';' || cp[i] == ' ')
-            break;
+    for (i = 0; cp[i] && cp[i] != ';' && cp[i] != ' ' && i < sizeof(type)-1;
+         i++)
         type[i] = cp[i];
-    }
     type[i] = '\0';
     charset[0] = '\0';
-    while (type_spec[i] == ';')
+    format[0] = '\0';
+    while (1)
     {
+        while (cp[i] == ' ')
+            i++;
+        if (cp[i] != ';')
+            break;
         i++;
-        while (type_spec[i] == ' ')
+        while (cp[i] == ' ')
             i++;
-        if (!strncmp(type_spec+i, "charset=", 8))
+        if (!strncmp(cp + i, "charset=", 8))
         {
-            int j = 0;
+            size_t j = 0;
             i = i + 8; /* skip charset= */
-            for (j = 0; type_spec[i]  && j < sizeof(charset)-1; i++, j++)
+            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
             {
-                if (type_spec[i] == ';' || type_spec[i] == ' ')
-                    break;
-                charset[j] = cp[i];
+                if (j < sizeof(charset)-1)
+                    charset[j++] = cp[i];
             }
             charset[j] = '\0';
         }
-        else if (!strncmp(type_spec+i, "xpath=", 6))
+        else if (!strncmp(cp + i, "format=", 7))
         {
-            int j = 0; 
-            i = i + 6;
-            for (j = 0; type_spec[i] && j < sizeof(xpath)-1; i++, j++)
-                xpath[j] = cp[i];
-            xpath[j] = '\0';
+            size_t j = 0; 
+            i = i + 7;
+            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
+            {
+                if (j < sizeof(format)-1)
+                    format[j++] = cp[i];
+            }
+            format[j] = '\0';
         } 
-        while (type_spec[i] == ' ')
-            i++;
     }
     if (!strcmp(type, "database"))
     {
@@ -2146,15 +2204,22 @@ ZOOM_API(const char *)
     /* from now on - we have a database record .. */
     if (!strcmp(type, "render"))
     {
-        return return_record(rec, len, npr, YAZ_MARC_LINE, charset);
+        return get_record_format(rec, len, npr, YAZ_MARC_LINE, charset, format);
     }
     else if (!strcmp(type, "xml"))
     {
-        return return_record(rec, len, npr, YAZ_MARC_MARCXML, charset);
+        return get_record_format(rec, len, npr, YAZ_MARC_MARCXML, charset,
+                                 format);
+    }
+    else if (!strcmp(type, "txml"))
+    {
+        return get_record_format(rec, len, npr, YAZ_MARC_TURBOMARC, charset,
+                                 format);
     }
     else if (!strcmp(type, "raw"))
     {
-        return return_record(rec, len, npr, YAZ_MARC_ISO2709, charset);
+        return get_record_format(rec, len, npr, YAZ_MARC_ISO2709, charset,
+            format);
     }
     else if (!strcmp(type, "ext"))
     {
@@ -2164,7 +2229,8 @@ ZOOM_API(const char *)
     else if (!strcmp(type, "opac"))
     {
         if (npr->u.databaseRecord->which == Z_External_OPAC)
-            return return_record(rec, len, npr, YAZ_MARC_MARCXML, charset);
+            return get_record_format(rec, len, npr, YAZ_MARC_MARCXML, charset,
+                format);
     }
     return 0;
 }
@@ -2209,6 +2275,9 @@ static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr,
         rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
         rc->rec.odr = 0;
         rc->rec.wrbuf = 0;
+#if YAZ_HAVE_XML2
+        rc->rec.xml_mem = 0;
+#endif
         rc->elementSetName = odr_strdup_null(r->odr, elementSetName);
         
         rc->syntax = odr_strdup_null(r->odr, syntax);
@@ -2332,7 +2401,10 @@ static void handle_records(ZOOM_connection c, Z_Records *sr,
             {
                 /* present response and we didn't get any records! */
                 Z_NamePlusRecord *myrec = 
-                    zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
+                    zget_surrogateDiagRec(
+                        resultset->odr, 0, 
+                        YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
+                        "ZOOM C generated. Present phase and no records");
                 record_cache_add(resultset, myrec, *start,
                                  syntax, elementSetName, 0, 0);
             }
@@ -2341,7 +2413,10 @@ static void handle_records(ZOOM_connection c, Z_Records *sr,
         {
             /* present response and we didn't get any records! */
             Z_NamePlusRecord *myrec = 
-                zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
+                zget_surrogateDiagRec(
+                    resultset->odr, 0,
+                    YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
+                    "ZOOM C generated: Present response and no records");
             record_cache_add(resultset, myrec, *start, syntax, elementSetName,
                              0, 0);
         }
@@ -2911,7 +2986,7 @@ ZOOM_API(size_t)
 }
 
 static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
-                                int *occ,
+                                size_t *occ,
                                 const char **value_term, size_t *value_len,
                                 const char **disp_term, size_t *disp_len)
 {
@@ -2924,7 +2999,7 @@ static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
     *disp_len = 0;
 
     *occ = 0;
-    if (pos >= noent || pos < 0)
+    if (pos >= noent)
         return;
     if (scan->scan_response)
     {
@@ -2969,7 +3044,7 @@ static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
 
 ZOOM_API(const char *)
     ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
-                      int *occ, int *len)
+                      size_t *occ, size_t *len)
 {
     const char *value_term = 0;
     size_t value_len = 0;
@@ -2985,7 +3060,7 @@ ZOOM_API(const char *)
 
 ZOOM_API(const char *)
     ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
-                              int *occ, int *len)
+                              size_t *occ, size_t *len)
 {
     const char *value_term = 0;
     size_t value_len = 0;
@@ -3552,7 +3627,8 @@ ZOOM_API(void)
     ZOOM_options_setl(p->options, key, val, len);
 }
 
-static int ZOOM_connection_exec_task(ZOOM_connection c)
+ZOOM_API(int)
+    ZOOM_connection_exec_task(ZOOM_connection c)
 {
     ZOOM_task task = c->tasks;
     zoom_ret ret = zoom_complete;
@@ -4054,6 +4130,63 @@ static void handle_srw_scan_response(ZOOM_connection c,
 #endif
 
 #if YAZ_HAVE_XML2
+static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url)
+{
+    Z_GDU *p = z_get_HTTP_Request(odr);
+    const char *host = url;
+    const char *cp0 = strstr(host, "://");
+    const char *cp1 = 0;
+    if (cp0)
+        cp0 = cp0+3;
+    else
+        cp0 = host;
+    
+    cp1 = strchr(cp0, '/');
+    if (!cp1)
+        cp1 = cp0 + strlen(cp0);
+    
+    if (cp0 && cp1)
+    {
+        char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
+        memcpy (h, cp0, cp1 - cp0);
+        h[cp1-cp0] = '\0';
+        z_HTTP_header_add(odr, &p->u.HTTP_Request->headers, "Host", h);
+    }
+    p->u.HTTP_Request->path = odr_strdup(odr, *cp1 ? cp1 : "/");
+    return p;
+}
+
+static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
+                                  Z_HTTP_Response *cookie_hres)
+{
+    struct Z_HTTP_Header *h;
+    Z_GDU *gdu = get_HTTP_Request_url(c->odr_out, uri);
+
+    gdu->u.HTTP_Request->method = odr_strdup(c->odr_out, "GET");
+    z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, "Accept",
+                      "text/xml");
+    
+    for (h = cookie_hres->headers; h; h = h->next)
+    {
+        if (!strcmp(h->name, "Set-Cookie"))
+            z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
+                              "Cookie", h->value);
+    }
+    if (c->user && c->password)
+    {
+        z_HTTP_header_add_basic_auth(c->odr_out, &gdu->u.HTTP_Request->headers,
+                                     c->user, c->password);
+    }
+    if (!z_GDU(c->odr_out, &gdu, 0, 0))
+        return zoom_complete;
+    if (c->odr_print)
+        z_GDU(c->odr_print, &gdu, 0, 0);
+    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
+
+    odr_reset(c->odr_out);
+    return do_write(c);
+}
+
 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 {
     zoom_ret cret = zoom_complete;
@@ -4061,46 +4194,78 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
     const char *addinfo = 0;
     const char *connection_head = z_HTTP_header_lookup(hres->headers,
                                                        "Connection");
+    const char *location;
+
     ZOOM_connection_set_mask(c, 0);
     yaz_log(log_details, "%p handle_http", c);
     
-    if (!yaz_srw_check_content_type(hres))
-        addinfo = "content-type";
-    else
+    if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get
+        && (location = z_HTTP_header_lookup(hres->headers, "Location")))
     {
-        Z_SOAP *soap_package = 0;
-        ODR o = c->odr_in;
-        Z_SOAP_Handler soap_handlers[2] = {
-            {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
-            {0, 0, 0}
-        };
-        ret = z_soap_codec(o, &soap_package,
-                           &hres->content_buf, &hres->content_len,
-                           soap_handlers);
-        if (!ret && soap_package->which == Z_SOAP_generic &&
-            soap_package->u.generic->no == 0)
+        c->no_redirects++;
+        if (c->no_redirects > 10)
         {
-            Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
-
-            ZOOM_options_set(c->options, "sru_version", sr->srw_version);
-            ZOOM_options_setl(c->options, "sru_extra_response_data",
-                sr->extraResponseData_buf, sr->extraResponseData_len);
-            if (sr->which == Z_SRW_searchRetrieve_response)
-                cret = handle_srw_response(c, sr->u.response);
-            else if (sr->which == Z_SRW_scan_response)
-                handle_srw_scan_response(c, sr->u.scan_response);
-            else
-                ret = -1;
+            set_HTTP_error(c, hres->code, 0, 0);
+            c->no_redirects = 0;
+            do_close(c);
         }
-        else if (!ret && (soap_package->which == Z_SOAP_fault
-                          || soap_package->which == Z_SOAP_error))
+        else
         {
-            set_HTTP_error(c, hres->code,
-                           soap_package->u.fault->fault_code,
-                           soap_package->u.fault->fault_string);
+            /* since redirect may change host we just reconnect. A smarter
+               implementation might check whether it's the same server */
+            do_connect_host(c, location, 0);
+            send_SRW_redirect(c, location, hres);
+            /* we're OK for now. Operation is not really complete */
+            ret = 0;
+            cret = zoom_pending;
         }
+    }
+    else 
+    {   /* not redirect (normal response) */
+        if (!yaz_srw_check_content_type(hres))
+            addinfo = "content-type";
         else
-            ret = -1;
+        {
+            Z_SOAP *soap_package = 0;
+            ODR o = c->odr_in;
+            Z_SOAP_Handler soap_handlers[2] = {
+                {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
+                {0, 0, 0}
+            };
+            ret = z_soap_codec(o, &soap_package,
+                               &hres->content_buf, &hres->content_len,
+                               soap_handlers);
+            if (!ret && soap_package->which == Z_SOAP_generic &&
+                soap_package->u.generic->no == 0)
+            {
+                Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
+                
+                ZOOM_options_set(c->options, "sru_version", sr->srw_version);
+                ZOOM_options_setl(c->options, "sru_extra_response_data",
+                                  sr->extraResponseData_buf, sr->extraResponseData_len);
+                if (sr->which == Z_SRW_searchRetrieve_response)
+                    cret = handle_srw_response(c, sr->u.response);
+                else if (sr->which == Z_SRW_scan_response)
+                    handle_srw_scan_response(c, sr->u.scan_response);
+                else
+                    ret = -1;
+            }
+            else if (!ret && (soap_package->which == Z_SOAP_fault
+                              || soap_package->which == Z_SOAP_error))
+            {
+                set_HTTP_error(c, hres->code,
+                               soap_package->u.fault->fault_code,
+                               soap_package->u.fault->fault_string);
+            }
+            else
+                ret = -1;
+        }   
+        if (ret == 0)
+        {
+            if (c->no_redirects) /* end of redirect. change hosts again */
+                do_close(c);
+        }
+        c->no_redirects = 0;
     }
     if (ret)
     {
@@ -4621,6 +4786,11 @@ ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
     return ZOOM_options_get_int(c->options, "timeout", 30);
 }
 
+ZOOM_API(void) ZOOM_connection_close(ZOOM_connection c)
+{
+    do_close(c);
+}
+
 /*
  * Local variables:
  * c-basic-offset: 4