| 
1 | 1 | #include "exec_endpoint.hpp"  | 
2 | 2 | 
 
  | 
3 |  | -#include <sstream>  | 
 | 3 | +#include "util.hpp"  | 
 | 4 | + | 
 | 5 | +#include <cerrno>  | 
 | 6 | +#include <cstring>  | 
 | 7 | +#include <cstdlib>  | 
 | 8 | + | 
 | 9 | +#include <array>  | 
 | 10 | +#include <algorithm>  | 
 | 11 | +#include <unordered_map>  | 
 | 12 | +#include <string_view>  | 
 | 13 | +#include <stdexcept>  | 
 | 14 | + | 
 | 15 | +#include <cppevent_base/util.hpp>  | 
 | 16 | + | 
 | 17 | +#include <unistd.h>  | 
 | 18 | +#include <fcntl.h>  | 
 | 19 | + | 
 | 20 | +constexpr long BUF_LEN = 1024;  | 
 | 21 | +constexpr long MAX_RESPONSE_SIZE = 10 * 1024;   | 
 | 22 | + | 
 | 23 | +constexpr std::string_view COMPILE_ERR = "compile_err.out";  | 
 | 24 | +constexpr std::string_view RUN_STDOUT = "run.out";  | 
 | 25 | + | 
 | 26 | +executor::exec_endpoint::exec_endpoint(cppevent::event_loop& e_loop): m_loop(e_loop) {  | 
 | 27 | +}  | 
 | 28 | + | 
 | 29 | +cppevent::awaitable_task<void> download(long content_len,  | 
 | 30 | +                                        cppevent::stream& s_stdin,  | 
 | 31 | +                                        int fd) {  | 
 | 32 | +    std::array<uint8_t, BUF_LEN> buffer;  | 
 | 33 | +    while (content_len > 0) {  | 
 | 34 | +        long download_size = std::min(BUF_LEN, content_len);  | 
 | 35 | +        co_await s_stdin.read(buffer.data(), download_size, true);  | 
 | 36 | +        cppevent::throw_if_error(write(fd, buffer.data(), download_size), "Failed to download: ");  | 
 | 37 | +        content_len -= download_size;   | 
 | 38 | +    }  | 
 | 39 | +}  | 
 | 40 | + | 
 | 41 | +template <long BUFFER_SIZE>  | 
 | 42 | +bool read_to_buffer(std::array<uint8_t, BUFFER_SIZE>& buffer, long& read_size, int fd) {  | 
 | 43 | +    read_size = read(fd, buffer.data(), BUFFER_SIZE);  | 
 | 44 | +    return read_size > 0;  | 
 | 45 | +}  | 
 | 46 | + | 
 | 47 | +cppevent::awaitable_task<void> upload(std::string_view dir, std::string_view name,  | 
 | 48 | +                                      cppevent::output& o_stdout) {  | 
 | 49 | +    int fd = executor::open_file(dir, name, O_RDONLY);  | 
 | 50 | +    std::array<uint8_t, BUF_LEN> buffer;  | 
 | 51 | +    long response_size = 0;  | 
 | 52 | +    long read_size;  | 
 | 53 | +    while (response_size < MAX_RESPONSE_SIZE && read_to_buffer<BUF_LEN>(buffer, read_size, fd)) {  | 
 | 54 | +        co_await o_stdout.write(buffer.data(), read_size);  | 
 | 55 | +        response_size += read_size;  | 
 | 56 | +    }  | 
 | 57 | +    close(fd);  | 
 | 58 | +}  | 
 | 59 | + | 
 | 60 | +const std::unordered_map<std::string_view, std::string_view> source_file_names = {  | 
 | 61 | +    { "cpp", "source.cpp" },  | 
 | 62 | +    { "java", "Source.java" },  | 
 | 63 | +    { "python", "source.py" }  | 
 | 64 | +};  | 
4 | 65 | 
 
  | 
