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 }