/* **************************************************************************** * * jsonTreat - */ std::string jsonTreat(const char* content, ConnectionInfo* ciP, ParseData* parseDataP, RequestType request, std::string payloadWord, JsonRequest** reqPP) { std::string res = "OK"; JsonRequest* reqP = jsonRequestGet(request, ciP->method); LM_T(LmtParse, ("Treating a JSON request: '%s'", content)); ciP->parseDataP = parseDataP; if (reqP == NULL) { std::string errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", requestType(request), SccBadRequest, std::string("Sorry, no request treating object found for RequestType '") + requestType(request) + "'"); LM_RE(errorReply, ("Sorry, no request treating object found for RequestType %d (%s)", request, requestType(request))); } if (reqPP != NULL) *reqPP = reqP; LM_T(LmtParse, ("Treating '%s' request", reqP->keyword.c_str())); reqP->init(parseDataP); try { res = jsonParse(ciP, content, reqP->keyword, reqP->parseVector, parseDataP); if (ciP->inCompoundValue == true) orion::compoundValueEnd(ciP, parseDataP); if ((lmTraceIsSet(LmtCompoundValueShow)) && (ciP->compoundValueP != NULL)) ciP->compoundValueP->shortShow("after parse: "); } catch (std::exception &e) { std::string errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", reqP->keyword, SccBadRequest, std::string("JSON Parse Error: ") + e.what()); LM_E(("JSON Parse Error: '%s'", e.what())); LM_RE(errorReply, (res.c_str())); } if (res != "OK") { LM_E(("JSON parse error: %s", res.c_str())); ciP->httpStatusCode = SccBadRequest; std::string answer = restErrorReplyGet(ciP, ciP->outFormat, "", payloadWord, ciP->httpStatusCode, res); return answer; } reqP->present(parseDataP); LM_T(LmtParseCheck, ("Calling check for JSON parsed tree (%s)", ciP->payloadWord)); res = reqP->check(parseDataP, ciP); reqP->present(parseDataP); return res; }
/* **************************************************************************** * * contentTypeCheck - * * NOTE * Any failure about Content-Type is an error in the HTTP layer (not exclusively NGSI) * so we don't want to use the default 200 * * NOTE * In version 1 of the protocol, we admit ONLY application/json * In version 2 of the protocol, we admit ONLY application/json and text/plain */ static int contentTypeCheck(ConnectionInfo* ciP) { // // Five cases: // 1. If there is no payload, the Content-Type is not interesting // 2. Payload present but no Content-Type // 3. Content-Type present but not supported // 4. API version 2 and not 'application/json' || text/plain // // Case 1 if (ciP->httpHeaders.contentLength == 0) { return 0; } // Case 2 if (ciP->httpHeaders.contentType == "") { std::string details = "Content-Type header not used, default application/octet-stream is not supported"; ciP->httpStatusCode = SccUnsupportedMediaType; ciP->answer = restErrorReplyGet(ciP, "", "OrionError", SccUnsupportedMediaType, details); ciP->httpStatusCode = SccUnsupportedMediaType; return 1; } // Case 3 if ((ciP->apiVersion == "v1") && (ciP->httpHeaders.contentType != "application/json")) { std::string details = std::string("not supported content type: ") + ciP->httpHeaders.contentType; ciP->httpStatusCode = SccUnsupportedMediaType; ciP->answer = restErrorReplyGet(ciP, "", "OrionError", SccUnsupportedMediaType, details); ciP->httpStatusCode = SccUnsupportedMediaType; return 1; } // Case 4 if ((ciP->apiVersion == "v2") && (ciP->httpHeaders.contentType != "application/json") && (ciP->httpHeaders.contentType != "text/plain")) { std::string details = std::string("not supported content type: ") + ciP->httpHeaders.contentType; ciP->httpStatusCode = SccUnsupportedMediaType; ciP->answer = restErrorReplyGet(ciP, "", "OrionError", SccUnsupportedMediaType, details); ciP->httpStatusCode = SccUnsupportedMediaType; return 1; } return 0; }
/* **************************************************************************** * * putAvailabilitySubscriptionConvOp - */ std::string putAvailabilitySubscriptionConvOp ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string subscriptionId = (compV[0] == "v1")? compV[3] : compV[2]; UpdateContextAvailabilitySubscriptionRequest* ucasP = &parseDataP->ucas.res; if (subscriptionId != ucasP->subscriptionId.get()) { std::string out; out = restErrorReplyGet(ciP, ciP->outFormat, "", "updateContextAvailabilitySubscription", SccBadRequest, std::string("unmatching subscriptionId URI/payload: /") + subscriptionId + "/ vs /" + ucasP->subscriptionId.get() + "/"); return out; } return postUpdateContextAvailabilitySubscription(ciP, components, compV, parseDataP); }
/* **************************************************************************** * * putSubscriptionConvOp - */ std::string putSubscriptionConvOp ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string subscriptionId = compV[2]; UpdateContextSubscriptionRequest* ucsrP = &parseDataP->ucsr.res; if (subscriptionId != ucsrP->subscriptionId.get()) { std::string out; out = restErrorReplyGet(ciP, ciP->outFormat, "", "updateContextSubscription", SccBadRequest, std::string("unmatching subscriptionId URI/payload: '") + subscriptionId + "' vs '" + ucsrP->subscriptionId.get() + "'"); return out; } return postUpdateContextSubscription(ciP, components, compV, parseDataP); }
/* **************************************************************************** * * badNgsi10Request - */ std::string badNgsi10Request(ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP) { std::string answer; answer = restErrorReplyGet(ciP, ciP->outFormat, "", ciP->payloadWord, SccBadRequest, std::string("ngsi10 service '") + ciP->url +"' not found"); return answer; }
/* **************************************************************************** * * contentTypeCheck - * * NOTE * Any failure about Content-Type is an error in the HTTP layer (not exclusively NGSI) * so we don't want to use the default 200 */ static int contentTypeCheck(ConnectionInfo* ciP) { std::string details = ""; // // Four cases: // 1. If there is no payload, the Content-Type is not interesting // 2. Payload present but no Content-Type // 3. text/xml used and acceptTextXml is setto true (iotAgent only) // 4. Content-Type present but not supported // Case 1 if (ciP->httpHeaders.contentLength == 0) return 0; // Case 2 if (ciP->httpHeaders.contentType == "") { std::string details = "Content-Type header not used, default application/octet-stream is not supported"; ciP->httpStatusCode = SccUnsupportedMediaType; ciP->answer = restErrorReplyGet(ciP, ciP->outFormat, "", "OrionError", SccUnsupportedMediaType, details); ciP->httpStatusCode = SccUnsupportedMediaType; return 1; } // Case 3 if ((acceptTextXml == true) && (ciP->httpHeaders.contentType == "text/xml")) return 0; // Case 4 if ((ciP->httpHeaders.contentType != "application/xml") && (ciP->httpHeaders.contentType != "application/json")) { std::string details = std::string("not supported content type: ") + ciP->httpHeaders.contentType; ciP->httpStatusCode = SccUnsupportedMediaType; ciP->answer = restErrorReplyGet(ciP, ciP->outFormat, "", "OrionError", SccUnsupportedMediaType, details); ciP->httpStatusCode = SccUnsupportedMediaType; return 1; } return 0; }
/* **************************************************************************** * * badNgsi10Request - */ std::string badNgsi10Request ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { std::string answer; std::string details = std::string("service '") + ciP->url + "' not found"; alarmMgr.badInput(clientIp, details); restErrorReplyGet(ciP, SccBadRequest, "service not found", &answer); return answer; }
/* **************************************************************************** * * outMimeTypeCheck - */ static int outMimeTypeCheck(ConnectionInfo* ciP) { ciP->outMimeType = wantedOutputSupported(ciP->apiVersion, ciP->httpHeaders.accept, &ciP->charset); if (ciP->outMimeType == NOMIMETYPE) { /* This is actually an error in the HTTP layer (not exclusively NGSI) so we don't want to use the default 200 */ ciP->httpStatusCode = SccNotAcceptable; ciP->answer = restErrorReplyGet(ciP, "", "OrionError", SccNotAcceptable, std::string("acceptable MIME types: application/json. Accept header in request: ") + ciP->httpHeaders.accept); ciP->outMimeType = JSON; // We use JSON as default mimeType ciP->httpStatusCode = SccNotAcceptable; return 1; } return 0; }
/* **************************************************************************** * * outFormatCheck - */ static int outFormatCheck(ConnectionInfo* ciP) { ciP->outFormat = wantedOutputSupported(ciP->httpHeaders.accept, &ciP->charset); if (ciP->outFormat == NOFORMAT) { /* This is actually an error in the HTTP layer (not exclusively NGSI) so we don't want to use the default 200 */ ciP->httpStatusCode = SccNotAcceptable; ciP->answer = restErrorReplyGet(ciP, XML, "", "OrionError", SccNotAcceptable, std::string("acceptable MIME types: application/xml, application/json. Accept header in request: ") + ciP->httpHeaders.accept); ciP->outFormat = XML; // We use XML as default format ciP->httpStatusCode = SccNotAcceptable; return 1; } return 0; }
/* **************************************************************************** * * connectionTreat - * * This is the MHD_AccessHandlerCallback function for MHD_start_daemon * This function returns: * o MHD_YES if the connection was handled successfully * o MHD_NO if the socket must be closed due to a serious error * * - This function is called once when the headers are read and the ciP is created. * - Then it is called for data payload and once all the payload an acknowledgement * must be done, setting *upload_data_size to ZERO. * - The last call is made with *upload_data_size == 0 and now is when the connection * is open to send responses. * * Call 1: *con_cls == NULL * Call 2: *con_cls != NULL AND *upload_data_size != 0 * Call 3: *con_cls != NULL AND *upload_data_size == 0 */ static int connectionTreat ( void* cls, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls ) { ConnectionInfo* ciP = (ConnectionInfo*) *con_cls; size_t dataLen = *upload_data_size; // 1. First call - setup ConnectionInfo and get/check HTTP headers if (ciP == NULL) { // // IP Address and port of caller // char ip[32]; unsigned short port = 0; const union MHD_ConnectionInfo* mciP = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); if (mciP != NULL) { port = (mciP->client_addr->sa_data[0] << 8) + mciP->client_addr->sa_data[1]; snprintf(ip, sizeof(ip), "%d.%d.%d.%d", mciP->client_addr->sa_data[2] & 0xFF, mciP->client_addr->sa_data[3] & 0xFF, mciP->client_addr->sa_data[4] & 0xFF, mciP->client_addr->sa_data[5] & 0xFF); } else { port = 0; snprintf(ip, sizeof(ip), "IP unknown"); } // // ConnectionInfo // if ((ciP = new ConnectionInfo(url, method, version, connection)) == NULL) { LM_E(("Runtime Error (error allocating ConnectionInfo)")); return MHD_NO; } *con_cls = (void*) ciP; // Pointer to ConnectionInfo for subsequent calls ciP->port = port; ciP->ip = ip; // // Transaction starts here // LM_TRANSACTION_START("from", ip, port, url); // Incoming REST request starts // // URI parameters // ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = DEFAULT_PARAM_NOTIFY_FORMAT; ciP->uriParam[URI_PARAM_PAGINATION_OFFSET] = DEFAULT_PAGINATION_OFFSET; ciP->uriParam[URI_PARAM_PAGINATION_LIMIT] = DEFAULT_PAGINATION_LIMIT; ciP->uriParam[URI_PARAM_PAGINATION_DETAILS] = DEFAULT_PAGINATION_DETAILS; MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, uriArgumentGet, ciP); if (ciP->httpStatusCode != SccOk) { LM_W(("Bad Input (error in URI parameters)")); restReply(ciP, ciP->answer); return MHD_YES; } LM_T(LmtUriParams, ("notifyFormat: '%s'", ciP->uriParam[URI_PARAM_NOTIFY_FORMAT].c_str())); MHD_get_connection_values(connection, MHD_HEADER_KIND, httpHeaderGet, &ciP->httpHeaders); char tenant[128]; ciP->tenantFromHttpHeader = strToLower(tenant, ciP->httpHeaders.tenant.c_str(), sizeof(tenant)); LM_T(LmtTenant, ("HTTP tenant: '%s'", ciP->httpHeaders.tenant.c_str())); ciP->outFormat = wantedOutputSupported(ciP->httpHeaders.accept, &ciP->charset); if (ciP->outFormat == NOFORMAT) ciP->outFormat = XML; // XML is default output format ciP->servicePath = ciP->httpHeaders.servicePath; if (servicePathSplit(ciP) != 0) { LM_W(("Bad Input (error in ServicePath http-header)")); restReply(ciP, ciP->answer); } if (contentTypeCheck(ciP) != 0) { LM_W(("Bad Input (invalid mime-type in Content-Type http-header)")); restReply(ciP, ciP->answer); } else if (outFormatCheck(ciP) != 0) { LM_W(("Bad Input (invalid mime-type in Accept http-header)")); restReply(ciP, ciP->answer); } else ciP->inFormat = formatParse(ciP->httpHeaders.contentType, NULL); // Set default mime-type for notifications if (ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] == "") { if (ciP->outFormat == XML) ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = "XML"; else if (ciP->outFormat == JSON) ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = "JSON"; else ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = "XML"; LM_T(LmtUriParams, ("'default' value for notifyFormat (ciP->outFormat == %d)): '%s'", ciP->outFormat, ciP->uriParam[URI_PARAM_NOTIFY_FORMAT].c_str())); } return MHD_YES; } // // 2. Data gathering calls // // 2-1. Data gathering calls, just wait // 2-2. Last data gathering call, acknowledge the receipt of data // if (dataLen != 0) { if (dataLen == ciP->httpHeaders.contentLength) { if (ciP->httpHeaders.contentLength <= PAYLOAD_MAX_SIZE) { if (ciP->httpHeaders.contentLength > STATIC_BUFFER_SIZE) ciP->payload = (char*) malloc(ciP->httpHeaders.contentLength + 1); else ciP->payload = static_buffer; ciP->payloadSize = dataLen; memcpy(ciP->payload, upload_data, dataLen); ciP->payload[dataLen] = 0; } else { char details[256]; snprintf(details, sizeof(details), "payload size: %d, max size supported: %d", ciP->httpHeaders.contentLength, PAYLOAD_MAX_SIZE); ciP->answer = restErrorReplyGet(ciP, ciP->outFormat, "", ciP->url, SccRequestEntityTooLarge, details); ciP->httpStatusCode = SccRequestEntityTooLarge; } // All payload received, acknowledge! *upload_data_size = 0; } else LM_T(LmtPartialPayload, ("Got %d of payload of %d bytes", dataLen, ciP->httpHeaders.contentLength)); return MHD_YES; } // 3. Finally, serve the request (unless an error has occurred) if (((ciP->verb == POST) || (ciP->verb == PUT)) && (ciP->httpHeaders.contentLength == 0) && (strncasecmp(ciP->url.c_str(), "/log/", 5) != 0)) { std::string errorMsg = restErrorReplyGet(ciP, ciP->outFormat, "", url, SccLengthRequired, "Zero/No Content-Length in PUT/POST request"); ciP->httpStatusCode = SccLengthRequired; restReply(ciP, errorMsg); } else if (ciP->answer != "") restReply(ciP, ciP->answer); else serveFunction(ciP); return MHD_YES; }
/* **************************************************************************** * * xmlTreat - */ std::string xmlTreat ( const char* content, ConnectionInfo* ciP, ParseData* parseDataP, RequestType request, std::string payloadWord, XmlRequest** reqPP, std::string* errorMsgP ) { xml_document<> doc; char* xmlPayload = (char*) content; // // If the payload is empty, the XML parsing library does an assert // and the broker dies. // Therefore, this check here is important, to avoid death. // // 'OK' is returned as there is no error to send a request without payload. // if ((content == NULL) || (*content == 0)) { return "OK"; } try { doc.parse<0>(xmlPayload); } catch (parse_error& e) { std::string errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", "unknown", SccBadRequest, "XML Parse Error"); LM_W(("Bad Input ('%s', '%s')", content, e.what())); if (errorMsgP) { *errorMsgP = std::string("XML parse error exception: ") + e.what(); } return errorReply; } catch (...) { std::string errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", "unknown", SccBadRequest, "XML Parse Error"); LM_W(("Bad Input (%s)", content)); if (errorMsgP) { *errorMsgP = std::string("XML parse generic exception"); } return errorReply; } xml_node<>* father = doc.first_node(); XmlRequest* reqP = xmlRequestGet(request, ciP->method); ciP->parseDataP = parseDataP; if (father == NULL) { std::string errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", "unknown", SccBadRequest, "XML Parse Error"); LM_W(("Bad Input (XML parse error)")); if (errorMsgP) { *errorMsgP = std::string("XML parse error: invalid XML input"); } return errorReply; } if (reqP == NULL) { std::string errorReply = restErrorReplyGet( ciP, ciP->outFormat, "", requestType(request), SccBadRequest, std::string("Sorry, no request treating object found for RequestType /") + requestType(request) + "/, method /" + ciP->method + "/"); LM_W(("Bad Input (no request treating object found for RequestType %d (%s), method %s)", request, requestType(request), ciP->method.c_str())); LM_W(("Bad Input (no request treating object found for RequestType %d (%s), method %s)", request, requestType(request), ciP->method.c_str())); if (errorMsgP) { *errorMsgP = std::string("Unable to treat ") + requestType(request) + " requests"; } return errorReply; } if (reqPP != NULL) { *reqPP = reqP; } // // Checking that the payload matches the URL // if (((ciP->verb == POST) || (ciP->verb == PUT)) && (payloadWord.length() != 0)) { std::string errorReply; char* payloadStart = (char*) content; // Skip '<?xml version="1.0" encoding="UTF-8"?> ' ? if (strncmp(payloadStart, "<?xml", 5) == 0) { ++payloadStart; payloadStart = strstr(payloadStart, "<"); } // Skip '<' if (*payloadStart == '<') { ++payloadStart; } if (strncasecmp(payloadWord.c_str(), payloadStart, payloadWord.length()) != 0) { errorReply = restErrorReplyGet(ciP, ciP->outFormat, "", reqP->keyword, SccBadRequest, std::string("Expected /") + payloadWord + "/ payload, got /" + payloadStart + "/"); LM_W(("Bad Input (invalid payload: wanted: '%s', got '%s')", payloadWord.c_str(), payloadStart)); if (errorMsgP) { *errorMsgP = std::string("Bad Input (invalid payload, expecting '") + payloadWord + "', got '" + payloadStart + "')"; } return errorReply; } } if (reqP->init == NULL) // No payload treating function { return "OK"; } reqP->init(parseDataP); ciP->httpStatusCode = SccOk; xmlParse(ciP, NULL, father, "", "", reqP->parseVector, parseDataP, errorMsgP); if (ciP->httpStatusCode != SccOk) { LM_W(("Bad Input (XML parse error)")); return restErrorReplyGet(ciP, ciP->outFormat, "", payloadWord, ciP->httpStatusCode, ciP->answer); } LM_T(LmtParseCheck, ("Calling check for XML parsed tree (%s)", ciP->payloadWord)); std::string check = reqP->check(parseDataP, ciP); if (check != "OK") { LM_W(("Bad Input (%s: %s)", reqP->keyword.c_str(), check.c_str())); if (errorMsgP) { *errorMsgP = std::string("Bad Input: ") + check; } } reqP->present(parseDataP); if (check != "OK") { if (errorMsgP) { *errorMsgP = std::string("Bad Input: ") + check; } } return check; }
/* **************************************************************************** * * restService - */ std::string restService(ConnectionInfo* ciP, RestService* serviceV) { std::vector<std::string> compV; int components; JsonRequest* jsonReqP = NULL; ParseData parseData; JsonDelayedRelease jsonRelease; if ((ciP->url.length() == 0) || ((ciP->url.length() == 1) && (ciP->url.c_str()[0] == '/'))) { OrionError error(SccBadRequest, "The Orion Context Broker is a REST service, not a 'web page'"); std::string response = error.render(ciP, ""); alarmMgr.badInput(clientIp, "The Orion Context Broker is a REST service, not a 'web page'"); restReply(ciP, response); return std::string("Empty URL"); } ciP->httpStatusCode = SccOk; components = stringSplit(ciP->url, '/', compV); for (unsigned int ix = 0; serviceV[ix].treat != NULL; ++ix) { if ((serviceV[ix].components != 0) && (serviceV[ix].components != components)) { continue; } if ((ciP->method != serviceV[ix].verb) && (serviceV[ix].verb != "*")) { continue; } strncpy(ciP->payloadWord, serviceV[ix].payloadWord.c_str(), sizeof(ciP->payloadWord)); bool match = true; for (int compNo = 0; compNo < components; ++compNo) { if (serviceV[ix].compV[compNo] == "*") { continue; } if (strcasecmp(serviceV[ix].compV[compNo].c_str(), compV[compNo].c_str()) != 0) { match = false; break; } } if (match == false) { continue; } if ((ciP->payload != NULL) && (ciP->payloadSize != 0) && (ciP->payload[0] != 0) && (serviceV[ix].verb != "*")) { std::string response; LM_T(LmtParsedPayload, ("Parsing payload for URL '%s', method '%s', service vector index: %d", ciP->url.c_str(), ciP->method.c_str(), ix)); ciP->parseDataP = &parseData; LM_T(LmtPayload, ("Parsing payload '%s'", ciP->payload)); response = payloadParse(ciP, &parseData, &serviceV[ix], &jsonReqP, &jsonRelease, compV); LM_T(LmtParsedPayload, ("payloadParse returns '%s'", response.c_str())); if (response != "OK") { alarmMgr.badInput(clientIp, response); restReply(ciP, response); if (jsonReqP != NULL) { jsonReqP->release(&parseData); } if (ciP->apiVersion == "v2") { delayedRelease(&jsonRelease); } compV.clear(); return response; } } LM_T(LmtService, ("Treating service %s %s", serviceV[ix].verb.c_str(), ciP->url.c_str())); // Sacred - used in 'heavyTest' if (ciP->payloadSize == 0) { ciP->inFormat = NOFORMAT; } statisticsUpdate(serviceV[ix].request, ciP->inFormat); // Tenant to connectionInfo ciP->tenant = ciP->tenantFromHttpHeader; lmTransactionSetService(ciP->tenant.c_str()); // // A tenant string must not be longer than 50 characters and may only contain // underscores and alphanumeric characters. // std::string result; if ((ciP->tenant != "") && ((result = tenantCheck(ciP->tenant)) != "OK")) { OrionError error(SccBadRequest, result); std::string response = error.render(ciP, ""); alarmMgr.badInput(clientIp, result); if (ciP->apiVersion != "v1") { ciP->httpStatusCode = SccBadRequest; // FIXME P9: OK for all versions? } restReply(ciP, response); if (jsonReqP != NULL) { jsonReqP->release(&parseData); } if (ciP->apiVersion == "v2") { delayedRelease(&jsonRelease); } compV.clear(); return response; } LM_T(LmtTenant, ("tenant: '%s'", ciP->tenant.c_str())); commonFilters(ciP, &parseData, &serviceV[ix]); scopeFilter(ciP, &parseData, &serviceV[ix]); // // If we have gotten this far the Input is OK. // Except for all the badVerb/badRequest, etc. // A common factor for all these 'services' is that the verb is '*' // // So, the 'Bad Input' alarm is cleared for this client. // if (serviceV[ix].verb != "*") { alarmMgr.badInputReset(clientIp); } std::string response = serviceV[ix].treat(ciP, components, compV, &parseData); filterRelease(&parseData, serviceV[ix].request); if (jsonReqP != NULL) { jsonReqP->release(&parseData); } if (ciP->apiVersion == "v2") { delayedRelease(&jsonRelease); } compV.clear(); if (response == "DIE") { orionExitFunction(0, "Received a 'DIE' request on REST interface"); } restReply(ciP, response); return response; } std::string details = std::string("service '") + ciP->url + "' not recognized"; alarmMgr.badInput(clientIp, details); ciP->httpStatusCode = SccBadRequest; std::string answer = restErrorReplyGet(ciP, "", ciP->payloadWord, SccBadRequest, std::string("unrecognized request")); restReply(ciP, answer); compV.clear(); return answer; }
/* **************************************************************************** * * restService - */ std::string restService(ConnectionInfo* ciP, RestService* serviceV) { std::vector<std::string> compV; int components; XmlRequest* reqP = NULL; JsonRequest* jsonReqP = NULL; ParseData parseData; if ((ciP->url.length() == 0) || ((ciP->url.length() == 1) && (ciP->url.c_str()[0] == '/'))) { OrionError error(SccBadRequest, "The Orion Context Broker is a REST service, not a 'web page'"); std::string response = error.render(ciP->outFormat, ""); LM_W(("Bad Input (The Orion Context Broker is a REST service, not a 'web page')")); restReply(ciP, response); return std::string("Empty URL"); } ciP->httpStatusCode = SccOk; components = stringSplit(ciP->url, '/', compV); for (unsigned int ix = 0; serviceV[ix].treat != NULL; ++ix) { if ((serviceV[ix].components != 0) && (serviceV[ix].components != components)) { continue; } if ((ciP->method != serviceV[ix].verb) && (serviceV[ix].verb != "*")) { continue; } strncpy(ciP->payloadWord, serviceV[ix].payloadWord.c_str(), sizeof(ciP->payloadWord)); bool match = true; for (int compNo = 0; compNo < components; ++compNo) { if (serviceV[ix].compV[compNo] == "*") { continue; } if (strcasecmp(serviceV[ix].compV[compNo].c_str(), compV[compNo].c_str()) != 0) { match = false; break; } } if (match == false) { continue; } if ((ciP->payload != NULL) && (ciP->payloadSize != 0) && (ciP->payload[0] != 0) && (serviceV[ix].verb != "*")) { std::string response; LM_T(LmtParsedPayload, ("Parsing payload for URL '%s', method '%s', service vector index: %d", ciP->url.c_str(), ciP->method.c_str(), ix)); ciP->parseDataP = &parseData; response = payloadParse(ciP, &parseData, &serviceV[ix], &reqP, &jsonReqP); LM_T(LmtParsedPayload, ("payloadParse returns '%s'", response.c_str())); if (response != "OK") { restReply(ciP, response); if (reqP != NULL) { reqP->release(&parseData); } if (jsonReqP != NULL) { jsonReqP->release(&parseData); } compV.clear(); return response; } } LM_T(LmtService, ("Treating service %s %s", serviceV[ix].verb.c_str(), ciP->url.c_str())); // Sacred - used in 'heavyTest' statisticsUpdate(serviceV[ix].request, ciP->inFormat); // Tenant to connectionInfo ciP->tenant = ciP->tenantFromHttpHeader; // // A tenant string must not be longer than 50 characters and may only contain // underscores and alphanumeric characters. // std::string result; if ((ciP->tenant != "") && ((result = tenantCheck(ciP->tenant)) != "OK")) { OrionError error(SccBadRequest, "tenant name not accepted - a tenant string must not be longer than " MAX_TENANT_NAME_LEN_STRING " characters" " and may only contain underscores and alphanumeric characters"); std::string response = error.render(ciP->outFormat, ""); LM_W(("Bad Input (%s)", error.details.c_str())); restReply(ciP, response); if (reqP != NULL) { reqP->release(&parseData); } if (jsonReqP != NULL) { jsonReqP->release(&parseData); } compV.clear(); return response; } LM_T(LmtTenant, ("tenant: '%s'", ciP->tenant.c_str())); commonFilters(ciP, &parseData, &serviceV[ix]); scopeFilter(ciP, &parseData, &serviceV[ix]); std::string response = serviceV[ix].treat(ciP, components, compV, &parseData); filterRelease(&parseData, serviceV[ix].request); if (reqP != NULL) { reqP->release(&parseData); } if (jsonReqP != NULL) { jsonReqP->release(&parseData); } compV.clear(); if (response == "DIE") { orionExitFunction(0, "Received a 'DIE' request on REST interface"); } restReply(ciP, response); return response; } LM_W(("Bad Input (service '%s' not recognized)", ciP->url.c_str())); ciP->httpStatusCode = SccBadRequest; std::string answer = restErrorReplyGet(ciP, ciP->outFormat, "", ciP->payloadWord, SccBadRequest, std::string("unrecognized request")); restReply(ciP, answer); compV.clear(); return answer; }
/* **************************************************************************** * * postAttributeValueInstanceWithTypeAndId - * * POST /ngsi10/contextEntities/type/{type}/id/{id}/attributes/{attributeName}/{valueID} * * Payload: UpdateContextAttributeRequest */ std::string postAttributeValueInstanceWithTypeAndId ( ConnectionInfo* ciP, int components, std::vector<std::string>& compV, ParseData* parseDataP ) { UpdateContextRequest request; UpdateContextResponse response; std::string entityType = compV[3]; std::string entityId = compV[5]; std::string attributeName = compV[7]; std::string valueId = compV[8]; UpdateContextAttributeRequest* upcarP = &parseDataP->upcar.res; bool idFound = false; // // Any metadata ID in the payload? // // If so, the value must be equal to the {valueID} of the URL // for (unsigned int ix = 0; ix < upcarP->metadataVector.size(); ++ix) { Metadata* mP = upcarP->metadataVector.get(ix); if (mP->name == "ID") { if (mP->value != valueId) { std::string out; out = restErrorReplyGet(ciP, ciP->outFormat, "", "StatusCode", SccBadRequest, std::string("unmatching metadata ID value URI/payload: '") + valueId + "' vs '" + mP->value + "'"); return out; } else { idFound = true; } } } ContextAttribute* attributeP = new ContextAttribute(attributeName, "", upcarP->contextValue); ContextElement* ceP = new ContextElement(); // Copy the metadata vector of the input payload attributeP->metadataVector.fill((MetadataVector*) &upcarP->metadataVector); // If no "ID" metadata was in the payload, add it if (idFound == false) { Metadata* mP = new Metadata("ID", "", valueId); attributeP->metadataVector.push_back(mP); } // Filling the rest of the structure for mongoUpdateContext ceP->entityId.fill(entityId, entityType, "false"); ceP->attributeDomainName.set(""); ceP->contextAttributeVector.push_back(attributeP); request.contextElementVector.push_back(ceP); request.updateActionType.set("APPEND"); response.errorCode.code = SccNone; ciP->httpStatusCode = mongoUpdateContext(&request, &response, ciP->tenant, ciP->servicePathV, ciP->uriParam); StatusCode statusCode; if (response.contextElementResponseVector.size() == 0) { statusCode.fill(SccContextElementNotFound, std::string("Entity-Attribute pair: '") + entityId + "-" + attributeName + "'"); } else if (response.contextElementResponseVector.size() == 1) { ContextElementResponse* cerP = response.contextElementResponseVector.get(0); if (response.errorCode.code != SccNone) { statusCode.fill(&response.errorCode); } else if (cerP->statusCode.code != SccNone) { statusCode.fill(&cerP->statusCode); } else { statusCode.fill(SccOk); } } else { statusCode.fill(SccReceiverInternalError, "More than one response from postAttributeValueInstanceWithTypeAndId::mongoUpdateContext"); } request.release(); return statusCode.render(ciP->outFormat, "", false, false); }
/* **************************************************************************** * * connectionTreat - * * This is the MHD_AccessHandlerCallback function for MHD_start_daemon * This function returns: * o MHD_YES if the connection was handled successfully * o MHD_NO if the socket must be closed due to a serious error * * - This function is called once when the headers are read and the ciP is created. * - Then it is called for data payload and once all the payload an acknowledgement * must be done, setting *upload_data_size to ZERO. * - The last call is made with *upload_data_size == 0 and now is when the connection * is open to send responses. * * Call 1: *con_cls == NULL * Call 2: *con_cls != NULL AND *upload_data_size != 0 * Call 3: *con_cls != NULL AND *upload_data_size == 0 */ static int connectionTreat ( void* cls, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls ) { ConnectionInfo* ciP = (ConnectionInfo*) *con_cls; size_t dataLen = *upload_data_size; // 1. First call - setup ConnectionInfo and get/check HTTP headers if (ciP == NULL) { if ((ciP = new ConnectionInfo(url, method, version, connection)) == NULL) LM_RE(MHD_NO, ("Error allocating ConnectionInfo")); *con_cls = (void*) ciP; // Pointer to ConnectionInfo for subsequent calls MHD_get_connection_values(connection, MHD_HEADER_KIND, httpHeaderGet, &ciP->httpHeaders); ciP->outFormat = wantedOutputSupported(ciP->httpHeaders.accept, &ciP->charset); if (ciP->outFormat == NOFORMAT) ciP->outFormat = XML; // XML is default output format if (contentTypeCheck(ciP) != 0) { LM_W(("Error in Content-Type")); restReply(ciP, ciP->answer); } else if (outFormatCheck(ciP) != 0) { LM_W(("Bad Accepted Out-Format (in Accept header)")); restReply(ciP, ciP->answer); } else ciP->inFormat = formatParse(ciP->httpHeaders.contentType, NULL); return MHD_YES; } // // 2. Data gathering calls // // 2-1. Data gathering calls, just wait // 2-2. Last data gathering call, acknowledge the receipt of data // if (dataLen != 0) { if (dataLen == ciP->httpHeaders.contentLength) { if (ciP->httpHeaders.contentLength <= PAYLOAD_MAX_SIZE) { if (ciP->httpHeaders.contentLength > STATIC_BUFFER_SIZE) ciP->payload = (char*) malloc(ciP->httpHeaders.contentLength); else ciP->payload = static_buffer; ciP->payloadSize = dataLen; memcpy(ciP->payload, upload_data, dataLen); ciP->payload[dataLen] = 0; } else { char details[256]; snprintf(details, sizeof(details), "payload size: %d, max size supported: %d", ciP->httpHeaders.contentLength, PAYLOAD_MAX_SIZE); ciP->answer = restErrorReplyGet(ciP, ciP->outFormat, "", ciP->url, SccRequestEntityTooLarge, details); ciP->httpStatusCode = SccRequestEntityTooLarge; } // All payload received, acknowledge! *upload_data_size = 0; } else LM_T(LmtPartialPayload, ("Got %d of payload of %d bytes", dataLen, ciP->httpHeaders.contentLength)); return MHD_YES; } // 3. Finally, serve the request (unless an error has occurred) if (((ciP->verb == POST) || (ciP->verb == PUT)) && (ciP->httpHeaders.contentLength == 0)) { std::string errorMsg = restErrorReplyGet(ciP, ciP->outFormat, "", url, SccLengthRequired, "Zero/No Content-Length in PUT/POST request"); ciP->httpStatusCode = SccLengthRequired; restReply(ciP, errorMsg); } else if (ciP->answer != "") restReply(ciP, ciP->answer); else serveFunction(ciP); return MHD_YES; }
/* **************************************************************************** * * restErrorReplyGet - */ TEST(restReply, restErrorReplyGet) { const char* rcrOutfile01 = "ngsi9.restReply.registerContext01.ok.valid.xml"; const char* rcrOutfile02 = "ngsi9.restReply.registerContext02.ok.valid.xml"; const char* dcarOutfile01 = "ngsi9.restReply.discovery01.ok.valid.xml"; const char* dcarOutfile02 = "ngsi9.restReply.discovery02.ok.valid.xml"; const char* scarOutfile01 = "ngsi9.restReply.subscribeContextAvailability01.ok.valid.xml"; const char* scarOutfile02 = "ngsi9.restReply.subscribeContextAvailability02.ok.valid.xml"; const char* ucasOutfile01 = "ngsi9.restReply.updateContextAvailabilitySubscription01.ok.valid.xml"; const char* ucasOutfile02 = "ngsi9.restReply.updateContextAvailabilitySubscription02.ok.valid.xml"; const char* ucarOutfile01 = "ngsi9.restReply.unsubscribeContextAvailability01.ok.valid.xml"; const char* ucarOutfile02 = "ngsi9.restReply.unsubscribeContextAvailability02.ok.valid.xml"; const char* ncarOutfile01 = "ngsi9.restReply.notifyContextAvailabilityRequest01.ok.valid.xml"; const char* ncarOutfile02 = "ngsi9.restReply.notifyContextAvailabilityRequest02.ok.valid.xml"; const char* qcrOutfile01 = "ngsi10.restReply.queryContextResponse01.ok.valid.xml"; const char* qcrOutfile02 = "ngsi10.restReply.queryContextResponse02.ok.valid.xml"; const char* scrOutfile01 = "ngsi10.restReply.subscribeContextResponse01.ok.valid.xml"; const char* scrOutfile02 = "ngsi10.restReply.subscribeContextResponse02.ok.valid.xml"; const char* ucsOutfile01 = "ngsi10.restReply.updateContextSubscriptionResponse01.ok.valid.xml"; const char* ucsOutfile02 = "ngsi10.restReply.updateContextSubscriptionResponse02.ok.valid.xml"; const char* uscrOutfile01 = "ngsi10.restReply.unsubscribeContextResponse01.ok.valid.xml"; const char* uscrOutfile02 = "ngsi10.restReply.unsubscribeContextResponse02.ok.valid.xml"; const char* ucrOutfile01 = "ngsi10.restReply.updateContextResponse01.ok.valid.xml"; const char* ucrOutfile02 = "ngsi10.restReply.updateContextResponse02.ok.valid.xml"; const char* ncrOutfile01 = "ngsi10.restReply.notifyContextResponse01.ok.valid.xml"; const char* ncrOutfile02 = "ngsi10.restReply.notifyContextResponse02.ok.valid.xml"; std::string rcr1 = "registerContext"; std::string rcr2 = "/ngsi9/registerContext"; std::string rcr3 = "/NGSI9/registerContext"; std::string rcr4 = "registerContextRequest"; std::string dcar1 = "discoverContextAvailability"; std::string dcar2 = "/ngsi9/discoverContextAvailability"; std::string dcar3 = "/NGSI9/discoverContextAvailability"; std::string dcar4 = "discoverContextAvailabilityRequest"; std::string scar1 = "subscribeContextAvailability"; std::string scar2 = "/ngsi9/subscribeContextAvailability"; std::string scar3 = "/NGSI9/subscribeContextAvailability"; std::string scar4 = "subscribeContextAvailabilityRequest"; std::string ucas1 = "updateContextAvailabilitySubscription"; std::string ucas2 = "/ngsi9/updateContextAvailabilitySubscription"; std::string ucas3 = "/NGSI9/updateContextAvailabilitySubscription"; std::string ucas4 = "updateContextAvailabilitySubscriptionRequest"; std::string ucar1 = "unsubscribeContextAvailability"; std::string ucar2 = "/ngsi9/unsubscribeContextAvailability"; std::string ucar3 = "/NGSI9/unsubscribeContextAvailability"; std::string ucar4 = "unsubscribeContextAvailabilityRequest"; std::string ncar1 = "notifyContextAvailability"; std::string ncar2 = "/ngsi9/notifyContextAvailability"; std::string ncar3 = "/NGSI9/notifyContextAvailability"; std::string ncar4 = "notifyContextAvailabilityRequest"; std::string qcr1 = "queryContext"; std::string qcr2 = "/ngsi10/queryContext"; std::string qcr3 = "/NGSI10/queryContext"; std::string qcr4 = "queryContextRequest"; std::string scr1 = "subscribeContext"; std::string scr2 = "/ngsi10/subscribeContext"; std::string scr3 = "/NGSI10/subscribeContext"; std::string scr4 = "subscribeContextRequest"; std::string ucs1 = "updateContextSubscription"; std::string ucs2 = "/ngsi10/updateContextSubscription"; std::string ucs3 = "/NGSI10/updateContextSubscription"; std::string ucs4 = "updateContextSubscriptionRequest"; std::string uscr1 = "unsubscribeContext"; std::string uscr2 = "/ngsi10/unsubscribeContext"; std::string uscr3 = "/NGSI10/unsubscribeContext"; std::string uscr4 = "unsubscribeContextRequest"; std::string ucr1 = "updateContext"; std::string ucr2 = "/ngsi10/updateContext"; std::string ucr3 = "/NGSI10/updateContext"; std::string ucr4 = "updateContextRequest"; std::string ncr1 = "notifyContext"; std::string ncr2 = "/ngsi10/notifyContext"; std::string ncr3 = "/NGSI10/notifyContext"; std::string ncr4 = "notifyContextRequest"; std::string out; ConnectionInfo ci("/ngsi/test", "POST", "1.1"); utInit(); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), rcrOutfile01)) << "Error getting test data from '" << rcrOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", rcr1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", rcr2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", rcr3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", rcr4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), rcrOutfile02)) << "Error getting test data from '" << rcrOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", rcr1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", rcr2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", rcr3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", rcr4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), dcarOutfile01)) << "Error getting test data from '" << dcarOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", dcar1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", dcar2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", dcar3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", dcar4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), dcarOutfile02)) << "Error getting test data from '" << dcarOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", dcar1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", dcar2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", dcar3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", dcar4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), scarOutfile01)) << "Error getting test data from '" << scarOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", scar1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scar2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scar3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scar4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), scarOutfile02)) << "Error getting test data from '" << scarOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", scar1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scar2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scar3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scar4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ucasOutfile01)) << "Error getting test data from '" << ucasOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", ucas1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucas2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucas3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucas4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ucasOutfile02)) << "Error getting test data from '" << ucasOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", ucas1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucas2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucas3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucas4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ucarOutfile01)) << "Error getting test data from '" << ucarOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", ucar1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucar2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucar3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucar4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ucarOutfile02)) << "Error getting test data from '" << ucarOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", ucar1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucar2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucar3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucar4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ncarOutfile01)) << "Error getting test data from '" << ncarOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", ncar1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncar2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncar3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncar4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ncarOutfile02)) << "Error getting test data from '" << ncarOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", ncar1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncar2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncar3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncar4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), qcrOutfile01)) << "Error getting test data from '" << qcrOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", qcr1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", qcr2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", qcr3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", qcr4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), qcrOutfile02)) << "Error getting test data from '" << qcrOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", qcr1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", qcr2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", qcr3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", qcr4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), scrOutfile01)) << "Error getting test data from '" << scrOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", scr1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scr2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scr3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scr4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), scrOutfile02)) << "Error getting test data from '" << scrOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", scr1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scr2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scr3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", scr4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ucsOutfile01)) << "Error getting test data from '" << ucsOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", ucs1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucs2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucs3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucs4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ucsOutfile02)) << "Error getting test data from '" << ucsOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", ucs1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucs2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucs3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucs4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), uscrOutfile01)) << "Error getting test data from '" << uscrOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", uscr1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", uscr2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", uscr3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", uscr4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), uscrOutfile02)) << "Error getting test data from '" << uscrOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", uscr1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", uscr2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", uscr3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", uscr4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ucrOutfile01)) << "Error getting test data from '" << ucrOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", ucr1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucr2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucr3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucr4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ucrOutfile02)) << "Error getting test data from '" << ucrOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", ucr1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucr2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucr3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ucr4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ncrOutfile01)) << "Error getting test data from '" << ncrOutfile01 << "'"; out = restErrorReplyGet(&ci, XML, "", ncr1, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncr2, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncr3, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncr4, SccOk, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); EXPECT_EQ("OK", testDataFromFile(expectedBuf, sizeof(expectedBuf), ncrOutfile02)) << "Error getting test data from '" << ncrOutfile02 << "'"; out = restErrorReplyGet(&ci, XML, "", ncr1, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncr2, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncr3, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); out = restErrorReplyGet(&ci, XML, "", ncr4, SccBadRequest, "detail"); EXPECT_STREQ(expectedBuf, out.c_str()); utExit(); }
static int connectionTreat ( void* cls, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls ) { ConnectionInfo* ciP = (ConnectionInfo*) *con_cls; size_t dataLen = *upload_data_size; // 1. First call - setup ConnectionInfo and get/check HTTP headers if (ciP == NULL) { // // First thing to do on a new connection, set correlator to N/A. // After reading HTTP headers, the correlator id either changes due to encountering a // Fiware-Correlator HTTP Header, or, if no HTTP header with Fiware-Correlator is found, // a new correlator is generated. // correlatorIdSet("N/A"); // // IP Address and port of caller // char ip[32]; unsigned short port = 0; const union MHD_ConnectionInfo* mciP = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); if (mciP != NULL) { struct sockaddr* addr = (struct sockaddr*) mciP->client_addr; port = (addr->sa_data[0] << 8) + addr->sa_data[1]; snprintf(ip, sizeof(ip), "%d.%d.%d.%d", addr->sa_data[2] & 0xFF, addr->sa_data[3] & 0xFF, addr->sa_data[4] & 0xFF, addr->sa_data[5] & 0xFF); snprintf(clientIp, sizeof(clientIp), "%s", ip); } else { port = 0; snprintf(ip, sizeof(ip), "IP unknown"); } // // Reset time measuring? // if (timingStatistics) { memset(&threadLastTimeStat, 0, sizeof(threadLastTimeStat)); } // // ConnectionInfo // // FIXME P1: ConnectionInfo could be a thread variable (like the static_buffer), // as long as it is properly cleaned up between calls. // We would save the call to new/free for each and every request. // Once we *really* look to scratch some efficiency, this change should be made. // // Also, is ciP->ip really used? // if ((ciP = new ConnectionInfo(url, method, version, connection)) == NULL) { LM_E(("Runtime Error (error allocating ConnectionInfo)")); return MHD_NO; } if (timingStatistics) { clock_gettime(CLOCK_REALTIME, &ciP->reqStartTime); } LM_T(LmtRequest, ("")); // WARNING: This log message below is crucial for the correct function of the Behave tests - CANNOT BE REMOVED LM_T(LmtRequest, ("--------------------- Serving request %s %s -----------------", method, url)); *con_cls = (void*) ciP; // Pointer to ConnectionInfo for subsequent calls ciP->port = port; ciP->ip = ip; ciP->callNo = reqNo; ++reqNo; // // URI parameters // // FIXME P1: We might not want to do all these assignments, they are not used in all requests ... // Once we *really* look to scratch some efficiency, this change should be made. // ciP->uriParam[URI_PARAM_PAGINATION_OFFSET] = DEFAULT_PAGINATION_OFFSET; ciP->uriParam[URI_PARAM_PAGINATION_LIMIT] = DEFAULT_PAGINATION_LIMIT; ciP->uriParam[URI_PARAM_PAGINATION_DETAILS] = DEFAULT_PAGINATION_DETAILS; MHD_get_connection_values(connection, MHD_HEADER_KIND, httpHeaderGet, &ciP->httpHeaders); char correlator[CORRELATOR_ID_SIZE + 1]; if (ciP->httpHeaders.correlator == "") { correlatorGenerate(correlator); ciP->httpHeaders.correlator = correlator; } correlatorIdSet(ciP->httpHeaders.correlator.c_str()); ciP->httpHeader.push_back("Fiware-Correlator"); ciP->httpHeaderValue.push_back(ciP->httpHeaders.correlator); // // Transaction starts here // lmTransactionStart("from", ip, port, url); // Incoming REST request starts /* X-Forwared-For (used by a potential proxy on top of Orion) overrides ip */ if (ciP->httpHeaders.xforwardedFor == "") { lmTransactionSetFrom(ip); } else { lmTransactionSetFrom(ciP->httpHeaders.xforwardedFor.c_str()); } ciP->apiVersion = apiVersionGet(ciP->url.c_str()); char tenant[SERVICE_NAME_MAX_LEN + 1]; ciP->tenantFromHttpHeader = strToLower(tenant, ciP->httpHeaders.tenant.c_str(), sizeof(tenant)); ciP->outMimeType = wantedOutputSupported(ciP->apiVersion, ciP->httpHeaders.accept, &ciP->charset); if (ciP->outMimeType == NOMIMETYPE) { ciP->outMimeType = JSON; // JSON is default output mimeType } MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, uriArgumentGet, ciP); return MHD_YES; } // // 2. Data gathering calls // // 2-1. Data gathering calls, just wait // 2-2. Last data gathering call, acknowledge the receipt of data // if (dataLen != 0) { // // If the HTTP header says the request is bigger than our PAYLOAD_MAX_SIZE, // just silently "eat" the entire message // if (ciP->httpHeaders.contentLength > PAYLOAD_MAX_SIZE) { *upload_data_size = 0; return MHD_YES; } // // First call with payload - use the thread variable "static_buffer" if possible, // otherwise allocate a bigger buffer // // FIXME P1: This could be done in "Part I" instead, saving an "if" for each "Part II" call // Once we *really* look to scratch some efficiency, this change should be made. // if (ciP->payloadSize == 0) // First call with payload { if (ciP->httpHeaders.contentLength > STATIC_BUFFER_SIZE) { ciP->payload = (char*) malloc(ciP->httpHeaders.contentLength + 1); } else { ciP->payload = static_buffer; } } // Copy the chunk LM_T(LmtPartialPayload, ("Got %d of payload of %d bytes", dataLen, ciP->httpHeaders.contentLength)); memcpy(&ciP->payload[ciP->payloadSize], upload_data, dataLen); // Add to the size of the accumulated read buffer ciP->payloadSize += *upload_data_size; // Zero-terminate the payload ciP->payload[ciP->payloadSize] = 0; // Acknowledge the data and return *upload_data_size = 0; return MHD_YES; } // // 3. Finally, serve the request (unless an error has occurred) // // URL and headers checks are delayed to the "third" MHD call, as no // errors can be sent before all the request has been read // if (urlCheck(ciP, ciP->url) == false) { alarmMgr.badInput(clientIp, "error in URI path"); restReply(ciP, ciP->answer); } ciP->servicePath = ciP->httpHeaders.servicePath; lmTransactionSetSubservice(ciP->servicePath.c_str()); if (servicePathSplit(ciP) != 0) { alarmMgr.badInput(clientIp, "error in ServicePath http-header"); restReply(ciP, ciP->answer); } if (contentTypeCheck(ciP) != 0) { alarmMgr.badInput(clientIp, "invalid mime-type in Content-Type http-header"); restReply(ciP, ciP->answer); } else if (outMimeTypeCheck(ciP) != 0) { alarmMgr.badInput(clientIp, "invalid mime-type in Accept http-header"); restReply(ciP, ciP->answer); } else { ciP->inMimeType = mimeTypeParse(ciP->httpHeaders.contentType, NULL); } if (ciP->httpStatusCode != SccOk) { alarmMgr.badInput(clientIp, "error in URI parameters"); restReply(ciP, ciP->answer); return MHD_YES; } // // Here, if the incoming request was too big, return error about it // if (ciP->httpHeaders.contentLength > PAYLOAD_MAX_SIZE) { char details[256]; snprintf(details, sizeof(details), "payload size: %d, max size supported: %d", ciP->httpHeaders.contentLength, PAYLOAD_MAX_SIZE); alarmMgr.badInput(clientIp, details); ciP->answer = restErrorReplyGet(ciP, "", ciP->url, SccRequestEntityTooLarge, details); ciP->httpStatusCode = SccRequestEntityTooLarge; } // // Requests of verb POST, PUT or PATCH are considered erroneous if no payload is present - with two exceptions. // // - Old log requests (URL contains '/log/') // - New log requests (URL is exactly '/admin/log') // if (((ciP->verb == POST) || (ciP->verb == PUT) || (ciP->verb == PATCH )) && (ciP->httpHeaders.contentLength == 0) && ((strncasecmp(ciP->url.c_str(), "/log/", 5) != 0) && (strncasecmp(ciP->url.c_str(), "/admin/log", 10) != 0))) { std::string errorMsg = restErrorReplyGet(ciP, "", url, SccContentLengthRequired, "Zero/No Content-Length in PUT/POST/PATCH request"); ciP->httpStatusCode = SccContentLengthRequired; restReply(ciP, errorMsg); alarmMgr.badInput(clientIp, errorMsg); } else if (ciP->answer != "") { alarmMgr.badInput(clientIp, ciP->answer); restReply(ciP, ciP->answer); } else { serveFunction(ciP); } return MHD_YES; }