5 | 66 | cppevent::awaitable_task<void> executor::exec_endpoint::process(const cppevent::context& cont,  | 
6 | 67 |                                                                 cppevent::stream& s_stdin,  | 
7 | 68 |                                                                 cppevent::output& o_stdout) {  | 
8 | 69 |     auto lang_opt = cont.get_path_segment("lang");  | 
9 | 70 |     auto& lang = lang_opt.value();  | 
10 |  | -    std::stringstream ss;  | 
11 |  | -    ss << "content-length: " << 6 + lang.size() << "\ncontent-type: text/plain\n\n";  | 
12 |  | -    ss << "hello " << lang;  | 
13 |  | -    co_await o_stdout.write(ss.str());  | 
 | 71 | +    long content_len = cont.get_content_len();  | 
 | 72 | +    auto it = source_file_names.find(lang);  | 
 | 73 | +    if (it == source_file_names.end() || content_len == 0) {  | 
 | 74 | +        co_await o_stdout.write("status: 400\ncontent-length: 16\n\nunknown language");  | 
 | 75 | +        co_return;  | 
 | 76 | +    }  | 
 | 77 | +    char dir_name[12];  | 
 | 78 | +    strcpy(dir_name, "code_XXXXXX");  | 
 | 79 | +    if (mkdtemp(dir_name) == NULL) {  | 
 | 80 | +        co_await o_stdout.write("status: 500\ncontent-type: text/plain\n\n");  | 
 | 81 | +        co_await o_stdout.write(strerror(errno));  | 
 | 82 | +        co_return;  | 
 | 83 | +    }  | 
 | 84 | + | 
 | 85 | +    const std::string_view& source_file_name = it->second;  | 
 | 86 | +    const int source_fd = open_file(dir_name, source_file_name, O_WRONLY | O_CREAT);  | 
 | 87 | +    co_await download(content_len, s_stdin, source_fd);  | 
 | 88 | +    close(source_fd);  | 
 | 89 | + | 
 | 90 | +    const int compile_err_fd = open_file(dir_name, COMPILE_ERR, O_WRONLY | O_CREAT);  | 
 | 91 | +    bool compiled_success = co_await await_compile(compile_err_fd, m_loop, dir_name, lang);  | 
 | 92 | +    close(compile_err_fd);  | 
 | 93 | + | 
 | 94 | +    if (!compiled_success) {  | 
 | 95 | +        co_await o_stdout.write("status: 200\nx-exec-status: compile_error\ncontent-type: text/plain\n\n");  | 
 | 96 | +        co_await upload(dir_name, COMPILE_ERR, o_stdout);  | 
 | 97 | +        co_return;  | 
 | 98 | +    }  | 
 | 99 | + | 
 | 100 | +    const int run_out_fd = open_file(dir_name, RUN_STDOUT, O_WRONLY | O_CREAT);  | 
 | 101 | +    CODE_EXEC_STATUS run_status = co_await await_run(run_out_fd, m_loop, dir_name, lang);  | 
 | 102 | +    close(run_out_fd);  | 
 | 103 | + | 
 | 104 | +    switch (run_status) {  | 
 | 105 | +        case CODE_EXEC_STATUS::RUN_ERROR:  | 
 | 106 | +            co_await o_stdout.write("status: 200\nx-exec-status: run_error\ncontent-type: text/plain\n\n");  | 
 | 107 | +            break;  | 
 | 108 | +        case CODE_EXEC_STATUS::RUN_TIMEOUT:  | 
 | 109 | +            co_await o_stdout.write("status: 200\nx-exec-status: timeout\ncontent-type: text/plain\n\n");  | 
 | 110 | +            break;  | 
 | 111 | +        case CODE_EXEC_STATUS::RUN_SUCCESS:  | 
 | 112 | +            co_await o_stdout.write("status: 200\nx-exec-status: success\ncontent-type: text/plain\n\n");  | 
 | 113 | +            break;  | 
 | 114 | +    }  | 
 | 115 | + | 
 | 116 | +    co_await upload(dir_name, RUN_STDOUT, o_stdout);  | 
14 | 117 | }  | 
0 commit comments