nghttp2をmac osx でビルドしたい

  • 4
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

http2confにいるんだが、nghttp2くらいはbuildしてくるべきだった、とかつぶやいたら色々とソリューションを教えてもらった。

自動化させたら世界一の @deeeet さんからDockerfileとかももらったんだけど、Dockerよりももうちょっと手軽にお願いしたかった。nghttp2がmacでビルドできればベスト。

色々試したけど、最終的に夏風さんに頼んでbrewでinstallできるようにしてもらった。

install nghttp2 by brew

$ curl -o /usr/local/Library/Formula/nghttp2.rb https://gist.githubusercontent.com/summerwind/6295114/raw/nghttp2.rb

$ brew install nghttp2
$ nghttpd --no-tls -v 8888

こうしておいて、後は夏風さんのclientを実装する。途中までclient頑張って書いてたけど、途中からずるしてコピペした。

client.js

var net = require('net'),
    hpack = require('./hpack');

var FRAME_HEADER_LEN = 9;

function createSettingsFrame(ack) {
  var flag = ack ? 0x1 : 0x0;

  var frameHeader = new Buffer(FRAME_HEADER_LEN);
  frameHeader.writeUInt32BE(0x0, 0);
  frameHeader.writeUInt8(0x4, 3);
  frameHeader.writeUInt8(flag, 4);
  frameHeader.writeUInt32BE(0x0, 5);

  return frameHeader;
}

function encodeHeaders(headers) {
  var headerBlocks = [];

  headers.forEach(function(header){
    var prefix = new Buffer(1);
    prefix.fill(0);
    headerBlocks.push(prefix);

    var name = hpack.encodeString(header[0]);
    headerBlocks.push(name);

    var value = hpack.encodeString(header[1]);
    headerBlocks.push(value);
  });

  return Buffer.concat(headerBlocks);
}

function createHeadersFrame(headers) {
  var headerBlocks = encodeHeaders(headers);

  var frameHeader = new Buffer(FRAME_HEADER_LEN);
  frameHeader.writeUInt32BE(headerBlocks.length << 8, 0);
  frameHeader.writeUInt8(0x1, 3);
  frameHeader.writeUInt8(0x5, 4);
  frameHeader.writeUInt32BE(0x1, 5);

  return Buffer.concat([frameHeader, headerBlocks]);
};

function debug(msg) {
  console.log('[debug]', msg);
}


var conn = net.connect(8888, '127.0.0.1');
var frameBuffer = new Buffer(0);
var bodyBuffer = [];

conn.on('connect', function(){
  conn.write('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n', 'utf8');
  conn.write(createSettingsFrame(false));
  debug('Send SETTINGS frame');
});

conn.on('data', function(chunk){
  frameBuffer = Buffer.concat([frameBuffer, chunk]);

  while (frameBuffer.length >= FRAME_HEADER_LEN) {
    var length = FRAME_HEADER_LEN;
        length += frameBuffer.readUInt32BE(0) >> 8;
    if (frameBuffer.length < length) {
      return;
    }

    var frame = frameBuffer.slice(0, length);
    frameBuffer = frameBuffer.slice(length);

    if (frame[3] === 0x4 && frame[4] === 0x0) {
      conn.write(createSettingsFrame(true));
      debug('Send SETTINGS frame with ACK flag');
      continue;
    }

    if (frame[3] === 0x4 && frame[4] === 0x1) {
      var headers = [
        [ ':method', 'GET' ],
        [ ':path', '/' ],
        [ ':scheme', 'http' ],
        [ ':authority', 'localhost:8888' ]
      ];

      conn.write(createHeadersFrame(headers));
      debug('Send HEADERS frame');
      continue;
    }

    if (frame[3] === 0x0 && frame[4] === 0x0) {
      bodyBuffer.push(frame.slice(FRAME_HEADER_LEN));
    }

    if (frame[3] === 0x0 && frame[4] === 0x1) {
      var body = Buffer.concat(bodyBuffer);
      console.log(body.toString());
      conn.end();
    }
  }
});

hpack.js

function encodeInteger(num, prefix) {
  var limit = Math.pow(2, prefix) - 1;

  if (num < limit) {
    return new Buffer([num]);
  }

  var octets = [limit];
  num -= limit;
  while (num >= 128) {
    octets.push(num % 128 | 0x80);
    num >>= 7;
  }
  octets.push(num);

  return new Buffer(octets);
}

function encodeString(str) {
  var buffers = [];
  var value = new Buffer(str, 'ascii');

  buffers.push(encodeInteger(value.length, 7));
  buffers.push(value);

  return Buffer.concat(buffers);
}

exports.encodeInteger = encodeInteger;
exports.encodeString = encodeString;

こんな感じで動く。

$ node client.js
<html><head><title>404</title></head><body><h1>404</h1><hr><address>nghttpd nghttp2/0.6.4 at port 8888</address></body></html><html><head><title>404</title></head><body><h1>404</h1><hr><address>nghttpd nghttp2/0.6.4 at port 8888</address></body></html>

こんなのが返ってくればOK