diff --git a/src/XrdHttp/XrdHttpProtocol.cc b/src/XrdHttp/XrdHttpProtocol.cc index 2b1e74fe7a0..a2935c2ac37 100644 --- a/src/XrdHttp/XrdHttpProtocol.cc +++ b/src/XrdHttp/XrdHttpProtocol.cc @@ -94,6 +94,7 @@ BIO *XrdHttpProtocol::sslbio_err = 0; XrdCryptoFactory *XrdHttpProtocol::myCryptoFactory = 0; XrdHttpSecXtractor *XrdHttpProtocol::secxtractor = 0; XrdHttpExtHandler *XrdHttpProtocol::exthandler = 0; +std::map< std::string, std::string > XrdHttpProtocol::hdr2cgimap; static const unsigned char *s_server_session_id_context = (const unsigned char *) "XrdHTTPSessionCtx"; static int s_server_session_id_context_len = 18; @@ -868,6 +869,7 @@ int XrdHttpProtocol::Config(const char *ConfigFN, XrdOucEnv *myEnv) { else if TS_Xeq("staticredir", xstaticredir); else if TS_Xeq("staticpreload", xstaticpreload); else if TS_Xeq("listingdeny", xlistdeny); + else if TS_Xeq("header2cgi", xheader2cgi); else { eDest.Say("Config warning: ignoring unknown directive '", var, "'."); Config.Echo(); @@ -2224,7 +2226,68 @@ int XrdHttpProtocol::xexthandler(XrdOucStream & Config, const char *ConfigFN, Xr +/* Function: xheader2cgi + * + * Purpose: To parse the directive: header2cgi + * + * the name of an incoming HTTP header + * to be transformed + * the name to be given when adding it to the cgi info + * that is kept only internally + * + * Output: 0 upon success or !0 upon failure. + */ +int XrdHttpProtocol::xheader2cgi(XrdOucStream & Config) { + char *val, keybuf[1024], parmbuf[1024]; + char *parm; + + // Get the path + // + val = Config.GetWord(); + if (!val || !val[0]) { + eDest.Emsg("Config", "No headerkey specified."); + return 1; + } else { + + // Trim the beginning, in place + while ( *val && !isalnum(*val) ) val++; + strcpy(keybuf, val); + + // Trim the end, in place + char *pp; + pp = keybuf + strlen(keybuf) - 1; + while ( (pp >= keybuf) && (!isalnum(*pp)) ) { + *pp = '\0'; + pp--; + } + + parm = Config.GetWord(); + + // Trim the beginning, in place + while ( *parm && !isalnum(*parm) ) parm++; + strcpy(parmbuf, parm); + + // Trim the end, in place + pp = parmbuf + strlen(parmbuf) - 1; + while ( (pp >= parmbuf) && (!isalnum(*pp)) ) { + *pp = '\0'; + pp--; + } + + // Add this mapping to the map that will be used + try { + hdr2cgimap[keybuf] = parmbuf; + } catch ( ... ) { + eDest.Emsg("Config", "Can't insert new header2cgi rule. key: '", keybuf, "'"); + return 1; + } + + } + + + return 0; +} diff --git a/src/XrdHttp/XrdHttpProtocol.hh b/src/XrdHttp/XrdHttpProtocol.hh index 4731112d5d5..51c0efe4a35 100644 --- a/src/XrdHttp/XrdHttpProtocol.hh +++ b/src/XrdHttp/XrdHttpProtocol.hh @@ -167,7 +167,8 @@ private: static int xsslcafile(XrdOucStream &Config); static int xsslverifydepth(XrdOucStream &Config); static int xsecretkey(XrdOucStream &Config); - + static int xheader2cgi(XrdOucStream &Config); + static XrdHttpSecXtractor *secxtractor; static XrdHttpExtHandler *exthandler; @@ -343,5 +344,8 @@ protected: /// Our role static kXR_int32 myRole; + /// Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption + static std::map< std::string, std::string > hdr2cgimap; + }; #endif diff --git a/src/XrdHttp/XrdHttpReq.cc b/src/XrdHttp/XrdHttpReq.cc index 3168d92a5ed..108a354698b 100644 --- a/src/XrdHttp/XrdHttpReq.cc +++ b/src/XrdHttp/XrdHttpReq.cc @@ -192,6 +192,23 @@ int XrdHttpReq::parseLine(char *line, int len) { } else if (!strcmp(key, "Expect") && strstr(val, "100-continue")) { sendcontinue = true; + } else { + // Some headers need to be translated into "local" cgi info. In theory they should already be quoted + std::map< std:: string, std:: string > ::iterator it = prot->hdr2cgimap.find(key); + if (it != prot->hdr2cgimap.end()) { + std:: string s; + s.assign(val, line+len-val); + trim(s); + + if (hdr2cgistr.length() > 0) { + hdr2cgistr.append("&"); + } + hdr2cgistr.append(it->second); + hdr2cgistr.append("="); + hdr2cgistr.append(s); + + + } } // We memorize the heaers also as a string @@ -767,7 +784,25 @@ int XrdHttpReq::ProcessHTTPReq() { } - + /// If we have to add extra header information, add it here. + if (!hdr2cgistr.empty()) { + const char *p = strchr(resourceplusopaque.c_str(), '?'); + if (p) { + resourceplusopaque.append("&"); + } else { + resourceplusopaque.append("?"); + } + + char *q = quote(hdr2cgistr.c_str()); + resourceplusopaque.append(q); + TRACEI(DEBUG, "Appended header fields to opaque info: '" << hdr2cgistr << "'"); + free(q); + + // Once we've appended the authorization to the full resource+opaque string, + // reset the authz to empty: this way, any operation that triggers repeated ProcessHTTPReq + // calls won't also trigger multiple copies of the authz. + hdr2cgistr = ""; + } // // Here we process the request locally @@ -2232,6 +2267,7 @@ void XrdHttpReq::reset() { host = ""; destination = ""; + hdr2cgistr = ""; iovP = 0; iovN = 0; diff --git a/src/XrdHttp/XrdHttpReq.hh b/src/XrdHttp/XrdHttpReq.hh index 91898076d47..68d051bf04f 100644 --- a/src/XrdHttp/XrdHttpReq.hh +++ b/src/XrdHttp/XrdHttpReq.hh @@ -195,7 +195,9 @@ public: /// The destination field specified in the req std::string destination; - + /// Additional opaque info that may come from the hdr2cgi directive + std::string hdr2cgistr; + // // Area for coordinating request and responses to/from the bridge // diff --git a/src/XrdHttp/XrdHttpUtils.cc b/src/XrdHttp/XrdHttpUtils.cc index 94b6f096607..bfcac258392 100644 --- a/src/XrdHttp/XrdHttpUtils.cc +++ b/src/XrdHttp/XrdHttpUtils.cc @@ -318,7 +318,7 @@ char *unquote(char *str) { // Quote a string and return a new one -char *quote(char *str) { +char *quote(const char *str) { int l = strlen(str); char *r = (char *) malloc(l*3 + 1); r[0] = '\0'; diff --git a/src/XrdHttp/XrdHttpUtils.hh b/src/XrdHttp/XrdHttpUtils.hh index cc15be94728..ab5acbcf80d 100644 --- a/src/XrdHttp/XrdHttpUtils.hh +++ b/src/XrdHttp/XrdHttpUtils.hh @@ -73,7 +73,7 @@ int compareHash( // Create a new quoted string -char *quote(char *str); +char *quote(const char *str); // unquote a string and return a new one char *unquote(char *str);