Help us understand the problem. What is going on with this article?

HTTP2最速実装をmain()関数だけで簡単に説明する(SSL編)

More than 3 years have passed since last update.

概要

C++のmain関数に全てベタ書きで書いてHTTP2の通信を説明します。
SSL編です。
非暗号化編はこちらです

最速実装なので最低限の通信に限定しています。
MacとWindows両方でビルドできます。

接続先は「nghttp2.org」を使わせていただきます。
変更したい人はhostの部分を書き換えてください。(その場合はHEADERS_FRAMEも書き換える必要あり)

main関数だけといいつつも、3つだけ冗長な処理を関数化してます。数行程度の定型処理です。

参考にしたのはこちらの資料です。
https://speakerdeck.com/syucream/2-zui-su-shi-zhuang-v3
 

依存

OpenSSL1.0.2以上を使用してください。

Macでインストールする場合は
brew install openssl
既にインストールされている場合は
brew upgrade openssl
です。

パスは環境によるかもですが、

ヘッダファイルパス ライブラリファイルパス 依存ライブラリ
usr/local/opt/openssl/include /usr/local/opt/openssl/lib libcrypto.a、libssl.a

gccでのコンパイルはこんな感じ。
gcc -g -Wall -L/usr/local/opt/openssl/lib -I/usr/local/opt/openssl/include -lssl -lcrypto -lstdc++ -o hellohttp2.o hellohttp2.cpp 

windowsの場合はOpenSSLをインストールしてインクルードパスとライブラリパスを設定して、下記の依存ライブラリを追加します。
 Ws2_32.lib
 ssleay32MTd.lib
 libeay32MTd.lib
デバッグ版とリリース版は各自切り分けて。

目的

HTTP2の最初のハードルを超えるための勉強。
main関数に全て書く事によって処理の流れを明確にする。
バイナリもそのままソースに打ち込むことによってどのようなデータが流れているのかを確認できるようにする。

実装

