Exemple #1
0
void init_superblock(struct castle_slave_superblock_public *super, int is_ssd)
{
  super->magic1 = CASTLE_SLAVE_MAGIC1;
  super->magic2 = CASTLE_SLAVE_MAGIC2;
  super->magic3 = CASTLE_SLAVE_MAGIC3;
  super->version = CASTLE_SLAVE_VERSION;
  super->uuid = get_random_uuid();
  super->used = 1; /* we are responsible for writing JUST the
                      slave's superblock */
  super->flags = CASTLE_SLAVE_NEWDEV | (is_ssd ? CASTLE_SLAVE_SSD : 0);
  super->size = -1;
  super->checksum = 0;
}
/// Get data; return a HTTP return code
HTTPCode HttpConnection::send_request(const std::string& path,       //< Absolute path to request from server - must start with "/"
                                      std::string body,              //< Body to send on the request
                                      std::string& doc,              //< OUT: Retrieved document
                                      const std::string& username,   //< Username to assert (if assertUser was true, else ignored).
                                      SAS::TrailId trail,            //< SAS trail to use
                                      const std::string& method_str, // The method, used for logging.
                                      CURL* curl)
{
  std::string url = "http://" + _server + path;
  struct curl_slist *extra_headers = NULL;
  PoolEntry* entry;
  int event_id;
  CURLcode rc = curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char**)&entry);
  assert(rc == CURLE_OK);

  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &doc);

  if (!body.empty())
  {
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
  }

  // Create a UUID to use for SAS correlation and add it to the HTTP message.
  boost::uuids::uuid uuid = get_random_uuid();
  std::string uuid_str = boost::uuids::to_string(uuid);
  extra_headers = curl_slist_append(extra_headers,
                                    (SASEvent::HTTP_BRANCH_HEADER_NAME + ": " + uuid_str).c_str());

  // Now log the marker to SAS. Flag that SAS should not reactivate the trail
  // group as a result of associations on this marker (doing so after the call
  // ends means it will take a long time to be searchable in SAS).
  SAS::Marker corr_marker(trail, MARKER_ID_VIA_BRANCH_PARAM, 0);
  corr_marker.add_var_param(uuid_str);
  SAS::report_marker(corr_marker, SAS::Marker::Scope::Trace, false);

  // Add the user's identity (if required).
  if (_assert_user)
  {
    extra_headers = curl_slist_append(extra_headers,
                                      ("X-XCAP-Asserted-Identity: " + username).c_str());
  }
  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, extra_headers);

  // Determine whether to recycle the connection, based on
  // previously-calculated deadline.
  struct timespec tp;
  int rv = clock_gettime(CLOCK_MONOTONIC, &tp);
  assert(rv == 0);
  unsigned long now_ms = tp.tv_sec * 1000 + (tp.tv_nsec / 1000000);
  bool recycle_conn = entry->is_connection_expired(now_ms);

  // Resolve the host.
  std::vector<AddrInfo> targets;
  _resolver->resolve(_host, _port, MAX_TARGETS, targets, trail);

  // If we're not recycling the connection, try to get the current connection
  // IP address and add it to the front of the target list.
  if (!recycle_conn)
  {
    char* primary_ip;
    if (curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP, &primary_ip) == CURLE_OK)
    {
      AddrInfo ai;
      _resolver->parse_ip_target(primary_ip, ai.address);
      ai.port = (_port != 0) ? _port : 80;
      ai.transport = IPPROTO_TCP;

      targets.erase(std::remove(targets.begin(), targets.end(), ai), targets.end());
      targets.insert(targets.begin(), ai);
    }
  }

  // If the list of targets only contains 1 target, clone it - we always want
  // to retry at least once.
  if (targets.size() == 1)
  {
    targets.push_back(targets[0]);
  }

  // Report the request to SAS.
  event_id = ((_sas_log_level == SASEvent::HttpLogLevel::PROTOCOL) ?
                 SASEvent::TX_HTTP_REQ : SASEvent::TX_HTTP_REQ_DETAIL);
  SAS::Event tx_http_req(trail, event_id,  0);
  tx_http_req.add_var_param(method_str);
  tx_http_req.add_var_param(url);
  tx_http_req.add_var_param(body);
  SAS::report_event(tx_http_req);

  // Track the number of HTTP 503 and 504 responses and the number of timeouts
  // or I/O errors.
  int num_http_503_responses = 0;
  int num_http_504_responses = 0;
  int num_timeouts_or_io_errors = 0;

  // Track the IP addresses we're connecting to.  If we fail, we failed to
  // resolve the host, so default to that.
  const char *remote_ip = NULL;
  rc = CURLE_COULDNT_RESOLVE_HOST;

  // Try to get a decent connection - try each of the hosts in turn (although
  // we might quit early if we have too many HTTP-level failures).
  for (std::vector<AddrInfo>::const_iterator i = targets.begin();
       i != targets.end();
       ++i)
  {
    curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, recycle_conn ? 1L : 0L);

    // Convert the target IP address into a string and fix up the URL.  It
    // would be nice to use curl_easy_setopt(CURL_RESOLVE) here, but its
    // implementation is incomplete.
    char buf[100];
    remote_ip = inet_ntop(i->address.af, &i->address.addr, buf, sizeof(buf));
    std::string ip_url = "http://" + std::string(remote_ip) + ":" + std::to_string(i->port) + path;
    curl_easy_setopt(curl, CURLOPT_URL, ip_url.c_str());

    // Send the request.
    doc.clear();
    LOG_DEBUG("Sending HTTP request : %s (trying %s) %s", url.c_str(), remote_ip, (recycle_conn) ? "on new connection" : "");
    rc = curl_easy_perform(curl);

    // If we performed an HTTP transaction (successfully or otherwise, get the
    // return code.
    long http_rc = 0;
    if ((rc == CURLE_OK) || (rc == CURLE_HTTP_RETURNED_ERROR))
    {
      curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_rc);
    }

    if (rc == CURLE_OK)
    {
      LOG_DEBUG("Received HTTP response : %s", doc.c_str());

      // Report the response to SAS.
      sas_log_http_rsp(trail, curl, http_rc, method_str, url, doc, 0);

      if (recycle_conn)
      {
        entry->update_deadline(now_ms);
      }

      // Success!
      break;
    }
    else
    {
      LOG_ERROR("%s failed at server %s : %s (%d %d) : fatal",
                url.c_str(), remote_ip, curl_easy_strerror(rc), rc, http_rc);

      if (rc == CURLE_HTTP_RETURNED_ERROR)
      {
        // Report the response to SAS
        sas_log_http_rsp(trail, curl, http_rc, method_str, url, doc, 0);
      }
      else
      {
        // Report the error to SAS.
        event_id = ((_sas_log_level == SASEvent::HttpLogLevel::PROTOCOL) ?
                       SASEvent::HTTP_REQ_ERROR : SASEvent::HTTP_REQ_ERROR_DETAIL);
        SAS::Event http_err(trail, event_id, 0);
        http_err.add_var_param(method_str);
        http_err.add_var_param(url);
        http_err.add_static_param(rc);
        http_err.add_var_param(curl_easy_strerror(rc));
        http_err.add_var_param(remote_ip);
        SAS::report_event(http_err);
      }

      // If we forced a new connection and we failed even to establish an HTTP
      // connection, blacklist this IP address.
      if (recycle_conn &&
          (rc != CURLE_HTTP_RETURNED_ERROR) &&
          (rc != CURLE_REMOTE_FILE_NOT_FOUND) &&
          (rc != CURLE_REMOTE_ACCESS_DENIED))
      {
        _resolver->blacklist(*i, BLACKLIST_DURATION);
      }

      // Determine the failure mode and update the correct counter.
      bool fatal_http_error = false;
      if (rc == CURLE_HTTP_RETURNED_ERROR)
      {
        if (http_rc == 503)
        {
          num_http_503_responses++;
        }
        // LCOV_EXCL_START fakecurl doesn't let us return custom return codes.
        else if (http_rc == 504)
        {
          num_http_504_responses++;
        }
        else
        {
          fatal_http_error = true;
        }
        // LCOV_EXCL_STOP
      }
      else if ((rc == CURLE_REMOTE_FILE_NOT_FOUND) ||
               (rc == CURLE_REMOTE_ACCESS_DENIED))
      {
        fatal_http_error = true;
      }
      else if ((rc == CURLE_OPERATION_TIMEDOUT) ||
               (rc == CURLE_SEND_ERROR) ||
               (rc == CURLE_RECV_ERROR))
      {
        num_timeouts_or_io_errors++;
      }

      // Decide whether to keep trying.
      if ((num_http_503_responses + num_timeouts_or_io_errors >= 2) ||
          (num_http_504_responses >= 1) ||
          fatal_http_error ||
          (rc == CURLE_COULDNT_CONNECT))
      {
        break;
      }
    }
  }

  // Check whether we should apply a penalty. We do this when:
  //  - both attempts return 503 errors, which means the downstream node is
  //    overloaded/requests to it are timeing.
  //  - the error is a 504, which means that the node downsteam of the node
  //    we're connecting to currently has reported that it is overloaded/was
  //    unresponsive.
  if (((num_http_503_responses >= 2) ||
       (num_http_504_responses >= 1)) &&
      (_load_monitor != NULL))
  {
    _load_monitor->incr_penalties();
  }

  if ((rc == CURLE_OK) || (rc == CURLE_HTTP_RETURNED_ERROR))
  {
    entry->set_remote_ip(remote_ip);
  }
  else
  {
    entry->set_remote_ip("");
  }

  HTTPCode http_code = curl_code_to_http_code(curl, rc);
  if ((rc != CURLE_OK) && (rc != CURLE_REMOTE_FILE_NOT_FOUND))
  {
    LOG_ERROR("cURL failure with cURL error code %d (see man 3 libcurl-errors) and HTTP error code %ld", (int)rc, http_code);  // LCOV_EXCL_LINE
  }

  reset_curl_handle(curl);
  curl_slist_free_all(extra_headers);
  return http_code;
}