예제 #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;
}
예제 #2
0
/* ****************************************************************************
*
* updateForward - 
*
* An entity/attribute has been found on some context provider.
* We need to forward the update request to the context provider, indicated in upcrsP->contextProvider
*
* 1. Parse the providing application to extract IP, port and URI-path
* 2. Render the string of the request we want to forward
* 3. Send the request to the providing application (and await the response)
* 4. Parse the response and fill in a binary UpdateContextResponse
* 5. Fill in the response from the redirection into the response of this function
* 6. 'Fix' StatusCode
* 7. Freeing memory
*
*
* FIXME P5: The function 'updateForward' is implemented to pick the format (XML or JSON) based on the
*           count of the Format for all the participating attributes. If we have more attributes 'preferring'
*           XML than JSON, the forward is done in XML, etc. This is all OK.
*           What is not OK is that the Accept HTTP header is set to the same format as the Content-Type HTTP Header.
*           While this is acceptable, it is not great. As the broker understands both XML and JSON, we could send
*           the forward message with an Acceot header of XML/JSON and then at reading the response, instead of 
*           throwing away the HTTP headers, we could read the "Content-Type" and do the parse according the Content-Type.
*/
static void updateForward(ConnectionInfo* ciP, UpdateContextRequest* upcrP, UpdateContextResponse* upcrsP, Format format)
{
  std::string     ip;
  std::string     protocol;
  int             port;
  std::string     prefix;
  std::string     answer;


  //
  // 1. Parse the providing application to extract IP, port and URI-path
  //
  if (parseUrl(upcrP->contextProvider, ip, port, prefix, protocol) == false)
  {
    LM_W(("Bad Input (invalid providing application '%s')", upcrP->contextProvider.c_str()));
    //  Somehow, if we accepted this providing application, it is the brokers fault ...
    //  SccBadRequest should have been returned before, when it was registered!

    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    return;
  }


  //
  // 2. Render the string of the request we want to forward
  //
  Format       outFormat = ciP->outFormat;
  std::string  payload;
  char*        cleanPayload;

  ciP->outFormat  = format;
  payload         = upcrP->render(ciP, UpdateContext, "");
  ciP->outFormat  = outFormat;
  cleanPayload    = (char*) payload.c_str();

  if (format == XML)
  {
    if ((cleanPayload = xmlPayloadClean(payload.c_str(), "<updateContextRequest>")) == NULL)
    {
      LM_E(("Runtime Error (error rendering forward-request)"));
      upcrsP->errorCode.fill(SccContextElementNotFound, "");
      return;
    }
  }


  //
  // 3. Send the request to the Context Provider (and await the reply)
  // FIXME P7: Should Rush be used?
  //
  std::string     out;
  std::string     verb         = "POST";
  std::string     resource     = prefix + "/updateContext";
  std::string     tenant       = ciP->tenant;
  std::string     servicePath  = (ciP->httpHeaders.servicePathReceived == true)? ciP->httpHeaders.servicePath : "";
  std::string     mimeType     = (format == XML)? "application/xml" : "application/json";

  out = httpRequestSend(ip,
                        port,
                        protocol,
                        verb,
                        tenant,
                        servicePath,
                        ciP->httpHeaders.xauthToken,
                        resource,
                        mimeType,
                        cleanPayload,
                        false,
                        true,
                        mimeType);

  if ((out == "error") || (out == ""))
  {
    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    LM_E(("Runtime Error (error forwarding 'Update' to providing application)"));
    return;
  }


  //
  // 4. Parse the response and fill in a binary UpdateContextResponse
  //
  std::string  s;
  std::string  errorMsg;

  if (format == XML)
  {
    cleanPayload = xmlPayloadClean(out.c_str(), "<updateContextResponse>");
  }
  else
  {
    cleanPayload = jsonPayloadClean(out.c_str());
  }

  if ((cleanPayload == NULL) || (cleanPayload[0] == 0))
  {
    //
    // This is really an internal error in the Context Provider
    // It is not in the orion broker though, so 404 is returned
    //
    LM_W(("Other Error (context provider response to UpdateContext is empty)"));
    upcrsP->errorCode.fill(SccContextElementNotFound, "invalid context provider response");
    return;
  }

  //
  // NOTE
  // When coming from a convenience operation, such as GET /v1/contextEntities/EID/attributes/attrName,
  // the verb/method in ciP is GET. However, the parsing function expects a POST, as if it came from a 
  // POST /v1/updateContext. 
  // So, here we change the verb/method for POST.
  //
  ParseData parseData;

  ciP->verb   = POST;
  ciP->method = "POST";

  parseData.upcrs.res.errorCode.fill(SccOk);

  if (format == XML)
  {
    s = xmlTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, "updateContextResponse", NULL, &errorMsg);
  }
  else
  {
    s = jsonTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, "updateContextResponse", NULL);
  }

  if (s != "OK")
  {
    LM_W(("Internal Error (error parsing reply from prov app: %s)", errorMsg.c_str()));
    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    parseData.upcr.res.release();
    parseData.upcrs.res.release();
    return;
  }


  //
  // 5. Fill in the response from the redirection into the response of this function
  //
  upcrsP->fill(&parseData.upcrs.res);


  //
  // 6. 'Fix' StatusCode
  //
  if (upcrsP->errorCode.code == SccNone)
  {
    upcrsP->errorCode.fill(SccOk);
  }

  if ((upcrsP->contextElementResponseVector.size() == 1) && (upcrsP->contextElementResponseVector[0]->statusCode.code == SccContextElementNotFound))
  {
    upcrsP->errorCode.fill(SccContextElementNotFound);
  }

  
  //
  // 7. Freeing memory
  //
  parseData.upcr.res.release();
  parseData.upcrs.res.release();
}
/* ****************************************************************************
*
* updateForward -
*
* An entity/attribute has been found on some context provider.
* We need to forward the update request to the context provider, indicated in upcrsP->contextProvider
*
* 1. Parse the providing application to extract IP, port and URI-path
* 2. Render the string of the request we want to forward
* 3. Send the request to the providing application (and await the response)
* 4. Parse the response and fill in a binary UpdateContextResponse
* 5. Fill in the response from the redirection into the response of this function
* 6. 'Fix' StatusCode
* 7. Freeing memory
*
*/
static bool updateForward(ConnectionInfo* ciP, UpdateContextRequest* upcrP, UpdateContextResponse* upcrsP)
{
  std::string      ip;
  std::string      protocol;
  int              port;
  std::string      prefix;

  bool asJsonObject = (ciP->uriParam[URI_PARAM_ATTRIBUTE_FORMAT] == "object" && ciP->outMimeType == JSON);

  //
  // 1. Parse the providing application to extract IP, port and URI-path
  //
  if (parseUrl(upcrP->contextProvider, ip, port, prefix, protocol) == false)
  {
    std::string details = std::string("invalid providing application '") + upcrP->contextProvider + "'";

    alarmMgr.badInput(clientIp, details);

    //
    //  Somehow, if we accepted this providing application, it is the brokers fault ...
    //  SccBadRequest should have been returned before, when it was registered!
    //
    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    return false;
  }

  LM_T(LmtForward, ("*** Provider Format: %d", upcrP->providerFormat));

  //
  // 2. Render the string of the request we want to forward
  //
  MimeType     outMimeType = ciP->outMimeType;
  std::string  payload;
  char*        cleanPayload;

  ciP->outMimeType  = JSON;

  std::string     verb;
  std::string     resource;
  std::string     tenant       = ciP->tenant;
  std::string     servicePath  = (ciP->httpHeaders.servicePathReceived == true)? ciP->httpHeaders.servicePath : "";
  std::string     mimeType     = "application/json";
  std::string     out;
  int             r;

  if (upcrP->providerFormat == PfJson)
  {
    TIMED_RENDER(payload = upcrP->toJsonV1(asJsonObject));

    verb     = "POST";
    resource = prefix + "/updateContext";
  }
  else
  {
    TIMED_RENDER(payload = upcrP->toJson());

    verb     = "POST";
    resource = prefix + "/op/update";
#if 0
    // FIXME #3485: this part is not removed by the moment, in the case it may be useful in the
    // context of issue #3485

    std::vector<std::string>  nullFilter;
    Entity*                   eP = upcrP->entityVector[0];

    eP->renderId = false;

    TIMED_RENDER(payload = eP->toJson(NGSI_V2_NORMALIZED, nullFilter, false, nullFilter));

    resource = prefix + "/entities/" + eP->id + "/attrs";
    verb     = "PATCH";

    if (eP->type != "")
    {
      // Add ?type=<TYPE> to 'resource'
      resource += "?type=" + eP->type;
    }
#endif
  }

  ciP->outMimeType  = outMimeType;
  cleanPayload      = (char*) payload.c_str();

  //
  // 3. Send the request to the Context Provider (and await the reply)
  // FIXME P7: Should Rush be used?
  //
  LM_T(LmtCPrForwardRequestPayload, ("forward updateContext request payload: %s", payload.c_str()));

  std::map<std::string, std::string>  noHeaders;
  long long                           statusCode; // not used by the moment

  r = httpRequestSend(fromIp,   // thread variable
                      ip,
                      port,
                      protocol,
                      verb,
                      tenant,
                      servicePath,
                      ciP->httpHeaders.xauthToken,
                      resource,
                      mimeType,
                      cleanPayload,
                      ciP->httpHeaders.correlator,
                      "",
                      false,
                      &out,
                      &statusCode,
                      noHeaders,
                      mimeType);

  if (r != 0)
  {
    upcrsP->errorCode.fill(SccContextElementNotFound, "error forwarding update");
    LM_E(("Runtime Error (error '%s' forwarding 'Update' to providing application)", out.c_str()));
    return false;
  }

  LM_T(LmtCPrForwardResponsePayload, ("forward updateContext response payload: %s", out.c_str()));

  //
  // If NGSIv1 (providerFormat == PfJson):
  //   4. Parse the response and fill in a binary UpdateContextResponse
  //   5. Fill in the response from the redirection into the response of this function
  //   6. 'Fix' StatusCode
  //   7. Free up memory
  //
  // If NGSIv2:
  //   4. Look for "204 No Content" in the response of the forwarded request
  //   5. If found: OK, else, error
  //
  if (upcrP->providerFormat == PfJson)
  {
    LM_T(LmtForward, ("upcrP->providerFormat == PfJson"));
    //
    // 4. Parse the response and fill in a binary UpdateContextResponse
    //
    std::string  s;
    std::string  errorMsg;

    cleanPayload = jsonPayloadClean(out.c_str());

    if ((cleanPayload == NULL) || (cleanPayload[0] == 0))
    {
      //
      // This is really an internal error in the Context Provider
      // It is not in the orion broker though, so 404 is returned
      //
      LM_W(("Other Error (context provider response to UpdateContext is empty)"));
      upcrsP->errorCode.fill(SccContextElementNotFound, "invalid context provider response");
      return false;
    }

    //
    // NOTE
    // When coming from a convenience operation, such as GET /v1/contextEntities/EID/attributes/attrName,
    // the verb/method in ciP is GET. However, the parsing function expects a POST, as if it came from a
    // POST /v1/updateContext.
    // So, here we change the verb/method for POST.
    //
    ParseData parseData;

    ciP->verb   = POST;
    ciP->method = "POST";

    parseData.upcrs.res.errorCode.fill(SccOk);

    LM_T(LmtForward, ("Parsing Response of Forwarded Request: '%s'", cleanPayload));
    s = jsonTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, NULL);
    LM_T(LmtForward, ("Parse Result: %s", s.c_str()));

    if (s != "OK")
    {
      LM_W(("Internal Error (error parsing reply from prov app: %s)", errorMsg.c_str()));
      upcrsP->errorCode.fill(SccContextElementNotFound, "");
      parseData.upcr.res.release();
      parseData.upcrs.res.release();
      return false;
    }


    //
    // 5. Fill in the response from the redirection into the response of this function
    //
    upcrsP->fill(&parseData.upcrs.res);


    //
    // 6. 'Fix' StatusCode
    //
    if (upcrsP->errorCode.code == SccNone)
    {
      upcrsP->errorCode.fill(SccOk);
    }

    if ((upcrsP->contextElementResponseVector.size() == 1) && (upcrsP->contextElementResponseVector[0]->statusCode.code == SccContextElementNotFound))
    {
      upcrsP->errorCode.fill(SccContextElementNotFound);
    }

    //
    // 7. Free up memory
    //
    parseData.upcr.res.release();
    parseData.upcrs.res.release();

    return true;
  }
  else  // NGSIv2
  {
    LM_T(LmtForward, ("upcrP->providerFormat == V2. out: '%s'", out.c_str()));
    // NGSIv2 forward - no payload to be received, just a 204 No Content HTTP Header
    if (strstr(out.c_str(), "204 No Content") != NULL)
    {
      LM_T(LmtForward, ("Found '204 No Content'"));
      upcrsP->errorCode.fill(SccNone);
      return true;
    }

    LM_W(("Other Error (unexpected response from context provider: %s)", out.c_str()));
    upcrsP->errorCode.fill(SccReceiverInternalError);
    return false;
  }

  // Can't reach this point - no return-statement needed
}
예제 #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;
}