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