Skip to content

Commit

Permalink
Add controller.
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewerf committed Sep 26, 2024
1 parent 2c02ddb commit ca51a45
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 117 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "3rdparty/drogon"]
path = 3rdparty/drogon
url = https://github.com/drogonframework/drogon.git
[submodule "3rdparty/magic_enum"]
path = 3rdparty/magic_enum
url = https://github.com/Neargye/magic_enum.git
1 change: 1 addition & 0 deletions 3rdparty/magic_enum
Submodule magic_enum added at 126539
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ option(BUILD_EXAMPLES "Build examples" OFF)
option(BUILD_CTL "Build drogon_ctl" OFF)
add_subdirectory(3rdparty/drogon)

add_subdirectory(3rdparty/magic_enum)

set(Boost_USE_STATIC_LIBS ON)
find_package(Boost REQUIRED COMPONENTS program_options)


add_subdirectory(src)

add_executable(simple_inference_server main.cpp)
target_link_libraries(simple_inference_server drogon Boost::program_options)
target_link_libraries(simple_inference_server Boost::program_options libsimple_inference_server)
122 changes: 6 additions & 116 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,118 +4,11 @@
#include <spdlog/spdlog.h>
#include <fstream>

#include "src/Controller.h"

using namespace drogon;


struct GlobalConfig
{
std::filesystem::path invokePath;
std::filesystem::path indexPath;
} globalConfig;

bool invokeProcessing( const std::filesystem::path& input, const std::filesystem::path& output )
{
auto exitCode = system( fmt::format( "{} {} {}", globalConfig.invokePath.string(), input.string(), output.string() ).c_str() );
return exitCode == 0;
}

std::string getInputFileName( const std::string& taskId )
{
return taskId + ".input.zip";
}
std::filesystem::path getInputPath( const std::string& taskId )
{
return std::filesystem::path{ app().getUploadPath() } / getInputFileName( taskId );
}
std::filesystem::path getOutputPath( const std::string& taskId )
{
return std::filesystem::path{ app().getUploadPath() } / ( taskId + ".zip" );
}


Task<HttpResponsePtr> trackResultHandler( HttpRequestPtr req )
{
const auto maybeTask = req->getOptionalParameter<std::string>( "task" );
const auto maybeOutput = maybeTask.transform( getOutputPath );

if ( !maybeOutput || !std::filesystem::exists( *maybeOutput ) )
{
auto resp = HttpResponse::newHttpResponse();
resp->setBody( "The task is not available (yet)" );
resp->setStatusCode( HttpStatusCode::k200OK );
resp->addHeader( "Refresh", "10" );
co_return resp;
}

auto resp = HttpResponse::newHttpResponse();
resp->setBody( fmt::format( "Result can be downloaded <a href=\"./get_result?task={}\">here</a>", *maybeTask ) );
resp->setStatusCode( HttpStatusCode::k200OK );
co_return resp;
}

Task<HttpResponsePtr> getResultHandler( HttpRequestPtr req )
{
const auto maybeTask = req->getOptionalParameter<std::string>( "task" );
const auto maybeOutput = maybeTask.transform( getOutputPath );
if ( !maybeOutput || !std::filesystem::exists( *maybeOutput ) )
{
co_return HttpResponse::newNotFoundResponse();
}

auto resp = HttpResponse::newFileResponse( *maybeOutput, maybeOutput->filename() );
co_return resp;
}

Task<HttpResponsePtr> submitTaskHandler( HttpRequestPtr req )
{
MultiPartParser filesUpload;
if ( filesUpload.parse(req) != 0 || filesUpload.getFiles().size() != 1 )
{
auto resp = HttpResponse::newHttpResponse();
resp->setBody( "Must only be one file" );
resp->setStatusCode( k403Forbidden );
co_return resp;
}

auto file = filesUpload.getFiles()[0];
if ( !file.getFileName().ends_with( ".zip" ) )
{
auto resp = HttpResponse::newHttpResponse();
resp->setBody( "Must be zip file" );
resp->setStatusCode( k403Forbidden );
co_return resp;
}

const auto taskId = drogon::utils::getUuid();
file.saveAs( getInputFileName( taskId ) );

app().getLoop()->queueInLoop( [taskId]
{
const auto inputPath = getInputPath( taskId );
const auto outputPath = getOutputPath( taskId );
invokeProcessing( inputPath, outputPath );
} );
using namespace drogon;

auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode( HttpStatusCode::k202Accepted );
resp->setBody( fmt::format( "Your requested has been accepted. You can track it <a href=\"./track_result?task={}\">here</a>",
taskId ) );
co_return resp;
}

Task<HttpResponsePtr> rootHandler( HttpRequestPtr req )
{
auto resp = HttpResponse::newHttpResponse( HttpStatusCode::k200OK, CT_TEXT_HTML );
if ( exists( globalConfig.indexPath ) )
{
std::ifstream fin( globalConfig.indexPath );
std::stringstream buffer;
buffer << fin.rdbuf();
resp->setBody( buffer.str() );
}
co_return resp;
}

// Fix parsing std::filesystem::path with spaces (see https://github.com/boostorg/program_options/issues/69)
namespace boost
Expand All @@ -133,12 +26,13 @@ int main( int argc, char** argv )
std::string host;
std::filesystem::path certPath, keyPath;
int port;
Controller::Config config;

po::options_description desc( "Simple Inference Server" );
desc.add_options()
( "help,h", "Display help message" )
( "invokePath", po::value( &globalConfig.invokePath )->required(), "Path to the script that will be invoked" )
( "indexPath", po::value( &globalConfig.indexPath )->default_value( {} ), "Path to the index.html" )
( "invokePath", po::value( &config.invokePath )->required(), "Path to the script that will be invoked" )
( "indexPath", po::value( &config.indexPath )->default_value( {} ), "Path to the index.html" )
( "host", po::value( &host )->default_value( "127.0.0.1" ), "Host to bind to" )
( "port", po::value( &port )->default_value( 7654 ), "Port to bind to" )
( "cert", po::value( &certPath )->default_value( {} ), "Path to SSL certificate" )
Expand All @@ -163,11 +57,6 @@ int main( int argc, char** argv )
return -1;
}

app().registerHandler( "/track_result", std::function{ trackResultHandler } );
app().registerHandler( "/get_result", std::function{ getResultHandler } );
app().registerHandler( "/submit", std::function{ submitTaskHandler }, {Post } );
app().registerHandler( "/", std::function{ rootHandler } );

const bool useSSL = exists( certPath ) && exists( keyPath );
if ( useSSL && !app().supportSSL() )
{
Expand All @@ -185,6 +74,7 @@ int main( int argc, char** argv )
app()
.setClientMaxBodySize( 1024*1024*1024 ) // 1gb
.addListener( host, port, useSSL, certPath, keyPath )
.registerController( std::make_shared<Controller>( config ) )
.run();

return 0;
Expand Down
6 changes: 6 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
project(libsimple_inference_server)


file(GLOB source *.cpp)
add_library(libsimple_inference_server STATIC ${source})
target_link_libraries(libsimple_inference_server drogon magic_enum::magic_enum)
Loading

0 comments on commit ca51a45

Please sign in to comment.