1 /*! \file
2
3 Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
4 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
5 2015, 2016, 2017, 2018
6 University Corporation for Atmospheric Research/Unidata.
7
8 See \ref copyright file for more info.
9
10 */
11
12 #ifndef NCTESTSERVER_H
13 #define NCTESTSERVER_H 1
14
15 #include "config.h"
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <curl/curl.h>
20 #include "netcdf.h"
21
22 #undef FINDTESTSERVER_DEBUG
23
24 enum KIND {NOKIND, DAP2KIND, DAP4KIND, THREDDSKIND};
25
26 #define MAXSERVERURL 4096
27 #define TIMEOUT 10 /*seconds*/
28 #define BUFSIZE 8192 /*bytes*/
29 #define MAXREMOTETESTSERVERS 4096
30
31 #ifndef HAVE_CURLINFO_RESPONSE_CODE
32 #define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE
33 #endif
34
35 static int ping(const char* url);
36 static int timedping(const char* url, long timeout);
37
38 static char**
parseServers(const char * remotetestservers)39 parseServers(const char* remotetestservers)
40 {
41 char* rts;
42 char** servers = NULL;
43 char** list = NULL;
44 char* p;
45 char* svc;
46 char** l;
47 size_t rtslen = strlen(remotetestservers);
48
49 /* Keep LGTM quiet */
50 if(rtslen > MAXREMOTETESTSERVERS) goto done;
51 list = (char**)malloc(sizeof(char*) * (int)(rtslen/2));
52 if(list == NULL) return NULL;
53 rts = strdup(remotetestservers);
54 if(rts == NULL) goto done;
55 l = list;
56 p = rts;
57 for(;;) {
58 svc = p;
59 p = strchr(svc,',');
60 if(p != NULL) *p = '\0';
61 *l++ = strdup(svc);
62 if(p == NULL) break;
63 p++;
64 }
65 *l = NULL;
66 servers = list;
67 list = NULL;
68 done:
69 if(rts) free(rts);
70 if(list) free(list);
71 return servers;
72 }
73
74 /**
75 Given a partial suffix path and a specified
76 protocol, test if a request to any of the test
77 servers + path returns some kind of result.
78 This indicates that the server is up and running.
79 Return the complete url for the server plus the path.
80 */
81
82 char*
nc_findtestserver(const char * path,const char * serverlist)83 nc_findtestserver(const char* path, const char* serverlist)
84 {
85 char** svclist;
86 char** svc;
87 char url[MAXSERVERURL];
88 char* match = NULL;
89 int reportsearch;
90
91 if((svclist = parseServers(serverlist))==NULL) {
92 fprintf(stderr,"cannot parse test server list: %s\n",serverlist);
93 return NULL;
94 }
95 reportsearch = (getenv("NC_REPORTSEARCH") != NULL);
96 for(svc=svclist;*svc;svc++) {
97 if(strlen(*svc) == 0)
98 goto done;
99 if(path == NULL) path = "";
100 if(strlen(path) > 0 && path[0] == '/')
101 path++;
102 if(reportsearch)
103 fprintf(stderr,"nc_findtestserver: candidate=%s/%s: found=",*svc,path);
104 /* Try https: first */
105 snprintf(url,MAXSERVERURL,"https://%s/%s",*svc,path);
106 if(ping(url) == NC_NOERR) {
107 if(reportsearch) fprintf(stderr,"yes\n");
108 match = strdup(url);
109 goto done;
110 }
111 /* Try http: next */
112 snprintf(url,MAXSERVERURL,"http://%s/%s",*svc,path);
113 if(ping(url) == NC_NOERR) {
114 if(reportsearch) fprintf(stderr,"yes\n");
115 match = strdup(url);
116 goto done;
117 }
118 if(reportsearch) fprintf(stderr,"no\n");
119 }
120 done:
121 if(reportsearch) fflush(stderr);
122 /* Free up the envv list of servers */
123 if(svclist != NULL) {
124 char** p;
125 for(p=svclist;*p;p++)
126 free(*p);
127 free(svclist);
128 }
129 return match;
130 }
131
132 #define CERR(expr) if((cstat=(expr)) != CURLE_OK) goto done;
133
134 struct Buffer {
135 char data[BUFSIZE];
136 size_t offset; /* into buffer */
137 };
138
139 static size_t
WriteMemoryCallback(void * ptr,size_t size,size_t nmemb,void * data)140 WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
141 {
142 struct Buffer* buffer = (struct Buffer*)data;
143 size_t total = size * nmemb;
144 size_t canwrite = total; /* assume so */
145 if(total == 0) {
146 fprintf(stderr,"WriteMemoryCallback: zero sized chunk\n");
147 goto done;
148 }
149 if((buffer->offset + total) > sizeof(buffer->data))
150 canwrite = (sizeof(buffer->data) - buffer->offset); /* partial read */
151 if(canwrite > 0)
152 memcpy(&(buffer->data[buffer->offset]),ptr,canwrite);
153 buffer->offset += canwrite;
154 done:
155 return total; /* pretend we captured everything */
156 }
157
158 /*
159 See if a server is responding.
160 Return NC_ECURL if the ping fails, NC_NOERR otherwise
161 */
162
163 static int
ping(const char * url)164 ping(const char* url)
165 {
166 return timedping(url,TIMEOUT);
167 }
168
169 static int
timedping(const char * url,long timeout)170 timedping(const char* url, long timeout)
171 {
172 int stat = NC_NOERR;
173 CURLcode cstat = CURLE_OK;
174 CURL* curl = NULL;
175 long http_code = 0;
176 struct Buffer data;
177
178 /* Create a CURL instance */
179 curl = curl_easy_init();
180 if (curl == NULL) {cstat = CURLE_OUT_OF_MEMORY; goto done;}
181 CERR((curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1)));
182
183 /* Use redirects */
184 CERR((curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L)));
185 CERR((curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L)));
186
187 /* use very short timeouts: 10 seconds */
188 CERR((curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, (long)timeout)));
189 CERR((curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout)));
190
191 /* fail on HTTP 400 code errors */
192 CERR((curl_easy_setopt(curl, CURLOPT_FAILONERROR, (long)1)));
193
194 /* Set the URL */
195 CERR((curl_easy_setopt(curl, CURLOPT_URL, (void*)url)));
196
197 /* send all data to this function */
198 CERR((curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback)));
199
200 /* we pass our file to the callback function */
201 CERR((curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&data)));
202
203 data.offset = 0;
204 memset(data.data,0,sizeof(data.data));
205
206 CERR((curl_easy_perform(curl)));
207
208 /* Don't trust curl to return an error when request gets 404 */
209 CERR((curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &http_code)));
210 if(http_code >= 400) {
211 cstat = CURLE_HTTP_RETURNED_ERROR;
212 goto done;
213 }
214
215 done:
216 if(cstat != CURLE_OK) {
217 #ifdef FINDTESTSERVER_DEBUG
218 fprintf(stderr, "curl error: %s; url=%s\n",
219 curl_easy_strerror(cstat),url);
220 #endif
221 stat = NC_ECURL;
222 }
223 if (curl != NULL)
224 curl_easy_cleanup(curl);
225 return stat;
226 }
227 #endif /*NCTESTSERVER_H*/
228