[https://github.com/0xfffffff7/HelloHttp2ssl]

//*****************************************************
// OpenSSL1.0.2以上を使用.
//*****************************************************

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string>

#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#pragma warning(disable:4996)
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#define SOCKET int
#define SD_BOTH SHUT_WR
#endif

#include <openssl/ssl.h>
#include <openssl/err.h>

#define READ_BUF_SIZE 4096
#define BUF_SIZE 4097
#define PORT 443
#define BINARY_FRAME_LENGTH 9


// ALPN識別子. h2
static const unsigned char protos[] = { 0x02, 0x68, 0x32 };
static const char cmp_protos[] = { 0x68, 0x32 };
static int protos_len = 3;

//ドラフト14を使う場合
// ALPN識別子. h2-14
//static const uint8_t protos[] = { 0x05, 0x68, 0x32, 0x2d, 0x31, 0x36 };
//static const uint8_t cmp_protos[] = { 0x68, 0x32, 0x2d, 0x31, 0x36 };
//static int protos_len = 6;


#define CLIENT_CONNECTION_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"


// 3バイトのネットワークオーダーを4バイト整数へ変換する関数.
char* to_framedata3byte(char *p, int &n);
int get_error();
void close_socket(SOCKET socket, SSL_CTX *_ctx, SSL *_ssl);


int main(int argc, char **argv)
{

    //------------------------------------------------------------
    // 接続先ホスト名.
    // HTTP2に対応したホストを指定します.
    //------------------------------------------------------------
    std::string host = "nghttp2.org";


    //------------------------------------------------------------
    // TCPの準備.
    //------------------------------------------------------------
#ifdef WIN32
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        return 0;
    }
#endif


    //------------------------------------------------------------
    // SSLの準備.
    //------------------------------------------------------------
    SSL *_ssl;
    SSL_CTX *_ctx;

    // SSLライブラリの初期化.
    SSL_library_init();

    // エラーを文字列化するための準備.
    SSL_load_error_strings();

    // グローバルコンテキスト初期化.
    const SSL_METHOD *meth = SSLv23_method();
    _ctx = SSL_CTX_new(meth);


    int error = 0;
    struct hostent *hp;
    struct sockaddr_in addr;
    SOCKET _socket;

    if (!(hp = gethostbyname(host.c_str()))){
        return -1;
    }
    memset(&addr, 0, sizeof(addr));
    addr.sin_addr = *(struct in_addr*)hp->h_addr_list[0];
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);

    if ((_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0){
        return -1;
    }
    if (connect(_socket, (struct sockaddr *)&addr, sizeof(addr))<0){
        return -1;
    }

    // sslセッションオブジェクトを作成する.
    _ssl = SSL_new(_ctx);

    // ソケットと関連づける.
    SSL_set_fd(_ssl, _socket);



    //------------------------------------------------------------
    // HTTP2の準備.
    //
    // プロトコルのネゴシエーションにALPNという方法を使います。
    // 具体的にはTLSのClientHelloのALPN拡張領域ににこれから使うプロトコル名を記述します.
    // SPDYではNPNという方法が使われましたが、現在のHTTP2仕様ではNPNは廃止されています.
    //
    // protosには文字列ではなくバイナリで、「0x02, 'h','2'」と指定する。
    // 最初の0x02は「h2」の長さを表している.
    //------------------------------------------------------------
    SSL_set_alpn_protos(_ssl, protos, protos_len);

    // SSL接続.
    if (SSL_connect(_ssl) <= 0){
        error = get_error();
        ::shutdown(_socket, SD_BOTH);
        close_socket(_socket, _ctx, _ssl);
        return 0;
    }

    // 採用されたALPNを確認する.
    const unsigned char  *ret_alpn;
    unsigned int  alpn_len;
    SSL_get0_alpn_selected(_ssl, &ret_alpn, &alpn_len);

    if ((int)alpn_len < protos_len - 1){
        error = get_error();
        close_socket(_socket, _ctx, _ssl);
        return 0;
    }

    if (memcmp(ret_alpn, cmp_protos, alpn_len) != 0){
        error = get_error();
        close_socket(_socket, _ctx, _ssl);
        return 0;
    }



    //------------------------------------------------------------
    // これからHTTP2通信を開始する合図.
    //
    // 24オクテットのバイナリを送信します
    // PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
    //------------------------------------------------------------
    int r = 0;
    char buf[BUF_SIZE] = { 0 };
    char* p = buf;
    bool b = false;
    int payload_length = 0;
    int frame_type = 0;
    int ret = 0;

    while (1){

        r = SSL_write(_ssl, CLIENT_CONNECTION_PREFACE, (int)strlen(CLIENT_CONNECTION_PREFACE));
        ret = SSL_get_error(_ssl, r);
        switch (ret){
            case SSL_ERROR_NONE:
                b = true;
                break;
            case SSL_ERROR_WANT_WRITE:
                continue;
            default:
                if (r == -1){
                    error = get_error();
                    close_socket(_socket, _ctx, _ssl);
                    return 0;
                }
        }
        if (b) break;
    }


    //------------------------------------------------------------
    // 全てのデータはバイナリフレームで送受信される
    // バイナリフレームは共通の9バイトヘッダと、データ本体であるpayloadを持つ
    //
    // ●ヘッダ部分のフォーマット
    //
    //   1-3バイト目  payloadの長さ。長さにヘッダの9バイトは含まれない。.
    //   4バイト目 フレームのタイプ.
    //   5バイト目 フラグ.
    //   6-9バイト目 ストリームID.(最初の1bitは予約で必ず0)
    //
    //  |Length(24bit)|Type(8bit)|Flags(8bit)|Reserve(1bit)|Stream Identifier(31bit)|
    //  |Frame Payload(Lengthバイト分)|
    //
    //
    // [フレームのタイプ]
    //
    // DATA(0x00)  リクエストボディや、レスポンスボディを転送する
    // HEADERS(0x01)  圧縮済みのHTTPヘッダーを転送する
    // PRIORITY(0x02)  ストリームの優先度を変更する
    // RST_STREAM(0x03)  ストリームの終了を通知する
    // SETTINGS(0x04)  接続に関する設定を変更する
    // PUSH_PROMISE(0x05)  サーバーからのリソースのプッシュを通知する
    // PING(0x06)  接続状況を確認する
    // GOAWAY(0x07)  接続の終了を通知する
    // WINDOW_UPDATE(0x08)   フロー制御ウィンドウを更新する
    // CONTINUATION(0x09)  HEADERSフレームやPUSH_PROMISEフレームの続きのデータを転送する
    //
    // それぞれのリクエストやレスポンスにはストリームIDが付与される.
    // クライアントから発行されるストリームIDは奇数.
    // サーバーから発行されるストリームIDは偶数.
    // ストリームには優先順位が付けられています.
    // 今回はストリームID「1」だけを使用します.
    //------------------------------------------------------------



    //------------------------------------------------------------
    // HTTP2通信のフロー
    //
    // まず最初にSettingフレームを必ず交換します.
    // Settingフレームを交換したら、設定を適用したことを伝えるために必ずACKを送ります.
    //
    // Client -> Server  SettingFrame
    // Client <- Server  SettingFrame
    // Client -> Server  ACK
    // Client <- Server  ACK
    //
    // Client -> Server  HEADERS_FRAME (GETなど)
    // Client <- Server  HEADERS_FRAME (ステータスコードなど)
    // Client <- Server  DATA_FRAME (Body)
    // 
    // Client -> Server  GOAWAY_FRAME (送信終了)
    //------------------------------------------------------------



    //------------------------------------------------------------
    // Settingフレームの送信.
    // フレームタイプは「0x04」
    // 全てデフォルト値を採用するためpayloadは空です。
    // SettingフレームのストリームIDは0です.
    //
    // 今回は空ですがSettingフレームのpayloadは次のフォーマットです.
    //
    // |Identifer(16bit)|Value(32bit)|
    // 上記を設定値の数だけ連結させ、最終的な長さをヘッダフレームのLengthに記述します.
    //
    // Identiferは次のものが定義されています。
    // SETTINGS_HEADER_TABLE_SIZE (0x1)  初期値は 4,096 オクテット
    // SETTINGS_ENABLE_PUSH (0x2)  初期値は1
    // SETTINGS_MAX_CONCURRENT_STREAMS (0x3)  初期状態では無制限
    // SETTINGS_INITIAL_WINDOW_SIZE (0x4)   初期値は 2^16-1 (65,535)
    // SETTINGS_MAX_FRAME_SIZE (0x5)    初期値は 2^24-1 (16777215)
    // SETTINGS_MAX_HEADER_LIST_SIZE (0x6)   初期値は無制限
    //------------------------------------------------------------
    const unsigned char settingframe[BINARY_FRAME_LENGTH] = { 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00};

    while (1){

        r = SSL_write(_ssl, settingframe, BINARY_FRAME_LENGTH);

        ret = SSL_get_error(_ssl, r);
        switch (ret){
            case SSL_ERROR_NONE:
                b = true;
                break;
            case SSL_ERROR_WANT_WRITE:
                continue;
            default:
                if (r == -1){
                    error = get_error();
                    close_socket(_socket, _ctx, _ssl);
                    return 0;
                }
        }
        if (b) break;
    }


    //------------------------------------------------------------
    // Settingフレームの受信.
    //------------------------------------------------------------
    memset(buf, 0x00, BUF_SIZE);
    p = buf;

    while (1){

        r = SSL_read(_ssl, p, READ_BUF_SIZE);
        ret = SSL_get_error(_ssl, r);
        switch (ret){
            case SSL_ERROR_NONE:
                b = true;
                break;
            case SSL_ERROR_WANT_READ:
                continue;
            default:
                if (r == -1){
                    error = get_error();
                    close_socket(_socket, _ctx, _ssl);
                    return 0;
                }
        }
        if (b) break;
    }



    //------------------------------------------------------------
    // ACKの送信.
    // ACKはSettingフレームを受け取った側が送る必要がある.
    // ACKはSettingフレームのフラグに0x01を立ててpayloadを空にしたもの.
    //
    // フレームタイプは「0x04」
    // 5バイト目にフラグ0x01を立てます。
    //------------------------------------------------------------
    const unsigned char settingframeAck[BINARY_FRAME_LENGTH] = { 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00 };
    while (1){

        r = SSL_write(_ssl, settingframeAck, BINARY_FRAME_LENGTH);

        ret = SSL_get_error(_ssl, r);
        switch (ret){
            case SSL_ERROR_NONE:
                b = true;
                break;
            case SSL_ERROR_WANT_WRITE:
                continue;
            default:
                if (r == -1){
                    error = get_error();
                    close_socket(_socket, _ctx, _ssl);
                    return 0;
                }
        }
        if (b) break;
    }

    // サーバーからのACKの受信は下でやります..

    //------------------------------------------------------------
    // HEADERSフレームの送信.
    //
    // フレームタイプは「0x01」
    // このフレームに必要なヘッダがすべて含まれていてこれでストリームを終わらせることを示すために、
    // END_STREAM(0x1)とEND_HEADERS(0x4)を有効にします。
    // 具体的には5バイト目のフラグに「0x05」を立てます。
    // ストリームIDは「0x01」を使います.
    //
    // ここまででヘッダフレームは「ペイロードの長さ(3バイト), 0x01, 0x05, 0x00, 0x00, 0x00, 0x01」になります.
    //
    //
    // ●HTTP1.1でのセマンティクス
    //   "GET / HTTP1/1"
    //   "Host: nghttp2.org
    //
    // ●HTTP2でのセマンティクス
    //      :method GET
    //      :path /
    //      :scheme https
    //      :authority nghttp2.org
    //
    // 本来HTTP2はHPACKという方法で圧縮します.
    // 今回は上記のHTTP2のセマンティクスを圧縮なしで記述します.
    //
    // 一つのヘッダフィールドの記述例
    //
    // |0|0|0|0|      0|   // 最初の4ビットは圧縮に関する情報、次の4ビットはヘッダテーブルのインデクス.(今回は圧縮しないのですべて0)
    // |0|            7|   // 最初の1bitは圧縮に関する情報(今回は0)、次の7bitはフィールドの長さ
    // |:method|           // フィールドをそのままASCIIのオクテットで書く。
    // |0|            3|   // 最初の1bitは圧縮に関する情報(今回は0)、次の7bitはフィールドの長さ
    // |GET|               // 値をそのままASCIIのオクテットで書く。
    //
    // 上記が一つのヘッダフィールドの記述例で、ヘッダーフィールドの数だけこれを繰り返す.
    //
    //------------------------------------------------------------
    const unsigned char headersframe[70] = {
        0x00, 0x00, 0x3d, 0x01, 0x05, 0x00, 0x00, 0x00, 0x01,   // ヘッダフレーム
        0x00,                                                   // 圧縮情報
        0x07, 0x3a, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,         // 7 :method
        0x03, 0x47, 0x45, 0x54,                                 // 3 GET
        0x00,                                                   // 圧縮情報
        0x05, 0x3a, 0x70, 0x61, 0x74, 0x68,                     // 5 :path
        0x01, 0x2f,                                             // 1 /
        0x00,                                                   // 圧縮情報
        0x07, 0x3a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65,         // 7 :scheme
        0x05, 0x68, 0x74, 0x74, 0x70, 0x73,                     // 5 https
        0x00,                                                   // 圧縮情報
        0x0a, 0x3a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,           // 10 :authority
        0x0b, 0x6e, 0x67, 0x68, 0x74, 0x74, 0x70, 0x32, 0x2e, 0x6f, 0x72, 0x67 };   // 11 nghttp2.org

    while (1){

        r = SSL_write(_ssl, headersframe, 70);

        ret = SSL_get_error(_ssl, r);
        switch (ret){
            case SSL_ERROR_NONE:
                b = true;
                break;
            case SSL_ERROR_WANT_WRITE:
                continue;
            default:
                if (r == -1){
                    error = get_error();
                    close_socket(_socket, _ctx, _ssl);
                    return 0;
                }
        }
        if (b) break;
    }




    //------------------------------------------------------------
    // HEADERSフレームの受信.
    //------------------------------------------------------------

    // まずはヘッダフレームを受信してpayloadのlengthを取得する。
    while (1){

        memset(buf, 0x00, BINARY_FRAME_LENGTH);
        p = buf;

        while (1){

            r = SSL_read(_ssl, p, BINARY_FRAME_LENGTH);
            ret = SSL_get_error(_ssl, r);
            switch (ret){
                case SSL_ERROR_NONE:
                    b = true;
                    break;
                case SSL_ERROR_WANT_READ:
                    continue;
                default:
                    if (r == -1){
                        error = get_error();
                        close_socket(_socket, _ctx, _ssl);
                        return 0;
                    }
            }
            if (b) break;
        }

        if (r == 0) continue;

        // ACKが返ってくる場合があるのでACKなら無視して次を読む。
        if (memcmp(buf, settingframeAck, BINARY_FRAME_LENGTH) == 0){
            continue;
        }
        else{

            // payloadの長さを取得する。
            p = to_framedata3byte(p, payload_length);

            // フレームタイプがHEADERS_FRAMEではなかったら読み飛ばす。
            memcpy(&frame_type, p, 1);
            if (frame_type != 1){

                while (payload_length > 0){

                    r = SSL_read(_ssl, p, payload_length);
                    ret = SSL_get_error(_ssl, r);
                    switch (ret){
                        case SSL_ERROR_NONE:
                            b = true;
                            break;
                        case SSL_ERROR_WANT_READ:
                            continue;
                        default:
                            if (r == -1){
                                error = get_error();
                                close_socket(_socket, _ctx, _ssl);
                                return 0;
                            }
                    }
                    payload_length -= r;
                }
                continue;
            }
            break;
        }
    }


    //------------------------------------------------------------
    // HEADERSフレームのpayloadの受信.
    //------------------------------------------------------------
    while (payload_length > 0){

        memset(buf, 0x00, BUF_SIZE);
        p = buf;

        r = SSL_read(_ssl, p, payload_length);
        ret = SSL_get_error(_ssl, r);
        switch (ret){
            case SSL_ERROR_NONE:
                break;
            case SSL_ERROR_WANT_READ:
                continue;
            default:
                if (r == -1){
                    error = get_error();
                    close_socket(_socket, _ctx, _ssl);
                    return 0;
                }
        }
        payload_length -= r;
    }


    //------------------------------------------------------------
    // DATAフレームの受信.
    //------------------------------------------------------------

    // まずはヘッダフレームを受信してpayloadのlengthを取得する。
    while (1){

        memset(buf, 0x00, BUF_SIZE);
        p = buf;

        r = SSL_read(_ssl, p, BINARY_FRAME_LENGTH);
        ret = SSL_get_error(_ssl, r);
        switch (ret){
            case SSL_ERROR_NONE:
                break;
            case SSL_ERROR_WANT_READ:
                continue;
            default:
                if (r == -1){
                    error = get_error();
                    close_socket(_socket, _ctx, _ssl);
                    return 0;
                }
        }
        if (b) break;
    }

    to_framedata3byte(p, payload_length);

    // 次にpayloadを受信する。
    while (payload_length > 0){

        memset(buf, 0x00, BUF_SIZE);
        p = buf;

        r = SSL_read(_ssl, p, READ_BUF_SIZE);
        ret = SSL_get_error(_ssl, r);
        switch (ret){
            case SSL_ERROR_NONE:
                break;
            case SSL_ERROR_WANT_READ:
                continue;
            default:
                if (r == -1){
                    error = get_error();
                    close_socket(_socket, _ctx, _ssl);
                    return 0;
                }
        }

        payload_length -= r;

        printf("%s", p);
    }


    //------------------------------------------------------------
    // GOAWAYの送信.
    //
    // これ以上データを送受信しない場合はGOAWAYフレームを送信します.
    // フレームタイプは「0x07」
    // ストリームIDは「0x00」(コネクション全体に適用するため)
    //------------------------------------------------------------
    const char goawayframe[17] = { 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 };

    while (1){

        r = SSL_write(_ssl, goawayframe, 17);

        ret = SSL_get_error(_ssl, r);
        switch (ret){
            case SSL_ERROR_NONE:
                b = true;
                break;
            case SSL_ERROR_WANT_WRITE:
                continue;
            default:
                if (r == -1){
                    error = get_error();
                    close_socket(_socket, _ctx, _ssl);
                    return 0;
                }
        }
        if (b) break;
    }



    //------------------------------------------------------------
    // 後始末.
    //------------------------------------------------------------
    close_socket(_socket, _ctx, _ssl);


    return 0;

}

