| 183 | |
| 184 | |
| 185 | void HTTPHandler::processQuery( |
| 186 | HTTPServerRequest & request, |
| 187 | HTMLForm & params, |
| 188 | HTTPServerResponse & response, |
| 189 | Output & used_output, |
| 190 | QueryScope & query_scope, |
| 191 | const ProfileEvents::Event & write_event) |
| 192 | { |
| 193 | using namespace Poco::Net; |
| 194 | |
| 195 | LOG_TRACE(log, "Request URI: {}", request.getURI()); |
| 196 | |
| 197 | if (!authenticateUser(request, params, response)) |
| 198 | return; // '401 Unauthorized' response with 'Negotiate' has been sent at this point. |
| 199 | |
| 200 | /// The user could specify session identifier and session timeout. |
| 201 | /// It allows to modify settings, create temporary tables and reuse them in subsequent requests. |
| 202 | |
| 203 | String session_id; |
| 204 | std::chrono::steady_clock::duration session_timeout; |
| 205 | bool session_is_set = params.has("session_id"); |
| 206 | const auto & config = server.config(); |
| 207 | |
| 208 | /// Close http session (if any) after processing the request |
| 209 | bool close_session = false; |
| 210 | if (params.getParsed<bool>("close_session", false) && server.config().getBool("enable_http_close_session", true)) |
| 211 | close_session = true; |
| 212 | |
| 213 | if (session_is_set) |
| 214 | { |
| 215 | session_id = params.get("session_id"); |
| 216 | if (session_id.empty()) |
| 217 | throw Exception(ErrorCodes::SESSION_ID_EMPTY, "Session id query parameter was provided, but it was empty"); |
| 218 | session_timeout = parseSessionTimeout(config, params); |
| 219 | std::string session_check = params.get("session_check", ""); |
| 220 | session->makeSessionContext(session_id, session_timeout, session_check == "1"); |
| 221 | } |
| 222 | else |
| 223 | { |
| 224 | session_id = ""; |
| 225 | /// We should create it even if we don't have a session_id |
| 226 | session->makeSessionContext(); |
| 227 | } |
| 228 | |
| 229 | /// We need to have both releasing/closing a session here and below. The problem with having it only as a SCOPE_EXIT |
| 230 | /// is that it will be invoked after finalizing the buffer in the end of processQuery, and that technically means that |
| 231 | /// the client has received all the data, but the session is not released yet. And it can (and sometimes does) happen |
| 232 | /// that we'll try to acquire the same session in another request before releasing the session here, and the session for |
| 233 | /// the following request will be technically locked, while it shouldn't be. |
| 234 | /// Also, SCOPE_EXIT is still needed to release a session in case of any exception. If the exception occurs at some point |
| 235 | /// after releasing the session below, this whole call will be no-op (due to named_session being nullptr already inside a session). |
| 236 | SCOPE_EXIT_SAFE({ releaseOrCloseSession(session_id, close_session); }); |
| 237 | |
| 238 | auto context = session->makeQueryContext(); |
| 239 | |
| 240 | auto roles = params.getAll("role"); |
| 241 | if (!roles.empty()) |
| 242 | context->setCurrentRoles(roles); |
nothing calls this directly
no test coverage detected