Reuse sessions with same idAuthentication information.
[yazproxy-moved-to-github.git] / src / yaz-proxy.cpp
index f296d79..1fb30d9 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: yaz-proxy.cpp,v 1.39 2005-11-25 19:37:28 adam Exp $
+/* $Id: yaz-proxy.cpp,v 1.43 2006-03-09 14:12:57 adam Exp $
    Copyright (c) 1998-2005, Index Data.
 
 This file is part of the yaz-proxy.
@@ -106,18 +106,18 @@ void Auth_Msg::result()
 {
     yaz_log(YLOG_LOG, "Auth_Msg:result proxy ok buf=%p len=%d",
             m_apdu_buf, m_apdu_len);
-    odr_setbuf(m_proxy->odr_decode(), m_apdu_buf, m_apdu_len, 0);
-    Z_APDU *apdu = 0;
-    int r = z_APDU(m_proxy->odr_decode(), &apdu, 0, 0);
-    if (r)
-        yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU OK");
-    else
-        yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU failed");
-    if (m_proxy->dec_ref())
+    if (m_proxy->dec_ref(false))
         yaz_log(YLOG_LOG, "Auth_Msg::proxy deleted meanwhile");
     else
     {
         yaz_log(YLOG_LOG, "Auth_Msg::proxy still alive");
+        odr_setbuf(m_proxy->odr_decode(), m_apdu_buf, m_apdu_len, 0);
+        Z_APDU *apdu = 0;
+        int r = z_APDU(m_proxy->odr_decode(), &apdu, 0, 0);
+        if (r)
+            yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU OK");
+        else
+            yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU failed");
         m_proxy->result_authentication(apdu, m_ret);
     }
     delete this;
@@ -132,10 +132,19 @@ void Yaz_Proxy::result_authentication(Z_APDU *apdu, int ret)
         Z_APDU *apdu_reject = zget_APDU(odr_encode(), Z_APDU_initResponse);
         *apdu_reject->u.initResponse->result = 0;
         send_to_client(apdu_reject);
-        dec_ref();
+        dec_ref(false);
     }
     else
+    {
+        if (apdu->which == Z_APDU_initRequest)
+        {
+            Yaz_ProxyConfig *cfg = check_reconfigure();
+            if (cfg)
+                cfg->target_authentication(m_default_target, odr_encode(), 
+                                           apdu->u.initRequest);
+        }
         handle_incoming_Z_PDU_2(apdu);
+    }
 }
 
 static const char *apdu_name(Z_APDU *apdu)
@@ -280,6 +289,7 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
         low_socket_open();
     m_my_thread = 0;
     m_ref_count = 1;
+    m_main_ptr_dec = false;
     m_peername = 0;
 }
 
@@ -619,10 +629,10 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
             }
         }
     }
-    else if (!c &&
-            apdu->which == Z_APDU_initRequest &&
-            apdu->u.initRequest->idAuthentication == 0 &&
-            !ODR_MASK_GET(apdu->u.initRequest->options, Z_Options_negotiationModel))
+    else if (!c 
+             && apdu->which == Z_APDU_initRequest 
+             && !ODR_MASK_GET(apdu->u.initRequest->options,
+                              Z_Options_negotiationModel))
     {
         // anonymous sessions without cookie.
         // if authentication is set it is NOT anonymous se we can't share them.
@@ -631,9 +641,9 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
         {
             assert(c->m_prev);
             assert(*c->m_prev == c);
-            if (c->m_server == 0 && c->m_cookie == 0 && 
-                c->m_waiting == 0 && 
-                !strcmp(m_proxyTarget, c->get_hostname()))
+            if (c->m_server == 0 && c->m_cookie == 0 &&  c->m_waiting == 0 
+                && c->compare_idAuthentication(apdu)
+                && !strcmp(m_proxyTarget, c->get_hostname()))
             {
                 // found it in cache
                 yaz_log (YLOG_LOG, "%sREUSE %d %s",
@@ -663,14 +673,6 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
             yaz_log (YLOG_LOG, "%sno init request as first PDU", m_session_str);
             return 0;
         }
-        Z_InitRequest *initRequest = apdu->u.initRequest;
-
-        if (initRequest->idAuthentication)
-        {
-            // the client uses authentication. We set the keepalive PDU
-            // to 0 so we don't cache it in releaseClient
-            m_keepalive_limit_pdu = 0;
-        }
         // go through list of clients - and find the lowest/oldest one.
         Yaz_ProxyClient *c_min = 0;
         int min_seq = -1;
@@ -765,6 +767,8 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
             c->set_APDU_yazlog(1);
         else
             c->set_APDU_yazlog(0);
+
+        c->set_idAuthentication(apdu);
     }
     yaz_log (YLOG_DEBUG, "get_client 3 %p %p", this, c);
     return c;
@@ -1040,7 +1044,7 @@ void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p,
             {
                 WRBUF w = wrbuf_alloc();
 
-                yaz_display_OPAC(w, r->u.opac, 0);
+                yaz_opac_decode_wrbuf(mt, r->u.opac, w);
                 npr->u.databaseRecord = z_ext_record(
                     odr_encode(), VAL_TEXT_XML,
                     wrbuf_buf(w), wrbuf_len(w)
@@ -1165,14 +1169,11 @@ int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
 int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
 {
     ODR o = odr_encode();
-    Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
-    Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
-
-    srw_res->num_diagnostics = 1;
-    srw_res->diagnostics = (Z_SRW_diagnostic *)
-        odr_malloc(o, sizeof(*srw_res->diagnostics));
-    yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
-    return send_srw_response(srw_pdu);
+    Z_SRW_diagnostic *diagnostic = (Z_SRW_diagnostic *)
+        odr_malloc(o, sizeof(*diagnostic));
+    int num_diagnostic = 1;
+    yaz_mk_std_diagnostic(o, diagnostic, srw_error, add);
+    return send_srw_search_response(diagnostic, num_diagnostic);
 }
 
 int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
@@ -1249,6 +1250,18 @@ int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
     
 }
 
+int Yaz_Proxy::send_srw_search_response(Z_SRW_diagnostic *diagnostics,
+                                        int num_diagnostics)
+{
+    ODR o = odr_encode();
+    Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
+    Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
+
+    srw_res->num_diagnostics = num_diagnostics;
+    srw_res->diagnostics = diagnostics;
+    return send_srw_response(srw_pdu);
+}
+
 int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
                                         int num_diagnostics)
 {
@@ -1499,6 +1512,38 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu)
     return r;
 }
 
+void Yaz_ProxyClient::set_idAuthentication(Z_APDU *apdu)
+{
+    Z_IdAuthentication *t = apdu->u.initRequest->idAuthentication;
+    
+    odr_reset(m_idAuthentication_odr);
+    z_IdAuthentication(m_idAuthentication_odr, &t, 1, 0);
+    m_idAuthentication_ber_buf =
+        odr_getbuf(m_idAuthentication_odr, 
+                   &m_idAuthentication_ber_size, 0);
+}
+
+bool Yaz_ProxyClient::compare_idAuthentication(Z_APDU *apdu)
+{
+    Z_IdAuthentication *t = apdu->u.initRequest->idAuthentication;
+    ODR odr = odr_createmem(ODR_ENCODE);
+
+    z_IdAuthentication(odr, &t, 1, 0);
+    int sz;
+    char *buf = odr_getbuf(odr, &sz, 0);
+    if (buf && m_idAuthentication_ber_buf
+        && sz == m_idAuthentication_ber_size
+        && !memcmp(m_idAuthentication_ber_buf, buf, sz))
+    {
+        odr_destroy(odr);
+        return true;
+    }
+    odr_destroy(odr);
+    if (!buf && !m_idAuthentication_ber_buf)
+        return true;
+    return false;
+}
+
 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
 {
     int len = 0;
@@ -2019,9 +2064,10 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
                 {
                     ODR_MASK_SET(initResponse->options,
                                  Z_Options_negotiationModel);
-                    ODR_MASK_SET(m_initRequest_options,
-                                 Z_Options_negotiationModel);
-
+                    if (m_initRequest_options)
+                        ODR_MASK_SET(m_initRequest_options,
+                                     Z_Options_negotiationModel);
+                    
                     oi->which = Z_OtherInfo_externallyDefinedInfo;    
                     oi->information.externallyDefinedInfo =
                         yaz_set_response_charneg(
@@ -2187,9 +2233,6 @@ int Yaz_Proxy::handle_authentication(Z_APDU *apdu)
     else
         ret = cfg->client_authentication(m_default_target, 0, 0, 0,
                                          m_peername);
-
-    cfg->target_authentication(m_default_target, odr_encode(), req);
-
     return ret;
 }
 
@@ -3058,10 +3101,19 @@ void Yaz_Proxy::releaseClient()
         m_parent->pre_init();
 }
 
-bool Yaz_Proxy::dec_ref()
+bool Yaz_Proxy::dec_ref(bool main_ptr)
 {
     yaz_log(YLOG_LOG, "%sdec_ref count=%d", m_session_str, m_ref_count);
+
     assert(m_ref_count > 0);
+    if (main_ptr)
+    {
+        if (m_main_ptr_dec)
+            return false;
+        m_main_ptr_dec = true;
+    }
+
+    m_http_keepalive = 0;
 
     --m_ref_count;
     if (m_ref_count > 0)
@@ -3088,7 +3140,7 @@ void Yaz_ProxyClient::shutdown()
     if (m_server)
     {
         m_waiting = 1;      // ensure it's released from Proxy in releaseClient
-        m_server->dec_ref();
+        m_server->dec_ref(true);
     }
     else
         delete this;
@@ -3099,15 +3151,35 @@ void Yaz_Proxy::failNotify()
     inc_request_no();
     yaz_log (YLOG_LOG, "%sConnection closed by client",
              get_session_str());
-    dec_ref();
+    dec_ref(true);
 }
 
+void Yaz_Proxy::send_response_fail_client(const char *addr)
+{
+    yaz_log(YLOG_LOG, "%ssend_close_response", get_session_str());
+    if (m_http_version)
+    {
+        Z_SRW_diagnostic *diagnostic = 0;
+        int num_diagnostic = 0;
+        
+        yaz_add_srw_diagnostic(odr_encode(),
+                               &diagnostic, &num_diagnostic,
+                               YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE, addr);
+        if (m_s2z_search_apdu)
+            send_srw_search_response(diagnostic, num_diagnostic);
+        else
+            send_srw_explain_response(diagnostic, num_diagnostic);
+    }            
+}
 void Yaz_ProxyClient::failNotify()
 {
     if (m_server)
         m_server->inc_request_no();
     yaz_log (YLOG_LOG, "%sConnection closed by target %s", 
              get_session_str(), get_hostname());
+
+    if (m_server)
+        m_server->send_response_fail_client(get_hostname());
     shutdown();
 }
 
@@ -3136,6 +3208,7 @@ Yaz_ProxyClient::~Yaz_ProxyClient()
         m_next->m_prev = m_prev;
     m_waiting = 2;     // for debugging purposes only.
     odr_destroy(m_init_odr);
+    odr_destroy(m_idAuthentication_odr);
     delete m_last_query;
     xfree (m_last_resultSetId);
     xfree (m_cookie);
@@ -3279,7 +3352,7 @@ void Yaz_Proxy::timeoutNotify()
             inc_request_no();
             m_in_queue.clear();
             yaz_log (YLOG_LOG, "%sTimeout (client to proxy)", m_session_str);
-            dec_ref();
+            dec_ref(true);
             break;
         case timeout_reduce:
             timeout(m_client_idletime);
@@ -3314,17 +3387,12 @@ void Yaz_ProxyClient::timeoutNotify()
 
     yaz_log (YLOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
              get_hostname());
-    m_waiting = 1;
-    m_root->pre_init();
-    if (m_server && m_init_flag)
-    {
-        // target timed out in a session that was properly initialized
-        // server object stay alive but we mark it as invalid so it
-        // gets initialized again
-        m_server->markInvalid();
-        m_server = 0;
-    }
+
+    if (m_server)
+        m_server->send_response_fail_client(get_hostname());
     shutdown();
+
+    m_root->pre_init();
 }
 
 Yaz_ProxyClient::Yaz_ProxyClient(IPDU_Observable *the_PDU_Observable,
@@ -3354,6 +3422,9 @@ Yaz_ProxyClient::Yaz_ProxyClient(IPDU_Observable *the_PDU_Observable,
     m_seqno = 0;
     m_target_idletime = 600;
     m_root = parent;
+    m_idAuthentication_odr = odr_createmem(ODR_ENCODE);
+    m_idAuthentication_ber_buf = 0;
+    m_idAuthentication_ber_size = 0;
 }
 
 const char *Yaz_Proxy::option(const char *name, const char *value)