void close_socket(SOCKET socket, SSL_CTX *_ctx, SSL *_ssl){

    // SSL/TLS接続をシャットダウンする。
    SSL_shutdown(_ssl);
    SSL_free(_ssl);

    ::shutdown(socket, SD_BOTH);

#ifdef WIN32
    ::closesocket(socket);
    WSACleanup();
#else
    ::close(socket);
#endif

    SSL_CTX_free(_ctx);
    ERR_free_strings();

}

int get_error(){
#ifdef WIN32
    return WSAGetLastError();
#endif
    return errno;
}

char* to_framedata3byte(char *p, int &n){
    u_char buf[4] = { 0 };
    memcpy(&(buf[1]), p, 3);
    memcpy(&n, buf, 4);
    n = ntohl(n);
    p += 3;
    return p;
}

 
 

結果

通信に問題なければ、コンソールに次のようなhtmlが表示されるでしょう。

<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
  <meta charset="utf-8">
  <title>Nghttp2: HTTP/2 C Library - nghttp2.org</title>
  <meta name="author" content="Tatsuhiro Tsujikawa">


  <meta name="description" content="Nghttp2: HTTP/2 C Library Feb 16th, 2015 11:16 pm nghttp2 is an implementation of HTTP/2 in C.
HTTP/2 and HPACK has been approved by IETF,
we are &hellip;">


  <!-- http://t.co/dKP3o1e -->
  <meta name="HandheldFriendly" content="True">
  <meta name="MobileOptimized" content="320">
  <meta name="viewport" content="width=device-width, initial-scale=1">


  <link rel="canonical" href="//nghttp2.org">
  <link href="/favicon.png" rel="icon">
  <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
  <link href="/atom.xml" rel="alternate" title="nghttp2.org" type="application/atom+xml">
  <script src="/javascripts/modernizr-2.0.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <script>!window.jQuery && document.write(unescape('%3Cscript src="./javascripts/libs/jquery.min.js"%3E%3C/script%3E'))</script>
  <script src="/javascripts/octopress.js" type="text/javascript"></script>
  <!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="//fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">

  </head>

