~ubuntu-branches/ubuntu/precise/ncbi-tools6/precise

« back to all changes in this revision

Viewing changes to connect/ncbi_service_dispd.c

  • Committer: Bazaar Package Importer
  • Author(s): Aaron M. Ucko
  • Date: 2005-03-27 12:00:15 UTC
  • mfrom: (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050327120015-embhesp32nj73p9r
Tags: 6.1.20041020-3
* Fix FTBFS under GCC 4.0 caused by inconsistent use of "static" on
  functions.  (Closes: #295110.)
* Add a watch file, now that we can.  (Upstream's layout needs version=3.)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  $Id: ncbi_service_dispd.c,v 6.34 2001/12/04 15:57:05 lavr Exp $
2
 
 * ===========================================================================
3
 
 *
4
 
 *                            PUBLIC DOMAIN NOTICE
5
 
 *               National Center for Biotechnology Information
6
 
 *
7
 
 *  This software/database is a "United States Government Work" under the
8
 
 *  terms of the United States Copyright Act.  It was written as part of
9
 
 *  the author's official duties as a United States Government employee and
10
 
 *  thus cannot be copyrighted.  This software/database is freely available
11
 
 *  to the public for use. The National Library of Medicine and the U.S.
12
 
 *  Government have not placed any restriction on its use or reproduction.
13
 
 *
14
 
 *  Although all reasonable efforts have been taken to ensure the accuracy
15
 
 *  and reliability of the software and data, the NLM and the U.S.
16
 
 *  Government do not and cannot warrant the performance or results that
17
 
 *  may be obtained by using this software or data. The NLM and the U.S.
18
 
 *  Government disclaim all warranties, express or implied, including
19
 
 *  warranties of performance, merchantability or fitness for any particular
20
 
 *  purpose.
21
 
 *
22
 
 *  Please cite the author in any work or product based on this material.
23
 
 *
24
 
 * ===========================================================================
25
 
 *
26
 
 * Author:  Anton Lavrentiev
27
 
 *
28
 
 * File Description:
29
 
 *   Low-level API to resolve NCBI service name to the server meta-address
30
 
 *   with the use of NCBI network dispatcher (DISPD).
31
 
 *
32
 
 * --------------------------------------------------------------------------
33
 
 * $Log: ncbi_service_dispd.c,v $
34
 
 * Revision 6.34  2001/12/04 15:57:05  lavr
35
 
 * Change log correction
36
 
 *
37
 
 * Revision 6.33  2001/10/01 19:53:39  lavr
38
 
 * -s_FreeData(), -s_ResetData() - do everything in s_Close()/s_Reset() instead
39
 
 *
40
 
 * Revision 6.32  2001/09/29 19:33:04  lavr
41
 
 * BUGFIX: SERV_Update() requires VT bound (was not the case in constructor)
42
 
 *
43
 
 * Revision 6.31  2001/09/29 18:41:03  lavr
44
 
 * "Server-Keyed-Info:" removed from protocol
45
 
 *
46
 
 * Revision 6.30  2001/09/28 20:52:16  lavr
47
 
 * Update VT method revised as now called on a per-line basis
48
 
 *
49
 
 * Revision 6.29  2001/09/24 20:30:01  lavr
50
 
 * Reset() VT method added and utilized
51
 
 *
52
 
 * Revision 6.28  2001/09/10 21:23:53  lavr
53
 
 * "Relay-Mode:" tag eliminated from the dispatcher protocol
54
 
 *
55
 
 * Revision 6.27  2001/07/24 18:02:02  lavr
56
 
 * Seed random generator at Open()
57
 
 *
58
 
 * Revision 6.26  2001/07/18 17:41:25  lavr
59
 
 * BUGFIX: In code for selecting services by preferred host
60
 
 *
61
 
 * Revision 6.25  2001/07/03 20:49:44  lavr
62
 
 * RAND_MAX included in the interval search
63
 
 *
64
 
 * Revision 6.24  2001/06/25 15:36:38  lavr
65
 
 * s_GetNextInfo now takes one additional argument for host environment
66
 
 *
67
 
 * Revision 6.23  2001/06/20 17:27:49  kans
68
 
 * include <time.h> for Mac compiler
69
 
 *
70
 
 * Revision 6.22  2001/06/19 19:12:01  lavr
71
 
 * Type change: size_t -> TNCBI_Size; time_t -> TNCBI_Time
72
 
 *
73
 
 * Revision 6.21  2001/05/17 15:02:51  lavr
74
 
 * Typos corrected
75
 
 *
76
 
 * Revision 6.20  2001/05/11 15:30:31  lavr
77
 
 * Protocol change: REQUEST_FAILED -> DISP_FAILURES
78
 
 *
79
 
 * Revision 6.19  2001/05/03 16:58:16  lavr
80
 
 * FIX: Percent is taken of local bonus coef instead of the value itself
81
 
 *
82
 
 * Revision 6.18  2001/05/03 16:35:53  lavr
83
 
 * Local bonus coefficient modified: meaning of negative value changed
84
 
 *
85
 
 * Revision 6.17  2001/04/26 20:20:01  lavr
86
 
 * Better way of choosing local server with a tiny (e.g. penalized) status
87
 
 *
88
 
 * Revision 6.16  2001/04/24 21:35:46  lavr
89
 
 * Treatment of new bonus coefficient for local servers
90
 
 *
91
 
 * Revision 6.15  2001/03/21 21:24:11  lavr
92
 
 * Type match (int) for %n in scanf
93
 
 *
94
 
 * Revision 6.14  2001/03/06 23:57:27  lavr
95
 
 * SERV_DISPD_LOCAL_SVC_BONUS used for services running locally
96
 
 *
97
 
 * Revision 6.13  2001/03/05 23:10:46  lavr
98
 
 * SERV_ReadInfo takes only one argument now
99
 
 *
100
 
 * Revision 6.12  2001/03/01 00:33:12  lavr
101
 
 * FIXES: Empty update does not generate parse error
102
 
 *        Dispathing error is only logged in debug mode; milder severity
103
 
 *
104
 
 * Revision 6.11  2001/02/09 17:36:48  lavr
105
 
 * Modified: fSERV_StatelessOnly overrides info->stateless
106
 
 *
107
 
 * Revision 6.10  2001/01/25 17:06:36  lavr
108
 
 * s_FreeData now calls ConnNetInfo_Destroy() unconditionally
109
 
 *
110
 
 * Revision 6.9  2001/01/12 23:51:40  lavr
111
 
 * Message logging modified for use LOG facility only
112
 
 *
113
 
 * Revision 6.8  2001/01/08 23:48:14  lavr
114
 
 * (unsigned char) conversion in isspace
115
 
 *
116
 
 * Revision 6.7  2001/01/08 22:40:23  lavr
117
 
 * Further development of service-mapping protocol: stateless/stateful
118
 
 * is now separated from firewall/direct mode (see also in few more files)
119
 
 *
120
 
 * Revision 6.6  2000/12/29 18:05:46  lavr
121
 
 * First working revision.
122
 
 *
123
 
 * Revision 6.5  2000/10/20 17:36:05  lavr
124
 
 * Partially working dispd dispatcher client (service mapping works)
125
 
 * Checkin for backup purposes; working code '#if 0'-ed out
126
 
 *
127
 
 * Revision 6.4  2000/10/05 22:43:30  lavr
128
 
 * Another dummy revision: still in development
129
 
 *
130
 
 * Revision 6.3  2000/10/05 22:34:23  lavr
131
 
 * Temporary (dummy) revision for compilation to go
132
 
 *
133
 
 * Revision 6.2  2000/05/22 16:53:12  lavr
134
 
 * Rename service_info -> server_info everywhere (including
135
 
 * file names) as the latter name is more relevant
136
 
 *
137
 
 * Revision 6.1  2000/05/12 18:43:59  lavr
138
 
 * Initial revision
139
 
 *
140
 
 * ==========================================================================
141
 
 */
142
 
 
143
 
#include "ncbi_comm.h"
144
 
#if defined(_DEBUG) && !defined(NDEBUG)
145
 
#include "ncbi_priv.h"
146
 
#endif
147
 
#include "ncbi_servicep_dispd.h"
148
 
#include <connect/ncbi_ansi_ext.h>
149
 
#include <connect/ncbi_connection.h>
150
 
#include <connect/ncbi_http_connector.h>
151
 
#include <ctype.h>
152
 
#include <stdlib.h>
153
 
#include <string.h>
154
 
#include <time.h>
155
 
 
156
 
 
157
 
/* Lower bound of up-to-date/out-of-date ratio */
158
 
#define SERV_DISPD_STALE_RATIO_OK  0.8
159
 
/* Default rate increase if svc runs locally */
160
 
#define SERV_DISPD_LOCAL_SVC_BONUS 1.2
161
 
 
162
 
 
163
 
#ifdef __cplusplus
164
 
extern "C" {
165
 
#endif
166
 
    static void s_Reset(SERV_ITER);
167
 
    static SSERV_Info* s_GetNextInfo(SERV_ITER, char**);
168
 
    static int/*bool*/ s_Update(SERV_ITER, TNCBI_Time, const char*);
169
 
    static void s_Close(SERV_ITER);
170
 
 
171
 
    static const SSERV_VTable s_op = {
172
 
        s_Reset, s_GetNextInfo, s_Update, 0, s_Close, "DISPD"
173
 
    };
174
 
#ifdef __cplusplus
175
 
} /* extern "C" */
176
 
#endif
177
 
 
178
 
 
179
 
static int s_RandomSeed = 0;
180
 
 
181
 
 
182
 
typedef struct {
183
 
    SSERV_Info*   info;
184
 
    double        status;
185
 
} SDISPD_Node;
186
 
 
187
 
 
188
 
typedef struct {
189
 
    SConnNetInfo* net_info;
190
 
    SDISPD_Node*  s_node;
191
 
    size_t        n_node;
192
 
    size_t        n_max_node;
193
 
} SDISPD_Data;
194
 
 
195
 
 
196
 
static int/*bool*/ s_AddServerInfo(SDISPD_Data* data, SSERV_Info* info)
197
 
{
198
 
    size_t i;
199
 
 
200
 
    /* First check that the new server info is updating existing one */
201
 
    for (i = 0; i < data->n_node; i++) {
202
 
        if (SERV_EqualInfo(data->s_node[i].info, info)) {
203
 
            /* Replace older version */
204
 
            free(data->s_node[i].info);
205
 
            data->s_node[i].info = info;
206
 
            return 1;
207
 
        }
208
 
    }
209
 
 
210
 
    /* Next, add new service to the list */
211
 
    if (data->n_node == data->n_max_node) {
212
 
        size_t n = data->n_max_node + 10;
213
 
        SDISPD_Node* temp;
214
 
 
215
 
        if (data->s_node)
216
 
            temp = (SDISPD_Node*) realloc(data->s_node, sizeof(*temp) * n);
217
 
        else
218
 
            temp = (SDISPD_Node*) malloc(sizeof(*temp) * n);
219
 
        if (!temp)
220
 
            return 0;
221
 
 
222
 
        data->s_node = temp;
223
 
        data->n_max_node = n;
224
 
    }
225
 
 
226
 
    data->s_node[data->n_node++].info = info;
227
 
    return 1;
228
 
}
229
 
 
230
 
 
231
 
#ifdef __cplusplus
232
 
extern "C" {
233
 
    static int s_ParseHeader(const char*, void*, int);
234
 
}
235
 
#endif /* __cplusplus */
236
 
 
237
 
static int/*bool*/ s_ParseHeader(const char* header, void *data,
238
 
                          int/*bool, ignored*/ server_error)
239
 
{
240
 
    SERV_Update((SERV_ITER) data, header);
241
 
    return 1/*header parsed okay*/;
242
 
}
243
 
 
244
 
 
245
 
static int/*bool*/ s_Resolve(SERV_ITER iter)
246
 
{
247
 
    static const char service[] = "service=";
248
 
    static const char stateless[] = "Client-Mode: STATELESS_ONLY\r\n";
249
 
    static const char dispatch_mode[] = "Dispatch-Mode: INFORMATION_ONLY\r\n";
250
 
    static const char stateful_capable[] = "Client-Mode: STATEFUL_CAPABLE\r\n";
251
 
    SConnNetInfo *net_info = ((SDISPD_Data*) iter->data)->net_info;
252
 
    const char *tag;
253
 
    size_t buflen;
254
 
    CONNECTOR c;
255
 
    BUF buf = 0;
256
 
    CONN conn;
257
 
    char *s;
258
 
 
259
 
    /* Form service name argument (as CGI argument) */
260
 
    if (strlen(iter->service) + sizeof(service) > sizeof(net_info->args))
261
 
        return 0/*failed*/;
262
 
    strcpy(net_info->args, service);
263
 
    strcat(net_info->args, iter->service);
264
 
    /* Reset request method to be GET (as no HTTP body will follow) */
265
 
    net_info->req_method = eReqMethod_Get;
266
 
    /* Obtain additional header information */
267
 
    if ((s = SERV_Print(iter)) != 0) {
268
 
        int status = BUF_Write(&buf, s, strlen(s));
269
 
        free(s);
270
 
        if (!status) {
271
 
            BUF_Destroy(buf);
272
 
            return 0/*failure*/;
273
 
        }
274
 
    }
275
 
    tag = net_info->stateless ? stateless : stateful_capable;
276
 
    if (!BUF_Write(&buf, tag, strlen(tag)) ||
277
 
        !BUF_Write(&buf, dispatch_mode, sizeof(dispatch_mode)-1)) {
278
 
        BUF_Destroy(buf);
279
 
        return 0/*failure*/;
280
 
    }
281
 
    /* Now the entire user header is ready, take it out of the buffer */
282
 
    buflen = BUF_Size(buf);
283
 
    assert(buflen != 0);
284
 
    if ((s = (char*) malloc(buflen + 1)) != 0) {
285
 
        if (BUF_Read(buf, s, buflen) != buflen) {
286
 
            free(s);
287
 
            s = 0;
288
 
        } else
289
 
            s[buflen] = '\0';
290
 
    }
291
 
    BUF_Destroy(buf);
292
 
    if (!s)
293
 
        return 0/*failure*/;
294
 
    ConnNetInfo_SetUserHeader(net_info, s);
295
 
    assert(strcmp(net_info->http_user_header, s) == 0);
296
 
    free(s);
297
 
    /* All the rest in the net_info structure is fine with us */
298
 
    if (!(c = HTTP_CreateConnectorEx(net_info, 0/*flags*/, s_ParseHeader,
299
 
                                     0/*adj.info*/, iter/*data*/, 0/*clnup*/)))
300
 
        return 0/*failed*/;
301
 
    if (CONN_Create(c, &conn) != eIO_Success)
302
 
        return 0/*failed*/;
303
 
    /* This dummy read will send all the HTTP data, we'll get a callback */
304
 
    CONN_Read(conn, 0, 0, &buflen, eIO_Plain);
305
 
    CONN_Close(conn);
306
 
    return ((SDISPD_Data*) iter->data)->n_node != 0;
307
 
}
308
 
 
309
 
 
310
 
static int/*bool*/ s_Update(SERV_ITER iter, TNCBI_Time now, const char* text)
311
 
{
312
 
    static const char server_info[] = "Server-Info-";
313
 
    SDISPD_Data* data = (SDISPD_Data*) iter->data;
314
 
 
315
 
    if (strncasecmp(text, server_info, sizeof(server_info) - 1) == 0) {
316
 
        const char* p = text + sizeof(server_info) - 1;
317
 
        SSERV_Info* info;
318
 
        unsigned int d1;
319
 
        int d2;
320
 
 
321
 
        if (sscanf(p, "%u: %n", &d1, &d2) < 1)
322
 
            return 0/*not updated*/;
323
 
        if ((info = SERV_ReadInfo(p + d2)) != 0) {
324
 
            info->time += now; /* expiration time now */
325
 
            if (s_AddServerInfo(data, info))
326
 
                return 1/*updated*/;
327
 
            free(info);
328
 
        }
329
 
    } else if (strncasecmp(text, HTTP_DISP_FAILURES,
330
 
                           sizeof(HTTP_DISP_FAILURES) - 1) == 0) {
331
 
#if defined(_DEBUG) && !defined(NDEBUG)
332
 
        const char* p = text + sizeof(HTTP_DISP_FAILURES) - 1;
333
 
        while (*p && isspace((unsigned char)(*p)))
334
 
            p++;
335
 
        if (data->net_info->debug_printout)
336
 
            CORE_LOGF(eLOG_Warning, ("[DISPATCHER] %s", p));
337
 
#endif
338
 
        return 1/*updated*/;
339
 
    }
340
 
 
341
 
    return 0/*not updated*/;
342
 
}
343
 
 
344
 
 
345
 
static int/*bool*/ s_IsUpdateNeeded(SDISPD_Data *data)
346
 
{
347
 
    double status = 0.0, total = 0.0;
348
 
    TNCBI_Time t = (TNCBI_Time) time(0);
349
 
    size_t i = 0;
350
 
 
351
 
    while (i < data->n_node) {
352
 
        SSERV_Info* info = data->s_node[i].info;
353
 
 
354
 
        total += info->rate;
355
 
        if (info->time < t) {
356
 
            if (i < --data->n_node)
357
 
                memmove(data->s_node + i, data->s_node + i + 1,
358
 
                        (data->n_node - i)*sizeof(*data->s_node));
359
 
            free(info);
360
 
        } else {
361
 
            status += info->rate;
362
 
            i++;
363
 
        }
364
 
    }
365
 
    return total != 0.0 ? (status/total < SERV_DISPD_STALE_RATIO_OK) : 1;
366
 
}
367
 
 
368
 
 
369
 
static SSERV_Info* s_GetNextInfo(SERV_ITER iter, char** env)
370
 
{
371
 
    double total = 0.0, point = -1.0, access = 0.0, p = 0.0, status;
372
 
    SDISPD_Data* data = (SDISPD_Data*) iter->data;
373
 
    SSERV_Info* info;
374
 
    size_t i;
375
 
 
376
 
    if (!data)
377
 
        return 0;
378
 
 
379
 
    if (s_IsUpdateNeeded(data) && !s_Resolve(iter))
380
 
        return 0;
381
 
    assert(data->n_node != 0);
382
 
 
383
 
    for (i = 0; i < data->n_node; i++) {
384
 
        info = data->s_node[i].info;
385
 
        status = info->rate;
386
 
        assert(status != 0.0);
387
 
 
388
 
        if (info->host == iter->preferred_host) {
389
 
            if (info->coef <= 0.0) {
390
 
                status *= SERV_DISPD_LOCAL_SVC_BONUS;
391
 
                if (info->coef < 0.0 && access < status) {
392
 
                    access =  status;
393
 
                    point  =  total + status; /* Latch this local server */
394
 
                    p      = -info->coef;
395
 
                }
396
 
            } else
397
 
                status *= info->coef;
398
 
        }
399
 
        total                 += status;
400
 
        data->s_node[i].status = total;
401
 
    }
402
 
 
403
 
    /* We will take pre-chosen local server only if its status is not less
404
 
       than p% of the average rest status; otherwise, we ignore the server,
405
 
       and apply the general procedure by seeding a random point. */
406
 
    if (point < 0.0 || access*(data->n_node - 1) < p*0.01*(total - access))
407
 
        point = (total * rand()) / (double) RAND_MAX;
408
 
    for (i = 0; i < data->n_node; i++) {
409
 
        if (point <= data->s_node[i].status)
410
 
            break;
411
 
    }
412
 
    assert(i < data->n_node);
413
 
 
414
 
    info = data->s_node[i].info;
415
 
    info->rate = data->s_node[i].status - (i ? data->s_node[i-1].status : 0.0);
416
 
    if (i < --data->n_node) {
417
 
        memmove(data->s_node + i, data->s_node + i + 1,
418
 
                (data->n_node - i)*sizeof(*data->s_node));
419
 
    }
420
 
    if (env)
421
 
        *env = 0;
422
 
 
423
 
    return info;
424
 
}
425
 
 
426
 
 
427
 
static void s_Reset(SERV_ITER iter)
428
 
{
429
 
    SDISPD_Data* data = (SDISPD_Data*) iter->data;
430
 
    if (data && data->s_node) {
431
 
        size_t i;
432
 
        assert(data->n_max_node);
433
 
        for (i = 0; i < data->n_node; i++)
434
 
            free(data->s_node[i].info);
435
 
        data->n_node = 0;
436
 
    }
437
 
}
438
 
 
439
 
 
440
 
static void s_Close(SERV_ITER iter)
441
 
{
442
 
    SDISPD_Data* data = (SDISPD_Data*) iter->data;
443
 
    assert(data->n_node == 0); /* s_Reset() had to be called before */
444
 
    if (data->s_node)
445
 
        free(data->s_node);
446
 
    ConnNetInfo_Destroy(data->net_info);
447
 
    free(data);
448
 
    iter->data = 0;
449
 
}
450
 
 
451
 
 
452
 
/***********************************************************************
453
 
 *  EXTERNAL
454
 
 ***********************************************************************/
455
 
 
456
 
const SSERV_VTable* SERV_DISPD_Open(SERV_ITER iter,
457
 
                                    const SConnNetInfo* net_info)
458
 
{
459
 
    SDISPD_Data* data;
460
 
 
461
 
    if (!(data = (SDISPD_Data*) malloc(sizeof(*data))))
462
 
        return 0;
463
 
    if (!s_RandomSeed) {
464
 
        s_RandomSeed = (int)time(0) + (int)SOCK_gethostbyname(0);
465
 
        srand(s_RandomSeed);
466
 
    }
467
 
    data->net_info = ConnNetInfo_Clone(net_info);
468
 
    if (iter->type & fSERV_StatelessOnly)
469
 
        data->net_info->stateless = 1/*true*/;
470
 
    if (iter->type & fSERV_Firewall)
471
 
        data->net_info->firewall = 1/*true*/;
472
 
    data->n_node = data->n_max_node = 0;
473
 
    data->s_node = 0;
474
 
    iter->data = data;
475
 
 
476
 
    iter->op = &s_op; /* SERV_Update() - from HTTP callback - expects this */
477
 
    if (!s_Resolve(iter)) {
478
 
        iter->op = 0;
479
 
        s_Reset(iter);
480
 
        s_Close(iter);
481
 
        return 0;
482
 
    }
483
 
 
484
 
    return &s_op;
485
 
}