Edited at

nghttp2をmac osx でビルドしたい

More than 3 years have passed since last update.

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