New utility yaz_url: fetches HTTP content
[yaz-moved-to-github.git] / src / url.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file url.c
7  * \brief URL fetch utility
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <yaz/url.h>
14 #include <yaz/comstack.h>
15 #include <yaz/log.h>
16
17 struct yaz_url {
18     ODR odr_in;
19     ODR odr_out;
20     char *proxy;
21 };
22
23 yaz_url_t yaz_url_create(void)
24 {
25     yaz_url_t p = xmalloc(sizeof(*p));
26     p->odr_in = odr_createmem(ODR_DECODE);
27     p->odr_out = odr_createmem(ODR_ENCODE);
28     p->proxy = 0;
29     return p;
30 }
31
32 void yaz_url_destroy(yaz_url_t p)
33 {
34     if (p)
35     {
36         odr_destroy(p->odr_in);
37         odr_destroy(p->odr_out);
38         xfree(p->proxy);
39         xfree(p);
40     }
41 }
42
43 void yaz_url_set_proxy(yaz_url_t p, const char *proxy)
44 {
45     xfree(p->proxy);
46     p->proxy = 0;
47     if (proxy && *proxy)
48         p->proxy = xstrdup(proxy);
49 }
50
51 Z_HTTP_Response *yaz_url_exec(yaz_url_t p, const char *uri,
52                               const char *method,
53                               Z_HTTP_Header *headers,
54                               const char *buf, size_t len)
55 {
56     Z_HTTP_Response *res = 0;
57     int number_of_redirects = 0;
58
59     while (1)
60     {
61         void *add;
62         COMSTACK conn = 0;
63         int code;
64         struct Z_HTTP_Header **last_header_entry;
65         const char *location = 0;
66         Z_GDU *gdu = z_get_HTTP_Request_uri(p->odr_out, uri, 0,
67                                             p->proxy ? 1 : 0);
68         gdu->u.HTTP_Request->method = odr_strdup(p->odr_out, method);
69
70         res = 0;
71         last_header_entry = &gdu->u.HTTP_Request->headers;
72         while (*last_header_entry)
73             last_header_entry = &(*last_header_entry)->next;
74         *last_header_entry = headers; /* attach user headers */
75
76         if (buf && len)
77         {
78             gdu->u.HTTP_Request->content_buf = (char *) buf;
79             gdu->u.HTTP_Request->content_len = len;
80         }
81         if (!z_GDU(p->odr_out, &gdu, 0, 0))
82         {
83             yaz_log(YLOG_WARN, "Can not encode HTTP request URL:%s", uri);
84             return 0;
85         }
86         conn = cs_create_host_proxy(uri, 1, &add, p->proxy);
87         if (!conn)
88         {
89             yaz_log(YLOG_WARN, "Bad address for URL:%s", uri);
90         }
91         else if (cs_connect(conn, add) < 0)
92         {
93             yaz_log(YLOG_WARN, "Can not connect to URL:%s", uri);
94         }
95         else
96         {
97             int len;
98             char *buf = odr_getbuf(p->odr_out, &len, 0);
99             
100             if (cs_put(conn, buf, len) < 0)
101                 yaz_log(YLOG_WARN, "cs_put failed URL:%s", uri);
102             else
103             {
104                 char *netbuffer = 0;
105                 int netlen = 0;
106                 int cs_res = cs_get(conn, &netbuffer, &netlen);
107                 if (cs_res <= 0)
108                 {
109                     yaz_log(YLOG_WARN, "cs_get failed URL:%s", uri);
110                 }
111                 else
112                 {
113                     Z_GDU *gdu;
114                     odr_setbuf(p->odr_in, netbuffer, cs_res, 0);
115                     if (!z_GDU(p->odr_in, &gdu, 0, 0)
116                         || gdu->which != Z_GDU_HTTP_Response)
117                     {
118                         yaz_log(YLOG_WARN, "HTTP decoding failed "
119                                 "URL:%s", uri);
120                     }
121                     else
122                     {
123                         res = gdu->u.HTTP_Response;
124                     }
125                 }
126                 xfree(netbuffer);
127             }
128         }
129         if (conn)
130             cs_close(conn);
131         if (!res)
132             break;
133         code = res->code;
134         location = z_HTTP_header_lookup(res->headers, "Location");
135         if (++number_of_redirects < 10 &&
136             location && (code == 301 || code == 302 || code == 307))
137         {
138             odr_reset(p->odr_out);
139             uri = odr_strdup(p->odr_out, location);
140             odr_reset(p->odr_in);
141         }
142         else
143             break;
144     }
145     return res;
146 }
147
148 /*
149  * Local variables:
150  * c-basic-offset: 4
151  * c-file-style: "Stroustrup"
152  * indent-tabs-mode: nil
153  * End:
154  * vim: shiftwidth=4 tabstop=8 expandtab
155  */
156