LoginSignup
9
9

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-08-28

使用環境例

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

  • 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 クライアントを使って下さい。

参考

9
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9