mirror of https://github.com/mamba-org/mamba.git
build: Remove server (#3685)
Signed-off-by: Julien Jerphanion <git@jjerphan.xyz>
This commit is contained in:
parent
dd5abc39b4
commit
392c5f15c2
|
@ -23,7 +23,6 @@
|
|||
{
|
||||
"cacheVariables": {
|
||||
"BUILD_MICROMAMBA": "ON",
|
||||
"BUILD_MICROMAMBA_SERVER": "ON",
|
||||
"BUILD_STATIC": "ON"
|
||||
},
|
||||
"hidden": true,
|
||||
|
|
|
@ -46,11 +46,6 @@ set(
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/version.hpp
|
||||
)
|
||||
|
||||
if(UNIX AND BUILD_MICROMAMBA_SERVER)
|
||||
list(APPEND MICROMAMBA_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/server.cpp)
|
||||
add_definitions(-DMICROMAMBA_SERVER)
|
||||
endif()
|
||||
|
||||
# Targets and link
|
||||
# ================
|
||||
|
||||
|
|
|
@ -1,414 +0,0 @@
|
|||
// Copyright (c) Alex Movsisyan
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
// associated documentation files (the 'Software'), to deal in the Software without restriction,
|
||||
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright notice and this
|
||||
// permission notice shall be included in all copies or substantial portions of the Software. THE
|
||||
// SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. original
|
||||
// source: https://github.com/konteck/wpp
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <fmt/format.h>
|
||||
#include <limits.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <spdlog/logger.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mamba/core/output.hpp"
|
||||
#include "mamba/core/thread_utils.hpp"
|
||||
#include "mamba/util/string.hpp"
|
||||
|
||||
#include "version.hpp"
|
||||
|
||||
#define BUFSIZE 8096
|
||||
|
||||
#define SERVER_NAME "micromamba"
|
||||
#define SERVER_VERSION UMAMBA_VERSION_STRING
|
||||
|
||||
namespace microserver
|
||||
{
|
||||
struct Request
|
||||
{
|
||||
std::string method;
|
||||
std::string path;
|
||||
std::string params;
|
||||
std::string body;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::map<std::string, std::string> query;
|
||||
std::map<std::string, std::string> cookies;
|
||||
};
|
||||
|
||||
class Response
|
||||
{
|
||||
public:
|
||||
|
||||
Response()
|
||||
{
|
||||
code = 200;
|
||||
phrase = "OK";
|
||||
type = "text/html";
|
||||
body << "";
|
||||
|
||||
// set current date and time for "Date: " header
|
||||
char buffer[100];
|
||||
time_t now = time(0);
|
||||
struct tm tstruct = *gmtime(&now);
|
||||
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S %Z", &tstruct);
|
||||
date = buffer;
|
||||
}
|
||||
|
||||
int code;
|
||||
std::string phrase;
|
||||
std::string type;
|
||||
std::string date;
|
||||
std::stringstream body;
|
||||
|
||||
void send(std::string_view str)
|
||||
{
|
||||
body << str;
|
||||
}
|
||||
};
|
||||
|
||||
class server_exception : public std::runtime_error
|
||||
{
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
using callback_function_t = std::function<void(const Request&, Response&)>;
|
||||
|
||||
struct Route
|
||||
{
|
||||
std::string path;
|
||||
std::string method;
|
||||
callback_function_t callback;
|
||||
std::string params;
|
||||
};
|
||||
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
|
||||
Server(const spdlog::logger& logger)
|
||||
: m_logger(logger)
|
||||
{
|
||||
}
|
||||
|
||||
void get(std::string, callback_function_t);
|
||||
void post(std::string, callback_function_t);
|
||||
void all(std::string, callback_function_t);
|
||||
|
||||
bool start(int port = 80);
|
||||
|
||||
private:
|
||||
|
||||
void main_loop(int port);
|
||||
std::pair<std::string, std::string> parse_header(std::string_view);
|
||||
void parse_headers(const std::string&, Request&, Response&);
|
||||
bool match_route(Request&, Response&);
|
||||
std::vector<Route> m_routes;
|
||||
spdlog::logger m_logger;
|
||||
};
|
||||
|
||||
std::pair<std::string, std::string> Server::parse_header(std::string_view header)
|
||||
{
|
||||
assert(header.size() >= 2);
|
||||
assert(header[header.size() - 1] == '\n' && header[header.size() - 2] == '\r');
|
||||
|
||||
auto colon_idx = header.find(':');
|
||||
if (colon_idx != std::string_view::npos)
|
||||
{
|
||||
std::string_view key, value;
|
||||
key = header.substr(0, colon_idx);
|
||||
colon_idx++;
|
||||
// remove spaces
|
||||
while (std::isspace(header[colon_idx]))
|
||||
{
|
||||
++colon_idx;
|
||||
}
|
||||
|
||||
// remove \r\n header ending
|
||||
value = header.substr(colon_idx, header.size() - colon_idx - 2);
|
||||
// http headers are case insensitive!
|
||||
std::string lkey = mamba::util::to_lower(key);
|
||||
|
||||
return std::make_pair(lkey, std::string(value));
|
||||
}
|
||||
return std::make_pair(std::string(), std::string(header));
|
||||
}
|
||||
|
||||
void Server::parse_headers(const std::string& headers, Request& req, Response&)
|
||||
{
|
||||
// Parse request headers
|
||||
int i = 0;
|
||||
// parse headers into lines delimited by newlines
|
||||
std::size_t delim_pos = headers.find_first_of("\n");
|
||||
std::string line = headers.substr(0, delim_pos + 1);
|
||||
|
||||
while (line.size() > 2 && i < 10)
|
||||
{
|
||||
if (i++ == 0)
|
||||
{
|
||||
auto R = mamba::util::split(line, " ", 3);
|
||||
|
||||
if (R.size() != 3)
|
||||
{
|
||||
throw server_exception("Header split returned wrong number");
|
||||
}
|
||||
|
||||
req.method = R[0];
|
||||
req.path = R[1];
|
||||
|
||||
size_t pos = req.path.find('?');
|
||||
|
||||
// We have GET params here
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto Q1 = mamba::util::split(req.path.substr(pos + 1), "&");
|
||||
|
||||
for (std::vector<std::string>::size_type q = 0; q < Q1.size(); q++)
|
||||
{
|
||||
auto Q2 = mamba::util::split(Q1[q], "=");
|
||||
|
||||
if (Q2.size() == 2)
|
||||
{
|
||||
req.query[Q2[0]] = Q2[1];
|
||||
}
|
||||
}
|
||||
|
||||
req.path = req.path.substr(0, pos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.insert(parse_header(line));
|
||||
}
|
||||
|
||||
std::size_t prev_pos = delim_pos;
|
||||
delim_pos = headers.find_first_of("\n", prev_pos + 1);
|
||||
line = headers.substr(prev_pos + 1, delim_pos - prev_pos);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::get(std::string path, callback_function_t callback)
|
||||
{
|
||||
Route r = { path, "GET", callback, "" };
|
||||
m_routes.push_back(r);
|
||||
}
|
||||
|
||||
void Server::post(std::string path, callback_function_t callback)
|
||||
{
|
||||
Route r = { path, "POST", callback, "" };
|
||||
m_routes.push_back(r);
|
||||
}
|
||||
|
||||
void Server::all(std::string path, callback_function_t callback)
|
||||
{
|
||||
Route r = { path, "ALL", callback, "" };
|
||||
m_routes.push_back(r);
|
||||
}
|
||||
|
||||
bool Server::match_route(Request& req, Response& res)
|
||||
{
|
||||
for (std::vector<Route>::size_type i = 0; i < m_routes.size(); i++)
|
||||
{
|
||||
if (m_routes[i].path == req.path
|
||||
&& (m_routes[i].method == req.method || m_routes[i].method == "ALL"))
|
||||
{
|
||||
req.params = m_routes[i].params;
|
||||
|
||||
try
|
||||
{
|
||||
m_routes[i].callback(req, res);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
m_logger.error("Error in callback: {}", e.what());
|
||||
res.code = 500;
|
||||
res.body << fmt::format("Internal server error. {}", e.what());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wait_for_socket(int fd)
|
||||
{
|
||||
struct pollfd fds[1];
|
||||
int ret;
|
||||
|
||||
fds[0].fd = fd;
|
||||
fds[0].events = POLLIN;
|
||||
|
||||
ret = poll(fds, 1, 500);
|
||||
|
||||
if (ret == -1)
|
||||
{
|
||||
throw microserver::server_exception("ERROR on poll");
|
||||
}
|
||||
|
||||
// There is data to read
|
||||
return fds[0].revents & POLLIN;
|
||||
}
|
||||
|
||||
void Server::main_loop(int port)
|
||||
{
|
||||
int newsc;
|
||||
|
||||
int sc = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (sc < 0)
|
||||
{
|
||||
throw microserver::server_exception("ERROR opening socket");
|
||||
}
|
||||
|
||||
struct sockaddr_in serv_addr, cli_addr;
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
serv_addr.sin_port = htons(port);
|
||||
|
||||
// allow faster reuse of the address
|
||||
int optval = 1;
|
||||
setsockopt(sc, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||||
|
||||
if (::bind(sc, reinterpret_cast<struct sockaddr*>(&serv_addr), sizeof(serv_addr)) != 0)
|
||||
{
|
||||
throw microserver::server_exception("ERROR on binding");
|
||||
}
|
||||
|
||||
listen(sc, 5);
|
||||
|
||||
socklen_t clilen;
|
||||
clilen = sizeof(cli_addr);
|
||||
|
||||
while (!mamba::is_sig_interrupted())
|
||||
{
|
||||
bool have_data = wait_for_socket(sc);
|
||||
|
||||
if (have_data)
|
||||
{
|
||||
std::chrono::time_point request_start = std::chrono::high_resolution_clock::now();
|
||||
newsc = accept(sc, reinterpret_cast<struct sockaddr*>(&cli_addr), &clilen);
|
||||
|
||||
if (newsc < 0)
|
||||
{
|
||||
throw microserver::server_exception("ERROR on accept");
|
||||
}
|
||||
|
||||
// handle new connection
|
||||
Request req;
|
||||
Response res;
|
||||
|
||||
static char buf[BUFSIZE + 1];
|
||||
std::string content;
|
||||
std::streamsize ret = read(newsc, buf, BUFSIZE);
|
||||
assert(ret >= 0);
|
||||
content = std::string(buf, static_cast<std::size_t>(ret));
|
||||
|
||||
std::size_t header_end = content.find("\r\n\r\n");
|
||||
if (header_end == std::string::npos)
|
||||
{
|
||||
throw microserver::server_exception("ERROR on parsing headers");
|
||||
}
|
||||
|
||||
parse_headers(content, req, res);
|
||||
|
||||
if (req.method == "POST")
|
||||
{
|
||||
std::string body = content.substr(header_end + 4, BUFSIZE - header_end - 4);
|
||||
std::streamsize content_length = stoll(req.headers["content-length"]);
|
||||
if (content_length - static_cast<std::streamsize>(body.size()) > 0)
|
||||
{
|
||||
// read the rest of the data and add to body
|
||||
std::streamsize remainder = content_length
|
||||
- static_cast<std::streamsize>(body.size());
|
||||
while (ret && remainder > 0)
|
||||
{
|
||||
std::streamsize read_ret = read(newsc, buf, BUFSIZE);
|
||||
body += std::string(buf, static_cast<std::size_t>(read_ret));
|
||||
remainder -= read_ret;
|
||||
}
|
||||
}
|
||||
req.body = body;
|
||||
}
|
||||
|
||||
if (!match_route(req, res))
|
||||
{
|
||||
res.code = 404;
|
||||
res.phrase = "Not Found";
|
||||
res.type = "text/plain";
|
||||
res.send("Not found");
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
std::string body = res.body.str();
|
||||
std::size_t body_len = body.size();
|
||||
|
||||
// build http response
|
||||
buffer << fmt::format("HTTP/1.0 {} {}\r\n", res.code, res.phrase)
|
||||
<< fmt::format("Server: {} {}\r\n", SERVER_NAME, SERVER_VERSION)
|
||||
<< fmt::format("Date: {}\r\n", res.date)
|
||||
<< fmt::format("Content-Type: {}\r\n", res.type)
|
||||
<< fmt::format("Content-Length: {}\r\n", body_len)
|
||||
// append extra crlf to indicate start of body
|
||||
<< "\r\n";
|
||||
|
||||
char addrbuf[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &cli_addr.sin_addr, addrbuf, sizeof(addrbuf));
|
||||
uint16_t used_port = htons(cli_addr.sin_port);
|
||||
std::chrono::time_point request_end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
m_logger.info(
|
||||
"{}:{} - {} {} {} (took {} ms)",
|
||||
addrbuf,
|
||||
used_port,
|
||||
req.method,
|
||||
req.path,
|
||||
fmt::styled(
|
||||
res.code,
|
||||
fmt::fg(res.code < 300 ? fmt::terminal_color::green : fmt::terminal_color::red)
|
||||
),
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(request_end - request_start)
|
||||
.count()
|
||||
);
|
||||
|
||||
std::string header_buffer = buffer.str();
|
||||
auto written = write(newsc, header_buffer.c_str(), header_buffer.size());
|
||||
if (written != static_cast<std::streamsize>(header_buffer.size()))
|
||||
{
|
||||
LOG_ERROR << "Could not write to socket " << strerror(errno);
|
||||
continue;
|
||||
}
|
||||
written = write(newsc, body.c_str(), body_len);
|
||||
if (written != static_cast<std::streamsize>(body_len))
|
||||
{
|
||||
LOG_ERROR << "Could not write to socket " << strerror(errno);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::start(int port)
|
||||
{
|
||||
this->main_loop(port);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
// Server side C/C++ program to demonstrate Socket
|
||||
// programming
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#define PORT 8080
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
|
||||
#include "mamba/api/channel_loader.hpp"
|
||||
#include "mamba/api/configuration.hpp"
|
||||
#include "mamba/core/channel_context.hpp"
|
||||
#include "mamba/core/context.hpp"
|
||||
#include "mamba/core/solver.hpp"
|
||||
#include "mamba/core/transaction.hpp"
|
||||
#include "mamba/core/virtual_packages.hpp"
|
||||
#include "mamba/util/string.hpp"
|
||||
|
||||
#include "common_options.hpp"
|
||||
#include "microserver.cpp"
|
||||
#include "umamba.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
|
||||
using namespace mamba;
|
||||
|
||||
Database
|
||||
load_pool(
|
||||
const std::vector<std::string>& channels,
|
||||
MultiPackageCache& package_caches,
|
||||
mamba::Context& ctx,
|
||||
mamba::ChannelContext& channel_context
|
||||
)
|
||||
{
|
||||
ctx.channels = channels;
|
||||
mamba::Database pool{ ctx, channel_context };
|
||||
auto exp_load = load_channels(ctx, pool, package_caches, false);
|
||||
if (!exp_load)
|
||||
{
|
||||
throw std::runtime_error(exp_load.error().what());
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
void
|
||||
handle_solve_request(
|
||||
const microserver::Request& req,
|
||||
microserver::Response& res,
|
||||
mamba::Context& ctx,
|
||||
mamba::ChannelContext& channel_context
|
||||
)
|
||||
{
|
||||
struct cache
|
||||
{
|
||||
std::optional<mamba::Database> pool;
|
||||
std::chrono::time_point<std::chrono::system_clock> last_update;
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, cache> cache_map;
|
||||
|
||||
auto j = nlohmann::json::parse(req.body);
|
||||
std::vector<std::string> specs = j["specs"].get<std::vector<std::string>>();
|
||||
std::vector<std::string> channels = j["channels"].get<std::vector<std::string>>();
|
||||
std::vector<std::string> virtual_packages = j["virtual_packages"].get<std::vector<std::string>>();
|
||||
std::string platform = j["platform"];
|
||||
|
||||
ctx.platform = platform;
|
||||
|
||||
for (const auto& s : specs)
|
||||
{
|
||||
if (auto m = MatchSpec::parse(s); m.channel.has_value())
|
||||
{
|
||||
channels.push_back(m.channel->str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string cache_key = mamba::util::join(", ", channels) + fmt::format(", {}", platform);
|
||||
MultiPackageCache package_caches(ctx.pkgs_dirs, ctx.validation_params);
|
||||
|
||||
if (cache_map.find(cache_key) == cache_map.end())
|
||||
{
|
||||
cache_map.insert_or_assign(
|
||||
cache_key,
|
||||
cache{ load_pool(channels, package_caches, ctx, channel_context),
|
||||
std::chrono::system_clock::now() }
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
cache& c = cache_map[cache_key];
|
||||
if (std::chrono::system_clock::now() - c.last_update > std::chrono::minutes(30))
|
||||
{
|
||||
cache_map.insert_or_assign(
|
||||
cache_key,
|
||||
cache{ load_pool(channels, package_caches, ctx, channel_context),
|
||||
std::chrono::system_clock::now() }
|
||||
);
|
||||
}
|
||||
}
|
||||
auto entry_it = cache_map.find(cache_key);
|
||||
if (entry_it == cache_map.end())
|
||||
{
|
||||
throw std::runtime_error("invalid cache state");
|
||||
}
|
||||
cache cache_entry = entry_it->second;
|
||||
|
||||
TemporaryDirectory tmp_dir;
|
||||
auto exp_prefix_data = PrefixData::create(tmp_dir.path(), channel_context);
|
||||
// if (!exp_prefix_data)
|
||||
// {
|
||||
// throw std::runtime_error(exp_prefix_data.error().what());
|
||||
// }
|
||||
PrefixData& prefix_data = exp_prefix_data.value();
|
||||
std::vector<PackageInfo> vpacks;
|
||||
for (const auto& s : virtual_packages)
|
||||
{
|
||||
auto elements = util::split(s, "=");
|
||||
vpacks.push_back(detail::make_virtual_package(
|
||||
elements[0],
|
||||
ctx.platform,
|
||||
elements.size() >= 2 ? elements[1] : "",
|
||||
elements.size() >= 3 ? elements[2] : ""
|
||||
));
|
||||
}
|
||||
prefix_data.add_packages(vpacks);
|
||||
|
||||
auto installed_repo = MRepo(*cache_entry.pool, prefix_data);
|
||||
|
||||
MSolver solver(
|
||||
*cache_entry.pool,
|
||||
{ { SOLVER_FLAG_ALLOW_UNINSTALL, ctx.allow_uninstall },
|
||||
{ SOLVER_FLAG_ALLOW_DOWNGRADE, ctx.allow_downgrade },
|
||||
{ SOLVER_FLAG_STRICT_REPO_PRIORITY, ctx.channel_priority == ChannelPriority::Strict } }
|
||||
);
|
||||
|
||||
solver.add_jobs(specs, SOLVER_INSTALL);
|
||||
|
||||
bool solved = solver.try_solve();
|
||||
|
||||
if (!solved)
|
||||
{
|
||||
nlohmann::json jout;
|
||||
jout["error_msg"] = solver.problems_to_str();
|
||||
res.send(jout.dump());
|
||||
}
|
||||
else
|
||||
{
|
||||
MTransaction trans{ *cache_entry.pool, solver, package_caches };
|
||||
auto to_install = std::get<1>(trans.to_conda());
|
||||
std::vector<nlohmann::json> packages;
|
||||
for (auto& p : to_install)
|
||||
{
|
||||
packages.push_back(nlohmann::json::parse(std::get<2>(p)));
|
||||
}
|
||||
nlohmann::json jout;
|
||||
jout["packages"] = packages;
|
||||
res.send(jout.dump());
|
||||
}
|
||||
|
||||
cache_entry.pool->remove_repo(installed_repo.id(), /* reuse_ids= */ true);
|
||||
pool_set_installed(*cache_entry.pool, nullptr);
|
||||
}
|
||||
|
||||
int
|
||||
run_server(int port, mamba::Context& ctx, mamba::ChannelContext& channel_context, Configuration& config)
|
||||
{
|
||||
config.load();
|
||||
std::signal(SIGPIPE, SIG_IGN);
|
||||
auto server_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||
server_sink->set_level(spdlog::level::debug);
|
||||
server_sink->set_pattern("%^[%H:%M:%S]%$ %v");
|
||||
|
||||
spdlog::logger logger("server", { server_sink });
|
||||
|
||||
microserver::Server xserver(logger);
|
||||
xserver.get(
|
||||
"/hello",
|
||||
[](const microserver::Request&, microserver::Response& res) { res.send("Hello World!"); }
|
||||
);
|
||||
xserver.get(
|
||||
"/",
|
||||
[](const microserver::Request&, microserver::Response& res)
|
||||
{
|
||||
res.type = "text/plain";
|
||||
std::stringstream ss;
|
||||
ss << "Micromamba version " << UMAMBA_VERSION_STRING << "\n";
|
||||
res.send(ss.str());
|
||||
}
|
||||
);
|
||||
xserver.post(
|
||||
"/solve",
|
||||
[&](const microserver::Request& req, microserver::Response& res)
|
||||
{ return handle_solve_request(req, res, ctx, channel_context); }
|
||||
);
|
||||
|
||||
Console::stream() << "Starting server on port http://localhost:" << port << std::endl;
|
||||
|
||||
xserver.start(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
set_server_command(CLI::App* subcom, mamba::Configuration& config)
|
||||
{
|
||||
init_general_options(subcom, config);
|
||||
|
||||
static int port = 1234;
|
||||
subcom->add_option("--port,-p", port, "The port to use for the server");
|
||||
|
||||
subcom->callback(
|
||||
[&config]
|
||||
{
|
||||
auto channel_context = mamba::ChannelContext::make_conda_compatible(config.context());
|
||||
return run_server(port, config.context(), channel_context, config);
|
||||
}
|
||||
);
|
||||
}
|
|
@ -110,10 +110,5 @@ set_umamba_command(CLI::App* com, mamba::Configuration& config)
|
|||
);
|
||||
set_repoquery_search_command(search_subcom, config);
|
||||
|
||||
#if !defined(_WIN32) && defined(MICROMAMBA_SERVER)
|
||||
CLI::App* server_subcom = com->add_subcommand("server", "Run micromamba server");
|
||||
set_server_command(server_subcom, config);
|
||||
#endif
|
||||
|
||||
com->require_subcommand(/* min */ 0, /* max */ 1);
|
||||
}
|
||||
|
|
|
@ -86,9 +86,4 @@ get_completions(CLI::App* app, mamba::Configuration& config, int argc, char** ar
|
|||
void
|
||||
set_auth_command(CLI::App* subcom);
|
||||
|
||||
#if !defined(_WIN32) && defined(MICROMAMBA_SERVER)
|
||||
void
|
||||
set_server_command(CLI::App* subcom, mamba::Configuration& config);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue