Esempio n. 1
0
/* ****************************************************************************
*
* startSenderThread -
*/
void* startSenderThread(void* p)
{
    SenderThreadParams* params = (SenderThreadParams*) p;
    char                portV[STRING_SIZE_FOR_INT];
    std::string         url;

    snprintf(portV, sizeof(portV), "%d", params->port);
    url = params->ip + ":" + portV + params->resource;

    strncpy(transactionId, params->transactionId, sizeof(transactionId));

    LM_T(LmtNotifier, ("sending to: host='%s', port=%d, verb=%s, tenant='%s', service-path: '%s', xauthToken: '%s', path='%s', content-type: %s", 
                       params->ip.c_str(),
                       params->port,
                       params->verb.c_str(),
                       params->tenant.c_str(),
                       params->servicePath.c_str(),
                       params->xauthToken.c_str(),
                       params->resource.c_str(),
                       params->content_type.c_str()));

    if (!simulatedNotification)
    {
      std::string  out;
      int          r;

      r = httpRequestSend(params->ip,
                          params->port,
                          params->protocol,
                          params->verb,
                          params->tenant,
                          params->servicePath,
                          params->xauthToken,
                          params->resource,
                          params->content_type,
                          params->content,
                          true,
                          NOTIFICATION_WAIT_MODE,
                          &out);

      if (r == 0)
      {
        statisticsUpdate(NotifyContextSent, params->format);
        alarmMgr.notificationErrorReset(url);
      }
    }
    else
    {
      LM_T(LmtNotifier, ("simulatedNotification is 'true', skipping outgoing request"));
      __sync_fetch_and_add(&noOfSimulatedNotifications, 1);
      alarmMgr.notificationError(url, "notification failure for sender-thread");
    }

    /* Delete the parameters after using them */
    delete params;

    pthread_exit(NULL);
    return NULL;
}
Esempio n. 2
0
/* ****************************************************************************
*
* 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;
}
Esempio n. 3
0
/* ****************************************************************************
*
* 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;
}
Esempio n. 4
0
/* ****************************************************************************
*
* templateNotify - 
*
* This function performs the necessary substitutions according to the template of
* subscription to form the desired notification and send it to the endpoint specified
* in the subscription.
* 
* 
*/
static bool templateNotify
(
  const SubscriptionId&            subscriptionId,
  const ContextElement&            ce,
  const ngsiv2::HttpInfo&          httpInfo,
  const std::string&               tenant,
  const std::string&               xauthToken,
  const std::string&               fiwareCorrelator,
  RenderFormat                     renderFormat,
  const std::vector<std::string>&  attrsOrder
)
{
  Verb                                verb = httpInfo.verb;
  std::string                         method;
  std::string                         url;
  std::string                         payload;
  std::string                         mimeType;
  std::map<std::string, std::string>  qs;
  std::map<std::string, std::string>  headers;


  //
  // 1. Verb/Method
  //
  if (verb == NOVERB)
  {
    // Default verb/method is POST
    verb = POST;
  }
  method = verbName(verb);


  //
  // 2. URL
  //
  macroSubstitute(&url, httpInfo.url, ce);


  //
  // 3. Payload
  //
  if (httpInfo.payload == "")
  {
    NotifyContextRequest   ncr;
    ContextElementResponse cer;
    
    cer.contextElement = ce;
    ncr.subscriptionId = subscriptionId;
    ncr.contextElementResponseVector.push_back(&cer);
    payload  = ncr.toJson(renderFormat, attrsOrder);
    mimeType = "application/json";
  }
  else
  {
    macroSubstitute(&payload, httpInfo.payload, ce);
    char* pload  = curl_unescape(payload.c_str(), payload.length());
    payload      = pload;
    renderFormat = NGSI_V2_CUSTOM;
    mimeType     = "text/plain";  // May be overridden by 'Content-Type' in 'headers'
    free(pload);
  }


  //
  // 4. URI Params (Query Strings)
  //
  for (std::map<std::string, std::string>::const_iterator it = httpInfo.qs.begin(); it != httpInfo.qs.end(); ++it)
  {
    std::string key   = it->first;
    std::string value = it->second;

    macroSubstitute(&key,   it->first, ce);
    macroSubstitute(&value, it->second, ce);
    if ((value == "") || (key == ""))
    {
      // To avoid e.g '?a=&b=&c='
      continue;
    }
    qs[key] = value;
  }


  //
  // 5. HTTP Headers
  //
  for (std::map<std::string, std::string>::const_iterator it = httpInfo.headers.begin(); it != httpInfo.headers.end(); ++it)
  {
    std::string key   = it->first;
    std::string value = it->second;

    macroSubstitute(&key,   it->first, ce);
    macroSubstitute(&value, it->second, ce);

    if (key == "")
    {
      // To avoid empty header name
      continue;
    }

    headers[key] = value;
  }


  //
  // 6. Split URI in protocol, host, port and path
  //
  std::string  protocol;
  std::string  host;
  int          port;
  std::string  uriPath;

  if (!parseUrl(url, host, port, uriPath, protocol))
  {
    LM_E(("Runtime Error (not sending NotifyContextRequest: malformed URL: '%s')", httpInfo.url.c_str()));
    return false;
  }


  //
  // 7. Add URI params from template to uriPath
  //
  std::string  uri = uriPath;

  if (qs.size() != 0)
  {
    uri += "?";

    int ix = 0;
    for (std::map<std::string, std::string>::iterator it = qs.begin(); it != qs.end(); ++it)
    {
      if (ix != 0)
      {
        uri += "&";
      }

      uri += it->first + "=" + it->second;
      ++ix;
    }
  }


  //
  // 8. Send the request
  //
  //    NOTE: the HTTP headers are sent to httpRequestSend via parameter 'extraHeaders'
  //
  std::string  out;
  int          r;

  r = httpRequestSend(host,
                      port,
                      protocol,
                      method,
                      tenant,
                      ce.entityId.servicePath,
                      xauthToken,
                      uri,
                      mimeType,
                      payload,
                      fiwareCorrelator,
                      renderFormatToString(renderFormat),
                      true,                // Use Rush if CLI '--rush' allows it
                      true,                // wait for response
                      &out,
                      headers,
                      "application/json",  // Accept Format
                      -1);                 // Timeout in milliseconds, depends on CLI '-httpTimeout'

  if (r == 0)
  {
    statisticsUpdate(NotifyContextSent, JSON);
    alarmMgr.notificationErrorReset(url);
    return true;
  }

  return false;
}
/* ****************************************************************************
*
* QueueWorkers::start() -
*/
static void *workerFunc(void* pSyncQ)
{
  SyncQOverflow<SenderThreadParams*> *queue = (SyncQOverflow<SenderThreadParams*> *) pSyncQ;
  CURL *curl;

  // Initialize curl context
  curl = curl_easy_init();

  if (curl == NULL)
  {
    LM_E(("Runtime Error (curl_easy_init)"));
    pthread_exit(NULL);
  }

  for (;;)
  {
    SenderThreadParams* params = queue->pop();
    struct timespec     now;
    struct timespec     howlong;
    size_t              estimatedQSize;

    QueueStatistics::incOut();
    clock_gettime(CLOCK_REALTIME, &now);
    clock_difftime(&now, &params->timeStamp, &howlong);
    estimatedQSize = queue->size();
    QueueStatistics::addTimeInQWithSize(&howlong, estimatedQSize);


    strncpy(transactionId, params->transactionId, sizeof(transactionId));

    LM_T(LmtNotifier, ("worker sending to: host='%s', port=%d, verb=%s, tenant='%s', service-path: '%s', xauthToken: '%s', path='%s', content-type: %s",
                       params->ip.c_str(),
                       params->port,
                       params->verb.c_str(),
                       params->tenant.c_str(),
                       params->servicePath.c_str(),
                       params->xauthToken.c_str(),
                       params->resource.c_str(),
                       params->content_type.c_str()));

    if (simulatedNotification)
    {
      LM_T(LmtNotifier, ("simulatedNotification is 'true', skipping outgoing request"));
      __sync_fetch_and_add(&noOfSimulatedNotifications, 1);
    }
    else // we'll send the notification
    {
      std::string  out;
      int          r;

      r =  httpRequestSendWithCurl(curl,
                                   params->ip,
                                   params->port,
                                   params->protocol,
                                   params->verb,
                                   params->tenant,
                                   params->servicePath,
                                   params->xauthToken,
                                   params->resource,
                                   params->content_type,
                                   params->content,
                                   true,
                                   NOTIFICATION_WAIT_MODE,
                                   &out);

      //
      // FIXME: ok and error counter should be incremented in the other notification modes (generalizing the concept, i.e.
      // not as member of QueueStatistics:: which seems to be tied to just the threadpool notification mode)
      //
      if (r == 0)
      {
        statisticsUpdate(NotifyContextSent, params->format);
        QueueStatistics::incSentOK();
      }
      else
      {
        QueueStatistics::incSentError();
      }

    }

    // Free params memory
    delete params;

    // Reset curl for next iteration
    curl_easy_reset(curl);
  }
}