/* **************************************************************************** * * payloadParse - */ std::string payloadParse(ConnectionInfo* ciP, ParseData* parseDataP, RestService* service, XmlRequest** reqPP, JsonRequest** jsonPP) { std::string result = "NONE"; LM_T(LmtParsedPayload, ("parsing data for service '%s'. Method: '%s'", requestType(service->request), ciP->method.c_str())); LM_T(LmtParsedPayload, ("outFormat: %s", formatToString(ciP->outFormat))); if (ciP->inFormat == XML) { LM_T(LmtParsedPayload, ("Calling xmlTreat for service request %d, payloadWord '%s'", service->request, service->payloadWord.c_str())); result = xmlTreat(ciP->payload, ciP, parseDataP, service->request, service->payloadWord, reqPP); } else if (ciP->inFormat == JSON) result = jsonTreat(ciP->payload, ciP, parseDataP, service->request, service->payloadWord, jsonPP); else { LM_W(("Bad Input (payload mime-type is neither JSON nor XML)")); return "Bad inFormat"; } LM_T(LmtParsedPayload, ("result: '%s'", result.c_str())); LM_T(LmtParsedPayload, ("outFormat: %s", formatToString(ciP->outFormat))); if (result != "OK") { restReply(ciP, result); } return result; }
/* **************************************************************************** * * json - */ TEST(restReply, json) { ConnectionInfo ci("/ngsi/XXX", "GET", "1.1"); utInit(); ci.outFormat = JSON; restReply(&ci, "123"); utExit(); }
/* **************************************************************************** * * payloadParse - */ std::string payloadParse ( ConnectionInfo* ciP, ParseData* parseDataP, RestService* service, JsonRequest** jsonPP, JsonDelayedRelease* jsonReleaseP, std::vector<std::string>& compV ) { std::string result = "NONE"; LM_T(LmtParsedPayload, ("parsing data for service '%s'. Method: '%s'", requestType(service->request), ciP->method.c_str())); LM_T(LmtParsedPayload, ("outFormat: %s", formatToString(ciP->outFormat))); ciP->requestType = service->request; if (ciP->inFormat == JSON) { if (compV[0] == "v2") { result = jsonRequestTreat(ciP, parseDataP, service->request, jsonReleaseP, compV); } else { result = jsonTreat(ciP->payload, ciP, parseDataP, service->request, service->payloadWord, jsonPP); } } else if (ciP->inFormat == TEXT) { result = textRequestTreat(ciP, parseDataP, service->request); } else { alarmMgr.badInput(clientIp, "payload mime-type is not JSON"); return "Bad inFormat"; } LM_T(LmtParsedPayload, ("result: '%s'", result.c_str())); LM_T(LmtParsedPayload, ("outFormat: %s", formatToString(ciP->outFormat))); if (result != "OK") { restReply(ciP, result); } return result; }
TEST(restReply, MHD_create_response_from_data_error) { ConnectionInfo ci("/ngsi/XXX", "GET", "1.1"); char* answer = (char*) malloc(TEST_SIZE); utInit(); if (answer != NULL) { memset(answer, 'x', TEST_SIZE - 1); answer[TEST_SIZE - 1] = 0; restReply(&ci, answer); free(answer); } utExit(); }
/* **************************************************************************** * * 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; }
/* **************************************************************************** * * 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; }
/* **************************************************************************** * * 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; }
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; }