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