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