Coverage Report

Created: 2026-08-14 13:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/service/http/action/download_action.cpp
Line
Count
Source
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 "service/http/action/download_action.h"
19
20
#include <memory>
21
#include <string>
22
#include <utility>
23
24
#include "common/config.h"
25
#include "common/logging.h"
26
#include "common/status.h"
27
#include "io/fs/local_file_system.h"
28
#include "runtime/exec_env.h"
29
#include "service/http/http_channel.h"
30
#include "service/http/http_request.h"
31
#include "service/http/utils.h"
32
33
namespace doris {
34
namespace {
35
const std::string FILE_PARAMETER = "file";
36
const std::string TOKEN_PARAMETER = "token";
37
const std::string CHANNEL_PARAMETER = "channel";
38
const std::string CHANNEL_INGEST_BINLOG_TYPE = "ingest_binlog";
39
const std::string ACQUIRE_MD5_PARAMETER = "acquire_md5";
40
} // namespace
41
42
DownloadAction::DownloadAction(ExecEnv* exec_env,
43
                               std::shared_ptr<bufferevent_rate_limit_group> rate_limit_group,
44
                               const std::vector<std::string>& allow_dirs)
45
4
        : HttpHandlerWithAuth(exec_env),
46
4
          _download_type(NORMAL),
47
4
          _rate_limit_group(std::move(rate_limit_group)) {
48
4
    for (const auto& dir : allow_dirs) {
49
4
        std::string p;
50
4
        Status st = io::global_local_filesystem()->canonicalize(dir, &p);
51
4
        if (!st.ok()) {
52
0
            continue;
53
0
        }
54
4
        _allow_paths.emplace_back(std::move(p));
55
4
    }
56
4
}
57
58
DownloadAction::DownloadAction(ExecEnv* exec_env, const std::string& error_log_root_dir)
59
1
        : HttpHandlerWithAuth(exec_env), _download_type(ERROR_LOG) {
60
#ifndef BE_TEST
61
    static_cast<void>(
62
            io::global_local_filesystem()->canonicalize(error_log_root_dir, &_error_log_root_dir));
63
#endif
64
1
}
65
66
4
void DownloadAction::handle_normal(HttpRequest* req, const std::string& file_param) {
67
    // check token
68
4
    Status status;
69
4
    if (config::enable_token_check) {
70
4
        status = check_token(req);
71
4
        if (!status.ok()) {
72
0
            std::string error_msg = status.to_string();
73
0
            if (status.is<ErrorCode::NOT_AUTHORIZED>()) {
74
0
                HttpChannel::send_reply(req, HttpStatus::UNAUTHORIZED, error_msg);
75
0
                return;
76
0
            } else {
77
0
                HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR, error_msg);
78
0
                return;
79
0
            }
80
0
        }
81
4
    }
82
83
4
    status = check_path_is_allowed(file_param);
84
4
    if (!status.ok()) {
85
0
        std::string error_msg = status.to_string();
86
0
        if (status.is<ErrorCode::NOT_FOUND>() || status.is<ErrorCode::IO_ERROR>()) {
87
0
            HttpChannel::send_reply(req, HttpStatus::NOT_FOUND, error_msg);
88
0
            return;
89
0
        } else if (status.is<ErrorCode::NOT_AUTHORIZED>()) {
90
0
            HttpChannel::send_reply(req, HttpStatus::UNAUTHORIZED, error_msg);
91
0
            return;
92
0
        } else {
93
0
            HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR, error_msg);
94
0
            return;
95
0
        }
96
0
    }
97
98
4
    bool is_dir = false;
99
4
    status = io::global_local_filesystem()->is_directory(file_param, &is_dir);
100
4
    if (!status.ok()) {
101
0
        HttpChannel::send_reply(req, status.to_string());
102
0
        return;
103
0
    }
104
105
4
    if (is_dir) {
106
1
        do_dir_response(file_param, req);
107
3
    } else {
108
3
        const auto& channel = req->param(CHANNEL_PARAMETER);
109
3
        bool ingest_binlog = (channel == CHANNEL_INGEST_BINLOG_TYPE);
110
3
        bool is_acquire_md5 = !req->param(ACQUIRE_MD5_PARAMETER).empty();
111
3
        auto* rate_limit_group = ingest_binlog ? _rate_limit_group.get() : nullptr;
112
3
        do_file_response(file_param, req, rate_limit_group, is_acquire_md5);
113
3
    }
