C++
nginx
Web
C++14
FCGI

nginx で C++14 の FCGI を動作させる簡単な方法の紹介

More than 1 year has passed since last update.


使用環境例

この記事で紹介する方法で動作可能な事を確認済みの環境を例示します。


  • ubuntu-16.04

  • nginx-1.10.0

  • clang-3.8 or gcc-5.4

  • libfcgi-2.4.0

  • spawn-fcgi-1.6.4

  • curl-7.47.0 (http動作確認用なので w3m でも telnet でも chromium でも何でもOK)


準備

少なくとも ubuntu-16.04 ではすべて apt で導入できます。他の環境ではそれぞれの環境に合わせて適当に調達して下さい。

sudo apt install nginx clang-3.8 libfcgi-dev spawn-fcgi


nginx で C++ の FCGI を動作させる簡単な方法


C++14 FCGI サンプルコード

// EXTERNAL

#include "fcgio.h"
// STD
#include <iostream>
#include <memory>
#include <functional>
#include <cstring>

auto main() -> int
{
// 元の標準入出力系の streambuf 群をバックアップ&カスタムデリーターによるリストア処理の予約
using streambuf_restorer_signature_type = auto ( std::streambuf* ) -> void;
using streambuf_restorer_functor_type = std::function< streambuf_restorer_signature_type >;
using streambuf_restorer_type = std::unique_ptr< std::streambuf, streambuf_restorer_functor_type >;

const auto cin_restorer = streambuf_restorer_type( std::cin.rdbuf(), []( auto in ) { std::cin.rdbuf( in ); } );
const auto cout_restorer = streambuf_restorer_type( std::cout.rdbuf(), []( auto in ) { std::cout.rdbuf( in ); } );
const auto cerr_restorer = streambuf_restorer_type( std::cerr.rdbuf(), []( auto in ) { std::cerr.rdbuf( in ); } );

FCGX_Request request;

FCGX_Init();
FCGX_InitRequest( &request, 0, 0 );

while ( FCGX_Accept_r( &request ) == 0 )
{
auto cin_buffer = fcgi_streambuf( request.in );
auto cout_buffer = fcgi_streambuf( request.out );
auto cerr_buffer = fcgi_streambuf( request.err );

std::cin.rdbuf( &cin_buffer );
std::cout.rdbuf( &cout_buffer );
std::cerr.rdbuf( &cerr_buffer );

constexpr auto content_header_part = u8R"(Content-type: text/html

<html>
<head>
<title>Hello, World!</title>
</head>
<body>
)";

constexpr auto content_footer_part = u8R"( </body>
</html>
)"
;

// リクエストにいわゆる POST で投げられたデータがあるか CONTENT_LENGTH で確認
const auto in_content_length_string = FCGX_GetParam( "CONTENT_LENGTH", request.envp );
const auto in_content_length = std::strlen( in_content_length_string )
? std::stoull( std::string( in_content_length_string ) )
: 0ull
;

// リクエストの CONTENT_LENGTH だけリクエストボディーを読み出す
std::string in_content;
in_content.resize( in_content_length );
std::cin.read( &in_content[0], in_content_length );

// レスポンスを出力する
std::cout
<< content_header_part
<< " <h1>Hello, World!</h>\n"
" <p>REQUEST_URI: " << FCGX_GetParam( "REQUEST_URI", request.envp ) << "</p>\n"
" <p>REQUEST CONTENT_LENGTH: " << in_content_length << "</p>\n"
" <p>REQUEST CONTENT: " << in_content << "</p>\n"
<< content_footer_part
;

// スコープの終了により明示的に std::flush して居なくても fcgi_streambuf のデストラクターでフラッシュされる
}

// スコープの終了により標準入出力系の streambuf 群は自動的にバックアップからリストアされる
}


ビルド

Clang:

clang++-3.8 -std=c++14 hello_world.cxx -lfcgi++ -lfcgi -o hello_world

または GCC:

g++-5 -std=c++14 hello_world.cxx -lfcgi++ -lfcgi -o hello_world


spawn-fcgi による FCGI プロセスの生成

spawn-fcgi -p 58000 -n hello_world


nginx による FCGI の動作設定

適当な server { } 設定の中で location /hello_world で動作させる例:

server

{
listen 80;
server_name localhost;

location /hello_world
{
# spawn-fcgi でプロセスを動作させる host:port
fastcgi_pass 127.0.0.1:58000;
# nginx の FCGI 各種パラメーター群の設定
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
}
}


動作確認

curl -i -s -X POST -d 'this is the post content.' http://localhost/test1/hoge/fuga/piyo

HTTP/1.1 200 OK

Server: nginx/1.10.0 (Ubuntu)
Date: Sun, 28 Aug 2016 02:21:31 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive

<html>
<head>
<title>Hello, World!</title>
</head>
<body>
<h1>Hello, World!</h>
<p>REQUEST_URI: /test1/hoge/fuga/piyo</p>
<p>REQUEST CONTENT_LENGTH: 25</p>
<p>REQUEST CONTENT: this is the post content.</p>
</body>
</html>

ここは curl である必要は特に無いので、お好みの動作確認可能な http クライアントを使って下さい。


参考