/* **************************************************************************** * * 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; }
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; }