Better result set optimisation for proxy.
[yazpp-moved-to-github.git] / src / yaz-proxy.cpp
1 /*
2  * Copyright (c) 1998-1999, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  * 
6  * $Log: yaz-proxy.cpp,v $
7  * Revision 1.7  1999-04-28 13:31:17  adam
8  * Better result set optimisation for proxy.
9  *
10  * Revision 1.6  1999/04/27 07:52:13  adam
11  * Improved proxy; added query match for result set re-use.
12  *
13  * Revision 1.5  1999/04/21 12:09:01  adam
14  * Many improvements. Modified to proxy server to work with "sessions"
15  * based on cookies.
16  *
17  * Revision 1.4  1999/04/20 10:30:05  adam
18  * Implemented various stuff for client and proxy. Updated calls
19  * to ODR to reflect new name parameter.
20  *
21  * Revision 1.3  1999/04/09 11:46:57  adam
22  * Added object Yaz_Z_Assoc. Much more functional client.
23  *
24  * Revision 1.2  1999/01/28 13:08:46  adam
25  * Yaz_PDU_Assoc better encapsulated. Memory leak fix in
26  * yaz-socket-manager.cc.
27  *
28  * Revision 1.1.1.1  1999/01/28 09:41:07  adam
29  * First implementation of YAZ++.
30  *
31  */
32
33 #include <assert.h>
34
35 #include <log.h>
36
37 #include <yaz-proxy.h>
38
39 Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable) :
40     Yaz_Z_Assoc(the_PDU_Observable)
41 {
42     m_PDU_Observable = the_PDU_Observable;
43     m_client = 0;
44     m_parent = 0;
45     m_clientPool = 0;
46     m_seqno = 1;
47     m_keepalive = 1;
48 }
49
50 Yaz_Proxy::~Yaz_Proxy()
51 {
52 }
53
54 IYaz_PDU_Observer *Yaz_Proxy::clone(IYaz_PDU_Observable
55                                     *the_PDU_Observable)
56 {
57     Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable);
58     new_proxy->m_parent = this;
59     new_proxy->timeout(120);
60     return new_proxy;
61 }
62
63 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
64 {
65     int oid[OID_SIZE];
66     Z_OtherInformationUnit *oi;
67     struct oident ent;
68     ent.proto = PROTO_Z3950;
69     ent.oclass = CLASS_USERINFO;
70     ent.value = (oid_value) VAL_COOKIE;
71     assert (oid_ent_to_oid (&ent, oid));
72
73     if (oid_ent_to_oid (&ent, oid) && 
74         (oi = update_otherInformation(otherInfo, 0, oid, 1)) &&
75         oi->which == Z_OtherInfo_characterInfo)
76         return oi->information.characterInfo;
77     return 0;
78 }
79
80 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
81 {
82     int oid[OID_SIZE];
83     Z_OtherInformationUnit *oi;
84     struct oident ent;
85     ent.proto = PROTO_Z3950;
86     ent.oclass = CLASS_USERINFO;
87     ent.value = (oid_value) VAL_PROXY;
88     if (oid_ent_to_oid (&ent, oid) &&
89         (oi = update_otherInformation(otherInfo, 0, oid, 1)) &&
90         oi->which == Z_OtherInfo_characterInfo)
91         return oi->information.characterInfo;
92     return 0;
93 }
94
95 Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu)
96 {
97     assert (m_parent);
98     Yaz_Proxy *parent = m_parent;
99     Z_OtherInformation **oi;
100     Yaz_ProxyClient *c = m_client;
101     
102     get_otherInfoAPDU(apdu, &oi);
103     char *cookie = get_cookie(oi);
104     logf (LOG_LOG, "Yaz_Proxy::get_client cookie=%s", cookie ? cookie :
105           "<null>");
106     if (cookie)
107     {
108         for (c = parent->m_clientPool; c; c = c->m_next)
109         {
110             assert (c->m_prev);
111             assert (*c->m_prev == c);
112             if (!strcmp(cookie,c->m_cookie))
113             {
114                 logf (LOG_LOG, "Yaz_Proxy::get_client cached");
115                 break;
116             }
117         }
118     }
119     else if (!m_client)
120     {
121         logf (LOG_LOG, "Yaz_Proxy::get_client creating new");
122         c = new Yaz_ProxyClient(m_PDU_Observable->clone());
123         c->m_next = parent->m_clientPool;
124         if (c->m_next)
125             c->m_next->m_prev = &c->m_next;
126         parent->m_clientPool = c;
127         c->m_prev = &parent->m_clientPool;
128
129         sprintf (c->m_cookie, "%d", parent->m_seqno);
130         (parent->m_seqno)++;
131
132         if (apdu->which == Z_APDU_initRequest)
133         {
134             logf (LOG_LOG, "got InitRequest");
135             
136             char *proxy_host = get_proxy(&apdu->u.initRequest->otherInfo);
137             if (proxy_host)
138                 c->client(proxy_host);
139             else
140                 c->client("localhost:9999");
141         }
142         c->timeout(600);
143     }
144     return c;
145 }
146
147 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
148 {
149     if (apdu->which != Z_APDU_searchRequest)
150         return apdu;
151     Z_SearchRequest *sr = apdu->u.searchRequest;
152     Yaz_Z_Query *this_query = new Yaz_Z_Query;
153     
154     this_query->set_Z_Query(sr->query);
155     
156     if (m_client->m_last_query &&
157         m_client->m_last_query->match(this_query))
158     {
159         delete this_query;
160         if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
161             m_client->m_last_resultCount < *sr->largeSetLowerBound)
162         {
163             // medium Set
164             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
165             Z_PresentRequest *pr = new_apdu->u.presentRequest;
166             pr->referenceId = sr->referenceId;
167             pr->resultSetId = sr->resultSetName;
168             pr->preferredRecordSyntax = sr->preferredRecordSyntax;
169             *pr->numberOfRecordsRequested = *sr->mediumSetPresentNumber;
170             if (sr->mediumSetElementSetNames)
171             {
172                 pr->recordComposition = (Z_RecordComposition *)
173                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
174                 pr->recordComposition->which = Z_RecordComp_simple;
175                 pr->recordComposition->u.simple = sr->mediumSetElementSetNames;
176             }
177             m_client->m_sr_transform = 1;
178             return new_apdu;
179         }
180         else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
181             m_client->m_last_resultCount == 0)
182         {
183             // large set
184             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
185             new_apdu->u.searchResponse->referenceId = sr->referenceId;
186             new_apdu->u.searchResponse->resultCount =
187                 &m_client->m_last_resultCount;
188             send_Z_PDU(new_apdu);
189             return 0;
190         }
191         else
192         {
193             // small set
194             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
195             Z_PresentRequest *pr = new_apdu->u.presentRequest;
196             pr->referenceId = sr->referenceId;
197             pr->resultSetId = sr->resultSetName;
198             pr->preferredRecordSyntax = sr->preferredRecordSyntax;
199             *pr->numberOfRecordsRequested = m_client->m_last_resultCount;
200             if (sr->smallSetElementSetNames)
201             {
202                 pr->recordComposition = (Z_RecordComposition *)
203                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
204                 pr->recordComposition->which = Z_RecordComp_simple;
205                 pr->recordComposition->u.simple = sr->smallSetElementSetNames;
206             }
207             m_client->m_sr_transform = 1;
208             return new_apdu;
209         }
210     }
211     else
212     {
213         logf (LOG_LOG, "query doesn't match");
214         delete m_client->m_last_query;
215         m_client->m_last_query = this_query;
216     }
217     return apdu;
218 }
219
220 void Yaz_Proxy::recv_Z_PDU(Z_APDU *apdu)
221 {
222     logf (LOG_LOG, "Yaz_Proxy::recv_Z_PDU");
223     // Determine our client.
224     m_client = get_client(apdu);
225     if (!m_client)
226     {
227         delete this;
228         return;
229     }
230     m_client->m_server = this;
231
232     Z_OtherInformation **oi;
233     get_otherInfoAPDU(apdu, &oi);
234     *oi = 0;
235     if (apdu->which == Z_APDU_initRequest)
236     {
237         if (m_client->m_init_flag)
238         {
239             Z_APDU *apdu = create_Z_PDU(Z_APDU_initResponse);
240             if (m_client->m_cookie)
241                 set_otherInformationString(apdu, VAL_COOKIE, 1,
242                                            m_client->m_cookie);
243             send_Z_PDU(apdu);
244             return;
245         }
246         m_client->m_init_flag = 1;
247     }
248     apdu = result_set_optimize(apdu);
249     if (!apdu)
250         return;
251
252     logf (LOG_LOG, "Yaz_ProxyClient::send_Z_PDU");
253     if (m_client->send_Z_PDU(apdu) < 0)
254     {
255         delete m_client;
256         delete this;
257     }
258 }
259
260 void Yaz_Proxy::failNotify()
261 {
262     logf (LOG_LOG, "failNotity server");
263     if (m_keepalive)
264     {
265         // Tell client (if any) that no server connection is there..
266         if (m_client)
267             m_client->m_server = 0;
268     }
269     else
270     {
271         delete m_client;
272     }
273     delete this;
274 }
275
276 void Yaz_ProxyClient::failNotify()
277 {
278     logf (LOG_LOG, "failNotity client");
279     delete m_server;
280     delete this;
281 }
282
283 IYaz_PDU_Observer *Yaz_ProxyClient::clone(IYaz_PDU_Observable
284                                           *the_PDU_Observable)
285 {
286     return new Yaz_ProxyClient(the_PDU_Observable);
287 }
288
289 Yaz_ProxyClient::~Yaz_ProxyClient()
290 {
291     if (m_prev)
292     {
293         *m_prev = m_next;
294         if (m_next)
295             m_next->m_prev = m_prev;
296     }
297     delete m_last_query;
298 }
299
300 void Yaz_Proxy::timeoutNotify()
301 {
302     failNotify();
303 }
304
305 void Yaz_ProxyClient::timeoutNotify()
306 {
307     failNotify();
308 }
309
310 Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable) :
311     Yaz_Z_Assoc (the_PDU_Observable)
312 {
313     m_cookie[0] = 0;
314     m_next = 0;
315     m_prev = 0;
316     m_init_flag = 0;
317     m_last_query = 0;
318     m_last_resultCount = 0;
319     m_sr_transform = 0;
320 }
321
322 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu)
323 {
324     logf (LOG_LOG, "Yaz_ProxyClient::recv_Z_PDU");
325     if (apdu->which == Z_APDU_searchResponse)
326         m_last_resultCount = *apdu->u.searchResponse->resultCount;
327     if (apdu->which == Z_APDU_presentResponse && m_sr_transform)
328     {
329         m_sr_transform = 0;
330         Z_PresentResponse *pr = apdu->u.presentResponse;
331         Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
332         Z_SearchResponse *sr = new_apdu->u.searchResponse;
333         sr->referenceId = pr->referenceId;
334         *sr->resultCount = m_last_resultCount;
335         sr->records = pr->records;
336         sr->nextResultSetPosition = pr->nextResultSetPosition;
337         sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
338         apdu = new_apdu;
339     }
340     if (m_cookie)
341         set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
342     if (m_server)
343     {
344         logf (LOG_LOG, "Yaz_Proxy::send_Z_PDU");
345         m_server->send_Z_PDU(apdu);
346     }
347 }