Back to index

nordugrid-arc-nox  1.1.0~rc6
MCCHTTP.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 #include <arc/XMLNode.h>
00006 #include <arc/StringConv.h>
00007 #include <arc/message/PayloadRaw.h>
00008 #include <arc/message/SecAttr.h>
00009 #include <arc/loader/Plugin.h>
00010 
00011 #include "PayloadHTTP.h"
00012 #include "MCCHTTP.h"
00013 
00014 
00015 
00016 Arc::Logger Arc::MCC_HTTP::logger(Arc::MCC::logger,"HTTP");
00017 
00018 Arc::MCC_HTTP::MCC_HTTP(Arc::Config *cfg) : Arc::MCC(cfg) {
00019 }
00020 
00021 static Arc::Plugin* get_mcc_service(Arc::PluginArgument* arg) {
00022     Arc::MCCPluginArgument* mccarg =
00023             arg?dynamic_cast<Arc::MCCPluginArgument*>(arg):NULL;
00024     if(!mccarg) return NULL;
00025     return new Arc::MCC_HTTP_Service((Arc::Config*)(*mccarg));
00026 }
00027 
00028 static Arc::Plugin* get_mcc_client(Arc::PluginArgument* arg) {
00029     Arc::MCCPluginArgument* mccarg =
00030             arg?dynamic_cast<Arc::MCCPluginArgument*>(arg):NULL;
00031     if(!mccarg) return NULL;
00032     return new Arc::MCC_HTTP_Client((Arc::Config*)(*mccarg));
00033 }
00034 
00035 Arc::PluginDescriptor PLUGINS_TABLE_NAME[] = {
00036     { "http.service", "HED:MCC", 0, &get_mcc_service },
00037     { "http.client",  "HED:MCC", 0, &get_mcc_client  },
00038     { NULL, NULL, 0, NULL }
00039 };
00040 
00041 namespace Arc {
00042 
00043 class HTTPSecAttr: public SecAttr {
00044  friend class MCC_HTTP_Service;
00045  friend class MCC_HTTP_Client;
00046  public:
00047   HTTPSecAttr(PayloadHTTP& payload);
00048   virtual ~HTTPSecAttr(void);
00049   virtual operator bool(void) const;
00050   virtual bool Export(SecAttrFormat format,XMLNode &val) const;
00051  protected:
00052   std::string action_;
00053   std::string object_;
00054   virtual bool equal(const SecAttr &b) const;
00055 };
00056 
00057 HTTPSecAttr::HTTPSecAttr(PayloadHTTP& payload) {
00058   action_=payload.Method();
00059   std::string path = payload.Endpoint();
00060   // Remove service, port and protocol - those will be provided by
00061   // another layer
00062   std::string::size_type p = path.find("://");
00063   if(p != std::string::npos) {
00064     p=path.find('/',p+3);
00065     if(p != std::string::npos) {
00066       path.erase(0,p);
00067     };
00068   };
00069   object_=path;
00070 }
00071 
00072 HTTPSecAttr::~HTTPSecAttr(void) {
00073 }
00074 
00075 HTTPSecAttr::operator bool(void) const {
00076   return true;
00077 }
00078 
00079 bool HTTPSecAttr::equal(const SecAttr &b) const {
00080   try {
00081     const HTTPSecAttr& a = (const HTTPSecAttr&)b;
00082     return ((action_ == a.action_) && (object_ == a.object_));
00083   } catch(std::exception&) { };
00084   return false;
00085 }
00086 
00087 bool HTTPSecAttr::Export(SecAttrFormat format,XMLNode &val) const {
00088   if(format == UNDEFINED) {
00089   } else if(format == ARCAuth) {
00090     NS ns;
00091     ns["ra"]="http://www.nordugrid.org/schemas/request-arc";
00092     val.Namespaces(ns); val.Name("ra:Request");
00093     XMLNode item = val.NewChild("ra:RequestItem");
00094     if(!object_.empty()) {
00095       XMLNode object = item.NewChild("ra:Resource");
00096       object=object_;
00097       object.NewAttribute("Type")="string";
00098       object.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/http/path";
00099     };
00100     if(!action_.empty()) {
00101       XMLNode action = item.NewChild("ra:Action");
00102       action=action_;
00103       action.NewAttribute("Type")="string";
00104       action.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/http/method";
00105     };
00106     return true;
00107   } else if(format == XACML) {
00108     NS ns;
00109     ns["ra"]="urn:oasis:names:tc:xacml:2.0:context:schema:os";
00110     val.Namespaces(ns); val.Name("ra:Request");
00111     if(!object_.empty()) {
00112       XMLNode object = val.NewChild("ra:Resource");
00113       XMLNode attr = object.NewChild("ra:Attribute");
00114       attr.NewChild("ra:AttributeValue") = object_;
00115       attr.NewAttribute("DataType")="xs:string";
00116       attr.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/http/path";
00117     };
00118     if(!action_.empty()) {
00119       XMLNode action = val.NewChild("ra:Action");
00120       XMLNode attr = action.NewChild("ra:Attribute");
00121       attr.NewChild("ra:AttributeValue") = action_;
00122       attr.NewAttribute("DataType")="xs:string";
00123       attr.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/http/method";
00124     };
00125     return true;
00126   } else {
00127   };
00128   return false;
00129 }
00130 
00131 MCC_HTTP_Service::MCC_HTTP_Service(Config *cfg):MCC_HTTP(cfg) {
00132 }
00133 
00134 MCC_HTTP_Service::~MCC_HTTP_Service(void) {
00135 }
00136 
00137 static MCC_Status make_http_fault(Logger& logger,PayloadStreamInterface& stream,Message& outmsg,int code,const char* desc = NULL) {
00138   if((desc == NULL) || (*desc == 0)) {
00139     switch(code) {
00140       case HTTP_BAD_REQUEST:  desc="Bad Request"; break;
00141       case HTTP_NOT_FOUND:    desc="Not Found"; break;
00142       case HTTP_INTERNAL_ERR: desc="Internal error"; break;
00143       default: desc="Something went wrong";
00144     };
00145   };
00146   logger.msg(WARNING, "HTTP Error: %d %s",code,desc);
00147   PayloadHTTP outpayload(code,desc,stream);
00148   if(!outpayload.Flush()) return MCC_Status();
00149   // Returning empty payload because response is already sent
00150   PayloadRaw* outpayload_e = new PayloadRaw;
00151   outmsg.Payload(outpayload_e);
00152   return MCC_Status(STATUS_OK);
00153 }
00154 
00155 static MCC_Status make_raw_fault(Message& outmsg,const char* desc = NULL) {
00156   PayloadRaw* outpayload = new PayloadRaw;
00157   if(desc) outpayload->Insert(desc,0);
00158   outmsg.Payload(outpayload);
00159   return MCC_Status();
00160 }
00161 
00162 static void parse_http_range(PayloadHTTP& http,Message& msg) {
00163   std::string http_range = http.Attribute("range");
00164   if(http_range.empty()) return;
00165   if(strncasecmp(http_range.c_str(),"bytes=",6) != 0) return;
00166   std::string::size_type p = http_range.find(',',6);
00167   if(p != std::string::npos) {
00168     http_range=http_range.substr(6,p-6);
00169   } else {
00170     http_range=http_range.substr(6);
00171   };
00172   p=http_range.find('-');
00173   std::string val;
00174   if(p != std::string::npos) {
00175     val=http_range.substr(0,p);
00176     if(!val.empty()) msg.Attributes()->set("HTTP:RANGESTART",val);
00177     val=http_range.substr(p+1);
00178     if(!val.empty()) msg.Attributes()->set("HTTP:RANGEEND",val);
00179   };
00180 }
00181 
00182 MCC_Status MCC_HTTP_Service::process(Message& inmsg,Message& outmsg) {
00183   // Extracting payload
00184   if(!inmsg.Payload()) return MCC_Status();
00185   PayloadStreamInterface* inpayload = NULL;
00186   try {
00187     inpayload = dynamic_cast<PayloadStreamInterface*>(inmsg.Payload());
00188   } catch(std::exception& e) { };
00189   if(!inpayload) return MCC_Status();
00190   // Converting stream payload to HTTP which also implements raw interface
00191   PayloadHTTP nextpayload(*inpayload);
00192   if(!nextpayload) {
00193     logger.msg(WARNING, "Cannot create http payload");
00194     return make_http_fault(logger,*inpayload,outmsg,HTTP_BAD_REQUEST);
00195   };
00196   if(nextpayload.Method() == "END") {
00197     return MCC_Status(SESSION_CLOSE);
00198   };
00199   bool keep_alive = nextpayload.KeepAlive();
00200   // Creating message to pass to next MCC and setting new payload.
00201   Message nextinmsg = inmsg;
00202   nextinmsg.Payload(&nextpayload);
00203   // Creating attributes
00204   // Endpoints must be URL-like so make sure HTTP path is
00205   // converted to HTTP URL
00206   std::string endpoint = nextpayload.Endpoint();
00207   {
00208     std::string::size_type p = endpoint.find("://");
00209     if(p == std::string::npos) {
00210       // TODO: Use Host attribute of HTTP
00211       std::string oendpoint = nextinmsg.Attributes()->get("ENDPOINT");
00212       p=oendpoint.find("://");
00213       if(p != std::string::npos) {
00214         oendpoint.erase(0,p+3);
00215       };
00216       // Assuming we have host:port here
00217       if(oendpoint.empty() ||
00218          (oendpoint[oendpoint.length()-1] != '/')) {
00219         if(endpoint[0] != '/') oendpoint+="/";
00220       };
00221       // TODO: HTTPS detection
00222       endpoint="http://"+oendpoint+endpoint;
00223     };
00224   };
00225   nextinmsg.Attributes()->set("ENDPOINT",endpoint);
00226   nextinmsg.Attributes()->set("HTTP:ENDPOINT",nextpayload.Endpoint());
00227   nextinmsg.Attributes()->set("HTTP:METHOD",nextpayload.Method());
00228   // Filling security attributes
00229   HTTPSecAttr* sattr = new HTTPSecAttr(nextpayload);
00230   nextinmsg.Auth()->set("HTTP",sattr);
00231   parse_http_range(nextpayload,nextinmsg);
00232   // Reason ?
00233   for(std::multimap<std::string,std::string>::const_iterator i =
00234       nextpayload.Attributes().begin();i!=nextpayload.Attributes().end();++i) {
00235     nextinmsg.Attributes()->add("HTTP:"+i->first,i->second);
00236   };
00237   if(!ProcessSecHandlers(nextinmsg,"incoming")) {
00238     return make_http_fault(logger,*inpayload,outmsg,HTTP_BAD_REQUEST); // Maybe not 400 ?
00239   };
00240   // Call next MCC
00241   MCCInterface* next = Next(nextpayload.Method());
00242   if(!next) {
00243     logger.msg(WARNING, "No next element in the chain");
00244     return make_http_fault(logger,*inpayload,outmsg,HTTP_NOT_FOUND);
00245   }
00246   Message nextoutmsg = outmsg; nextoutmsg.Payload(NULL);
00247   MCC_Status ret = next->process(nextinmsg,nextoutmsg);
00248   // Do checks and extract raw response
00249   if(!ret) {
00250     if(nextoutmsg.Payload()) delete nextoutmsg.Payload();
00251     logger.msg(WARNING, "next element of the chain returned error status");
00252     return make_http_fault(logger,*inpayload,outmsg,HTTP_INTERNAL_ERR);
00253   }
00254   if(!nextoutmsg.Payload()) {
00255     logger.msg(WARNING, "next element of the chain returned empty payload");
00256     return make_http_fault(logger,*inpayload,outmsg,HTTP_INTERNAL_ERR);
00257   }
00258   PayloadRawInterface* retpayload = NULL;
00259   PayloadStreamInterface* strpayload = NULL;
00260   try {
00261     retpayload = dynamic_cast<PayloadRawInterface*>(nextoutmsg.Payload());
00262   } catch(std::exception& e) { };
00263   if(!retpayload) try {
00264     strpayload = dynamic_cast<PayloadStreamInterface*>(nextoutmsg.Payload());
00265   } catch(std::exception& e) { };
00266   if((!retpayload) && (!strpayload)) {
00267     logger.msg(WARNING, "next element of the chain returned invalid/unsupported payload");
00268     delete nextoutmsg.Payload();
00269     return make_http_fault(logger,*inpayload,outmsg,HTTP_INTERNAL_ERR);
00270   };
00271   if(!ProcessSecHandlers(nextinmsg,"outgoing")) {
00272     delete nextoutmsg.Payload(); return make_http_fault(logger,*inpayload,outmsg,HTTP_BAD_REQUEST); // Maybe not 400 ?
00273   };
00274   // Create HTTP response from raw body content
00275   // Use stream payload of inmsg to send HTTP response
00276   int http_code = HTTP_OK;
00277   const char* http_resp = "OK";
00278   int l = 0;
00279 /*
00280   if(retpayload) {
00281     if(retpayload->BufferPos(0) != 0) {
00282       http_code=HTTP_PARTIAL;
00283       http_resp="Partial content";
00284     } else {
00285       for(int i = 0;;++i) {
00286         if(retpayload->Buffer(i) == NULL) break;
00287         l=retpayload->BufferPos(i) + retpayload->BufferSize(i);
00288       };
00289       if(l != retpayload->Size()) {
00290         http_code=HTTP_PARTIAL;
00291         http_resp="Partial content";
00292       };
00293     };
00294   } else {
00295     if((strpayload->Pos() != 0) || (strpayload->Limit() != strpayload->Size())) {
00296       http_code=HTTP_PARTIAL;
00297       http_resp="Partial content";
00298     };
00299   };
00300 */
00301   PayloadHTTP* outpayload = new PayloadHTTP(http_code,http_resp,*inpayload);
00302   // Use attributes which higher level MCC may have produced for HTTP
00303   for(AttributeIterator i = nextoutmsg.Attributes()->getAll();i.hasMore();++i) {
00304     const char* key = i.key().c_str();
00305     if(strncmp("HTTP:",key,5) == 0) {
00306       key+=5;
00307       // TODO: check for special attributes: method, code, reason, endpoint, etc.
00308       outpayload->Attribute(std::string(key),*i);
00309     };
00310   };
00311   outpayload->KeepAlive(keep_alive);
00312   if(retpayload) {
00313     outpayload->Body(*retpayload);
00314   } else {
00315     outpayload->Body(*strpayload);
00316   }
00317   bool flush_r = outpayload->Flush();
00318   delete outpayload;
00319   outmsg = nextoutmsg;
00320   // Returning empty payload because response is already sent through Flush
00321   PayloadRaw* outpayload_e = new PayloadRaw;
00322   outmsg.Payload(outpayload_e);
00323   if(!flush_r) {
00324     // If flush failed then we can't know if anything HTTPish was 
00325     // already sent. Hence we are just making lower level close
00326     // connection.
00327     logger.msg(WARNING, "Error to flush output payload");
00328     return MCC_Status(SESSION_CLOSE);
00329   };
00330   if(!keep_alive) return MCC_Status(SESSION_CLOSE);
00331   return MCC_Status(STATUS_OK);
00332 }
00333 
00334 MCC_HTTP_Client::MCC_HTTP_Client(Config *cfg):MCC_HTTP(cfg) {
00335   endpoint_=(std::string)((*cfg)["Endpoint"]);
00336   method_=(std::string)((*cfg)["Method"]);
00337 }
00338 
00339 MCC_HTTP_Client::~MCC_HTTP_Client(void) {
00340 }
00341 
00342 MCC_Status MCC_HTTP_Client::process(Message& inmsg,Message& outmsg) {
00343   // Take Raw payload, add HTTP stuf by using PayloadHTTP and
00344   // generate new Raw payload to pass further through chain.
00345   // TODO: do not create new object - use or acqure same one.
00346   // Extracting payload
00347   if(!inmsg.Payload()) return make_raw_fault(outmsg);
00348   PayloadRawInterface* inpayload = NULL;
00349   try {
00350     inpayload = dynamic_cast<PayloadRawInterface*>(inmsg.Payload());
00351   } catch(std::exception& e) { };
00352   if(!inpayload) return make_raw_fault(outmsg);
00353   // Making HTTP request
00354   // Use attributes which higher level MCC may have produced for HTTP
00355   std::string http_method = inmsg.Attributes()->get("HTTP:METHOD");
00356   std::string http_endpoint = inmsg.Attributes()->get("HTTP:ENDPOINT");
00357   if(http_method.empty()) http_method=method_;
00358   if(http_endpoint.empty()) http_endpoint=endpoint_;
00359   PayloadHTTP nextpayload(http_method,http_endpoint);
00360   for(AttributeIterator i = inmsg.Attributes()->getAll();i.hasMore();++i) {
00361     const char* key = i.key().c_str();
00362     if(strncmp("HTTP:",key,5) == 0) {
00363       key+=5;
00364       // TODO: check for special attributes: method, code, reason, endpoint, etc.
00365       if(strcmp(key,"METHOD") == 0) continue;
00366       if(strcmp(key,"ENDPOINT") == 0) continue;
00367       nextpayload.Attribute(std::string(key),*i);
00368     };
00369   };
00370   nextpayload.Attribute("User-Agent","ARC");
00371   nextpayload.Body(*inpayload,false);
00372   nextpayload.Flush();
00373   // Creating message to pass to next MCC and setting new payload..
00374   Message nextinmsg = inmsg;
00375   nextinmsg.Payload(&nextpayload);
00376 
00377   // Call next MCC
00378   MCCInterface* next = Next();
00379   if(!next) return make_raw_fault(outmsg);
00380   Message nextoutmsg = outmsg; nextoutmsg.Payload(NULL);
00381   MCC_Status ret = next->process(nextinmsg,nextoutmsg);
00382   // Do checks and process response - supported response so far is stream
00383   // Generated result is HTTP payload with Raw and Stream interfaces
00384   if(!ret) {
00385     if(nextoutmsg.Payload()) delete nextoutmsg.Payload();
00386     return make_raw_fault(outmsg);
00387   };
00388   if(!nextoutmsg.Payload()) return make_raw_fault(outmsg);
00389   PayloadStreamInterface* retpayload = NULL;
00390   try {
00391     retpayload = dynamic_cast<PayloadStreamInterface*>(nextoutmsg.Payload());
00392   } catch(std::exception& e) { };
00393   if(!retpayload) { delete nextoutmsg.Payload(); return make_raw_fault(outmsg); };
00394   // Stream retpayload becomes owned by outpayload. This is needed because
00395   // HTTP payload may postpone extracting information from stream till demanded.
00396   PayloadHTTP* outpayload  = new PayloadHTTP(*retpayload,true);
00397   if(!outpayload) { delete retpayload; return make_raw_fault(outmsg); };
00398   if(!(*outpayload)) { delete outpayload; return make_raw_fault(outmsg); };
00399   // Check for closed connection during response - not suitable in client mode
00400   if(outpayload->Method() == "END") { delete outpayload; return make_raw_fault(outmsg); };
00401   outmsg = nextoutmsg;
00402   // Payload returned by next.process is not destroyed here because
00403   // it is now owned by outpayload.
00404   outmsg.Payload(outpayload);
00405   outmsg.Attributes()->set("HTTP:CODE",tostring(outpayload->Code()));
00406   outmsg.Attributes()->set("HTTP:REASON",outpayload->Reason());
00407   for(std::map<std::string,std::string>::const_iterator i =
00408       outpayload->Attributes().begin();i!=outpayload->Attributes().end();++i) {
00409     outmsg.Attributes()->add("HTTP:"+i->first,i->second);
00410   };
00411   return MCC_Status(STATUS_OK);
00412 }
00413 
00414 } // namespace Arc