+ Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable);
+ new_proxy->m_parent = this;
+ new_proxy->timeout(500);
+ new_proxy->set_proxy_target(m_proxyTarget);
+ new_proxy->set_APDU_log(get_APDU_log());
+ new_proxy->set_proxy_authentication(m_proxy_authentication);
+ yaz_log (LOG_LOG, "New session p=%p", new_proxy);
+ return new_proxy;
+}
+
+char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
+{
+ int oid[OID_SIZE];
+ Z_OtherInformationUnit *oi;
+ struct oident ent;
+ ent.proto = PROTO_Z3950;
+ ent.oclass = CLASS_USERINFO;
+ ent.value = (oid_value) VAL_COOKIE;
+ assert (oid_ent_to_oid (&ent, oid));
+
+ if (oid_ent_to_oid (&ent, oid) &&
+ (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
+ oi->which == Z_OtherInfo_characterInfo)
+ return oi->information.characterInfo;
+ return 0;
+}
+
+char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
+{
+ int oid[OID_SIZE];
+ Z_OtherInformationUnit *oi;
+ struct oident ent;
+ ent.proto = PROTO_Z3950;
+ ent.oclass = CLASS_USERINFO;
+ ent.value = (oid_value) VAL_PROXY;
+ if (oid_ent_to_oid (&ent, oid) &&
+ (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
+ oi->which == Z_OtherInfo_characterInfo)
+ return oi->information.characterInfo;
+ return 0;
+}
+
+Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu)
+{
+ assert (m_parent);
+ Yaz_Proxy *parent = m_parent;
+ Z_OtherInformation **oi;
+ Yaz_ProxyClient *c = m_client;
+
+ get_otherInfoAPDU(apdu, &oi);
+ char *cookie = get_cookie(oi);
+
+ const char *proxy_host = get_proxy(oi);
+ if (proxy_host)
+ set_proxy_target(proxy_host);
+
+ // no target specified at all?
+ if (!m_proxyTarget)
+ return 0;
+
+ if (!strcmp(m_proxyTarget, "stop"))
+ exit (0);
+ if (cookie && *cookie)
+ {
+ Yaz_ProxyClient *cc = 0;
+
+ for (c = parent->m_clientPool; c; c = c->m_next)
+ {
+ assert (c->m_prev);
+ assert (*c->m_prev == c);
+ if (!strcmp(cookie,c->m_cookie) &&
+ !strcmp(m_proxyTarget, c->get_hostname()))
+ {
+ cc = c;
+ }
+ }
+ if (cc)
+ {
+ // found it in cache
+ c = cc;
+ // The following handles "cancel"
+ // If connection is busy (waiting for PDU) and
+ // we have an initRequest we can safely do re-open
+ if (c->m_waiting && apdu->which == Z_APDU_initRequest)
+ {
+ yaz_log (LOG_LOG, "reopen target=%s", c->get_hostname());
+ c->close();
+ c->client(m_proxyTarget);
+ c->m_init_flag = 0;
+
+ c->m_last_ok = 0;
+ c->m_last_resultCount = 0;
+ c->m_sr_transform = 0;
+ c->m_waiting = 0;
+ c->timeout(600);
+ }
+ c->m_seqno = parent->m_seqno;
+ if (c->m_server && c->m_server != this)
+ c->m_server->m_client = 0;
+ c->m_server = this;
+ c->m_seqno = parent->m_seqno;
+ (parent->m_seqno)++;
+ yaz_log (LOG_DEBUG, "get_client 1 %p %p", this, c);
+ return c;
+ }
+ }
+ if (!m_client)
+ {
+ if (apdu->which != Z_APDU_initRequest)
+ {
+ yaz_log (LOG_LOG, "no first INIT!");
+ return 0;
+ }
+ Z_InitRequest *initRequest = apdu->u.initRequest;
+
+ if (!initRequest->idAuthentication)
+ {
+ if (m_proxy_authentication)
+ {
+ initRequest->idAuthentication =
+ (Z_IdAuthentication *)
+ odr_malloc (odr_encode(),
+ sizeof(*initRequest->idAuthentication));
+ initRequest->idAuthentication->which =
+ Z_IdAuthentication_open;
+ initRequest->idAuthentication->u.open =
+ odr_strdup (odr_encode(), m_proxy_authentication);
+ }
+ }
+
+ // go through list of clients - and find the lowest/oldest one.
+ Yaz_ProxyClient *c_min = 0;
+ int min_seq = -1;
+ int no_of_clients = 0;
+ yaz_log (LOG_LOG, "Existing sessions");
+ for (c = parent->m_clientPool; c; c = c->m_next)
+ {
+ yaz_log (LOG_LOG, " Session %-3d wait=%d %s", c->m_seqno,
+ c->m_waiting, c->get_hostname());
+ no_of_clients++;
+ if (min_seq < 0 || c->m_seqno < min_seq)
+ {
+ min_seq = c->m_seqno;
+ c_min = c;
+ }
+ }
+ if (no_of_clients >= parent->m_max_clients)
+ {
+ c = c_min;
+ if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
+ {
+ yaz_log (LOG_LOG, "Replace session %d",
+ c->m_seqno);
+ if (c->m_server && c->m_server != this)
+ delete c->m_server;
+ c->m_server = 0;
+ }
+ else
+ {
+ yaz_log (LOG_LOG, "Reuse session %d to %d",
+ c->m_seqno, parent->m_seqno);
+ if (cookie)
+ strcpy (c->m_cookie, cookie);
+ else
+ c->m_cookie[0] = '\0';
+ c->m_seqno = parent->m_seqno;
+ if (c->m_server && c->m_server != this)
+ {
+ c->m_server->m_client = 0;
+ delete c->m_server;
+ }
+ (parent->m_seqno)++;
+ yaz_log (LOG_DEBUG, "get_client 2 %p %p", this, c);
+ return c;
+ }
+ }
+ else
+ {
+ yaz_log (LOG_LOG, "Making session %d %s", parent->m_seqno,
+ m_proxyTarget);
+ c = new Yaz_ProxyClient(m_PDU_Observable->clone());
+ c->m_next = parent->m_clientPool;
+ if (c->m_next)
+ c->m_next->m_prev = &c->m_next;
+ parent->m_clientPool = c;
+ c->m_prev = &parent->m_clientPool;
+ }
+ if (cookie)
+ strcpy (c->m_cookie, cookie);
+ else
+ c->m_cookie[0] = '\0';
+ yaz_log (LOG_LOG, "Connecting to %s", m_proxyTarget);
+ c->m_seqno = parent->m_seqno;
+ c->client(m_proxyTarget);
+ c->m_init_flag = 0;
+ c->m_last_resultCount = 0;
+ c->m_last_ok = 0;
+ c->m_sr_transform = 0;
+ c->m_waiting = 0;
+ c->timeout(10);
+
+ (parent->m_seqno)++;
+ }
+ yaz_log (LOG_DEBUG, "get_client 3 %p %p", this, c);
+ return c;
+}
+
+Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
+{
+ if (apdu->which != Z_APDU_searchRequest)
+ return apdu;
+ if (*m_parent->m_optimize != '1')
+ return apdu;
+ Z_SearchRequest *sr = apdu->u.searchRequest;
+ Yaz_Z_Query *this_query = new Yaz_Z_Query;
+ Yaz_Z_Databases this_databases;
+
+ this_databases.set(sr->num_databaseNames, (const char **)
+ sr->databaseNames);
+
+ this_query->set_Z_Query(sr->query);
+
+ if (m_client->m_last_ok && m_client->m_last_query &&
+ m_client->m_last_query->match(this_query) &&
+ m_client->m_last_databases.match(this_databases))
+ {
+ delete this_query;
+ if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
+ m_client->m_last_resultCount < *sr->largeSetLowerBound)
+ {
+ // medium Set
+ // send present request (medium size)
+ yaz_log (LOG_LOG, "Optimizing search for medium set");
+ Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
+ Z_PresentRequest *pr = new_apdu->u.presentRequest;
+ pr->referenceId = sr->referenceId;
+ pr->resultSetId = sr->resultSetName;
+ pr->preferredRecordSyntax = sr->preferredRecordSyntax;
+ *pr->numberOfRecordsRequested = *sr->mediumSetPresentNumber;
+ if (sr->mediumSetElementSetNames)
+ {
+ pr->recordComposition = (Z_RecordComposition *)
+ odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
+ pr->recordComposition->which = Z_RecordComp_simple;
+ pr->recordComposition->u.simple = sr->mediumSetElementSetNames;
+ }
+ m_client->m_sr_transform = 1;
+ return new_apdu;
+ }
+ else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
+ m_client->m_last_resultCount <= 0)
+ {
+ // large set. Return pseudo-search response immediately
+ yaz_log (LOG_LOG, "Optimizing search for large set");
+ Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
+ new_apdu->u.searchResponse->referenceId = sr->referenceId;
+ new_apdu->u.searchResponse->resultCount =
+ &m_client->m_last_resultCount;
+ send_Z_PDU(new_apdu);
+ return 0;
+ }
+ else
+ {
+ // small set
+ // send a present request (small set)
+ yaz_log (LOG_LOG, "Optimizing search for small set");
+ Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
+ Z_PresentRequest *pr = new_apdu->u.presentRequest;
+ pr->referenceId = sr->referenceId;
+ pr->resultSetId = sr->resultSetName;
+ pr->preferredRecordSyntax = sr->preferredRecordSyntax;
+ *pr->numberOfRecordsRequested = m_client->m_last_resultCount;
+ if (sr->smallSetElementSetNames)
+ {
+ pr->recordComposition = (Z_RecordComposition *)
+ odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
+ pr->recordComposition->which = Z_RecordComp_simple;
+ pr->recordComposition->u.simple = sr->smallSetElementSetNames;
+ }
+ m_client->m_sr_transform = 1;
+ return new_apdu;
+ }
+ }
+ else
+ {
+ delete m_client->m_last_query;
+ m_client->m_last_query = this_query;
+ m_client->m_last_ok = 0;
+ m_client->m_last_databases.set(sr->num_databaseNames,
+ (const char **) sr->databaseNames);
+ }
+ return apdu;
+}
+
+static const char *apdu_name(Z_APDU *apdu)
+{
+ switch (apdu->which)
+ {
+ case Z_APDU_initRequest:
+ return "initRequest";
+ case Z_APDU_initResponse:
+ return "initResponse";
+ case Z_APDU_searchRequest:
+ return "searchRequest";
+ case Z_APDU_searchResponse:
+ return "searchResponse";
+ case Z_APDU_presentRequest:
+ return "presentRequest";
+ case Z_APDU_presentResponse:
+ return "presentResponse";
+ }
+ return "other";