114
4
}
115
116
0
void DownloadAction::handle_error_log(HttpRequest* req, const std::string& file_param) {
117
0
    const std::string absolute_path = _error_log_root_dir + "/" + file_param;
118
119
0
    Status status = check_log_path_is_allowed(absolute_path);
120
0
    if (!status.ok()) {
121
0
        std::string error_msg = status.to_string();
122
0
        if (status.is<ErrorCode::NOT_AUTHORIZED>()) {
123
0
            HttpChannel::send_reply(req, HttpStatus::UNAUTHORIZED, error_msg);
124
0
            return;
125
0
        } else {
126
0
            HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR, error_msg);
127
0
            return;
128
0
        }
129
0
    }
130
131
0
    bool is_dir = false;
132
0
    status = io::global_local_filesystem()->is_directory(absolute_path, &is_dir);
133
0
    if (!status.ok()) {
134
0
        std::string error_msg = status.to_string();
135
0
        if (status.is<ErrorCode::NOT_FOUND>() || status.is<ErrorCode::IO_ERROR>()) {
136
0
            HttpChannel::send_reply(req, HttpStatus::NOT_FOUND, error_msg);
137
0
            return;
138
0
        } else if (status.is<ErrorCode::NOT_AUTHORIZED>()) {
139
0
            HttpChannel::send_reply(req, HttpStatus::UNAUTHORIZED, error_msg);
140
0
            return;
141
0
        } else {
142
0
            HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR, error_msg);
143
0
            return;
144
0
        }
145
0
    }
146
0
    if (is_dir) {
147
0
        std::string error_msg = "error log can only be file.";
148
0
        HttpChannel::send_reply(req, error_msg);
149
0
        return;
150
0
    }
151
152
0
    do_file_response(absolute_path, req);
153
0
}
154
155
4
void DownloadAction::handle(HttpRequest* req) {
156
4
    _handle(req);
157
4
}
158
159
4
void DownloadAction::_handle(HttpRequest* req) {
160
4
    VLOG_CRITICAL << "accept one download request " << req->debug_string();
161
162
    // Get 'file' parameter, then assembly file absolute path
163
4
    const std::string& file_path = req->param(FILE_PARAMETER);
164
4
    if (file_path.empty()) {
165
0
        std::string error_msg =
166
0
                std::string("parameter " + FILE_PARAMETER + " not specified in url.");
167
0
        HttpChannel::send_reply(req, error_msg);
168
0
        return;
169
0
    }
170
171
4
    if (_download_type == ERROR_LOG) {
172
0
        handle_error_log(req, file_path);
173
4
    } else if (_download_type == NORMAL) {
174
4
        handle_normal(req, file_path);
175
4
    }
176
177
4
    VLOG_CRITICAL << "deal with download request finished! ";
178
4
}
179
180
4
Status DownloadAction::check_token(HttpRequest* req) {
181
4
    const std::string& token_str = req->param(TOKEN_PARAMETER);
182
4
    if (token_str.empty()) {
183
0
        return Status::NotAuthorized("token is not specified.");
184
0
    }
185
186
4
    const std::string& local_token = _exec_env->token();
187
4
    if (token_str != local_token) {
188
0
        LOG(WARNING) << "invalid download token: " << token_str << ", local token: " << local_token;
189
0
        return Status::NotAuthorized("invalid token {}", token_str);
190
0
    }
191
192
4
    return Status::OK();
193
4
}
194
195
4
Status DownloadAction::check_path_is_allowed(const std::string& file_path) {
196
4
    DCHECK_EQ(_download_type, NORMAL);
197
198
4
    std::string canonical_file_path;
199
4
    RETURN_IF_ERROR(io::global_local_filesystem()->canonicalize(file_path, &canonical_file_path));
200
4
    for (auto& allow_path : _allow_paths) {
201
4
        if (io::LocalFileSystem::contain_path(allow_path, canonical_file_path)) {
202
4
            return Status::OK();
203
4
        }
204
4
    }
205
206
0
    return Status::NotAuthorized("file path is not allowed: {}", canonical_file_path);
207
4
}
208
209
0
Status DownloadAction::check_log_path_is_allowed(const std::string& file_path) {
210
0
    DCHECK_EQ(_download_type, ERROR_LOG);
211
212
0
    std::string canonical_file_path;
213
0
    RETURN_IF_ERROR(io::global_local_filesystem()->canonicalize(file_path, &canonical_file_path));
214
0
    if (io::LocalFileSystem::contain_path(_error_log_root_dir, canonical_file_path)) {
215
0
        return Status::OK();
216
0
    }
217
218
0
    return Status::NotAuthorized("file path is not allowed: {}", file_path);
219
0
}
220
221
} // end namespace doris