/****************************************************************************** ** @source AJAX HTTP (database) functions ** ** These functions control all aspects of AJAX http access ** via SEND/GET/POST protocols ** ** @author Copyright (C) 2010 Alan Bleasby ** @version 3.0 ** @@ ** ** This library is free software; you can redistribute it and/or ** modify it under the terms of the GNU Library General Public ** License as published by the Free Software Foundation; either ** version 2 of the License, or (at your option) any later version. ** ** This library is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** Library General Public License for more details. ** ** You should have received a copy of the GNU Library General Public ** License along with this library; if not, write to the ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, ** Boston, MA 02111-1307, USA. ******************************************************************************/ #include "ajax.h" #include #include #include #ifndef WIN32 #include #include #include #include #include #include #else #include #include #endif #include #include static FILE* httpSend(const AjPStr dbname, struct AJSOCKET sock, const AjPStr host, ajint iport, const AjPStr proxyauth, const AjPStr proxycreds, const AjPStr get); /* @func ajHttpGetProxyinfo *************************************************** ** ** Returns a proxy definition (if any). Any proxy string given as an ** argument overrides any EMBOSS_PROXY definition. This ** allows strings from DB definitions to override such an envvar. ** ** @param [r] dbproxy [const AjPStr] Primary proxy string (if any) ** @param [w] proxyport [ajint*] Proxy port ** @param [w] proxyname [AjPStr*] Proxy name ** @param [w] proxyauth [AjPStr*] Proxy authentication type (if any) ** @param [w] proxycreds [AjPStr*] Proxy auth credentials (if any) ** @return [AjBool] ajTrue if a proxy was defined ** @@ ******************************************************************************/ AjBool ajHttpGetProxyinfo(const AjPStr dbproxy, ajint* proxyport, AjPStr* proxyname, AjPStr* proxyauth, AjPStr* proxycreds) { AjPStr proxy = NULL; AjPStr portStr = NULL; const char *p = NULL; const char *q = NULL; AjPStrTok handle = NULL; AjPStr serv = NULL; AjPStr token = NULL; AjBool ret = ajFalse; ajStrDel(proxyname); *proxyport = 0; ajStrDel(proxyauth); ajStrDel(proxycreds); ajNamGetValueC("proxy", &proxy); if(ajStrGetLen(dbproxy)) ajStrAssignS(&proxy, dbproxy); token = ajStrNew(); ajStrTokenAssignC(&handle, proxy, " \n\r\t,"); ret = ajStrTokenNextParse(&handle, &token); if(!ret || ajStrMatchC(token, ":")) { ajStrDel(&proxy); ajStrDel(&token); ajStrTokenDel(&handle); return ajFalse; } serv = ajStrNew(); portStr = ajStrNew(); ajStrAssignS(&serv, token); if(ajStrMatchC(serv, ":")) ajStrAssignClear(&serv); p = ajStrGetPtr(serv); if((q = strchr(p, (int)':')) && p != q) { ajStrAssignSubC(proxyname,p,0,q-p-1); ajStrAssignC(&portStr,q+1); } if(!ajStrGetLen(*proxyname) || !ajStrGetLen(portStr)) { ajStrTokenDel(&handle); ajStrDel(&token); ajStrDel(&serv); ajStrDel(&proxy); ajStrDel(&portStr); return ajFalse; } ajStrToInt(portStr, proxyport); ajStrDel(&portStr); ajStrDel(&serv); /* Check for authentication type */ ret = ajStrTokenNextParse(&handle, &token); if(!ret) { ajStrTokenDel(&handle); ajStrDel(&token); ajStrDel(&proxy); return ajTrue; } ajStrAssignS(proxyauth, token); /* Check for authentication credentials */ ret = ajStrTokenNextParse(&handle, &token); if(!ret) { ajWarn("ajHttpGetProxyinfo: No credentials specified in proxy " "definition (%S)",proxy); ajStrTokenDel(&handle); ajStrDel(&token); ajStrDel(&proxy); return ajFalse; } ajStrAssignS(proxycreds,token); ajStrTokenDel(&handle); ajStrDel(&token); ajStrDel(&proxy); return ajTrue; } /* @func ajHttpGetVersion **************************************************** ** ** Returns the HTTP version. Any supplied version takes precedence over ** an EMBOSS_HTTPVERSION definition so allowing DB entries to ** override such a setting. ** ** @param [r] version [const AjPStr] Version or NULL (or zero-length string) ** @param [w] httpver [AjPStr*] HTTP version ** @return [AjBool] ajTrue if a version was defined ** @@ ******************************************************************************/ AjBool ajHttpGetVersion(const AjPStr version, AjPStr* httpver) { ajNamGetValueC("httpversion", httpver); ajDebug("httpver getValueC '%S'\n", *httpver); if(ajStrGetLen(version)) ajStrAssignS(httpver, version); ajDebug("httpver after qry '%S'\n", *httpver); if(!ajStrGetLen(*httpver)) { ajStrAssignC(httpver, "1.1"); return ajFalse; } if(!ajStrIsFloat(*httpver)) { ajWarn("Invalid HTTPVERSION '%S', reset to 1.1", *httpver); ajStrAssignC(httpver, "1.1"); return ajFalse; } ajDebug("httpver final '%S'\n", *httpver); return ajTrue; } /* @func ajHttpOpen *********************************************************** ** ** Opens an HTTP connection ** ** @param [r] dbname [const AjPStr] Database name (for error reporting) ** @param [r] host [const AjPStr] Host name ** @param [r] iport [ajint] Port ** @param [r] get [const AjPStr] GET string ** @param [u] Psock [struct AJSOCKET*] Socket returned to caller ** @return [FILE*] Open file on success, NULL on failure ** @@ ******************************************************************************/ FILE* ajHttpOpen(const AjPStr dbname, const AjPStr host, ajint iport, const AjPStr get, struct AJSOCKET *Psock) { FILE* fp; struct addrinfo hints; struct addrinfo *add = NULL; struct addrinfo *addinit = NULL; AjPStr portstr = NULL; const char *phost = NULL; const char *pport = NULL; struct AJSOCKET sock = *Psock; AjPStr errstr = NULL; int ret; phost = ajStrGetPtr(host); ajDebug("ajHttpOpen db: '%S' host '%S' port: %u get: '%S'\n", dbname, host, iport, get); memset(&hints,0,sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; portstr = ajStrNew(); ajFmtPrintS(&portstr,"%d",iport); pport = ajStrGetPtr(portstr); ret = getaddrinfo(phost, pport, &hints, &addinit); ajStrDel(&portstr); if(ret) { ajErr("[%s] Failed to find host '%S' for database '%S'", gai_strerror(ret), host, dbname); return NULL; } sock.sock = AJBADSOCK; for(add = addinit; add; add = add->ai_next) { sock.sock = ajSysFuncSocket(add->ai_family, add->ai_socktype, add->ai_protocol); if(sock.sock == AJBADSOCK) continue; if(connect(sock.sock, add->ai_addr, add->ai_addrlen)) { ajSysSocketclose(sock); sock.sock = AJBADSOCK; continue; } break; } freeaddrinfo(addinit); if(sock.sock == AJBADSOCK) { ajDebug("Socket connect failed\n"); ajFmtPrintS(&errstr, "socket connect failed for database '%S'", dbname); ajErr("%S", errstr); perror(ajStrGetPtr(errstr)); ajStrDel(&errstr); return NULL; } fp = httpSend(dbname, sock, host, iport, NULL, NULL, get); return fp; } /* @func ajHttpOpenProxy *************************************************** ** ** Opens an HTTP connection via a proxy ** ** @param [r] dbname [const AjPStr] Databse name (for error reporting) ** @param [r] proxyname [const AjPStr] Proxy name ** @param [r] proxyport [ajint] Proxy port ** @param [r] proxyauth [const AjPStr] Proxy auth type (if any) ** @param [r] proxycreds [const AjPStr] Proxy auth credentials (if any) ** @param [r] host [const AjPStr] Host name ** @param [r] iport [ajint] Port ** @param [r] get [const AjPStr] GET string ** @param [u] Psock [struct AJSOCKET*] Socket returned to caller ** @return [FILE*] Open file on success, NULL on failure ** @@ ******************************************************************************/ FILE* ajHttpOpenProxy(const AjPStr dbname, const AjPStr proxyname, ajint proxyport, const AjPStr proxyauth, const AjPStr proxycreds, const AjPStr host, ajint iport, const AjPStr get, struct AJSOCKET *Psock) { FILE* fp; struct addrinfo hints; struct addrinfo *add; struct addrinfo *addinit; AjPStr portstr = NULL; const char *phost = NULL; const char *pport = NULL; struct AJSOCKET sock = *Psock; AjPStr errstr = NULL; int ret; phost = ajStrGetPtr(proxyname); ajDebug("ajHttpOpenProxy db: '%S' host '%s' get: '%S'\n", dbname, phost, get); memset(&hints,0,sizeof(hints)); hints.ai_socktype = SOCK_STREAM; portstr = ajStrNew(); ajFmtPrintS(&portstr,"%d",proxyport); pport = ajStrGetPtr(portstr); ret = getaddrinfo(phost, pport, &hints, &addinit); if(ret) { ajErr("[%s] Failed to find host '%S' for service '%S'", gai_strerror(ret), proxyname, portstr); ajStrDel(&portstr); return NULL; } ajStrDel(&portstr); sock.sock = AJBADSOCK; for(add = addinit; add; add = add->ai_next) { sock.sock = ajSysFuncSocket(add->ai_family, add->ai_socktype, add->ai_protocol); if(sock.sock == AJBADSOCK) continue; if(connect(sock.sock, add->ai_addr, add->ai_addrlen)) { ajSysSocketclose(sock); sock.sock = AJBADSOCK; continue; } break; } freeaddrinfo(addinit); if(sock.sock == AJBADSOCK) { ajDebug("Socket connect failed\n"); ajFmtPrintS(&errstr, "socket connect failed for database '%S'", dbname); ajErr("%S", errstr); perror(ajStrGetPtr(errstr)); ajStrDel(&errstr); return NULL; } fp = httpSend(dbname, sock, host, iport, proxyauth, proxycreds, get); return fp; } /* @funcstatic httpSend ******************************************************* ** ** Send HTTP GET request to an open socket ** ** @param [r] dbname [const AjPStr] Database name (for error reporting) ** @param [u] sock [struct AJSOCKET] Socket structure ** @param [r] host [const AjPStr] Host name for Host header line ** @param [r] iport [ajint] Port for Host header line ** @param [r] proxyauth [const AjPStr] Proxy auth type (if any) ** @param [r] proxycreds [const AjPStr] Proxy auth credentials (if any) ** @param [r] get [const AjPStr] GET string ** @return [FILE*] Open file on success, NULL on failure ** @@ ******************************************************************************/ static FILE* httpSend(const AjPStr dbname, struct AJSOCKET sock, const AjPStr host, ajint iport, const AjPStr proxyauth, const AjPStr proxycreds, const AjPStr get) { FILE* fp = NULL; AjPStr gethead = NULL; AjPStr cred = NULL; ajuint isendlen; ajDebug("httpSend: Sending to socket\n"); gethead = ajStrNew(); isendlen = send(sock.sock, ajStrGetPtr(get), ajStrGetLen(get), 0); if(isendlen != ajStrGetLen(get)) ajErr("send failure, expected %d bytes returned %d : %s\n", ajStrGetLen(get), isendlen, ajMessGetSysmessageC()); ajDebug("sending: '%S'\n", get); ajDebug("send for GET errno %d msg '%s'\n", errno, ajMessGetSysmessageC()); if(ajStrGetLen(proxyauth)) { if(!ajStrMatchCaseC(proxyauth,"Basic")) ajErr("Only 'Basic' proxy authentication currently implemented,\n" "no 'Digest' or 'NTLM' [%S]",proxyauth); cred = ajStrNew(); ajUtilBase64EncodeC(&cred, ajStrGetLen(proxycreds), (const unsigned char *) ajStrGetPtr(proxycreds)); ajFmtPrintS(&gethead,"Proxy-Authorization: Basic %S\r\n",cred); isendlen = send(sock.sock, ajStrGetPtr(gethead), ajStrGetLen(gethead), 0); if(isendlen != ajStrGetLen(gethead)) ajErr("send failure, expected %d bytes returned %d : %s\n", ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC()); ajDebug("sending: '%S'\n", gethead); ajDebug("send for host errno %d msg '%s'\n", errno, ajMessGetSysmessageC()); ajStrDel(&cred); } ajFmtPrintS(&gethead, "User-Agent: EMBOSS/%S (%S)\r\n", ajNamValueVersion(), ajNamValueSystem()); isendlen = send(sock.sock, ajStrGetPtr(gethead), ajStrGetLen(gethead), 0); if(isendlen != ajStrGetLen(gethead)) ajErr("send failure, expected %d bytes returned %d : %s\n", ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC()); ajDebug("sending: '%S'\n", gethead); ajFmtPrintS(&gethead, "Host: %S:%d\r\n", host, iport); isendlen = send(sock.sock, ajStrGetPtr(gethead), ajStrGetLen(gethead), 0); if(isendlen != ajStrGetLen(gethead)) ajErr("send failure, expected %d bytes returned %d : %s\n", ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC()); ajDebug("sending: '%S'\n", gethead); ajDebug("send for host errno %d msg '%s'\n", errno, ajMessGetSysmessageC()); if(ajStrFindC(get,"HTTP/1.1") != -1) { ajFmtPrintS(&gethead, "Connection: close\r\n"); isendlen = send(sock.sock, ajStrGetPtr(gethead),ajStrGetLen(gethead), 0); if(isendlen != ajStrGetLen(gethead)) ajErr("send failure, expected %d bytes returned %d : %s\n", ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC()); ajDebug("sending: '%S'\n", gethead); ajDebug("send for host errno %d msg '%s'\n", errno, ajMessGetSysmessageC()); } ajFmtPrintS(&gethead, "\r\n"); isendlen = send(sock.sock, ajStrGetPtr(gethead), ajStrGetLen(gethead), 0); if(isendlen != ajStrGetLen(gethead)) ajErr("send failure, expected %d bytes returned %d : %s\n", ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC()); ajDebug("sending: '%S'\n", gethead); ajDebug("send for blankline errno %d msg '%s'\n", errno, ajMessGetSysmessageC()); ajStrDel(&gethead); fp = ajSysFdFromSocket(sock, "r"); if(!fp) { ajDebug("httpSend socket open failed\n"); ajErr("httpSend: socket open failed for database '%S'", dbname); return NULL; } return fp; } /* @func ajHttpUrlrefNew ****************************************************** ** ** Initialise a URL components object ** ** @return [AjPUrlref] URL Components ******************************************************************************/ AjPUrlref ajHttpUrlrefNew(void) { AjPUrlref ret = NULL; AJNEW0(ret); ret->Method = ajStrNew(); ret->Host = ajStrNew(); ret->Port = ajStrNew(); ret->Absolute = ajStrNew(); ret->Relative = ajStrNew(); ret->Fragment = ajStrNew(); ret->Username = ajStrNew(); ret->Password = ajStrNew(); return ret; } /* @func ajHttpUrlrefDel ****************************************************** ** ** Delete URL components object ** ** @param [u] thys [AjPUrlref*] URL components object ** @return [void] ******************************************************************************/ void ajHttpUrlrefDel(AjPUrlref *thys) { AjPUrlref pthis = NULL; if(!thys) return; if(!*thys) return; pthis = *thys; ajStrDel(&pthis->Method); ajStrDel(&pthis->Host); ajStrDel(&pthis->Port); ajStrDel(&pthis->Absolute); ajStrDel(&pthis->Relative); ajStrDel(&pthis->Fragment); ajStrDel(&pthis->Username); ajStrDel(&pthis->Password); AJFREE(pthis); *thys = NULL; return; } /* @func ajHttpUrlrefParseC *************************************************** ** ** Parse an IPV4/6 URL into its components ** ** @param [u] parts [AjPUrlref*] URL components object ** @param [u] url [const char*] URL ** @return [void] ******************************************************************************/ void ajHttpUrlrefParseC(AjPUrlref *parts, const char *url) { char *ucopy = NULL; char *p = NULL; char *post = NULL; char *dest = NULL; char *src = NULL; AjPUrlref comp = NULL; char *pmethod = NULL; char *phost = NULL; char *pabs = NULL; char *prel = NULL; if(!parts || !url) return; if(!*parts) return; ucopy = ajCharNewC(url); post = ucopy; comp = *parts; /* Get any fragment */ if ((p = strchr(ucopy, '#'))) { *p++ = '\0'; ajStrAssignC(&comp->Fragment,p); } if((p = strchr(ucopy, ' '))) *p++ = '\0'; for(p = ucopy; *p; ++p) { if (isspace((int) *p)) { dest = p; src = p+1; while ((*dest++ = *src++)); p = p-1; } if (*p == '/' || *p == '#' || *p == '?') break; if (*p == ':') { *p = '\0'; pmethod = post; post = p+1; if(ajCharPrefixCaseC(pmethod,"URL")) pmethod = NULL; else break; } } p = post; if(*p == '/') { if(p[1] == '/') { phost = p+2; /* There is a host */ *p = '\0'; p = strchr(phost,'/'); /* Find end of host */ if(p) { *p=0; /* and terminate it */ pabs = p+1; /* Path found */ } } else pabs = p+1; /* Path found but not host */ } else prel = (*post) ? post : NULL; if(pmethod) ajStrAssignC(&comp->Method,pmethod); if(phost) ajStrAssignC(&comp->Host,phost); if(pabs) ajStrAssignC(&comp->Absolute,pabs); if(prel) ajStrAssignC(&comp->Relative,prel); AJFREE(ucopy); return; } /* @func ajHttpUrlrefSplitPort ************************************************ ** ** Separate any port from a host specification (IPV4/6) ** ** @param [u] urli [AjPUrlref] URL components object ** @return [void] ******************************************************************************/ void ajHttpUrlrefSplitPort(AjPUrlref urli) { const char *p = NULL; const char *end = NULL; const char *start = NULL; ajint len; if(!(len = ajStrGetLen(urli->Host))) return; end = (start = ajStrGetPtr(urli->Host)) + len - 1; p = end; if(!isdigit((int) *p)) return; while(isdigit((int) *p) && p != start) --p; if(p == start) return; if(*p != ':') return; ajStrAssignC(&urli->Port,p+1); ajStrAssignSubC(&urli->Host,start,0,p-start-1); return; } /* @func ajHttpUrlrefSplitUsername ********************************************* ** ** Separate any username[:password] from a host specification (IPV4/6) ** ** @param [u] urli [AjPUrlref] URL components object ** @return [void] ******************************************************************************/ void ajHttpUrlrefSplitUsername(AjPUrlref urli) { const char *p = NULL; const char *end = NULL; AjPStr userpass = NULL; AjPStr host = NULL; ajint len; if(!ajStrGetLen(urli->Host)) return; if(!(end = strchr(ajStrGetPtr(urli->Host), (int)'@'))) return; p = ajStrGetPtr(urli->Host); len = end - p; if(!len) return; userpass = ajStrNew(); ajStrAssignSubC(&userpass, p, 0, end - p - 1); host = ajStrNew(); ajStrAssignC(&host,end + 1); ajStrAssignS(&urli->Host,host); if(!(end = strchr(ajStrGetPtr(userpass), (int)':'))) { ajStrAssignS(&urli->Username,userpass); ajStrDel(&userpass); ajStrDel(&host); return; } p = ajStrGetPtr(userpass); len = end - p; if(!len) ajWarn("ajHttpUrlrefSplitUsername: Missing username in URL [%S@%S]", userpass,host); else ajStrAssignSubC(&urli->Username,p,0,len - 1); ajStrAssignC(&urli->Password, end + 1); ajStrDel(&userpass); ajStrDel(&host); return; } /* @func ajHttpQueryUrl ******************************************************* ** ** Returns the components of a URL (IPV4/6) ** An equivalent for seqHttpUrl(). ** ** @param [r] qry [const AjPQuery] Database query ** @param [w] iport [ajint*] Port ** @param [w] host [AjPStr*] Host name ** @param [w] urlget [AjPStr*] URL for the HTTP header GET ** @return [AjBool] ajTrue if the URL was parsed ** @@ ******************************************************************************/ AjBool ajHttpQueryUrl(const AjPQuery qry, ajint* iport, AjPStr* host, AjPStr* urlget) { const AjPStr url = NULL; AjPUrlref uo = NULL; url = qry->DbUrl; if(!url) { ajErr("no URL defined for database %S", qry->DbName); return ajFalse; } uo = ajHttpUrlrefNew(); ajHttpUrlrefParseC(&uo, ajStrGetPtr(url)); ajHttpUrlrefSplitPort(uo); ajStrAssignS(host,uo->Host); ajFmtPrintS(urlget,"/%S",uo->Absolute); if(ajStrGetLen(uo->Port)) ajStrToInt(uo->Port,iport); ajHttpUrlrefDel(&uo); return ajTrue; } /* @func ajHttpUrlDeconstruct ************************************************* ** ** Deconstruct a URL (IPV4/6) ** ** @param [r] url [const AjPStr] url ** @param [w] iport [ajint*] Port ** @param [w] host [AjPStr*] Host name ** @param [w] urlget [AjPStr*] URL for the HTTP header GET ** @return [void] ** @@ ******************************************************************************/ void ajHttpUrlDeconstruct(const AjPStr url, ajint* iport, AjPStr* host, AjPStr* urlget) { AjPUrlref uo = NULL; uo = ajHttpUrlrefNew(); ajHttpUrlrefParseC(&uo, ajStrGetPtr(url)); ajHttpUrlrefSplitPort(uo); ajStrAssignS(host,uo->Host); ajFmtPrintS(urlget,"/%S",uo->Absolute); if(ajStrGetLen(uo->Port)) ajStrToInt(uo->Port,iport); ajHttpUrlrefDel(&uo); return; } /* @func ajHttpRedirect ******************************************************* ** ** Reads the header of http response in given buffer buff, ** if it includes a redirection response updates the host, port and get ** parameters using the 'Location' header ** ** @param [u] buff [AjPFilebuff] file buffer ** @param [w] host [AjPStr*] Host name ** @param [w] port [ajint*] Port ** @param [w] path [AjPStr*] part of URL after port number ** @return [AjBool] returns true if the header includes a redirection response ** @@ ******************************************************************************/ AjBool ajHttpRedirect(AjPFilebuff buff, AjPStr* host, ajint* port, AjPStr* path) { AjPRegexp httpexp = NULL; AjPRegexp nullexp = NULL; AjPRegexp redirexp = NULL; AjPStr codestr = NULL; AjPStr newurl = NULL; AjPStr newhost = NULL; AjPStr currline = NULL; ajuint httpcode = 0; AjBool isheader = ajFalse; AjBool ret = ajFalse; httpexp = ajRegCompC("^HTTP/\\S+\\s+(\\d+)"); if(!buff->Size) return 0; ajBuffreadLine(buff, &currline); ajDebug("ajHttpRedirect: First line: '%S'\n", currline); if(ajRegExec(httpexp, currline)) { isheader = ajTrue; ajRegSubI(httpexp, 1, &codestr); ajStrToUint(codestr, &httpcode); ajDebug("Header: codestr '%S' code '%u'\n", codestr, httpcode); ajStrDel(&codestr); } if(isheader) { if(httpcode == 301 || httpcode == 302 || httpcode==307) { redirexp = ajRegCompC("^Location: (\\S+)"); nullexp = ajRegCompC("^\r?\n?$"); while( ajBuffreadLine(buff, &currline) && !ajRegExec(nullexp, currline)) { ajDebug("ajHttpRedirect: header line: '%S'\n", currline); if(ajRegExec(redirexp, currline)) { ajRegSubI(redirexp, 1, &newurl); ajHttpUrlDeconstruct(newurl, port, &newhost, path); if(ajStrGetLen(newhost)) ajStrAssignS(host, newhost); ajStrDel(&newurl); ajStrDel(&newhost); ret = ajTrue; break; } } ajRegFree(&redirexp); ajRegFree(&nullexp); } } if(!ret) ajFilebuffReset(buff); ajRegFree(&httpexp); ajStrDel(&currline); return ret; } /* @func ajHttpRead ********************************************************** ** ** Reads the header of http response in given buffer buff, ** if it includes a redirection response updates the host, port and get ** parameters using the 'Location' header ** ** @param [r] dbhttpver [const AjPStr] DB http version ** @param [r] dbname [const AjPStr] DB name ** @param [r] dbproxy [const AjPStr] DB proxy ** @param [r] host [const AjPStr] Host name ** @param [r] port [ajint] Port ** @param [r] dbpath [const AjPStr] part of URL after port number ** @return [AjPFilebuff] http response ** @@ ******************************************************************************/ AjPFilebuff ajHttpRead(const AjPStr dbhttpver, const AjPStr dbname, const AjPStr dbproxy, const AjPStr host, ajint port, const AjPStr dbpath) { AjPStr get = NULL; AjPStr httpver = NULL; AjPStr proxyname = NULL; AjPStr proxyauth = NULL; AjPStr proxycred = NULL; AjPStr newhost = NULL; AjPStr path = NULL; AjPFilebuff buff = NULL; FILE *fp = NULL; struct AJSOCKET sock; struct AJTIMEOUT timo; ajint proxyport = 0; httpver = ajStrNew(); proxyname = ajStrNew(); proxyauth = ajStrNew(); proxycred = ajStrNew(); get = ajStrNew(); newhost = ajStrNew(); ajDebug("ajHttpRead db: '%S' host '%S' port: %u dbpath: '%S'\n", dbname, host, port, dbpath); ajStrAssignS(&newhost,host); ajStrAssignS(&path, dbpath); ajHttpGetVersion(dbhttpver, &httpver); ajHttpGetProxyinfo(dbproxy, &proxyport, &proxyname, &proxyauth, &proxycred); while (buff==NULL || ajHttpRedirect(buff, &newhost, &port, &path)) { if(buff) /* means buff includes http redirect response*/ ajFilebuffDel(&buff); if(ajStrGetCharFirst(path)!='/') ajStrInsertK(&path, 0, '/'); if(ajStrGetLen(proxyname)) { ajFmtPrintS(&get, "GET http://%S:%d%S HTTP/%S\r\n", host, port, path, httpver); fp = ajHttpOpenProxy(dbname, proxyname, proxyport, proxyauth, proxycred, newhost, port, get, &sock); } else { ajFmtPrintS(&get, "GET %S HTTP/%S\r\n", path, httpver); fp = ajHttpOpen(dbname, newhost, port, get, &sock); } if(!fp) { ajErr("ajHttpRead: cannot open HTTP connection '%S'\n", get); buff = NULL; break; } buff = ajFilebuffNewFromCfile(fp); if(!buff) { ajErr("ajHttpRead: socket buffer attach failed for host '%S'" ", HTTP get command was '%S'", host, get); break; } timo.seconds = 180; ajSysTimeoutSet(&timo); ajFilebuffLoadAll(buff); ajSysTimeoutUnset(&timo); } ajStrDel(&get); ajStrDel(&httpver); ajStrDel(&proxyname); ajStrDel(&newhost); ajStrDel(&path); return buff; }