From b167ce6b03ddea247bbe6c9fd3d713ffd379971d Mon Sep 17 00:00:00 2001 From: David Dias Date: Mon, 29 Jun 2015 22:13:01 -0700 Subject: [PATCH 1/5] .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1ca9571..e90b07d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ npm-debug.log +.vimrc From 6e56de30f31acc9c09c383b5ac2584a59fb88c41 Mon Sep 17 00:00:00 2001 From: David Dias Date: Tue, 30 Jun 2015 07:12:05 -0700 Subject: [PATCH 2/5] add client documentation to the readme, frame listener and go-node interop example --- README.md | 76 ++++++++++++++++++++++++++- examples/spdystream-interop/client.go | 42 +++++++++++++++ examples/spdystream-interop/client.js | 42 +++++++++++++++ examples/spdystream-interop/server.go | 26 +++++++++ examples/spdystream-interop/server.js | 32 +++++++++++ 5 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 examples/spdystream-interop/client.go create mode 100644 examples/spdystream-interop/client.js create mode 100644 examples/spdystream-interop/server.go create mode 100644 examples/spdystream-interop/server.js diff --git a/README.md b/README.md index 99a916c..31fea5d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ SPDY/HTTP2 generic transport implementation. ## Usage +### server + ```javascript var transport = require('spdy-transport'); @@ -17,16 +19,23 @@ var transport = require('spdy-transport'); // of `net.createServer`'s connection handler. var server = transport.connection.create(socket, { - protocol: 'http2', + protocol: 'http2', // or 'spdy' isServer: true }); server.on('stream', function(stream) { console.log(stream.method, stream.path, stream.headers); + + // necessary for the stream to be established in both ends stream.respond(200, { header: 'value' }); + // response body + stream.write() + + stream.end() // sends FLAG_FIN, setting the connection half open + stream.on('readable', function() { var chunk = stream.read(); if (!chunk) @@ -44,6 +53,71 @@ server.on('stream', function(stream) { }); ``` +### client + +```javascript +var transport = require('spdy-transport') + +// NOTE: socket is some stream or net.Socket instance, may be an argument +// of `net.createServer`'s connection handler. + +var client = transport.connection.create(socket, { + protocol: 'http2', + isServer: false +}) + +// optional for http2, however mandatory for spdy ([2, 3, 3.1]) +client.start(4) + +client.request({ + method: 'GET', + host: 'localhost', + path: '/', + headers: { + a: 'b', + c: 'd' + } + }, function (err, stream) { + if (err) { + return console.log(err) + } + + stream.on('response', function (code, headers) { + console.log(code, headers) + + // request body + stream.write() + + // And other node.js Stream APIs + // ... + }) + + stream.on('readable', function () { + var chunk = stream.read() + if (!chunk) { + return + } + console.log(chunk.toString()) + }) +}) +``` + +### frame listener + +```javascript +// either client or server +connection.on('frame', function (frame) { + console.log(frame.type) +}) +``` + + +## Interop + +`spdy-transport` is capable of interoping with other spdy framing layer implementations such as: + +- [spdystream](https://github.com/docker/spdystream), [example](/examples/spdystream-interop) + ## LICENSE This software is licensed under the MIT License. diff --git a/examples/spdystream-interop/client.go b/examples/spdystream-interop/client.go new file mode 100644 index 0000000..1fe3c60 --- /dev/null +++ b/examples/spdystream-interop/client.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "github.com/docker/spdystream" + "net" + "net/http" +) + +func main() { + conn, err := net.Dial("tcp", "localhost:9090") + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, false) + if err != nil { + panic(err) + } + + header := http.Header{} + // Although spdystream doesn't mind about :method and :path headers, these + // are necessary for interop with spdy-transport + header.Add(":method", "GET") + header.Add(":path", "/") + + go spdyConn.Serve(spdystream.NoOpStreamHandler) + + stream, err := spdyConn.CreateStream(header, nil, false) + if err != nil { + panic(err) + } + + stream.Wait() + + fmt.Fprint(stream, "Hey, how is it going! (go client asking)") + + buf := make([]byte, 35) + stream.Read(buf) + fmt.Println(string(buf)) + + stream.Close() +} diff --git a/examples/spdystream-interop/client.js b/examples/spdystream-interop/client.js new file mode 100644 index 0000000..f0eb367 --- /dev/null +++ b/examples/spdystream-interop/client.js @@ -0,0 +1,42 @@ +var tcp = require('net') +var transport = require('spdy-transport') + +var socket = tcp.connect({port: 9090}, function () { + + var client = transport.connection.create(socket, { + protocol: 'spdy', + isServer: false + }) + + client.on('frame', function (frame) { + console.log(frame.type) + }) + + client.start(3.1) + + client.request({ + method: 'GET', + host: 'localhost', + path: '/', + headers: { + a: 'b' + }}, function (err, stream) { + console.log('stream established') + if (err) { + return console.log(err) + } + stream.write('Hey, how is it going. (node client asking)!') + + stream.on('readable', function () { + var chunk = stream.read() + if (!chunk) { + return + } + console.log(chunk.toString()) + }) + + stream.on('response', function (code, headers) { + console.log(code, headers) + }) + }) +}) diff --git a/examples/spdystream-interop/server.go b/examples/spdystream-interop/server.go new file mode 100644 index 0000000..abc581e --- /dev/null +++ b/examples/spdystream-interop/server.go @@ -0,0 +1,26 @@ +package main + +import ( + "github.com/docker/spdystream" + "net" +) + +func main() { + listener, err := net.Listen("tcp", "localhost:9090") + if err != nil { + panic(err) + } + + for { + conn, err := listener.Accept() + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, true) + if err != nil { + panic(err) + } + // This is an "echo server" example, known in the go world as "mirror" + go spdyConn.Serve(spdystream.MirrorStreamHandler) + } +} diff --git a/examples/spdystream-interop/server.js b/examples/spdystream-interop/server.js new file mode 100644 index 0000000..7e371df --- /dev/null +++ b/examples/spdystream-interop/server.js @@ -0,0 +1,32 @@ +var tcp = require('net') +var transport = require('spdy-transport') + +tcp.createServer(function (socket) { + var server = transport.connection.create(socket, { + protocol: 'spdy', + isServer: true + }) + + server.on('stream', function (stream) { + console.log(stream.method, stream.path, stream.headers) + + stream.respond(200, { + c: 'd' + }) + stream.write('Hey, how is it going? (node server asking') + + stream.on('readable', function () { + var chunk = stream.read() + if (!chunk) { + return + } + console.log(chunk.toString()) + }) + + stream.on('end', function () { + console.log('end') + }) + }) + +}).listen(9090) + From 2cbef2716cd6d6ee28412bfe4109dda803496af9 Mon Sep 17 00:00:00 2001 From: David Dias Date: Tue, 30 Jun 2015 07:25:42 -0700 Subject: [PATCH 3/5] add implementation notes section --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 31fea5d..a29543b 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,10 @@ connection.on('frame', function (frame) { }) ``` +## Implementation notes + +- spdy-transport always sends a SETTINGS frame as the first frame once the socket is open +- the server requires a http `":method"` and `":path"` header. If they are not added on the client, `"GET"` and `"/"` are added by default ## Interop From b0e4d5e9e318be81c4f4660138c2fb846249c948 Mon Sep 17 00:00:00 2001 From: David Dias Date: Tue, 30 Jun 2015 17:12:58 -0700 Subject: [PATCH 4/5] improve the comment for client.start --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a29543b..c782138 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ var client = transport.connection.create(socket, { isServer: false }) -// optional for http2, however mandatory for spdy ([2, 3, 3.1]) +// client.start(), 4 for http2, [2,3,3.1] for spdy 2.0, 3.0 and 3.1 respectively client.start(4) client.request({ From 407d5ca6e7db5877bae256b6eac7e3c7a94eba8a Mon Sep 17 00:00:00 2001 From: David Dias Date: Tue, 30 Jun 2015 18:59:41 -0700 Subject: [PATCH 5/5] remove .on('readable'), add link to streams API, add semicollons to follow the module style --- README.md | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index c782138..9293643 100644 --- a/README.md +++ b/README.md @@ -32,31 +32,26 @@ server.on('stream', function(stream) { }); // response body - stream.write() + stream.write(); stream.end() // sends FLAG_FIN, setting the connection half open - stream.on('readable', function() { - var chunk = stream.read(); - if (!chunk) - return; - - console.log(chunk); - }); + var chunk = stream.read(); stream.on('end', function() { console.log('end'); }); + // And other node.js Stream APIs - // ... + // ... ref: https://nodejs.org/api/stream.html }); ``` ### client ```javascript -var transport = require('spdy-transport') +var transport = require('spdy-transport'); // NOTE: socket is some stream or net.Socket instance, may be an argument // of `net.createServer`'s connection handler. @@ -64,10 +59,10 @@ var transport = require('spdy-transport') var client = transport.connection.create(socket, { protocol: 'http2', isServer: false -}) +}); // client.start(), 4 for http2, [2,3,3.1] for spdy 2.0, 3.0 and 3.1 respectively -client.start(4) +client.start(4); client.request({ method: 'GET', @@ -79,27 +74,22 @@ client.request({ } }, function (err, stream) { if (err) { - return console.log(err) + return console.log(err); } stream.on('response', function (code, headers) { - console.log(code, headers) + console.log(code, headers); // request body - stream.write() + stream.write(); - // And other node.js Stream APIs - // ... }) - stream.on('readable', function () { - var chunk = stream.read() - if (!chunk) { - return - } - console.log(chunk.toString()) - }) -}) + var chunk = stream.read(); + + // And other node.js Stream APIs + // ... ref: https://nodejs.org/api/stream.html +}); ``` ### frame listener @@ -107,8 +97,8 @@ client.request({ ```javascript // either client or server connection.on('frame', function (frame) { - console.log(frame.type) -}) + console.log(frame.type); +}); ``` ## Implementation notes