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