crossroads

Git mirror of https://crossroads.e-tunity.com/
git clone git://git.finwo.net/app/crossroads
Log | Files | Refs | LICENSE

answer.cc (15910B)


      1 #include "webinterface"
      2 #include "balancer/balancer"
      3 
      4 static void stop_backend_thread(pthread_t id) {
      5     Threadinfo info = Threadlist::info(id);
      6     msg("Stopping thread " << id << 
      7 	" (backend socket " << info.backendfd() <<
      8 	", client socket " << info.clientfd() + ")\n");
      9     close(info.backendfd());
     10     close(info.clientfd());
     11     Threadlist::deregister(id);
     12 }    	
     13 
     14 static unsigned str2uns (string const &s, string const &desc) {
     15     unsigned ret;
     16 
     17     if (sscanf (s.c_str(), "%u", &ret) < 1)
     18 	throw Error("Bad " + desc);
     19     return (ret);
     20 }
     21 
     22 static double str2dbl (string const &s, string const &desc) {
     23     double ret;
     24 
     25     if (sscanf (s.c_str(), "%lf", &ret) < 0)
     26 	throw Error("Bad " + desc);
     27     return (ret);
     28 }
     29 
     30 static pthread_t str2threadid (string const &s, string const &desc) {
     31     pthread_t ret;
     32     long long val;
     33     int convret;
     34 
     35     if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
     36 	convret = sscanf(s.c_str() + 2, "%llx", &val);
     37     else
     38 	convret = sscanf(s.c_str(), "%lld", &val);
     39     if (convret < 1)
     40 	throw Error("Bad " + desc);
     41     memcpy (&ret, &val, sizeof(ret));
     42     return (ret);
     43 }
     44 
     45 static unsigned backendindex (string const &s) {
     46     unsigned ret;
     47 
     48     ret = str2uns (s, "back end index");
     49     if (ret >= balancer.nbackends())
     50 	throw Error("Back end index out of range");
     51     return (ret);
     52 }
     53 
     54 static unsigned headerindex (string const &s) {
     55     unsigned ret;
     56 
     57     ret = str2uns (s, "header index");
     58     if (ret >= config.nserverheaders())
     59 	throw Error("Server header index out of range");
     60     return (ret);
     61 }
     62 
     63 bool str2bool (string const &s, string const &desc) {
     64     int i;
     65     bool ret;
     66 
     67     if (sscanf (s.c_str(), "%d", &i) > 0)
     68 	ret = (i != 0);
     69     else if (s == "on" || s == "yes" || s == "true")
     70 	ret = true;
     71     else if (s == "off" || s == "no" || s == "false")
     72 	ret = false;
     73     else
     74 	throw Error("Bad " + desc + " switch '" + s + "'");
     75 
     76     return (ret);
     77 }
     78 
     79 string decode (string const &s) {
     80     string ret;
     81 
     82     for (char const *cp = s.c_str(); cp && *cp;) {
     83 	if (*cp == '%') {
     84 	    int v;
     85 	    cp++;
     86 	    if (sscanf (cp, "%2x", &v)) {
     87 		ret += static_cast<char>(v);
     88 		cp += 2;
     89 	    }else {
     90 		ret += '%';
     91 	    }
     92 	} else if (*cp == '+') {
     93 	    ret += ' ';
     94 	} else {
     95 	    ret += *cp;
     96 	    cp++;
     97 	}
     98     }
     99 
    100     // debugmsg ("Decoded: '" + s + "' into '" + ret + "'\n");
    101 
    102     return (ret);
    103 }
    104 
    105 void Webinterface::answer(Httpbuffer req) {
    106     if (req.requestmethod() != Httpbuffer::m_get)
    107 	throw Error("Only request method GET supported");
    108 
    109     // If web interface authentication is in effect, then we need the creds
    110     // for all requests.
    111     if (config.webinterface_auth() != "") {
    112 	// Incoming auth headers have the format:
    113 	// Authorization: Basic BASE64-ENCODED-UN:PW
    114 	string given_auth = req.headerval("Authorization:");
    115 	if (given_auth.length() > 6)
    116 	    given_auth = given_auth.substr(6);
    117 	if (base64_decode(given_auth) != config.webinterface_auth()) {
    118 	    string resp =
    119 		"HTTP/1.0 401 Authorization Required\r\n"
    120 		"WWW-Authenticate: Basic realm=\"Crossroads Web Interface\"\r\n"
    121 		"Content-Length: 0\r\n"
    122 		"\r\n";
    123 	    Netbuffer buf(resp);
    124 	    buf.netwrite(cfd, config.client_write_timeout());
    125 	    return;
    126 	}
    127     }
    128 
    129     string uri = req.requesturi();
    130 
    131     // Status overview
    132     if (uri == "/") {
    133 	answer_status();
    134 	return;
    135     }
    136 
    137     // XSLT request
    138     if (uri == "/xslt") {
    139 	answer_xslt();
    140 	return;
    141     }
    142 
    143     // /favicon.ico requests (those pesky browsers)
    144     if (uri == "/favicon.ico") {
    145 	string resp =
    146 	    "HTTP/1.0 404 Not Found\r\n"
    147 	    "Content-Length: 0\r\n"
    148 	    "\r\n";
    149 	Netbuffer buf(resp);
    150 	buf.netwrite(cfd, config.client_write_timeout());
    151 	return;
    152     }
    153 
    154     if (uri[0] == '/')	
    155 	uri = uri.substr(1);    
    156     vector<string> parts = str2parts (uri, '/');
    157     for (unsigned i = 0; i < parts.size(); i++)
    158 	parts[i] = decode(parts[i]);
    159 
    160     // server/buffersize/VALUE
    161     if (parts.size() == 3 &&
    162 	parts[0] == "server" && parts[1] == "buffersize") {
    163 	unsigned sz = str2uns (parts[2], "buffer size");
    164 	if (sz < 1)
    165 	    throw Error("Buffer size may not be less than 1");
    166 	config.buffersize(sz);
    167 	answer_status();
    168 	return;
    169     }
    170 
    171     // /server/webinterfaceauth/
    172     // /server/webinterfaceauth/USER:PASS
    173     if (parts.size() == 3 &&
    174 	parts[0] == "server" && parts[1] == "webinterfaceauth") {
    175 	config.webinterface_auth(parts[2]);
    176 	answer_status();
    177 	return;
    178     }
    179 
    180     // /server/maxconnections/
    181     // /server/maxconnections/NUMBER
    182     if (parts.size() == 3 &&
    183 	parts[0] == "server" && parts[1] == "maxconnections") {
    184 	unsigned num = 0;
    185 	if (parts[2] != "")
    186 	    num = str2uns (parts[2], "server weight");
    187 	config.maxconn(num);
    188 	answer_status();
    189 	return;
    190     }
    191 
    192     // /server/addxrversion/BOOLEAN
    193     if (parts.size() == 3 &&
    194 	parts[0] == "server" && parts[1] == "addxrversion") {
    195 	config.addxrversion (str2bool (parts[2], "addxrversion"));
    196 	answer_status();
    197 	return;
    198     }
    199 
    200     // /server/addxforwardedfor/BOOLEAN
    201     if (parts.size() == 3 &&
    202 	parts[0] == "server" && parts[1] == "addxforwardedfor") {
    203 	config.addxforwardedfor (str2bool (parts[2], "addxforwardedfor"));
    204 	answer_status();
    205 	return;
    206     }
    207 
    208     // /server/stickyhttp/BOOLEAN
    209     if (parts.size() == 3 &&
    210 	parts[0] == "server" && parts[1] == "stickyhttp") {
    211 	config.stickyhttp (str2bool(parts[2], "stickyhttp"));
    212 	answer_status();
    213 	return;
    214     }
    215 
    216     // /server/replacehostheader/BOOLEAN
    217     if (parts.size() == 3 &&
    218 	parts[0] == "server" && parts[1] == "replacehostheader") {
    219 	config.replacehostheader (str2bool(parts[2], "replacehostheader"));
    220 	answer_status();
    221 	return;
    222     }
    223 
    224     // /server/newheader/NEWHEADER
    225     if (parts.size() == 3 &&
    226 	parts[0] == "server" && parts[1] == "newheader") {
    227 	config.addserverheader(parts[2]);
    228 	answer_status();
    229 	return;
    230     }
    231 
    232     // /server/changeheader/NR
    233     // /server/changeheader/NR/VALUE
    234     if (parts.size() == 4 &&
    235 	parts[0] == "server" && parts[1] == "changeheader") {
    236 	unsigned ind = headerindex(parts[2]);
    237 	if (parts[3] == "")
    238 	    config.removeserverheader(ind);
    239 	else
    240 	    config.changeserverheader(ind, parts[3]);
    241 	answer_status();
    242 	return;
    243     }
    244 
    245     // /server/verbose/BOOLEAN
    246     if (parts.size() == 3 &&
    247 	parts[0] == "server" && parts[1] == "verbose") {
    248 	config.verbose(str2bool(parts[2], "verbose"));
    249 	answer_status();
    250 	return;
    251     }
    252 
    253     // /server/debug/VERBOSE
    254     if (parts.size() == 3 &&
    255 	parts[0] == "server" && parts[1] == "debug") {
    256 	config.debug(str2bool(parts[2], "debug"));
    257 	answer_status();
    258 	return;
    259     }
    260 
    261     // /server/logtrafficdir
    262     // /server/logtrafficdir/VALUE
    263     if (parts.size() == 3 &&
    264 	parts[0] == "server" && parts[1] == "logtrafficdir") {
    265 	config.dumpdir(parts[2]);
    266 	answer_status();
    267 	return;
    268     }
    269 
    270     // /server/clientreadtimeout
    271     // /server/clientreadtimeout/NUMBER
    272     if (parts.size() == 3 &&
    273 	parts[0] == "server" && parts[1] == "clientreadtimeout") {
    274 	unsigned num = 0;
    275 	if (parts[2] != "")
    276 	    num = str2uns (parts[2], "client read timeout");
    277 	config.client_read_timeout(num);
    278 	answer_status();
    279 	return;
    280     }
    281 
    282     // /server/clientwritetimeout
    283     // /server/clientwritetimeout/NUMBER
    284     if (parts.size() == 3 &&
    285 	parts[0] == "server" && parts[1] == "clientwritetimeout") {
    286 	unsigned num = 0;
    287 	if (parts[2] != "")
    288 	    num = str2uns (parts[2], "client write timeout");
    289 	config.client_write_timeout(num);
    290 	answer_status();
    291 	return;
    292     }
    293 
    294     // /server/backendreadtimeout
    295     // /server/backendreadtimeout/NUMBER
    296     if (parts.size() == 3 &&
    297 	parts[0] == "server" && parts[1] == "backendreadtimeout") {
    298 	unsigned num = 0;
    299 	if (parts[2] != "")
    300 	    num = str2uns (parts[2], "back end read timeout");
    301 	config.backend_read_timeout(num);
    302 	answer_status();
    303 	return;
    304     }
    305 
    306     // /server/backendwritetimeout
    307     // /server/backendwritetimeout/NUMBER
    308     if (parts.size() == 3 &&
    309 	parts[0] == "server" && parts[1] == "backendwritetimeout") {
    310 	unsigned num = 0;
    311 	if (parts[2] != "")
    312 	    num = str2uns (parts[2], "back end write timeout");
    313 	config.backend_write_timeout(num);
    314 	answer_status();
    315 	return;
    316     }
    317 
    318     // /server/dnscachetimeout
    319     // /server/dnscachetimeout/NUMBER
    320     if (parts.size() == 3 &&
    321 	parts[0] == "server" && parts[1] == "dnscachetimeout") {
    322 	unsigned num = 0;
    323 	if (parts[2] != "")
    324 	    num = str2uns (parts[2], "DNS cache timeout");
    325 	config.dnscachetimeout(num);
    326 	answer_status();
    327 	return;
    328     }
    329 
    330     // /server/wakeupinterval
    331     // /server/wakeupinterval/NUMBER
    332     if (parts.size() == 3 &&
    333 	parts[0] == "server" && parts[1] == "wakeupinterval") {
    334 	unsigned num = 0;
    335 	if (parts[2] != "")
    336 	    num = str2uns (parts[2], "wakeup interval");
    337 	if (num)
    338 	    config.checkupsec(0);
    339 	config.wakeupsec(num);
    340 	answer_status();
    341 	return;
    342     }
    343 
    344     // /server/checkupinterval
    345     // /server/checkupinterval/NUMBER
    346     if (parts.size() == 3 &&
    347 	parts[0] == "server" && parts[1] == "checkupinterval") {
    348 	unsigned num = 0;
    349 	if (parts[2] != "")
    350 	    num = str2uns (parts[2], "checkup interval");
    351 	if (num)
    352 	    config.wakeupsec(0);
    353 	config.checkupsec(num);
    354 	answer_status();
    355 	return;
    356     }
    357 
    358     // /server/timeinterval/SECS
    359     if (parts.size() == 3 &&
    360 	parts[0] == "server" && parts[1] == "timeinterval") {
    361 	unsigned num = str2uns(parts[2], "time interval");
    362 	if (num < 1)
    363 	    throw Error("Time interval may not be less than 1");
    364 	config.connrate_time(num);
    365 	answer_status();
    366 	return;
    367     }
    368 
    369     // /server/hardmaxconnrate/NUMBER
    370     if (parts.size() == 3 &&
    371 	parts[0] == "server" && parts[1] == "hardmaxconnrate") {
    372 	config.hardmaxconnrate(str2uns(parts[2], "hard maxconnrate"));
    373 	answer_status();
    374 	return;
    375     }
    376 
    377     // /server/softmaxconnrate/NUMBER
    378     if (parts.size() == 3 &&
    379 	parts[0] == "server" && parts[1] == "softmaxconnrate") {
    380 	config.softmaxconnrate(str2uns(parts[2], "soft maxconnrate"));
    381 	answer_status();
    382 	return;
    383     }
    384 
    385     // /server/defertime/NUMBER
    386     if (parts.size() == 3 &&
    387 	parts[0] == "server" && parts[1] == "defertime") {
    388 	unsigned num = str2uns(parts[2], "defer time");
    389 	if (num < 1)
    390 	    throw Error("Defer time may not be less than 1");
    391 	config.defertime(num);
    392 	answer_status();
    393 	return;
    394     }
    395 
    396     // /server/closesocketsfast/BOOL
    397     if (parts.size() == 3 &&
    398 	parts[0] == "server" && parts[1] == "closesocketsfast") {
    399 	config.fastclose(str2bool(parts[2], "close sockets fast"));
    400 	answer_status();
    401 	return;
    402     }
    403 
    404     // /server/addallowfrom/ADDRESS
    405     if (parts.size() == 3 &&
    406 	parts[0] == "server" && parts[1] == "addallowfrom") {
    407 	config.addallow(parts[2]);
    408 	answer_status();
    409 	return;
    410     }
    411 
    412     // /server/allowfrom/NR
    413     // /server/allowfrom/NR/ADDRESS
    414     if (parts.size() == 4 &&
    415 	parts[0] == "server" && parts[1] == "allowfrom") {
    416 	unsigned ind = str2uns(parts[2], "allowfrom index");
    417 	if (parts[3] != "")
    418 	    config.changeallow(parts[3], ind);
    419 	else
    420 	    config.deleteallow(ind);
    421 	answer_status();
    422 	return;
    423     }
    424 
    425     // /server/adddenyfrom/ADDRESS
    426     if (parts.size() == 3 &&
    427 	parts[0] == "server" && parts[1] == "adddenyfrom") {
    428 	config.adddeny(parts[2]);
    429 	answer_status();
    430 	return;
    431     }
    432 
    433     // /server/denyfrom/NR
    434     // /server/denyfrom/NR/ADDRESS
    435     if (parts.size() == 4 &&
    436 	parts[0] == "server" && parts[1] == "denyfrom") {
    437 	unsigned ind = str2uns(parts[2], "denyfrom index");
    438 	if (parts[3] != "")
    439 	    config.changedeny(parts[3], ind);
    440 	else
    441 	    config.deletedeny(ind);
    442 	answer_status();
    443 	return;
    444     }
    445 
    446     // /server/hardmaxconnexcess/
    447     // /server/hardmaxconnexcess/PROGRAM
    448     if (parts.size() == 3 &&
    449 	parts[0] == "server" && parts[1] == "hardmaxconnexcess") {
    450 	config.hardmaxconnexcess(parts[2]);
    451 	answer_status();
    452 	return;
    453     }
    454 
    455     // /server/softmaxconnexcess/
    456     // /server/softmaxconnexcess/PROGRAM
    457     if (parts.size() == 3 &&
    458 	parts[0] == "server" && parts[1] == "softmaxconnexcess") {
    459 	config.softmaxconnexcess(parts[2]);
    460 	answer_status();
    461 	return;
    462     }
    463 
    464     // /server/onstart/
    465     // /server/onstart/PROGRAM
    466     if (parts.size() == 3 &&
    467 	parts[0] == "server" && parts[1] == "onstart") {
    468 	config.onstart(parts[2]);
    469 	answer_status();
    470 	return;
    471     }
    472 
    473     // /server/onend/
    474     // /server/onend/PROGRAM
    475     if (parts.size() == 3 &&
    476 	parts[0] == "server" && parts[1] == "onend") {
    477 	config.onend(parts[2]);
    478 	answer_status();
    479 	return;
    480     }
    481 
    482     // /server/onfail/
    483     // /server/onfail/PROGRAM
    484     if (parts.size() == 3 &&
    485 	parts[0] == "server" && parts[1] == "onfail") {
    486 	config.onfail(parts[2]);
    487 	answer_status();
    488 	return;
    489     }
    490 
    491     // /server/addbackend/IP:PORT
    492     if (parts.size() == 3 &&
    493 	parts[0] == "server" && parts[1] == "addbackend") {
    494 	vector<string> address = str2parts(parts[2], ':');
    495 	if (address.size() != 2)
    496 	    throw Error("When adding back ends, the address must be IP:PORT");
    497 	Backend b;
    498 	b.server(address[0]);
    499 	b.port(str2uns(address[1], "back end port"));
    500 	balancer.addbackend(b, false, false, false);
    501 	answer_status();
    502 	return;
    503     }
    504 
    505     // /server/deletebackend/NR
    506     if (parts.size() == 3 &&
    507 	parts[0] == "server" && parts[1] == "deletebackend") {
    508 	balancer.deletebackend(backendindex(parts[2]));
    509 	answer_status();
    510 	return;
    511     }
    512 
    513     // /server/type/http, /server/type/tcp
    514     if (parts.size() == 3 && parts[0] == "server" && parts[1] == "type") {
    515 	config.stype(parts[2]);
    516 	answer_status();
    517 	return;
    518     }
    519 
    520     // /backend/NR/weight/NUMBER
    521     if (parts.size() == 4 &&
    522 	parts[0] == "backend" && parts[2] == "weight") {
    523 	unsigned ind = backendindex(parts[1]);
    524 	unsigned num = str2uns (parts[3], "back end weight");
    525 	if (num < 1)
    526 	    throw Error("Weight may not be less than 1");
    527 	balancer.backend(ind).weight(num);
    528 	answer_status();
    529 	return;
    530     }
    531 
    532     // /backend/NR/maxconnections/NUMBER
    533     if (parts.size() == 4 &&
    534 	parts[0] == "backend" && parts[2] == "maxconnections") {
    535 	unsigned ind = backendindex(parts[1]);
    536 	unsigned num = str2uns (parts[3], "back end maxconnections");
    537 	balancer.backend(ind).maxconn(num);
    538 	answer_status();
    539 	return;
    540     }
    541 
    542     // /backend/NR/loadavg/FLOAT
    543     if (parts.size() == 4 &&
    544 	parts[0] == "backend" && parts[2] == "loadavg") {
    545 	unsigned ind = backendindex(parts[1]);
    546 	double fnum = str2dbl (parts[3], "back end loadavg");
    547 	balancer.backend(ind).loadavg(fnum);
    548 	answer_status();
    549 	return;
    550     }
    551 
    552     // /backend/NR/hostmatch/EXPRESSION
    553     // /backend/NR/hostmatch
    554     if (parts.size() == 4 &&
    555 	parts[0] == "backend" && parts[2] == "hostmatch") {
    556 	unsigned ind = backendindex(parts[1]);
    557 	balancer.backend(ind).hostmatch(parts[3]);
    558 	answer_status();
    559 	return;
    560     }
    561 
    562     // /backend/NR/urlmatch/EXPRESSION
    563     // /backend/NR/urlmatch
    564     if (parts.size() == 4 &&
    565 	parts[0] == "backend" && parts[2] == "urlmatch") {
    566 	unsigned ind = backendindex(parts[1]);
    567 	balancer.backend(ind).urlmatch(parts[3]);
    568 	answer_status();
    569 	return;
    570     }
    571 
    572     // /backend/NR/up/BOOL
    573     if (parts.size() == 4 && parts[0] == "backend" && parts[2] == "up") {
    574 	unsigned ind = backendindex(parts[1]);
    575 	balancer.backend(ind).up(str2bool(parts[3], "up"));
    576 	answer_status();
    577 	return;
    578     }
    579 
    580     // /backend/NR/backendcheck/
    581     // /backend/NR/backendcheck/VALUE
    582     if (parts.size() == 4 &&
    583 	parts[0] == "backend" && parts[2] == "backendcheck") {
    584 	unsigned ind = backendindex(parts[1]);
    585 	BackendCheck check;
    586 	if (parts[3] != "")
    587 	    check.parse(parts[3]);
    588 	balancer.backend(ind).backendcheck(check);
    589 	answer_status();
    590 	return;
    591     }
    592 
    593     // /backend/NR/stopconnections
    594     if (parts.size() == 3 &&
    595 	parts[0] == "backend" && parts[2] == "stopconnections") {
    596 	unsigned ind = backendindex(parts[1]);
    597 	bool done = false;
    598 	while (!done) {
    599 	    done = true;
    600 	    for (Threadmap::iterator it = Threadlist::map().begin();
    601 		 it != Threadlist::map().end();
    602 		 it++) {
    603 		pthread_t thread_id = (*it).first;
    604 		Threadinfo thread_info = (*it).second;
    605 		if (thread_info.backend() == (int)ind) {
    606 		    stop_backend_thread(thread_id);
    607 		    done = false;
    608 		    break;
    609 		}
    610 	    }
    611 	}
    612 	answer_status();
    613 	return;
    614     }
    615 
    616     // /thread/kill/VALUE
    617     if (parts.size() == 3 && parts[0] == "thread" && parts[1] == "kill") {
    618 	pthread_t id = str2threadid(parts[2], "thread id");
    619 	stop_backend_thread(id);
    620 	answer_status();
    621 	return;
    622     }
    623 
    624     throw Error("No action for URI '/" + uri + "'");
    625 }