// GET / bool ApiRequest::index() { // FIXME: thread safety. // In order to make this method thread-safe, we must ensure that each director's // json-write is done from within the director's worker thread and finally // the reply be sent to the client from within the request's worker thread. Buffer result; JsonWriter json(result); json.beginObject(); for (auto di: *directors_) { Director* director = di.second; json.name(director->name()).value(*director); } json.endObject(); result << "\n"; char slen[32]; snprintf(slen, sizeof(slen), "%zu", result.size()); request_->responseHeaders.push_back("Cache-Control", "no-cache"); request_->responseHeaders.push_back("Content-Type", "application/json"); request_->responseHeaders.push_back("Access-Control-Allow-Origin", "*"); request_->responseHeaders.push_back("Content-Length", slen); request_->write<BufferSource>(result); request_->finish(); return true; }
void HealthMonitor::update() { Director* director = static_cast<Director*>(backend_->manager()); setRequest( "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "x0-Health-Check: yes\r\n" "x0-Director: %s\r\n" "x0-Backend: %s\r\n" "\r\n", director->healthCheckRequestPath().c_str(), director->healthCheckHostHeader().c_str(), director->name().c_str(), backend_->name().c_str()); }
void DirectorPlugin::pass(HttpRequest* r, const std::string& directorName, const std::string& backendName) { auto i = directors_.find(directorName); if (i == directors_.end()) { r->log(Severity::error, "director.pass(): No director with name '%s' configured.", directorName.c_str()); internalServerError(r); return; } Director* director = i->second.get(); // custom backend route Backend* backend = nullptr; if (!backendName.empty()) { backend = director->findBackend(backendName); if (!backend) { // explicit backend specified, but not found -> do not serve. r->log(Severity::error, "director: Requested backend '%s' not found.", backendName.c_str()); internalServerError(r); return; } } #if !defined(NDEBUG) server().log(Severity::trace, "director: passing request to %s [backend %s].", director->name().c_str(), backend->name().c_str()); #endif auto rn = requestNotes(r); rn->manager = director; r->onPostProcess.connect(std::bind(&DirectorPlugin::addVia, this, r)); director->schedule(rn, backend); return; }
void DirectorPlugin::balance(HttpRequest* r, const std::string& directorName, const std::string& bucketName) { auto i = directors_.find(directorName); if (i == directors_.end()) { r->log(Severity::error, "director.balance(): No director with name '%s' configured.", directorName.c_str()); internalServerError(r); return; } Director* director = i->second.get(); RequestShaper::Node* bucket = nullptr; if (!bucketName.empty()) { bucket = director->findBucket(bucketName); if (!bucket) { // explicit bucket specified, but not found -> ignore. bucket = director->rootBucket(); r->log(Severity::error, "director: Requested bucket '%s' not found in director '%s'. " "Assigning root bucket.", bucketName.c_str(), directorName.c_str()); } } else { bucket = director->rootBucket(); } auto rn = requestNotes(r); rn->manager = director; r->onPostProcess.connect(std::bind(&DirectorPlugin::addVia, this, r)); #if !defined(NDEBUG) server().log(Severity::trace, "director: passing request to %s [%s].", director->name().c_str(), bucket->name().c_str()); #endif director->schedule(rn, bucket); } // }}}
void HaproxyApi::csv(HttpRequest* r) { Buffer buf; buf.push_back("# pxname,svname,qcur,qmax,scur,smax,slim,stot,bin,bout,dreq,dresp,ereq,econ,eresp,wretr,wredis,status,weight,act,bck,chkfail,chkdown,lastchg,downtime,qlimit,pid,iid,sid,throttle,lbtot,tracked,type,rate,rate_lim,rate_max,check_status,check_code,check_duration,hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,req_rate,req_rate_max,req_tot,cli_abrt,srv_abrt,\n"); for (auto di: *directors_) { Director* director = di.second; buildFrontendCSV(buf, director); director->eachBackend([&](Backend* backend) { // 01 pxname: proxy name buf.push_back(director->name()); buf.push_back(','); // 02 svname: service name (FRONTEND for frontend, BACKEND for backend, any name for server) buf.push_back(backend->name()); // svname // 03 qcur: current queued requests // 04 qmax: max queued requests buf.push_back(",0,0,"); // qcur, qmax // 05 scur: current sessions buf.push_back(backend->load().current()); buf.push_back(','); // 06 smax: max sessions buf.push_back(backend->load().max()); buf.push_back(","); // 07 slim: sessions limit buf.push_back(","); // 08 stot: total sessions buf.push_back(backend->load().total()); // 09 bin: bytes in // 10 bout: bytes out buf.push_back(",0,0,"); // bin, bout // 11 dreq: denied requests // 12 dresp: denied responses buf.push_back("0,0,"); // dreq, dresp // 13 ereq: request errors // 14 econ: connection errors // 15 eresp: response errors (among which srv_abrt) buf.push_back("0,0,0,"); // ereq, econ, resp // 16 wretr: retries (warning) // 17 wredis: redispatches (warning) buf.push_back("0,0,"); // wretr, wredis // 18 status if (!backend->isEnabled()) { // status buf.push_back("MAINT"); } else if (backend->healthMonitor()) { switch (backend->healthMonitor()->state()) { case HealthState::Online: buf.push_back("UP"); break; case HealthState::Offline: buf.push_back("DOWN"); break; case HealthState::Undefined: buf.push_back("UNKNOWN"); break; } } else { buf.push_back("UP"); } // weight: server weight (server), total weight (backend) buf.push_back(",0,"); // weight // 20 act: server is active (server), number of active servers (backend) // 21 bck: server is backup (server), number of backup servers (backend) switch (director->backendRole(backend)) { // act, bck case BackendRole::Active: buf.push_back("1,0,"); break; case BackendRole::Backup: buf.push_back("0,1,"); break; case BackendRole::Terminate: buf.push_back("0,0,"); break; } // 22 chkfail: number of failed checks buf.push_back(','); // TODO // 23 chkdown: number of UP->DOWN transitions buf.push_back(','); // TODO // 24 lastchg: last status change (in seconds) buf.push_back(','); // TODO // 25 downtime: total downtime (in seconds) buf.push_back(','); // TODO // 26 qlimit: queue limit buf.push_back(','); // 27 pid: process id (0 for first instance, 1 for second, ...) buf.push_back("0,"); // 28 iid: unique proxy id buf.push_back(','); // 29 sid: service id (unique inside a proxy) buf.push_back(','); // 30 throttle: warm up status buf.push_back(','); // 31 lbtot: total number of times a server was selected buf.push_back(','); // TODO // 32 tracked: id of proxy/server if tracking is enabled buf.push_back(','); // 33 type (0=frontend, 1=backend, 2=server, 3=socket) buf.push_back("2,"); // 34 rate: number of sessions per second over last elapsed second buf.push_back(','); // TODO // 35 rate_lim: limit on new sessions per second buf.push_back(','); // 36 rate_max: max number of new sessions per second buf.push_back(','); // 37 check_status: status of last health check, one of: // - UNK -> unknown // - INI -> initializing // - SOCKERR -> socket error // - L4OK -> check passed on layer 4, no upper layers testing enabled // - L4TMOUT -> layer 1-4 timeout // - L4CON -> layer 1-4 connection problem, for example "Connection refused" (tcp rst) or "No route to host" (icmp) // - L6OK -> check passed on layer 6 // - L6TOUT -> layer 6 (SSL) timeout // - L6RSP -> layer 6 invalid response - protocol error // - L7OK -> check passed on layer 7 // - L7OKC -> check conditionally passed on layer 7, for example 404 with disable-on-404 // - L7TOUT -> layer 7 (HTTP/SMTP) timeout // - L7RSP -> layer 7 invalid response - protocol error // - L7STS -> layer 7 response error, for example HTTP 5xx buf.push_back("UNK,"); // TODO // 38 check_code: layer5-7 code, if available buf.push_back(','); // 39 check_duration: time in ms took to finish last health check buf.push_back(','); // TODO // 40 hrsp_1xx: http responses with 1xx code buf.push_back(','); // TODO // 41 hrsp_2xx: http responses with 2xx code buf.push_back(','); // TODO // 42 hrsp_3xx: http responses with 3xx code buf.push_back(','); // TODO // 43 hrsp_4xx: http responses with 4xx code buf.push_back(','); // TODO // 44 hrsp_5xx: http responses with 5xx code buf.push_back(','); // TODO // 45 hrsp_other: http responses with other codes (protocol error) buf.push_back(','); // TODO // 46 hanafail: failed health checks details buf.push_back(','); // TODO // 47 req_rate: HTTP requests per second over last elapsed second buf.push_back(','); // 48 req_rate_max: max number of HTTP requests per second observed buf.push_back(','); // 49 req_tot: total number of HTTP requests received buf.push_back(backend->load().total()); buf.push_back(','); // 50 cli_abrt: number of data transfers aborted by the client buf.push_back(','); // TODO // 51 srv_abrt: number of data transfers aborted by the server (inc. in eresp) buf.push_back(','); // TODO buf.push_back('\n'); }); } char slen[32]; snprintf(slen, sizeof(slen), "%zu", buf.size()); r->responseHeaders.push_back("Content-Length", slen); r->responseHeaders.push_back("Content-Type", "text/plain"); // HAproxy software does sent this one instead of text/csv. r->responseHeaders.push_back("Cache-Control", "no-cache"); r->write<x0::BufferSource>(buf); r->finish(); }