mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-21 03:13:42 +08:00
793ecc8e60
re: https://github.com/Unidata/netcdf-c/issues/1876 and: https://github.com/Unidata/netcdf-c/pull/1835 and: https://github.com/Unidata/netcdf4-python/issues/1041 The change in PR 1835 was correct with respect to using %20 instead of '+' for encoding blanks. However, it was a mistake to assume everything was unencoded and then to do encoding ourselves. The problem is that different servers do different things, with Columbia being an outlier. So, I have added a set of client controls that can at least give the caller some control over this. The caller can append the following fragment to his URL to control what gets encoded before sending it to the server. The syntax is as follows: ```` https://<host>/<path>/<query>#encode=path|query|all|none ```` The possible values: * path -- URL encode (i.e. %xx encode) as needed in the path part of the URL. * query -- URL encode as needed in the query part of the URL. * all -- equivalent to ````#encode=path,query````. * none -- do not url encode any part of the URL sent to the server; not strictly necessary, so mostly for completeness. Note that if "encode=" is used, then before it is processed, all encoding is turned of so that ````#encode=path```` will only encode the path and not the query. The default is ````#encode=query````, so the path is left untouched, but the query is always encoded. Internally, this required changes to pass the encode flags down into the OC2 library. Misc. Unrelated Changes: * Shut up those irritating warning from putget.m4
224 lines
6.0 KiB
C
224 lines
6.0 KiB
C
/* Copyright 2018, UCAR/Unidata and OPeNDAP, Inc.
|
|
See the COPYRIGHT file for more information. */
|
|
|
|
#include "config.h"
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef HAVE_STDINT_H
|
|
#include <stdint.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#ifdef _MSC_VER
|
|
#include <io.h>
|
|
#ifndef O_BINARY
|
|
#define O_BINARY _O_BINARY
|
|
#endif
|
|
#endif
|
|
#include "ncrc.h"
|
|
#include "ocinternal.h"
|
|
#include "ocdebug.h"
|
|
#include "ochttp.h"
|
|
#include "ocread.h"
|
|
#include "occurlfunctions.h"
|
|
#include "ncpathmgr.h"
|
|
|
|
/*Forward*/
|
|
static int readpacket(OCstate* state, NCURI*, NCbytes*, OCdxd, OCflags, long*);
|
|
static int readfile(const char* path, const char* suffix, NCbytes* packet);
|
|
static int readfiletofile(const char* path, const char* suffix, FILE* stream, off_t*);
|
|
|
|
int
|
|
readDDS(OCstate* state, OCtree* tree, OCflags flags)
|
|
{
|
|
int stat = OC_NOERR;
|
|
long lastmodified = -1;
|
|
|
|
ncurisetquery(state->uri,tree->constraint);
|
|
|
|
#ifdef OCDEBUG
|
|
fprintf(stderr,"readDDS:\n");
|
|
#endif
|
|
stat = readpacket(state,state->uri,state->packet,OCDDS, flags,
|
|
&lastmodified);
|
|
if(stat == OC_NOERR) state->ddslastmodified = lastmodified;
|
|
|
|
return stat;
|
|
}
|
|
|
|
int
|
|
readDAS(OCstate* state, OCtree* tree, OCflags flags)
|
|
{
|
|
int stat = OC_NOERR;
|
|
|
|
ncurisetquery(state->uri,tree->constraint);
|
|
#ifdef OCDEBUG
|
|
fprintf(stderr,"readDAS:\n");
|
|
#endif
|
|
stat = readpacket(state,state->uri,state->packet,OCDAS,flags,NULL);
|
|
|
|
return stat;
|
|
}
|
|
|
|
#if 0
|
|
int
|
|
readversion(OCstate* state, NCURI* url, NCbytes* packet)
|
|
{
|
|
return readpacket(state,url,packet,OCVER,NULL);
|
|
}
|
|
#endif
|
|
|
|
const char*
|
|
ocdxdextension(OCdxd dxd)
|
|
{
|
|
switch(dxd) {
|
|
case OCDDS: return ".dds";
|
|
case OCDAS: return ".das";
|
|
case OCDATADDS: return ".dods";
|
|
default: break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
readpacket(OCstate* state, NCURI* url, NCbytes* packet, OCdxd dxd, OCflags ocflags, long* lastmodified)
|
|
{
|
|
int stat = OC_NOERR;
|
|
int fileprotocol = 0;
|
|
const char* suffix = ocdxdextension(dxd);
|
|
char* fetchurl = NULL;
|
|
CURL* curl = state->curl;
|
|
|
|
fileprotocol = (strcmp(url->protocol,"file")==0);
|
|
|
|
if(fileprotocol) {
|
|
/* Short circuit file://... urls and read directly */
|
|
fetchurl = ncuribuild(url,NULL,NULL,NCURIBASE);
|
|
stat = readfile(fetchurl,suffix,packet);
|
|
} else {
|
|
int flags = NCURIBASE;
|
|
if(ocflags & OCENCODEPATH)flags |= NCURIENCODEPATH;
|
|
if(ocflags & OCENCODEQUERY) flags |= NCURIENCODEQUERY;
|
|
if(!fileprotocol) flags |= NCURIQUERY;
|
|
fetchurl = ncuribuild(url,NULL,suffix,flags);
|
|
MEMCHECK(fetchurl,OC_ENOMEM);
|
|
if(ocdebug > 0)
|
|
{fprintf(stderr,"fetch url=%s\n",fetchurl); fflush(stderr);}
|
|
stat = ocfetchurl(curl,fetchurl,packet,lastmodified);
|
|
if(stat)
|
|
oc_curl_printerror(state);
|
|
if(ocdebug > 0)
|
|
{fprintf(stderr,"fetch complete\n"); fflush(stderr);}
|
|
}
|
|
free(fetchurl);
|
|
#ifdef OCDEBUG
|
|
{
|
|
fprintf(stderr,"readpacket: packet.size=%lu\n",
|
|
(unsigned long)ncbyteslength(packet));
|
|
}
|
|
#endif
|
|
return OCTHROW(stat);
|
|
}
|
|
|
|
int
|
|
readDATADDS(OCstate* state, OCtree* tree, OCflags ocflags)
|
|
{
|
|
int stat = OC_NOERR;
|
|
long lastmod = -1;
|
|
|
|
#ifdef OCDEBUG
|
|
fprintf(stderr,"readDATADDS:\n");
|
|
#endif
|
|
if((ocflags & OCONDISK) == 0) {
|
|
ncurisetquery(state->uri,tree->constraint);
|
|
stat = readpacket(state,state->uri,state->packet,OCDATADDS,ocflags,&lastmod);
|
|
if(stat == OC_NOERR)
|
|
state->datalastmodified = lastmod;
|
|
tree->data.datasize = ncbyteslength(state->packet);
|
|
} else { /*((flags & OCONDISK) != 0) */
|
|
NCURI* url = state->uri;
|
|
int fileprotocol = 0;
|
|
char* readurl = NULL;
|
|
|
|
fileprotocol = (strcmp(url->protocol,"file")==0);
|
|
|
|
if(fileprotocol) {
|
|
readurl = ncuribuild(url,NULL,NULL,NCURIBASE);
|
|
stat = readfiletofile(readurl, ".dods", tree->data.file, &tree->data.datasize);
|
|
} else {
|
|
int flags = NCURIBASE;
|
|
if(ocflags & OCENCODEPATH)
|
|
flags |= NCURIENCODEPATH;
|
|
if(ocflags & OCENCODEQUERY)
|
|
flags |= NCURIENCODEQUERY;
|
|
if(!fileprotocol) flags |= NCURIQUERY;
|
|
ncurisetquery(url,tree->constraint);
|
|
readurl = ncuribuild(url,NULL,".dods",flags);
|
|
MEMCHECK(readurl,OC_ENOMEM);
|
|
if (ocdebug > 0)
|
|
{fprintf(stderr, "fetch url=%s\n", readurl);fflush(stderr);}
|
|
stat = ocfetchurl_file(state->curl, readurl, tree->data.file,
|
|
&tree->data.datasize, &lastmod);
|
|
if(stat == OC_NOERR)
|
|
state->datalastmodified = lastmod;
|
|
if (ocdebug > 0)
|
|
{fprintf(stderr,"fetch complete\n"); fflush(stderr);}
|
|
}
|
|
free(readurl);
|
|
}
|
|
return OCTHROW(stat);
|
|
}
|
|
|
|
static int
|
|
readfiletofile(const char* path, const char* suffix, FILE* stream, off_t* sizep)
|
|
{
|
|
int stat = OC_NOERR;
|
|
NCbytes* packet = ncbytesnew();
|
|
size_t len;
|
|
/* check for leading file:/// */
|
|
if(ocstrncmp(path,"file:///",8)==0) path += 7; /* assume absolute path*/
|
|
stat = readfile(path,suffix,packet);
|
|
#ifdef OCDEBUG
|
|
fprintf(stderr,"readfiletofile: packet.size=%lu\n",
|
|
(unsigned long)ncbyteslength(packet));
|
|
#endif
|
|
if(stat != OC_NOERR) goto unwind;
|
|
len = nclistlength(packet);
|
|
if(stat == OC_NOERR) {
|
|
size_t written;
|
|
fseek(stream,0,SEEK_SET);
|
|
written = fwrite(ncbytescontents(packet),1,len,stream);
|
|
if(written != len) {
|
|
#ifdef OCDEBUG
|
|
fprintf(stderr,"readfiletofile: written!=length: %lu :: %lu\n",
|
|
(unsigned long)written,(unsigned long)len);
|
|
#endif
|
|
stat = OC_EIO;
|
|
}
|
|
}
|
|
if(sizep != NULL) *sizep = len;
|
|
unwind:
|
|
ncbytesfree(packet);
|
|
return OCTHROW(stat);
|
|
}
|
|
|
|
static int
|
|
readfile(const char* path, const char* suffix, NCbytes* packet)
|
|
{
|
|
int stat = OC_NOERR;
|
|
char filename[1024];
|
|
/* check for leading file:/// */
|
|
if(ocstrncmp(path,"file://",7)==0) path += 7; /* assume absolute path*/
|
|
if(!occopycat(filename,sizeof(filename),2,path,(suffix != NULL ? suffix : "")))
|
|
return OCTHROW(OC_EOVERRUN);
|
|
stat = NC_readfile(filename,packet);
|
|
return OCTHROW(stat);
|
|
}
|
|
|
|
|