<body   >
  <header role="banner"><hgroup>
  <h1><a href="/">nghttp2.org</a></h1>

    <h2>HTTP/2 C library and tools</h2>

</hgroup>

</header>
  <nav role="navigation"><ul class="subscription" data-subscription="rss">
  <li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>

</ul>

<form action="https://www.google.com/search" method="get">
  <fieldset role="search">
    <input type="hidden" name="q" value="site://nghttp2.org" />
    <input class="search" type="text" name="q" results="0" placeholder="Search"/>
  </fieldset>
</form>

<ul class="main-navigation">
  <li><a href="/">Top</a></li>
  <li><a href="/blog/">Blog</a></li>
  <li><a href="/blog/archives">Archives</a></li>
  <li><a href="/documentation/">Documentation</a></li>
  <li><a href="/httpbin">Httpbin</a></li>
  <li><a href="https://github.com/tatsuhiro-t/nghttp2/releases">Releases</a></li>
  <li><a href="https://github.com/tatsuhiro-t/nghttp2">Source Code</a></li>
</ul>

</nav>
  <div id="main">
    <div id="content">
      <div>
<article role="article">

  <header>
    <h1 class="entry-title">Nghttp2: HTTP/2 C Library</h1>
    <p class="meta">

<time class='entry-date' datetime='2015-02-16T23:16:00+09:00'><span class='date'><span class='date-month'>Feb</span> <span class='date-day'>16</span><span class='date-suffix'>th</span>, <span class='date-year'>2015</span></span> <span class='time'>11:16 pm</span></time></p>
  </header>

  <p>nghttp2 is an implementation of HTTP/2 in C.
