Skip to content

Commit ef47176

Browse files
dynamic-entropyamadio
authored andcommitted
[XrdHttp] Use mapXrdErrToHttp to improve WebDAV error reporting
- Map XrdErrors to HTTP status codes - Remove old error mapping - Update XrdOssTests to allow mimic failures on read/write independently
1 parent e66f729 commit ef47176

File tree

4 files changed

+135
-178
lines changed

4 files changed

+135
-178
lines changed

src/XrdHttp/XrdHttpReq.cc

Lines changed: 33 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -817,124 +817,23 @@ void XrdHttpReq::parseResource(char *res) {
817817
resourceplusopaque.append('?');
818818
resourceplusopaque.append(p + 1);
819819
}
820-
821-
822-
823820
}
824821

825-
void XrdHttpReq::sendWebdavErrorMessage(
826-
XResponseType xrdresp, XErrorCode xrderrcode, XrdHttpReq::ReqType httpVerb,
827-
XRequestTypes xrdOperation, std::string etext, const char *desc,
828-
const char *header_to_add, bool keepalive) {
829-
int code{0};
830-
std::string errCode{"Unknown"};
831-
std::string statusText;
822+
void XrdHttpReq::generateWebdavErrMsg() {
832823

833-
switch (httpVerb) {
834-
case XrdHttpReq::rtPUT:
835-
if (xrdOperation == kXR_open) {
836-
if (xrderrcode == kXR_isDirectory) {
837-
code = 409;
838-
errCode = "8.1";
839-
} else if (xrderrcode == kXR_NoSpace) {
840-
code = 507;
841-
errCode = "8.3.1";
842-
} else if (xrderrcode == kXR_overQuota) {
843-
code = 507;
844-
errCode = "8.3.2";
845-
} else if (xrderrcode == kXR_NotAuthorized) {
846-
code = 403;
847-
errCode = "9.3";
848-
}
849-
} else if (xrdOperation == kXR_write) {
850-
if (xrderrcode == kXR_NoSpace) {
851-
code = 507;
852-
errCode = "8.4.1";
853-
} else if (xrderrcode == kXR_overQuota) {
854-
code = 507;
855-
errCode = "8.4.2";
856-
}
857-
}
858-
break;
859-
default:
860-
break;
824+
// This block is only used when sending an "X-Transfer-Status" trailer response.
825+
// We set the body to "OK" so that the trailer becomes "X-Transfer-Status: 200 OK",
826+
// indicating a successful transfer.
827+
if (xrdresp == kXR_ok) {
828+
httpStatusCode = 200;
829+
httpErrorBody = "OK";
830+
return;
861831
}
862832

863-
// Remove the if at the end of project completion
864-
// Till then status text defaults to as set by mapXrdResponseToHttpStatus
865-
if (code != 0) {
866-
httpStatusCode = code;
867-
httpErrorCode = errCode;
868-
httpErrorBody = "ERROR: " + errCode + ": " + etext + "\n";
833+
// default error
834+
httpStatusCode = mapXrdErrToHttp(xrderrcode);
835+
httpErrorBody = etext + "\n";
869836

870-
prot->SendSimpleResp(httpStatusCode, desc, header_to_add,
871-
httpErrorBody.c_str(), httpErrorBody.length(),
872-
keepalive);
873-
}
874-
}
875-
876-
// Map an XRootD error code to an appropriate HTTP status code and message
877-
// The variables httpStatusCode and httpErrorBody will be populated
878-
879-
void XrdHttpReq::mapXrdErrorToHttpStatus() {
880-
// Set default HTTP status values for an error case
881-
httpStatusCode = 500;
882-
httpErrorBody = "Unrecognized error";
883-
884-
// Do error mapping
885-
if (xrdresp == kXR_error) {
886-
switch (xrderrcode) {
887-
case kXR_AuthFailed:
888-
httpStatusCode = 401; httpErrorBody = "Unauthorized";
889-
break;
890-
case kXR_NotAuthorized:
891-
httpStatusCode = 403; httpErrorBody = "Operation not permitted";
892-
break;
893-
case kXR_NotFound:
894-
httpStatusCode = 404; httpErrorBody = "File not found";
895-
break;
896-
case kXR_Unsupported:
897-
httpStatusCode = 405; httpErrorBody = "Operation not supported";
898-
break;
899-
case kXR_FileLocked:
900-
httpStatusCode = 423; httpErrorBody = "Resource is a locked";
901-
break;
902-
case kXR_isDirectory:
903-
httpStatusCode = 409; httpErrorBody = "Resource is a directory";
904-
break;
905-
case kXR_ItExists:
906-
if(request != ReqType::rtDELETE) {
907-
httpStatusCode = 409; httpErrorBody = "File already exists";
908-
} else {
909-
// In the case the XRootD layer returns a kXR_ItExists after a deletion
910-
// was submitted, we return a 405 status code with the error message set by
911-
// the XRootD layer
912-
httpStatusCode = 405;
913-
}
914-
break;
915-
case kXR_InvalidRequest:
916-
httpStatusCode = 405; httpErrorBody = "Method is not allowed";
917-
break;
918-
case kXR_noserver:
919-
httpStatusCode = 502; httpErrorBody = "Bad Gateway";
920-
break;
921-
case kXR_TimerExpired:
922-
httpStatusCode = 504; httpErrorBody = "Gateway timeout";
923-
break;
924-
default:
925-
break;
926-
}
927-
928-
if (!etext.empty()) httpErrorBody = etext;
929-
930-
TRACEI(REQ, "PostProcessHTTPReq mapping Xrd error [" << xrderrcode
931-
<< "] to status code [" << httpStatusCode << "]");
932-
933-
httpErrorBody += "\n";
934-
} else {
935-
httpStatusCode = 200;
936-
httpErrorBody = "OK";
937-
}
938837
}
939838

940839
int XrdHttpReq::ProcessHTTPReq() {
@@ -1007,14 +906,9 @@ int XrdHttpReq::ProcessHTTPReq() {
1007906
switch (request) {
1008907
case XrdHttpReq::rtUnset:
1009908
case XrdHttpReq::rtUnknown:
1010-
{
1011-
prot->SendSimpleResp(400, NULL, NULL, (char *) "Request unknown", 0, false);
1012-
reset();
1013-
return -1;
1014-
}
1015-
case XrdHttpReq::rtMalformed:
1016-
{
1017-
prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed", 0, false);
909+
case XrdHttpReq::rtMalformed: {
910+
generateWebdavErrMsg();
911+
prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
1018912
reset();
1019913
return -1;
1020914
}
@@ -1035,7 +929,8 @@ int XrdHttpReq::ProcessHTTPReq() {
1035929
m_req_cksum = prot->cksumHandler.getChecksumToRun(m_req_digest);
1036930
if(!m_req_cksum) {
1037931
// No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1038-
prot->SendSimpleResp(403, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
932+
// We should not send body in response to HEAD request
933+
prot->SendSimpleResp(HTTP_METHOD_NOT_ALLOWED, NULL, NULL, NULL, 0, false);
1039934
return -1;
1040935
}
1041936
if (!opaque) {
@@ -1157,7 +1052,7 @@ int XrdHttpReq::ProcessHTTPReq() {
11571052
m_req_cksum = prot->cksumHandler.getChecksumToRun(m_req_digest);
11581053
if(!m_req_cksum) {
11591054
// No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1160-
prot->SendSimpleResp(403, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
1055+
prot->SendSimpleResp(HTTP_METHOD_NOT_ALLOWED, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
11611056
return -1;
11621057
}
11631058
m_resource_with_digest = resourceplusopaque;
@@ -1185,7 +1080,7 @@ int XrdHttpReq::ProcessHTTPReq() {
11851080
memcpy(xrdreq.close.fhandle, fhandle, 4);
11861081

11871082
if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1188-
mapXrdErrorToHttpStatus();
1083+
generateWebdavErrMsg();
11891084
return sendFooterError("Could not run close request on the bridge");
11901085
}
11911086
return 0;
@@ -1225,7 +1120,7 @@ int XrdHttpReq::ProcessHTTPReq() {
12251120
xrdreq.dirlist.dlen = htonl(l);
12261121

12271122
if (!prot->Bridge->Run((char *) &xrdreq, (char *) res.c_str(), l)) {
1228-
mapXrdErrorToHttpStatus();
1123+
generateWebdavErrMsg();
12291124
prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
12301125
sendFooterError("Could not run listing request on the bridge");
12311126
return -1;
@@ -1327,7 +1222,7 @@ int XrdHttpReq::ProcessHTTPReq() {
13271222
}
13281223

13291224
if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1330-
mapXrdErrorToHttpStatus();
1225+
generateWebdavErrMsg();
13311226
return sendFooterError("Could not run read request on the bridge");
13321227
}
13331228
} else {
@@ -1336,7 +1231,7 @@ int XrdHttpReq::ProcessHTTPReq() {
13361231
length = ReqReadV(readChunkList);
13371232

13381233
if (!prot->Bridge->Run((char *) &xrdreq, (char *) &ralist[0], length)) {
1339-
mapXrdErrorToHttpStatus();
1234+
generateWebdavErrMsg();
13401235
return sendFooterError("Could not run ReadV request on the bridge");
13411236
}
13421237

@@ -1473,7 +1368,7 @@ int XrdHttpReq::ProcessHTTPReq() {
14731368

14741369
TRACEI(REQ, "XrdHTTP PUT: Writing chunk of size " << bytes_to_write << " starting with '" << *(prot->myBuffStart) << "'" << " with " << chunk_bytes_remaining << " bytes remaining in the chunk");
14751370
if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_write)) {
1476-
mapXrdErrorToHttpStatus();
1371+
generateWebdavErrMsg();
14771372
return sendFooterError("Could not run write request on the bridge");
14781373
}
14791374
// If there are more bytes in the buffer, then immediately call us after the
@@ -1496,7 +1391,7 @@ int XrdHttpReq::ProcessHTTPReq() {
14961391

14971392
TRACEI(REQ, "Writing " << bytes_to_read);
14981393
if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_read)) {
1499-
mapXrdErrorToHttpStatus();
1394+
generateWebdavErrMsg();
15001395
return sendFooterError("Could not run write request on the bridge");
15011396
}
15021397

@@ -1519,7 +1414,7 @@ int XrdHttpReq::ProcessHTTPReq() {
15191414

15201415

15211416
if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1522-
mapXrdErrorToHttpStatus();
1417+
generateWebdavErrMsg();
15231418
return sendFooterError("Could not run close request on the bridge");
15241419
}
15251420

@@ -2134,7 +2029,7 @@ void XrdHttpReq::setTransferStatusHeader(std::string &header) {
21342029
int XrdHttpReq::PostProcessHTTPReq(bool final_) {
21352030

21362031
TRACEI(REQ, "PostProcessHTTPReq req: " << request << " reqstate: " << reqstate << " final_:" << final_);
2137-
mapXrdErrorToHttpStatus();
2032+
generateWebdavErrMsg();
21382033

21392034
if(xrdreq.set.requestid == htons(kXR_set)) {
21402035
// We have set the user agent, if it fails we return a 500 error, otherwise the callback is successful --> we continue
@@ -2370,8 +2265,7 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) {
23702265
{
23712266
if (!fopened) {
23722267
if (xrdresp != kXR_ok) {
2373-
sendWebdavErrorMessage(xrdresp, xrderrcode, XrdHttpReq::rtPUT,
2374-
kXR_open, etext, NULL, NULL, keepalive);
2268+
prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
23752269
return -1;
23762270
}
23772271

@@ -2389,9 +2283,13 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) {
23892283
break;
23902284
} else {
23912285

2392-
23932286
// If we are here it's too late to send a proper error message...
2394-
if (xrdresp == kXR_error) return -1;
2287+
// However, we decide to send a response anyway before we close the connection
2288+
// We are not sure if sending a final response before reading the entire request
2289+
if (xrdresp == kXR_error) {
2290+
prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2291+
return -1;
2292+
}
23952293

23962294
if (ntohs(xrdreq.header.requestid) == kXR_write) {
23972295
int l = ntohl(xrdreq.write.dlen);
@@ -2416,8 +2314,7 @@ int XrdHttpReq::PostProcessHTTPReq(bool final_) {
24162314
prot->SendSimpleResp(201, NULL, NULL, (char *)":-)", 0, keepalive);
24172315
return keepalive ? 1 : -1;
24182316
} else {
2419-
sendWebdavErrorMessage(xrdresp, xrderrcode, XrdHttpReq::rtPUT,
2420-
kXR_close, etext, NULL, NULL, keepalive);
2317+
prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
24212318
return -1;
24222319
}
24232320
}

src/XrdHttp/XrdHttpReq.hh

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,9 @@ private:
162162

163163
// Parse a resource string, typically a filename, setting the resource field and the opaque data
164164
void parseResource(char *url);
165-
// Map an XRootD error code to an appropriate HTTP status code and message
166-
void mapXrdErrorToHttpStatus();
167165

168166
// Set Webdav Error messages
169-
void sendWebdavErrorMessage(XResponseType xrdresp, XErrorCode xrderrcode,
170-
ReqType httpVerb, XRequestTypes xrdOperation,
171-
std::string etext, const char *desc,
172-
const char *header_to_add, bool keepalive);
167+
void generateWebdavErrMsg();
173168

174169
// Sanitize the resource from http[s]://[host]/ questionable prefix
175170
void sanitizeResourcePfx();

0 commit comments

Comments
 (0)