void BundleInstallHandler::run()
{
	ServiceRef::Ptr pInstallerRef = context()->registry().findByName(BundleInstallerService::SERVICE_NAME);
	AutoPtr<BundleInstallerService> pInstaller = pInstallerRef->castedInstance<BundleInstallerService>();

	HTMLForm form;
	InstallPartHandler partHandler(form, pInstaller);
	form.load(request(), request().stream(), partHandler);

	std::string title;
	std::string backLink;
	std::string backLinkTitle;
	std::string symbolicName = form.get("symbolicName", "");
	if (!symbolicName.empty())
	{
		Bundle::Ptr pBundle = context()->findBundle(symbolicName);
		title = text("upgradeBundle");
		title += ": ";
		title += symbolicName;
		backLink = bundlePath(symbolicName);
		backLinkTitle = text("bundle");
		backLinkTitle += ": ";
		backLinkTitle += pBundle->name();
	}
	else
	{
		title = text("installBundle");
		backLink = bundle()->properties().getString("web.path");
		backLinkTitle = text("installedBundles");
	}
		
	beginPage(title, backLink, backLinkTitle);
	if (partHandler.installed())
	{
		templat().clear();
		templat().setString("symbolicName", partHandler.bundle()->symbolicName());
		templat().setString("name", partHandler.bundle()->name());
		sendTemplate("html.installComplete");
		beginList();
		item(text("id"), NumberFormatter::format(partHandler.bundle()->id()));
		item(text("symbolicName"), partHandler.bundle()->symbolicName());
		item(text("version"), partHandler.bundle()->version().toString());
		item(text("path"), partHandler.bundle()->path());
		endList();
	}
	else if (symbolicName.empty())
	{
		sendTemplate("html.installForm");
	}
	else
	{
		templat().clear();
		templat().setString("symbolicName", symbolicName);
		sendTemplate("html.upgradeForm");
	}
	endPage();
}
void HTTPHandler::processQuery(
    Poco::Net::HTTPServerRequest & request,
    HTMLForm & params,
    Poco::Net::HTTPServerResponse & response,
    Output & used_output)
{
    Context context = server.context();
    context.setGlobalContext(server.context());

    CurrentThread::QueryScope query_scope(context);

    LOG_TRACE(log, "Request URI: " << request.getURI());

    std::istream & istr = request.stream();

    /// Part of the query can be passed in the 'query' parameter and the rest in the request body
    /// (http method need not necessarily be POST). In this case the entire query consists of the
    /// contents of the 'query' parameter, a line break and the request body.
    std::string query_param = params.get("query", "");
    if (!query_param.empty())
        query_param += '\n';

    /// The user and password can be passed by headers (similar to X-Auth-*),
    /// which is used by load balancers to pass authentication information.
    std::string user = request.get("X-ClickHouse-User", "");
    std::string password = request.get("X-ClickHouse-Key", "");
    std::string quota_key = request.get("X-ClickHouse-Quota", "");

    if (user.empty() && password.empty() && quota_key.empty())
    {
        /// User name and password can be passed using query parameters
        /// or using HTTP Basic auth (both methods are insecure).
        if (request.hasCredentials())
        {
            Poco::Net::HTTPBasicCredentials credentials(request);

            user = credentials.getUsername();
            password = credentials.getPassword();
        }
        else
        {
            user = params.get("user", "default");
            password = params.get("password", "");
        }

        quota_key = params.get("quota_key", "");
    }
    else
    {
        /// It is prohibited to mix different authorization schemes.
        if (request.hasCredentials()
            || params.has("user")
            || params.has("password")
            || params.has("quota_key"))
        {
            throw Exception("Invalid authentication: it is not allowed to use X-ClickHouse HTTP headers and other authentication methods simultaneously", ErrorCodes::REQUIRED_PASSWORD);
        }
    }

    std::string query_id = params.get("query_id", "");
    context.setUser(user, password, request.clientAddress(), quota_key);
    context.setCurrentQueryId(query_id);

    /// The user could specify session identifier and session timeout.
    /// It allows to modify settings, create temporary tables and reuse them in subsequent requests.

    std::shared_ptr<Context> session;
    String session_id;
    std::chrono::steady_clock::duration session_timeout;
    bool session_is_set = params.has("session_id");
    const auto & config = server.config();

    if (session_is_set)
    {
        session_id = params.get("session_id");
        session_timeout = parseSessionTimeout(config, params);
        std::string session_check = params.get("session_check", "");

        session = context.acquireSession(session_id, session_timeout, session_check == "1");

        context = *session;
        context.setSessionContext(*session);
    }

    SCOPE_EXIT({
        if (session_is_set)
            session->releaseSession(session_id, session_timeout);
    });