2 * Copyright (c) 1998-2001, Index Data.
3 * See the file LICENSE for details.
5 * $Id: yaz-proxy.cpp,v 1.30 2001-11-06 20:33:32 adam Exp $
12 #include <yaz++/yaz-proxy.h>
14 Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable) :
15 Yaz_Z_Assoc(the_PDU_Observable)
17 m_PDU_Observable = the_PDU_Observable;
26 m_optimize = xstrdup ("1");
29 Yaz_Proxy::~Yaz_Proxy()
31 xfree (m_proxyTarget);
35 void Yaz_Proxy::set_proxyTarget(const char *target)
37 xfree (m_proxyTarget);
40 m_proxyTarget = (char *) xstrdup (target);
43 IYaz_PDU_Observer *Yaz_Proxy::sessionNotify(IYaz_PDU_Observable
44 *the_PDU_Observable, int fd)
46 Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable);
47 new_proxy->m_parent = this;
48 new_proxy->timeout(500);
49 new_proxy->set_proxyTarget(m_proxyTarget);
50 new_proxy->set_APDU_log(get_APDU_log());
54 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
57 Z_OtherInformationUnit *oi;
59 ent.proto = PROTO_Z3950;
60 ent.oclass = CLASS_USERINFO;
61 ent.value = (oid_value) VAL_COOKIE;
62 assert (oid_ent_to_oid (&ent, oid));
64 if (oid_ent_to_oid (&ent, oid) &&
65 (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
66 oi->which == Z_OtherInfo_characterInfo)
67 return oi->information.characterInfo;
71 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
74 Z_OtherInformationUnit *oi;
76 ent.proto = PROTO_Z3950;
77 ent.oclass = CLASS_USERINFO;
78 ent.value = (oid_value) VAL_PROXY;
79 if (oid_ent_to_oid (&ent, oid) &&
80 (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
81 oi->which == Z_OtherInfo_characterInfo)
82 return oi->information.characterInfo;
86 Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu)
89 Yaz_Proxy *parent = m_parent;
90 Z_OtherInformation **oi;
91 Yaz_ProxyClient *c = m_client;
93 get_otherInfoAPDU(apdu, &oi);
94 char *cookie = get_cookie(oi);
96 const char *proxy_host = get_proxy(oi);
98 set_proxyTarget(proxy_host);
100 // no target specified at all?
104 if (!strcmp(m_proxyTarget, "stop"))
106 if (cookie && *cookie)
108 Yaz_ProxyClient *cc = 0;
110 for (c = parent->m_clientPool; c; c = c->m_next)
113 assert (*c->m_prev == c);
114 if (!strcmp(cookie,c->m_cookie) &&
115 !strcmp(m_proxyTarget, c->get_hostname()))
124 // The following handles "cancel"
125 // If connection is busy (waiting for PDU) and
126 // we have an initRequest we can safely do re-open
127 if (c->m_waiting && apdu->which == Z_APDU_initRequest)
129 yaz_log (LOG_LOG, "reopen target=%s", c->get_hostname());
131 c->client(m_proxyTarget);
134 delete c->m_last_query;
136 c->m_last_resultCount = 0;
137 c->m_sr_transform = 0;
141 c->m_seqno = parent->m_seqno;
142 if (c->m_server && c->m_server != this)
143 c->m_server->m_client = 0;
145 c->m_seqno = parent->m_seqno;
147 yaz_log (LOG_DEBUG, "get_client 1 %p %p", this, c);
153 if (apdu->which != Z_APDU_initRequest)
155 yaz_log (LOG_LOG, "no first INIT!");
158 yaz_log (LOG_LOG, "got InitRequest");
160 // go through list of clients - and find the lowest/oldest one.
161 Yaz_ProxyClient *c_min = 0;
163 int no_of_clients = 0;
164 for (c = parent->m_clientPool; c; c = c->m_next)
167 if (min_seq < 0 || c->m_seqno < min_seq)
169 min_seq = c->m_seqno;
173 if (no_of_clients >= parent->m_max_clients)
176 if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
178 yaz_log (LOG_LOG, "Yaz_Proxy::get_client re-init session %d",
180 if (c->m_server && c->m_server != this)
187 "Yaz_Proxy::get_client re-use session %d to %d",
188 c->m_seqno, parent->m_seqno);
190 strcpy (c->m_cookie, cookie);
192 c->m_cookie[0] = '\0';
193 c->m_seqno = parent->m_seqno;
194 if (c->m_server && c->m_server != this)
196 c->m_server->m_client = 0;
200 yaz_log (LOG_DEBUG, "get_client 2 %p %p", this, c);
206 yaz_log (LOG_LOG, "Yaz_Proxy::get_client making session %d",
208 c = new Yaz_ProxyClient(m_PDU_Observable->clone());
209 c->m_next = parent->m_clientPool;
211 c->m_next->m_prev = &c->m_next;
212 parent->m_clientPool = c;
213 c->m_prev = &parent->m_clientPool;
216 strcpy (c->m_cookie, cookie);
218 c->m_cookie[0] = '\0';
219 yaz_log (LOG_LOG, "Yaz_Proxy::get_client connect to %s",
221 c->m_seqno = parent->m_seqno;
222 c->client(m_proxyTarget);
225 delete c->m_last_query;
227 c->m_last_resultCount = 0;
228 c->m_sr_transform = 0;
234 yaz_log (LOG_DEBUG, "get_client 3 %p %p", this, c);
238 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
240 if (apdu->which != Z_APDU_searchRequest)
242 if (*m_parent->m_optimize != '1')
244 Z_SearchRequest *sr = apdu->u.searchRequest;
245 Yaz_Z_Query *this_query = new Yaz_Z_Query;
246 Yaz_Z_Databases this_databases;
248 this_databases.set(sr->num_databaseNames, (const char **)
251 this_query->set_Z_Query(sr->query);
253 if (m_client->m_last_query &&
254 m_client->m_last_query->match(this_query) &&
255 m_client->m_last_databases.match(this_databases))
258 if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
259 m_client->m_last_resultCount < *sr->largeSetLowerBound)
262 yaz_log (LOG_LOG, "Yaz_Proxy::result_set_optimize medium set");
263 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
264 Z_PresentRequest *pr = new_apdu->u.presentRequest;
265 pr->referenceId = sr->referenceId;
266 pr->resultSetId = sr->resultSetName;
267 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
268 *pr->numberOfRecordsRequested = *sr->mediumSetPresentNumber;
269 if (sr->mediumSetElementSetNames)
271 pr->recordComposition = (Z_RecordComposition *)
272 odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
273 pr->recordComposition->which = Z_RecordComp_simple;
274 pr->recordComposition->u.simple = sr->mediumSetElementSetNames;
276 m_client->m_sr_transform = 1;
279 else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
280 m_client->m_last_resultCount == 0)
283 yaz_log (LOG_LOG, "Yaz_Proxy::result_set_optimize large set");
284 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
285 new_apdu->u.searchResponse->referenceId = sr->referenceId;
286 new_apdu->u.searchResponse->resultCount =
287 &m_client->m_last_resultCount;
288 send_Z_PDU(new_apdu);
294 yaz_log (LOG_LOG, "Yaz_Proxy::result_set_optimize small set");
295 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
296 Z_PresentRequest *pr = new_apdu->u.presentRequest;
297 pr->referenceId = sr->referenceId;
298 pr->resultSetId = sr->resultSetName;
299 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
300 *pr->numberOfRecordsRequested = m_client->m_last_resultCount;
301 if (sr->smallSetElementSetNames)
303 pr->recordComposition = (Z_RecordComposition *)
304 odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
305 pr->recordComposition->which = Z_RecordComp_simple;
306 pr->recordComposition->u.simple = sr->smallSetElementSetNames;
308 m_client->m_sr_transform = 1;
314 yaz_log (LOG_LOG, "Yaz_Proxy::result_set_optimize new set");
315 delete m_client->m_last_query;
316 m_client->m_last_query = this_query;
317 m_client->m_last_databases.set(sr->num_databaseNames,
318 (const char **) sr->databaseNames);
323 void Yaz_Proxy::recv_Z_PDU(Z_APDU *apdu)
325 yaz_log (LOG_LOG, "Yaz_Proxy::recv_Z_PDU");
326 // Determine our client.
327 m_client = get_client(apdu);
333 m_client->m_server = this;
335 if (apdu->which == Z_APDU_initRequest)
337 if (m_client->m_init_flag)
339 Z_APDU *apdu = create_Z_PDU(Z_APDU_initResponse);
340 if (m_client->m_cookie)
341 set_otherInformationString(apdu, VAL_COOKIE, 1,
346 m_client->m_init_flag = 1;
348 apdu = result_set_optimize(apdu);
352 yaz_log (LOG_LOG, "Yaz_ProxyClient::send_Z_PDU %s",
353 m_client->get_hostname());
355 // delete other info part from PDU before sending to target
356 Z_OtherInformation **oi;
357 get_otherInfoAPDU(apdu, &oi);
361 if (m_client->send_Z_PDU(apdu) < 0)
368 m_client->m_waiting = 1;
371 void Yaz_Proxy::connectNotify()
375 void Yaz_Proxy::shutdown()
377 // only keep if keep_alive flag and cookie is set...
378 if (m_keepalive && m_client && m_client->m_cookie[0])
380 if (m_client->m_waiting == 2)
382 // Tell client (if any) that no server connection is there..
383 m_client->m_server = 0;
384 yaz_log (LOG_LOG, "shutdown (client to proxy) keepalive %s", m_client->get_hostname());
388 if (m_client->m_waiting == 2)
390 yaz_log (LOG_LOG, "shutdown (client to proxy) close %s", m_client->get_hostname());
395 yaz_log (LOG_LOG, "shutdown (client to proxy) bad state");
400 yaz_log (LOG_LOG, "shutdown (client to proxy)");
405 void Yaz_ProxyClient::shutdown()
407 yaz_log (LOG_LOG, "shutdown (proxy to server) %s", get_hostname());
412 void Yaz_Proxy::failNotify()
414 yaz_log (LOG_LOG, "Yaz_Proxy connection closed by client");
418 void Yaz_ProxyClient::failNotify()
420 yaz_log (LOG_LOG, "Yaz_ProxyClient connection closed by %s", get_hostname());
424 void Yaz_ProxyClient::connectNotify()
426 yaz_log (LOG_LOG, "Yaz_ProxyClient connection accepted by %s",
431 IYaz_PDU_Observer *Yaz_ProxyClient::sessionNotify(IYaz_PDU_Observable
432 *the_PDU_Observable, int fd)
434 return new Yaz_ProxyClient(the_PDU_Observable);
437 Yaz_ProxyClient::~Yaz_ProxyClient()
442 m_next->m_prev = m_prev;
443 m_waiting = 2; // for debugging purposes only.
447 void Yaz_Proxy::timeoutNotify()
449 yaz_log (LOG_LOG, "timeout (client to proxy)");
453 void Yaz_ProxyClient::timeoutNotify()
455 yaz_log (LOG_LOG, "timeout (proxy to target) %s", get_hostname());
459 Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable) :
460 Yaz_Z_Assoc (the_PDU_Observable)
467 m_last_resultCount = 0;
472 const char *Yaz_Proxy::option(const char *name, const char *value)
474 if (!strcmp (name, "optimize")) {
477 m_optimize = xstrdup (value);
484 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu)
487 yaz_log (LOG_LOG, "Yaz_ProxyClient::recv_Z_PDU %s", get_hostname());
488 if (apdu->which == Z_APDU_searchResponse)
490 m_last_resultCount = *apdu->u.searchResponse->resultCount;
491 int status = *apdu->u.searchResponse->searchStatus;
493 apdu->u.searchResponse->records &&
494 apdu->u.searchResponse->records->which != Z_Records_DBOSD))
500 if (apdu->which == Z_APDU_presentResponse && m_sr_transform)
503 Z_PresentResponse *pr = apdu->u.presentResponse;
504 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
505 Z_SearchResponse *sr = new_apdu->u.searchResponse;
506 sr->referenceId = pr->referenceId;
507 *sr->resultCount = m_last_resultCount;
508 sr->records = pr->records;
509 sr->nextResultSetPosition = pr->nextResultSetPosition;
510 sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
513 if (m_cookie && *m_cookie)
514 set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
517 yaz_log (LOG_LOG, "Yaz_Proxy::send_Z_PDU");
518 m_server->send_Z_PDU(apdu);