<a href="https://www.ietf.org/blog/2015/02/http2-approved/">HTTP/2 and HPACK has been approved by IETF</a>,
we are now waiting for their publication as RFCs.</p>

<p>The framing layer of HTTP/2 is implemented as a form of reusable C
library.  On top of that, we have implemented HTTP/2 <a href="/documentation/nghttp.1.html">client</a>, <a href="/documentation/nghttpd.1.html">server</a>
and <a href="/documentation/nghttpx.1.html">proxy</a>.
We have also developed <a href="/documentation/h2load.1.html">load test and benchmarking tool</a> for HTTP/2 and SPDY.</p>

<p>We have participated in httpbis working group since HTTP/2 draft-04,
which is the first implementation draft.  Since then we have updated
nghttp2 library constantly to latest specification and nghttp2 is now
one of the most mature <a href="https://github.com/http2/http2-spec/wiki/Implementations">HTTP/2 implementations</a>.</p>

<p>All C APIs are <a href="/documentation/apiref.html">fully documented</a>.</p>

<p>HTTP/2 utilizes header compression method called <a href="http://http2.github.io/http2-spec/compression.html">HPACK</a>.  We offer
HPACK encoder and decoder are available as <a href="/documentation/tutorial-hpack.html">public API</a>.</p>

<p>nghttp2 library itself is a bit low-level.  The experimental <a href="/documentation/libnghttp2_asio.html">high level C++ API</a> is also available.</p>

