#include "httpdispatcher"

void HttpDispatcher::dispatch() {
    PROFILE("HttpDispatcher::dispatch");

    unsigned stickytarget;
    string host_header = "";
    string url = "";

    // Try to dispatch. Since we're in HTTP mode, we must return an
    // error page when dispatching fails.
    try {

	// Get the client's request. May need for cookie inspection or for the
	// host header.
	while (!buf().headersreceived()) {
	    // cout << "BUFFER fetching headers: " << buf().as_string() << '\n';
	    if (! buf().netread(clientfd(), config.client_read_timeout())) {
		msg("Didn't receive a valid client request, stopping");
		throw Error("No HTTP request received");
	    }
	}
	msg("Received client request: '" + buf().firstline() + "'\n");
	// cout << "BUFFER with headers: " << buf().as_string() << '\n';

	// See if hostmatching or urlmatching is used.
	// This is true when hosts or urls are matched against non-dot.
	bool hostmatchused = false;
	bool urlmatchused = false;
	for (unsigned i = 0; i < balancer.nbackends(); i++) {
	    if (balancer.backend(i).hostmatch() != ".") 
		hostmatchused = true;
	    if (balancer.backend(i).urlmatch() != ".")
		urlmatchused = true;
	}
	
	// Build new target list if host- or url matching applies.
	if (hostmatchused || urlmatchused) {
	    msg("Creating matched target list for the HTTP dispatcher\n");
	    
	    if (hostmatchused)
		host_header = buf().headerval("Host");
	    if (urlmatchused)
		url = buf().url();
	    
	    BackendVector v;
	    v.isdefined(true);
	    
	    for (unsigned i = 0; i < balancer.nbackends(); i++) {
		if (! balancer.backend(i).available())
		    continue;
		bool host_allowed = true, url_allowed = true;
		if (hostmatchused &&
		    regexec(&(balancer.backend(i).hostregex()),
			    host_header.c_str(), 0, 0, 0)) {
		    debugmsg("Back end " +
			     balancer.backend(i).description() +
			     " forbidden due to hostmatch\n");		    
		    host_allowed = false;
		}
		if (urlmatchused &&
		    regexec(&(balancer.backend(i).urlregex()),
			    url.c_str(), 0, 0, 0)) {
		    debugmsg("Back end " +
			     balancer.backend(i).description() +
			     " forbidden due to urlmatch\n");		    
		    url_allowed = false;
		}
		if (host_allowed && url_allowed) {
		    v.add(i);
		    msg("Candidate target: " +
			balancer.backend(i).description() + "\n");
		}
	    }
	    targetlist(v);
	}
	
	// Dispatch as a normal backend if sticky HTTP is off, or if the
	// sticky target is badly specified.
	if (!config.stickyhttp() ||
	    (sscanf(buf().cookievalue("XRTarget").c_str(),
		    "%d", &stickytarget) < 1 &&
	     sscanf(buf().paramvalue("XRTarget").c_str(),
		    "%d", &stickytarget) < 1) ||
	    stickytarget >= balancer.nbackends()) {
	    issticky(false);
	    TcpDispatcher::dispatch();
	} else {
	    // Got a sticky target. Try to connect. If that fails, fallback
	    // to non-sticky dispatching.
	    targetbackend(stickytarget);
	    Backend tb = balancer.backend(stickytarget);
	    tb.live(true);
	    msg("Sticky HTTP request for " + tb.description() + "\n");
	    if (! tb.connect()) {
		balancer.backend(stickytarget).live(false);
		msg("Failed to connect to back end " + tb.description() +
		    ", trying to dispatch to other\n");
		issticky(false);
		TcpDispatcher::dispatch();
	    } else {
		backendfd(tb.sock());
		issticky(true);
	    }
	}
 

    } catch (Error const &e) {
	senderrorpage(e.what());
	throw e;
    }
}

