/root/doris/be/src/http/http_channel.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #include "http/http_channel.h" |
19 | | |
20 | | #include <event2/buffer.h> |
21 | | #include <event2/bufferevent.h> |
22 | | #include <event2/http.h> |
23 | | #include <event2/http_struct.h> |
24 | | |
25 | | #include <sstream> |
26 | | #include <string> |
27 | | #include <vector> |
28 | | |
29 | | #include "common/logging.h" |
30 | | #include "common/status.h" |
31 | | #include "gutil/strings/split.h" |
32 | | #include "gutil/strings/strip.h" |
33 | | #include "http/http_headers.h" |
34 | | #include "http/http_request.h" |
35 | | #include "http/http_status.h" |
36 | | #include "util/slice.h" |
37 | | #include "util/zlib.h" |
38 | | |
39 | | namespace doris { |
40 | | |
41 | | // Send Unauthorized status with basic challenge |
42 | 1 | void HttpChannel::send_basic_challenge(HttpRequest* req, const std::string& realm) { |
43 | 1 | static std::string s_prompt_str = "Please provide your userid and password\n"; |
44 | 1 | std::stringstream ss; |
45 | 1 | ss << "Basic realm=\"" << realm << "\""; |
46 | 1 | req->add_output_header(HttpHeaders::WWW_AUTHENTICATE, ss.str().c_str()); |
47 | 1 | send_reply(req, HttpStatus::UNAUTHORIZED, s_prompt_str); |
48 | 1 | } |
49 | | |
50 | 2 | void HttpChannel::send_error(HttpRequest* request, HttpStatus status) { |
51 | 2 | evhttp_send_error(request->get_evhttp_request(), status, default_reason(status).c_str()); |
52 | 2 | } |
53 | | |
54 | 28 | void HttpChannel::send_reply(HttpRequest* request, HttpStatus status) { |
55 | 28 | evhttp_send_reply(request->get_evhttp_request(), status, default_reason(status).c_str(), |
56 | 28 | nullptr); |
57 | 28 | } |
58 | | |
59 | 24 | void HttpChannel::send_reply(HttpRequest* request, HttpStatus status, const std::string& content) { |
60 | 24 | auto* evb = evbuffer_new(); |
61 | 24 | std::string compressed_content; |
62 | 24 | if (compress_content(request->header(HttpHeaders::ACCEPT_ENCODING), content, |
63 | 24 | &compressed_content)) { |
64 | 0 | request->add_output_header(HttpHeaders::CONTENT_ENCODING, "gzip"); |
65 | 0 | evbuffer_add(evb, compressed_content.c_str(), compressed_content.size()); |
66 | 24 | } else { |
67 | 24 | evbuffer_add(evb, content.c_str(), content.size()); |
68 | 24 | } |
69 | 24 | evhttp_send_reply(request->get_evhttp_request(), status, default_reason(status).c_str(), evb); |
70 | 24 | evbuffer_free(evb); |
71 | 24 | } |
72 | | |
73 | | void HttpChannel::send_file(HttpRequest* request, int fd, size_t off, size_t size, |
74 | 1 | bufferevent_rate_limit_group* rate_limit_group) { |
75 | 1 | auto* evb = evbuffer_new(); |
76 | 1 | evbuffer_add_file(evb, fd, off, size); |
77 | 1 | auto* evhttp_request = request->get_evhttp_request(); |
78 | 1 | if (rate_limit_group) { |
79 | 0 | auto* evhttp_connection = evhttp_request_get_connection(evhttp_request); |
80 | 0 | auto* buffer_event = evhttp_connection_get_bufferevent(evhttp_connection); |
81 | 0 | bufferevent_add_to_rate_limit_group(buffer_event, rate_limit_group); |
82 | 0 | } |
83 | 1 | evhttp_send_reply(evhttp_request, HttpStatus::OK, default_reason(HttpStatus::OK).c_str(), evb); |
84 | 1 | evbuffer_free(evb); |
85 | 1 | } |
86 | | |
87 | | void HttpChannel::send_files(HttpRequest* request, const std::string& root_dir, |
88 | | std::vector<std::string> local_files, |
89 | 0 | bufferevent_rate_limit_group* rate_limit_group) { |
90 | 0 | if (rate_limit_group) { |
91 | 0 | auto* evhttp_request = request->get_evhttp_request(); |
92 | 0 | auto* evhttp_connection = evhttp_request_get_connection(evhttp_request); |
93 | 0 | auto* buffer_event = evhttp_connection_get_bufferevent(evhttp_connection); |
94 | 0 | bufferevent_add_to_rate_limit_group(buffer_event, rate_limit_group); |
95 | 0 | } |
96 | |
|
97 | 0 | send_files(request, root_dir, std::move(local_files)); |
98 | 0 | } |
99 | | |
100 | | void HttpChannel::send_files(HttpRequest* request, const std::string& root_dir, |
101 | 1 | std::vector<std::string> local_files) { |
102 | 1 | std::unique_ptr<evbuffer, decltype(&evbuffer_free)> evb(evbuffer_new(), &evbuffer_free); |
103 | 35 | for (const std::string& file : local_files) { |
104 | 35 | std::string file_path = fmt::format("{}/{}", root_dir, file); |
105 | 35 | int fd = open(file_path.c_str(), O_RDONLY); |
106 | 35 | if (fd < 0) { |
107 | 0 | std::string error_msg = "Failed to open file: " + file_path; |
108 | 0 | LOG(WARNING) << "http channel send files: " << error_msg; |
109 | 0 | HttpChannel::send_reply(request, HttpStatus::NOT_FOUND, error_msg); |
110 | 0 | return; |
111 | 0 | } |
112 | 35 | struct stat st; |
113 | 35 | auto res = fstat(fd, &st); |
114 | 35 | if (res < 0) { |
115 | 0 | close(fd); |
116 | 0 | std::string error_msg = "Failed to open file: " + file_path; |
117 | 0 | LOG(WARNING) << "http channel send files: " << error_msg; |
118 | 0 | HttpChannel::send_reply(request, HttpStatus::NOT_FOUND, error_msg); |
119 | 0 | return; |
120 | 0 | } |
121 | | |
122 | 35 | int64_t file_size = st.st_size; |
123 | 35 | VLOG_DEBUG << "http channel send file " << file_path << ", size: " << file_size; |
124 | | |
125 | 35 | evbuffer_add_printf(evb.get(), "File-Name: %s\r\n", file.c_str()); |
126 | 35 | evbuffer_add_printf(evb.get(), "Content-Length: %" PRIi64 "\r\n", file_size); |
127 | | |
128 | 35 | evbuffer_add_printf(evb.get(), "\r\n"); |
129 | 35 | if (file_size > 0) { |
130 | 33 | evbuffer_add_file(evb.get(), fd, 0, file_size); |
131 | 33 | } |
132 | 35 | } |
133 | | |
134 | 1 | evhttp_send_reply(request->get_evhttp_request(), HttpStatus::OK, |
135 | 1 | default_reason(HttpStatus::OK).c_str(), evb.get()); |
136 | 1 | } |
137 | | |
138 | | bool HttpChannel::compress_content(const std::string& accept_encoding, const std::string& input, |
139 | 29 | std::string* output) { |
140 | | // Don't bother compressing empty content. |
141 | 29 | if (input.empty()) { |
142 | 2 | return false; |
143 | 2 | } |
144 | | |
145 | | // Check if gzip compression is accepted by the caller. If so, compress the |
146 | | // content and replace the prerendered output. |
147 | 27 | bool is_compressed = false; |
148 | 27 | std::vector<string> encodings = strings::Split(accept_encoding, ","); |
149 | 28 | for (string& encoding : encodings) { |
150 | 28 | StripWhiteSpace(&encoding); |
151 | 28 | if (encoding == "gzip") { |
152 | 2 | std::ostringstream oss; |
153 | 2 | Status s = zlib::CompressLevel(Slice(input), 1, &oss); |
154 | 2 | if (s.ok()) { |
155 | 2 | *output = oss.str(); |
156 | 2 | is_compressed = true; |
157 | 2 | } else { |
158 | 0 | LOG(WARNING) << "Could not compress output: " << s.to_string(); |
159 | 0 | } |
160 | 2 | break; |
161 | 2 | } |
162 | 28 | } |
163 | 27 | return is_compressed; |
164 | 29 | } |
165 | | |
166 | | } // namespace doris |