<p>We have <a href="/documentation/python-apiref.html">Python binding</a> of this libary, but we have not covered
everything yet.</p>

     <footer>
      <p class="meta">

<time class='entry-date' datetime='2015-02-16T23:16:00+09:00'><span class='date'><span class='date-month'>Feb</span> <span class='date-day'>16</span><span class='date-suffix'>th</span>, <span class='date-year'>2015</span></span> <span class='time'>11:16 pm</span></time>

      </p>

        <div class="sharing">

  <a href="//twitter.com/share" class="twitter-share-button" data-url="//nghttp2.org/index.html" data-via="" data-counturl="//nghttp2.org/index.html" >Tweet</a>



</div>


    </footer>

</article>

</div>

<aside class="sidebar">

    <section>
  <h1>Recent Posts</h1>
  <ul id="recent_posts">

      <li class="post">
        <a href="/blog/2015/03/14/nghttp2-v0-7-7/">Nghttp2 v0.7.7</a>
      </li>

      <li class="post">
        <a href="/blog/2015/03/14/nghttp2-v0-7-6/">Nghttp2 v0.7.6</a>
      </li>

      <li class="post">
        <a href="/blog/2015/02/27/nghttp2-v0-7-5/">Nghttp2 v0.7.5</a>
      </li>

      <li class="post">
        <a href="/blog/2015/02/15/nghttp2-v0-7-4/">Nghttp2 v0.7.4</a>
      </li>

      <li class="post">
        <a href="/blog/2015/02/10/nghttp2-dot-org-enabled-http2-server-push/">nghttp2.org Enabled HTTP/2 Server Push</a>
      </li>

  </ul>
</section>

</aside>
    </div>
  </div>
  <footer role="contentinfo"><p>
  Copyright &copy; 2015 - Tatsuhiro Tsujikawa -
  <span class="credit">Powered by <a href="http://octopress.org">Octopress</a></span>
</p>

</footer>

  <script type="text/javascript">
    (function(){
      var twitterWidgets = document.createElement('script');
      twitterWidgets.type = 'text/javascript';
      twitterWidgets.async = true;
      twitterWidgets.src = '//platform.twitter.com/widgets.js';
      document.getElementsByTagName('head')[0].appendChild(twitterWidgets);
    })();
  </script>

</body>
</html>
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした