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

#概要

C++のmain関数に全てベタ書きで書いてHTTP2の通信を説明します。
SSL編です。
非暗号化編は[こちら](http://qiita.com/0xfffffff7/items/0960527dca6de364daeb)です

最速実装なので最低限の通信に限定しています。
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]


```cpp
//*****************************************************
// 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>
```