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