/* ****************************************************************************
*
* httpRequestConnect -
*/
int httpRequestConnect(const std::string& host, unsigned short port)
{
    int                 fd;
    struct addrinfo     hints;
    struct addrinfo*    peer;
    char                port_str[10];

    LM_TRANSACTION_START("to", ip.c_str(), port, resource.c_str());

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;

    if (ipVersionUsed == IPV4)
    {
        hints.ai_family = AF_INET;
        LM_T(LmtIpVersion, ("Allow IPv4 only"));
    }
    else if (ipVersionUsed == IPV6)
    {
        hints.ai_family = AF_INET6;
        LM_T(LmtIpVersion, ("Allow  IPv6 only"));
    }
    else
    {
        hints.ai_family = AF_UNSPEC;
        LM_T(LmtIpVersion, ("Allow IPv4 or IPv6"));
    }

    snprintf(port_str, sizeof(port_str), "%d" , (int) port);

    if (getaddrinfo(host.c_str(), port_str, &hints, &peer) != 0)
    {
        LM_W(("Notification failure for %s:%d (getaddrinfo: %s)", host.c_str(), port, strerror(errno)));
        LM_TRANSACTION_END();
        return -1;
    }

    if ((fd = socket(peer->ai_family, peer->ai_socktype, peer->ai_protocol)) == -1)
    {
        LM_W(("Notification failure for %s:%d (socket: %s)", host.c_str(), port, strerror(errno)));
        LM_TRANSACTION_END();
        return -1;
    }

    if (connect(fd, peer->ai_addr, peer->ai_addrlen) == -1)
    {
        freeaddrinfo(peer);
        close(fd);
        LM_W(("Notification failure for %s:%d (connect: %s)", host.c_str(), port, strerror(errno)));
        LM_TRANSACTION_END();
        return -1;
    }

    freeaddrinfo(peer);
    LM_TRANSACTION_END();
    return fd;
}
예제 #2
0
/* ****************************************************************************
*
* requestCompleted -
*/
static void requestCompleted
(
  void*                       cls,
  MHD_Connection*             connection,
  void**                      con_cls,
  MHD_RequestTerminationCode  toe
)
{
  ConnectionInfo* ciP      = (ConnectionInfo*) *con_cls;

  if ((ciP->payload != NULL) && (ciP->payload != static_buffer))
    free(ciP->payload);

  delete(ciP);
  *con_cls = NULL;

  LM_TRANSACTION_END();  // Incoming REST request ends
}
/* ****************************************************************************
*
* httpRequestSend -
*/
std::string httpRequestSend
(
    const std::string&     _ip,
    unsigned short         port,
    const std::string&     protocol,
    const std::string&     verb,
    const std::string&     tenant,
    const std::string&     servicePath,
    const std::string&     xauthToken,
    const std::string&     resource,
    const std::string&     content_type,
    const std::string&     content,
    bool                   useRush,
    bool                   waitForResponse,
    const std::string&     acceptFormat,
    long                   timeoutInMilliseconds
)
{
    char                       portAsString[16];
    static unsigned long long  callNo             = 0;
    std::string                result;
    std::string                ip                 = _ip;
    struct curl_slist*         headers            = NULL;
    MemoryStruct*              httpResponse       = NULL;
    CURLcode                   res;
    int                        outgoingMsgSize       = 0;
    CURL*                      curl;
    struct curl_context        cc;

    ++callNo;

    if (timeoutInMilliseconds == -1)
    {
        timeoutInMilliseconds = defaultTimeout;
    }

    LM_TRANSACTION_START("to", ip.c_str(), port, resource.c_str());

    // Preconditions check
    if (port == 0)
    {
        LM_E(("Runtime Error (port is ZERO)"));
        LM_TRANSACTION_END();
        return "error";
    }

    if (ip.empty())
    {
        LM_E(("Runtime Error (ip is empty)"));
        LM_TRANSACTION_END();
        return "error";
    }

    if (verb.empty())
    {
        LM_E(("Runtime Error (verb is empty)"));
        LM_TRANSACTION_END();
        return "error";
    }

    if (resource.empty())
    {
        LM_E(("Runtime Error (resource is empty)"));
        LM_TRANSACTION_END();
        return "error";
    }

    if ((content_type.empty()) && (!content.empty()))
    {
        LM_E(("Runtime Error (Content-Type is empty but there is actual content)"));
        LM_TRANSACTION_END();
        return "error";
    }

    if ((!content_type.empty()) && (content.empty()))
    {
        LM_E(("Runtime Error (Content-Type non-empty but there is no content)"));
        LM_TRANSACTION_END();
        return "error";
    }

    get_curl_context(ip, &cc);
    if ((curl = cc.curl) == NULL)
    {
        LM_E(("Runtime Error (could not init libcurl)"));
        LM_TRANSACTION_END();
        release_curl_context(&cc);
        return "error";
    }

    // Allocate to hold HTTP response
    httpResponse = new MemoryStruct;
    httpResponse->memory = (char*) malloc(1); // will grow as needed
    httpResponse->size = 0; // no data at this point

    //
    // Rush
    // Every call to httpRequestSend specifies whether RUSH should be used or not.
    // But, this depends also on how the broker was started, so here the 'useRush'
    // parameter is cancelled in case the broker was started without rush.
    //
    // If rush is to be used, the IP/port is stored in rushHeaderIP/rushHeaderPort and
    // after that, the host and port of rush is set as ip/port for the message, that is
    // now sent to rush instead of to its final destination.
    // Also, a few HTTP headers for rush must be setup.
    //
    if ((rushPort == 0) || (rushHost == ""))
        useRush = false;

    if (useRush)
    {
        char         rushHeaderPortAsString[16];
        uint16_t     rushHeaderPort     = 0;
        std::string  rushHeaderIP       = "";
        std::string  headerRushHttp     = "";

        rushHeaderIP   = ip;
        rushHeaderPort = port;
        ip             = rushHost;
        port           = rushPort;

        snprintf(rushHeaderPortAsString, sizeof(rushHeaderPortAsString), "%d", rushHeaderPort);
        headerRushHttp = "X-relayer-host: " + rushHeaderIP + ":" + rushHeaderPortAsString;
        LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerRushHttp.c_str()));
        headers = curl_slist_append(headers, headerRushHttp.c_str());
        outgoingMsgSize += headerRushHttp.size();

        if (protocol == "https:")
        {
            headerRushHttp = "X-relayer-protocol: https";
            LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerRushHttp.c_str()));
            headers = curl_slist_append(headers, headerRushHttp.c_str());
            outgoingMsgSize += headerRushHttp.size();
        }
    }

    snprintf(portAsString, sizeof(portAsString), "%d", port);

    // ----- User Agent
    char cvBuf[CURL_VERSION_MAX_LENGTH];
    char headerUserAgent[HTTP_HEADER_USER_AGENT_MAX_LENGTH];

    snprintf(headerUserAgent, sizeof(headerUserAgent), "User-Agent: orion/%s libcurl/%s", versionGet(), curlVersionGet(cvBuf, sizeof(cvBuf)));
    LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerUserAgent));
    headers = curl_slist_append(headers, headerUserAgent);
    outgoingMsgSize += strlen(headerUserAgent) + 1;

    // ----- Host
    char headerHost[HTTP_HEADER_HOST_MAX_LENGTH];

    snprintf(headerHost, sizeof(headerHost), "Host: %s:%d", ip.c_str(), (int) port);
    LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerHost));
    headers = curl_slist_append(headers, headerHost);
    outgoingMsgSize += strlen(headerHost) + 1;

    // ----- Tenant
    if (tenant != "")
    {
        headers = curl_slist_append(headers, ("fiware-service: " + tenant).c_str());
        outgoingMsgSize += tenant.size() + 16; // "fiware-service: "
    }

    // ----- Service-Path
    if (servicePath != "")
    {
        headers = curl_slist_append(headers, ("Fiware-ServicePath: " + servicePath).c_str());
        outgoingMsgSize += servicePath.size() + strlen("Fiware-ServicePath: ");
    }

    // ----- X-Auth-Token
    if (xauthToken != "")
    {
        headers = curl_slist_append(headers, ("X-Auth-Token: " + xauthToken).c_str());
        outgoingMsgSize += xauthToken.size() + strlen("X-Auth-Token: ");
    }

    // ----- Accept
    std::string acceptedFormats = "application/xml, application/json";
    if (acceptFormat != "")
    {
        acceptedFormats = acceptFormat;
    }

    std::string acceptString = "Accept: " + acceptedFormats;
    headers = curl_slist_append(headers, acceptString.c_str());
    outgoingMsgSize += acceptString.size();

    // ----- Expect
    headers = curl_slist_append(headers, "Expect: ");
    outgoingMsgSize += 8; // from "Expect: "

    // ----- Content-length
    std::stringstream contentLengthStringStream;
    contentLengthStringStream << content.size();
    std::string headerContentLength = "Content-length: " + contentLengthStringStream.str();
    LM_T(LmtHttpHeaders, ("HTTP-HEADERS: '%s'", headerContentLength.c_str()));
    headers = curl_slist_append(headers, headerContentLength.c_str());
    outgoingMsgSize += contentLengthStringStream.str().size() + 16; // from "Content-length: "
    outgoingMsgSize += content.size();

    // ----- Content-type
    headers = curl_slist_append(headers, ("Content-type: " + content_type).c_str());
    outgoingMsgSize += content_type.size() + 14; // from "Content-type: "


    // Check if total outgoing message size is too big
    if (outgoingMsgSize > MAX_DYN_MSG_SIZE)
    {
        LM_E(("Runtime Error (HTTP request to send is too large: %d bytes)", outgoingMsgSize));

        // Cleanup curl environment
        curl_slist_free_all(headers);
        release_curl_context(&cc);

        free(httpResponse->memory);
        delete httpResponse;

        LM_TRANSACTION_END();
        return "error";
    }

    // Contents
    const char* payload = content.c_str();
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (u_int8_t*) payload);

    // Set up URL
    std::string url;
    if (isIPv6(ip))
        url = "[" + ip + "]";
    else
        url = ip;
    url = url + ":" + portAsString + (resource.at(0) == '/'? "" : "/") + resource;

    // Prepare CURL handle with obtained options
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb.c_str()); // Set HTTP verb
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Allow redirection (?)
    curl_easy_setopt(curl, CURLOPT_HEADER, 1); // Activate include the header in the body output
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Put headers in place
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeMemoryCallback); // Send data here
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) httpResponse); // Custom data for response handling

    //
    // There is a known problem in libcurl (see http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame)
    // which is solved using CURLOPT_NOSIGNAL. If we update some day from libcurl 7.19 (the one that comes with CentOS 6.x) to a newer version
    // (there are some reports about the bug is no longer in libcurl 7.32), using CURLOPT_NOSIGNAL could be not necessary and this be removed).
    // See issue #1016 for more details.
    //
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);

    //
    // Timeout
    //
    // The parameter timeoutInMilliseconds holds the timeout time in milliseconds.
    // If the timeout time requested is 0, then no timeuot is used.
    //
    if (timeoutInMilliseconds != 0)
    {
        curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeoutInMilliseconds);
    }


    // Synchronous HTTP request
    LM_T(LmtClientOutputPayload, ("Sending message %lu to HTTP server: sending message of %d bytes to HTTP server", callNo, outgoingMsgSize));
    res = curl_easy_perform(curl);

    if (res != CURLE_OK)
    {
        //
        // NOTE: This log line is used by the functional tests in cases/880_timeout_for_forward_and_notifications/
        //       So, this line should not be removed/altered, at least not without also modifying the functests.
        //
        LM_W(("Notification failure for %s:%s (curl_easy_perform failed: %s)", ip.c_str(), portAsString, curl_easy_strerror(res)));
        result = "";
    }
    else
    {
        // The Response is here
        LM_I(("Notification Successfully Sent to %s", url.c_str()));
        result.assign(httpResponse->memory, httpResponse->size);
    }

    // Cleanup curl environment
    curl_slist_free_all(headers);
    release_curl_context(&cc);

    free(httpResponse->memory);
    delete httpResponse;

    LM_TRANSACTION_END();

    return result;
}