Back to index

nordugrid-arc-nox  1.1.0~rc6
HTTPSClient.cpp
Go to the documentation of this file.
00001 #include <arc/StringConv.h>
00002 #include <arc/globusutils/GlobusErrorUtils.h>
00003 
00004 #include "HTTPSClient.h"
00005 
00006 namespace Arc {
00007 
00008   Logger HTTPSClient::logger(Logger::getRootLogger(), "HTTPSClient");
00009   
00010   HTTPSClient::HTTPSClient(const char* base,bool heavy_encryption,bool gssapi_server, int timeout, bool check_host_cert) try: base_url(base), timeout(timeout*1000){
00011     c=NULL;
00012     cred=new GSSCredential();
00013     valid=false; connected=false;
00014     /* initialize globus io connection related objects */
00015     if(base_url.Protocol() == "http") {
00016       const char * proxy = getenv("ARC_HTTP_PROXY");
00017       if(!proxy) proxy = getenv("NORDUGRID_HTTP_PROXY");
00018       if(proxy) {
00019         proxy_hostname = proxy;
00020         proxy_port = 8000;
00021         std::string::size_type n = proxy_hostname.find(':');
00022         if(n != std::string::npos) {
00023           proxy_port=atoi(proxy_hostname.c_str()+n+1);
00024           proxy_hostname.resize(n);
00025         };
00026       };
00027     };
00028     if(proxy_hostname.empty()) {
00029       if(!gssapi_server) {
00030         c=new HTTPSClientConnectorGlobus(base,heavy_encryption);
00031       } else {
00032         c=new HTTPSClientConnectorGSSAPI(base,heavy_encryption, timeout*1000, *cred, check_host_cert);
00033       };
00034     } else {
00035       std::string u = "http://"+proxy_hostname+":"+tostring<int>(proxy_port);
00036       if(!gssapi_server) {
00037         c=new HTTPSClientConnectorGlobus(u.c_str(),heavy_encryption);
00038       } else {
00039         c=new HTTPSClientConnectorGSSAPI(u.c_str(),heavy_encryption, timeout*1000, *cred, check_host_cert);
00040       };
00041     };
00042     valid=true;
00043   } catch(std::exception e) {
00044     valid=false; connected=false;
00045   }
00046   
00047   bool HTTPSClient::credentials(const char* filename) {
00048     if(!filename) return false;
00049     cred = new GSSCredential(filename, "", "");
00050     //gss_cred_id_t cred_new = *cred;
00051     if(*cred == GSS_C_NO_CREDENTIAL) {
00052       delete cred;
00053       return false;
00054     }
00055     if(!c->credentials(*cred)) {
00056       delete cred;
00057       return false;
00058     };
00059     return true;
00060   }
00061   
00062   int HTTPSClient::connect(void) {
00063     if(connected) return 0;
00064     if(!valid) return -1;
00065     bool timeout = false;
00066     if(!c->connect(timeout)) {
00067       if (timeout) return 1;
00068       return -1;
00069     }
00070     connected=true;
00071     return 0;
00072   }
00073   
00074   int HTTPSClient::disconnect(void) {
00075     if(!connected) return 0;
00076     c->disconnect();
00077     connected=false;
00078     return 0;
00079   }
00080   
00081   HTTPSClient::~HTTPSClient(void) {
00082     if(!valid) return;
00083     disconnect();
00084     if(c) delete c;
00085     if(*cred != GSS_C_NO_CREDENTIAL) {
00086       delete cred;
00087     };
00088   }
00089   
00090   void HTTPSClient::clear_input(void) {
00091     if(!valid) return;
00092     char buf[256];
00093     unsigned int l;
00094     bool isread,iswritten;
00095     for(;;) {
00096       l=sizeof(buf);
00097       if(!c->read(buf,&l)) return;
00098       if(!c->transfer(isread,iswritten,0)) { c->read(); return; };
00099       if(!isread) { c->read(); return; };
00100       logger.msg(DEBUG, "clear_input: %s", buf);
00101     };
00102   }
00103   
00104   void HTTPSClient::analyze_response_line(char* line) {
00105     for(;*line;line++) if(!isspace(*line)) break;
00106     int len = strlen(line);
00107     if(len<2) return; // too short
00108     if(answer_count==0) {  // first line
00109       bool answer_keep_alive = true;
00110       answer_code=0;
00111       char* p = line;
00112       char* http_version = p;
00113       for(;*p;p++) if(isspace(*p)) break; (*p)=0; p++;
00114       for(;*p;p++) if(!isspace(*p)) break;
00115       char* code = p;
00116       for(;*p;p++) if(isspace(*p)) break; (*p)=0; p++;
00117       for(;*p;p++) if(!isspace(*p)) break;
00118       char* reason = p;
00119       char *e;
00120       answer_code=strtoul(code,&e,10);
00121       if((*e) != 0) return;
00122       answer_reason=reason;
00123       answer_count++;
00124       if(strcmp(http_version,"HTTP/1.1") == 0) { answer_keep_alive=true; }
00125       else { answer_keep_alive=false; };
00126       fields.reset(answer_keep_alive);
00127     } else  {
00128       char* token = line; for(;*token;token++) if(isspace(*token)) break;
00129       if(*token) {
00130         (*token)=0; token++;
00131         for(;*token;token++) if(!isspace(*token)) break;
00132       };
00133       fields.set(line,token);
00134     };
00135   }
00136   
00137   // read server response
00138   int HTTPSClient::read_response_header(void) {
00139     bool isread,iswritten;
00140     answer_count=0; // line counter
00141     // let external procedures manage timeouts
00142     if(!c->transfer(isread,iswritten,-1)) {
00143       disconnect();
00144       return -1;
00145     };
00146     if(answer_size != 0) isread=true;
00147     if((!isread) && (!iswritten)) {
00148       disconnect();
00149       return -1;
00150     };
00151     char line_buf[256];
00152     int line_p = 0;
00153     for(;;) {
00154       answer_buf[answer_size]=0;
00155       char* p = strchr(answer_buf,'\n');
00156       unsigned int l = answer_size;
00157       if(p) l=(p-answer_buf)+1; // available data
00158       unsigned int ll = (sizeof(line_buf)-1-line_p);
00159       if(ll>l) ll=l; // available space
00160       memcpy(line_buf+line_p,answer_buf,ll); line_p+=ll; line_buf[line_p]=0;
00161       if(l<answer_size) { memmove(answer_buf,answer_buf+l,answer_size-l); };
00162       answer_size-=l;
00163       if(p) {  // eol 
00164         for(;line_p;) {
00165           if((line_buf[line_p-1] != '\r') && (line_buf[line_p-1] != '\n')) break;
00166           line_p--;
00167         };
00168         line_buf[line_p]=0;
00169         if(line_p == 0) break; // end of header
00170         logger.msg(DEBUG, "read_response_header: line: %s", line_buf);
00171         analyze_response_line(line_buf);
00172         line_p=0;
00173       };
00174       if(answer_size>0) continue;
00175       // request new portion
00176       answer_size=sizeof(answer_buf)-1; 
00177       if(isread && (!c->read(answer_buf,&answer_size))) {
00178         disconnect();
00179         return -1;
00180       };
00181       if(!c->transfer(isread,iswritten,timeout)) {
00182         logger.msg(ERROR, "Timeout while reading response header");
00183         disconnect();
00184         return -1;
00185       };
00186       if(!isread) {
00187         logger.msg(ERROR, "Error while reading response header");
00188         disconnect();
00189         return -1;
00190       };
00191     };
00192     logger.msg(DEBUG, "read_response_header: header finished");
00193     return 0;
00194   }
00195   
00196   int HTTPSClient::skip_response_entity(void) {
00197     logger.msg(DEBUG, "skip_response_entity");
00198     if(fields.haveContentLength() || fields.haveContentRange()) {
00199       unsigned long long int size = fields.ContentLength();
00200       logger.msg(DEBUG, "skip_response_entity: size: %llu", size);
00201       if(size<=answer_size) {
00202         memmove(answer_buf,answer_buf+size,answer_size-size);
00203         answer_size-=size;
00204         logger.msg(DEBUG, "skip_response_entity: already have all");
00205         return 0;
00206       };
00207       size-=answer_size;
00208       logger.msg(DEBUG, "skip_response_entity: size left: %llu", size);
00209       // read size bytes
00210       char buf[1024];
00211       for(;size;) {
00212         logger.msg(DEBUG, "skip_response_entity:  to read: %llu", size);
00213         answer_size=sizeof(buf);
00214         if(!c->read(buf,&answer_size)) {
00215           disconnect(); return -1;
00216         };
00217         bool isread,iswritten;
00218         if(!c->transfer(isread,iswritten,timeout)) {
00219           logger.msg(DEBUG, "skip_response_entity: timeout %llu", size);
00220           disconnect(); return -1; // timeout
00221         };
00222         if(!isread) { disconnect(); return -1; }; // failure
00223         size-=answer_size;
00224         logger.msg(DEBUG, "skip_response_entity: read: %u (%llu)", answer_size, size);
00225       };
00226       logger.msg(DEBUG, "skip_response_entity: read all");
00227       return 0;
00228     };
00229     if(fields.KeepAlive()) {
00230       logger.msg(DEBUG, "skip_response_entity: no entity");
00231       // no entity passed at all
00232       return 0;
00233     };
00234     logger.msg(DEBUG, "skip_response_entity: unknown size");
00235     // can't handle unknown size - let connection be closed
00236     return 0;
00237   }
00238   
00239   int HTTPSClient::make_header(const char* path,
00240         unsigned long long int offset,unsigned long long int size,
00241         unsigned long long int fd_size,std::string& header) {
00242     // create header
00243     if(!valid) return -1;
00244     if(path[0] == '/') path++;
00245     header = "PUT ";
00246     std::string url_path;
00247     if(proxy_hostname.length() == 0) {
00248       url_path=base_url.Path();
00249     } else {
00250       url_path=base_url.Protocol()+"://"+base_url.Host()+":"+tostring(base_url.Port())+base_url.Path();
00251     };
00252     if(path[0]) {
00253       if(url_path[url_path.length()-1] != '/') url_path+="/";
00254       url_path+=path;
00255     };
00256     if(!base_url.HTTPOptions().empty()) {
00257       url_path+='?'+URL::OptionString(base_url.HTTPOptions(), '&');
00258     };
00259     std::string url_host = base_url.Host()+":"+tostring(base_url.Port());
00260     header+=url_path; header+=" HTTP/1.1\r\n";
00261     header+="Host: "+url_host+"\r\n";
00262     header+="Connection: keep-alive\r\n";
00263     header+="Content-Length: "+tostring(size)+"\r\n";
00264     header+="Content-Range: bytes "+tostring(offset)+"-"+tostring(offset+size-1);
00265     if(fd_size>=size) {
00266       header+="/"+tostring(fd_size);
00267     };
00268     header+="\r\n";
00269     header+="\r\n";
00270     return 0;
00271   }
00272   
00273   /*
00274     HTTP PUT method
00275     Content of 'buf' with size 'size' is sent to 'path'. 
00276     Treated as part size 'fd_size' starting from 'offset'.
00277   */
00278   int HTTPSClient::PUT(const char* path,
00279         unsigned long long int offset,unsigned long long int size,
00280         const unsigned char* buf,unsigned long long int fd_size,bool wait) {
00281     if(!connected) {
00282       logger.msg(ERROR, "Not connected");
00283       return -1;
00284     };
00285     // send header
00286     // create header
00287     std::string header;
00288     make_header(path,offset,size,fd_size,header);
00289     c->clear();
00290     answer_size=sizeof(answer_buf)-1;
00291     if(!c->read(answer_buf,&answer_size)) {
00292       disconnect(); return -1;
00293     };
00294     // send header
00295     if(!c->write(header.c_str(),header.length())) {
00296       disconnect(); return -1;
00297     };
00298     bool isread,iswritten;
00299     if(!c->transfer(isread,iswritten,timeout)) {
00300       logger.msg(ERROR, "Timeout sending header");
00301       disconnect(); return -1;
00302     };
00303     if(!iswritten) { // Server responded too early
00304       logger.msg(ERROR, "Early response from server");
00305       disconnect(); return -1;
00306     };
00307     // send body
00308     if(!c->write((const char*)buf,size)) {
00309       disconnect(); return -1;
00310     };
00311     // read server response
00312     if(read_response_header() != 0) {
00313       logger.msg(ERROR, "No response from server received");
00314       disconnect(); return -1;
00315     };
00316     if(!c->eofwrite()) {
00317       logger.msg(ERROR, "Failed to send body");
00318       disconnect(); return -1;
00319     };
00320     if(fields.KeepAlive()) {  // skip entity only if trying to keep connection
00321       if(skip_response_entity() != 0) {
00322         logger.msg(ERROR, "Failure while receiving entity");
00323         disconnect();
00324         return -1;
00325       };
00326       c->read(); // just in case
00327     } else {
00328       disconnect();
00329     };
00330     if(answer_code != HTTP_OK) return -1;
00331     return 0;
00332   }
00333   
00334   /*
00335     HTTP GET method
00336   */
00337   int HTTPSClient::GET_header(const char* path,
00338         unsigned long long int offset,unsigned long long int size) {
00339     // create header
00340     if(!valid) return -1;
00341     // *** Generate header
00342     if(path[0] == '/') path++;
00343     std::string header = "GET ";
00344     std::string url_path;
00345     if(proxy_hostname.length() == 0) {
00346       url_path=base_url.Path();
00347     } else {
00348       url_path=base_url.Protocol()+"://"+base_url.Host()+":"+tostring(base_url.Port())+base_url.Path();
00349     };
00350     if(path[0]) {
00351       if(url_path[url_path.length()-1] != '/') url_path+="/"; 
00352       url_path+=path;
00353     };
00354     if(!base_url.HTTPOptions().empty()) {
00355       url_path+='?'+URL::OptionString(base_url.HTTPOptions(), '&');
00356     };
00357     std::string url_host = base_url.Host()+":"+tostring(base_url.Port());
00358     header+=url_path; header+=" HTTP/1.1\r\n";
00359     header+="Host: "+url_host+"\r\n";
00360     header+="Connection: keep-alive\r\n";
00361     header+="Range: bytes="+tostring(offset)+"-"+tostring(offset+size-1)+"\r\n";
00362     header+="\r\n";
00363     logger.msg(DEBUG, "header: %s", header);
00364     // *** Send header 
00365     c->clear();
00366     // get ready for any answer
00367     answer_size=sizeof(answer_buf);
00368     if(!c->read(answer_buf,&answer_size)) {
00369       disconnect();
00370       return -1;
00371     };
00372     // send
00373     if(!c->write(header.c_str(),header.length())) {
00374       disconnect();
00375       return -1;
00376     };
00377     bool isread, iswritten;
00378     for(;;) {
00379       if(!c->transfer(isread,iswritten,timeout)) {
00380         logger.msg(ERROR, "Timeout while sending header");
00381         disconnect();
00382         return -1;
00383       };
00384       if(iswritten) break;
00385       if(isread) continue;
00386       logger.msg(ERROR, "Failed to send header");
00387       disconnect();
00388       return -1;
00389     };
00390     return 0;
00391   }
00392   
00393   int HTTPSClient::GET(const char* path,
00394         unsigned long long int offset,unsigned long long int size,
00395         get_callback_t callback,void* arg,
00396         unsigned char* buf,unsigned long long int bufsize) {
00397     if(!connected) {
00398       logger.msg(ERROR, "Not connected");
00399       return -1;
00400     };
00401     // send header
00402     if(GET_header(path,offset,size)) {
00403       // There are servers which close connection even if they shouldn't - retry here
00404       if(connect()) return -1;
00405       if(GET_header(path,offset,size)) return -1;
00406     };
00407     // read server response
00408     if(read_response_header() != 0) {
00409       logger.msg(ERROR, "No response from server received");
00410       disconnect();
00411       return -1;
00412     };
00413     if(answer_code == 416) { // out of range
00414       if(skip_response_entity() != 0) {
00415         disconnect(); return -1;
00416       };
00417       if(!fields.KeepAlive()) {
00418         logger.msg(VERBOSE, "GET: connection to be closed");
00419         disconnect();
00420       };
00421       return 0;
00422     };
00423     if((answer_code != 200) && (answer_code != 206)) {
00424       if(skip_response_entity() != 0) {
00425         disconnect(); return -1;
00426       };
00427       if(!fields.KeepAlive()) {
00428       logger.msg(VERBOSE, "GET: connection to be closed");
00429         disconnect();
00430       };
00431       return -1;
00432     };
00433     logger.msg(VERBOSE, "GET: header is read - rest: %u", answer_size);
00434     unsigned long long c_offset = 0;
00435     if(fields.haveContentRange()) c_offset=fields.ContentStart();
00436     bool have_length = fields.haveContentLength() || fields.haveContentRange();
00437     unsigned long long length = fields.ContentLength();
00438     // take rest of already read data
00439     if(answer_size) {
00440       if(have_length) if(answer_size > length) answer_size=length;
00441       logger.msg(DEBUG, "GET: calling callback(rest): content: %s", answer_buf);
00442       logger.msg(DEBUG, "GET: calling callback(rest): size: %u", answer_size);
00443       logger.msg(DEBUG, "GET: calling callback(rest): offset: %llu", c_offset);
00444       unsigned char* in_buf = (unsigned char*)answer_buf;
00445       unsigned long  in_size = answer_size;
00446       for(;;) {
00447         if(in_size == 0) break;
00448         if(buf) {
00449           unsigned long l = in_size;
00450           if(l>bufsize) l=bufsize;
00451           memcpy(buf,in_buf,l);
00452           if(callback(c_offset,l,&buf,&bufsize,arg) != 0) {
00453             logger.msg(ERROR, "GET callback returned error");
00454             disconnect(); // ?????????????????
00455             return -1;
00456           };
00457           in_buf+=l; c_offset+=l; in_size-=l;
00458         } else {
00459           unsigned char* in_buf_ = in_buf;
00460           if(callback(c_offset,in_size,&in_buf_,&bufsize,arg) != 0) {
00461             logger.msg(ERROR, "GET callback returned error");
00462             disconnect(); // ?????????????????
00463             return -1;
00464           };
00465           if(in_buf_ != in_buf) buf=in_buf_;
00466           in_buf+=in_size; c_offset+=in_size; in_size=0;
00467         };
00468       };
00469       if(have_length) length-=answer_size;
00470     };
00471     unsigned char* in_buf = NULL;
00472     for(;;) {
00473       if(have_length) if(length == 0) break; 
00474       if(buf == NULL) {
00475         if(in_buf == NULL) in_buf=(unsigned char*)malloc(65536);
00476         if(in_buf == NULL) {
00477           logger.msg(ERROR, "Failed to allocate memory");
00478           disconnect();
00479           return -1;
00480         };
00481         buf=in_buf; bufsize=65536;
00482       };
00484       answer_size=bufsize;
00485       if(!c->read((char*)buf,&answer_size)) {
00486         logger.msg(ERROR, "Failed while reading response content");
00487         disconnect();
00488         if(in_buf) free(in_buf);
00489         return -1;
00490       };
00491       bool isread,iswritten;
00492       if(!c->transfer(isread,iswritten,timeout)) {
00493         logger.msg(ERROR, "Timeout while reading response content");
00494         disconnect();
00495         if(in_buf) free(in_buf);
00496         return -1; // timeout
00497       };
00498       if(!isread) { // failure
00499         if(c->eofread()) { // eof - ok if length not specified
00500           if(!have_length) { disconnect(); break; };
00501         };
00502         logger.msg(ERROR, "Error while reading response content");
00503         disconnect();
00504         if(in_buf) free(in_buf);
00505         return -1;
00506       };
00507       logger.msg(DEBUG, "GET: calling callback: content: %s", buf);
00508       logger.msg(DEBUG, "GET: calling callback: size: %u", answer_size);
00509       logger.msg(DEBUG, "GET: calling callback: offset: %llu", c_offset);
00510       if(callback(c_offset,answer_size,&buf,&bufsize,arg) != 0) {
00511         logger.msg(ERROR, "GET callback returned error");
00512         disconnect();
00513         if(in_buf) free(in_buf);
00514         return -1;
00515       };
00516       c_offset+=answer_size;
00517       if(have_length) length-=answer_size;
00518     };
00519     // cancel? - just in case
00520     if(in_buf) free(in_buf);
00521     if(!fields.KeepAlive()) {
00522       logger.msg(VERBOSE, "GET: connection to be closed");
00523       disconnect();
00524     };
00525     return 0;
00526   }
00527   
00528   // -------------------------------------------------
00529 
00530   SimpleCondition * HTTPSClientConnector::connect_lock = new SimpleCondition();
00531 
00532   bool HTTPSClientConnector::connect(bool& timeout) { return false; }
00533   
00534   bool HTTPSClientConnector::disconnect(void)  { return false; }
00535   
00536   bool HTTPSClientConnector::clear(void) { return false; }
00537   
00538   bool HTTPSClientConnector::read(char* buf,unsigned int* size) { return false; }
00539   
00540   bool HTTPSClientConnector::write(const char* buf,unsigned int size) { return false; }
00541   
00542   bool HTTPSClientConnector::transfer(bool& read,bool& write,int timeout) { return false; }
00543   
00544   bool HTTPSClientConnector::eofread(void) { return false; }
00545   
00546   bool HTTPSClientConnector::eofwrite(void) { return false; }
00547   
00548   HTTPSClientConnector::HTTPSClientConnector(void) { }
00549   
00550   HTTPSClientConnector::~HTTPSClientConnector(void) { }
00551   
00552   bool HTTPSClientConnector::credentials(gss_cred_id_t cred) { return false; }
00553   
00554 } // namespace Arc