// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #include "http/http_channel.h" #include #include #include #include #include #include #include #include "common/logging.h" #include "common/status.h" #include "gutil/strings/split.h" #include "gutil/strings/strip.h" #include "http/http_headers.h" #include "http/http_request.h" #include "http/http_status.h" #include "util/slice.h" #include "util/zlib.h" namespace doris { // Send Unauthorized status with basic challenge void HttpChannel::send_basic_challenge(HttpRequest* req, const std::string& realm) { static std::string s_prompt_str = "Please provide your userid and password\n"; std::stringstream ss; ss << "Basic realm=\"" << realm << "\""; req->add_output_header(HttpHeaders::WWW_AUTHENTICATE, ss.str().c_str()); send_reply(req, HttpStatus::UNAUTHORIZED, s_prompt_str); } void HttpChannel::send_error(HttpRequest* request, HttpStatus status) { evhttp_send_error(request->get_evhttp_request(), status, default_reason(status).c_str()); } void HttpChannel::send_reply(HttpRequest* request, HttpStatus status) { evhttp_send_reply(request->get_evhttp_request(), status, default_reason(status).c_str(), nullptr); } void HttpChannel::send_reply(HttpRequest* request, HttpStatus status, const std::string& content) { auto evb = evbuffer_new(); std::string compressed_content; if (compress_content(request->header(HttpHeaders::ACCEPT_ENCODING), content, &compressed_content)) { request->add_output_header(HttpHeaders::CONTENT_ENCODING, "gzip"); evbuffer_add(evb, compressed_content.c_str(), compressed_content.size()); } else { evbuffer_add(evb, content.c_str(), content.size()); } evhttp_send_reply(request->get_evhttp_request(), status, default_reason(status).c_str(), evb); evbuffer_free(evb); } void HttpChannel::send_file(HttpRequest* request, int fd, size_t off, size_t size, bufferevent_rate_limit_group* rate_limit_group) { auto evb = evbuffer_new(); evbuffer_add_file(evb, fd, off, size); auto* evhttp_request = request->get_evhttp_request(); if (rate_limit_group) { auto* evhttp_connection = evhttp_request_get_connection(evhttp_request); auto* buffer_event = evhttp_connection_get_bufferevent(evhttp_connection); bufferevent_add_to_rate_limit_group(buffer_event, rate_limit_group); } evhttp_send_reply(evhttp_request, HttpStatus::OK, default_reason(HttpStatus::OK).c_str(), evb); evbuffer_free(evb); } bool HttpChannel::compress_content(const std::string& accept_encoding, const std::string& input, std::string* output) { // Don't bother compressing empty content. if (input.empty()) { return false; } // Check if gzip compression is accepted by the caller. If so, compress the // content and replace the prerendered output. bool is_compressed = false; std::vector encodings = strings::Split(accept_encoding, ","); for (string& encoding : encodings) { StripWhiteSpace(&encoding); if (encoding == "gzip") { std::ostringstream oss; Status s = zlib::CompressLevel(Slice(input), 1, &oss); if (s.ok()) { *output = oss.str(); is_compressed = true; } else { LOG(WARNING) << "Could not compress output: " << s.to_string(); } break; } } return is_compressed; } } // namespace doris