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