#include "httpdispatcher"

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

    // The client request was already retrieved before starting the
    // dispatcher. We can continue by applying server-directed headers.
    if (config.addxrversion())
	buf().setheader ("XR", VER);
    if (config.addxforwardedfor())
	buf().addheader ("X-Forwarded-For",
			 inet2string(clientfd().clientaddr().sin_addr));
    for (unsigned n = 0; n < config.nserverheaders(); n++)
	buf().setheader (config.serverheader(n));

    // Patch up the Host: header if requested so.
    if (config.replacehostheader()) {
	ostringstream o;
	o << balancer.backend(targetbackend()).server()
	  << ":" << balancer.backend(targetbackend()).port();
	buf().replaceheader("Host:", o.str());
    }

    // Flush client info received so far to the back end.
    debugmsg("Sending client request to back end\n");
    buf().netwrite(backendfd(), config.backend_write_timeout());

    // Let's see if we will need to modify the server headers.
    bool modify_serverheaders = false;
    if (config.addxrversion() ||
	(config.stickyhttp() && !issticky()))
	modify_serverheaders = true;

    // Store the client request. May want to log it later.
    string client_request = buf().firstline();

    // Go into copy-thru mode. If required, catch the server headers on
    // their first appearance and modify them.
    bool backend_response_checked = false;
    while (1) {
	Fdset readset (maxtimeout(config.client_read_timeout(),
				  config.backend_read_timeout()));
	readset.add(clientfd());
	readset.add(backendfd());
	readset.wait_r();

	Socket sock;
	unsigned timeout;
	if (readset.readable(clientfd())) {
	    sock = clientfd();
	    timeout = config.client_read_timeout();
	} else if (readset.readable(backendfd())) {
	    sock = backendfd();
	    timeout = config.backend_read_timeout();
	} else
	    break;

	buf().reset();

	if (!buf().netread(sock, timeout))
	    break;

	if (sock == backendfd() && modify_serverheaders) {
	    debugmsg("Back end response seen, applying modifications\n");
	    modify_serverheaders = false;
	    while (! buf().headersreceived())
		if (!buf().netread (sock, config.backend_read_timeout()))
		    throw Error("Failed to get headers from back end");
	    if (config.addxrversion())
		buf().setheader("XR", VER);
	    if (config.stickyhttp() && !issticky()) {
		ostringstream o;
		o << "XRTarget=" << targetbackend() << "; path=/";
		buf().setheader("Set-Cookie", o.str());
	    }
	}

	// The back end response may now get flushed to the client.
	// If the response code is 4** or 5**, log it as a warning.
	if (!backend_response_checked &&
	    sock == backendfd() && buf().headersreceived()) {
	    string respcode = buf().stringat(9, 3);
	    if (respcode[0] == '4' || respcode[0] == '5')
		warnmsg("HTTP back end indicates fault: '" <<
			buf().firstline() << "' as response to '" <<
			client_request << "'\n");
	    backend_response_checked = true;
	}

	// Flush info to the other connected side.
	Socket othersock;
	if (sock == clientfd()) {
	    othersock = backendfd();
	    timeout = config.backend_write_timeout();
	    // Re-patch Host header if requested
	    if (config.replacehostheader()) {
		ostringstream o;
		o << balancer.backend(targetbackend()).server()
		  << ":" << balancer.backend(targetbackend()).port();
		buf().replaceheader("Host:", o.str());
	    }
	} else {
	    othersock = clientfd();
	    timeout = config.client_write_timeout();
	}

	debugmsg ("Had data on " << sock.fd() <<
		  ", sending to " << othersock.fd() << '\n');

	buf().netwrite(othersock, timeout);
	if (sock == backendfd())
	    balancer.backend(targetbackend()).addbytes(buf().bufsz());
    }
}
