Skip to content

Commit

Permalink
improved nodejs example with filter role
Browse files Browse the repository at this point in the history
  • Loading branch information
yookoala committed Feb 8, 2018
1 parent ecb92fd commit 2be5b56
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 15 deletions.
1 change: 1 addition & 0 deletions example/nodejs/assets/content.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello world here
46 changes: 46 additions & 0 deletions example/nodejs/nodejs.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,49 @@ func NewHandler(entrypoint, network, address string) http.Handler {
)
return h
}

// NewFilterHandler for advanced test for muxing
// which will pass a requested file, in root folder, to
// the fastcgi application for filtering
func NewFilterHandler(root string, clientFactory gofast.ClientFactory) http.Handler {
return gofast.NewHandler(
gofast.NewFilterLocalFS(root)(gofast.BasicSession),
clientFactory,
)
}

// NewResponderHandler for advanced test for muxing
func NewResponderHandler(entrypoint string, clientFactory gofast.ClientFactory) http.Handler {
return gofast.NewHandler(
gofast.NewFileEndpoint(entrypoint)(gofast.BasicSession),
clientFactory,
)
}

// NewMuxHandler create advanced muxing example
func NewMuxHandler(
root string, // root folder for filter data
entrypoint string, // entrypoint for building params to responder
network, address string,
) http.Handler {

// common client pool for both filter and responder handler
connFactory := gofast.SimpleConnFactory(network, address)
pool := gofast.NewClientPool(
gofast.SimpleClientFactory(connFactory, 0),
10,
60*time.Second,
)

// mux filter and responder in different folder
mux := http.NewServeMux()
mux.Handle("/filter/", http.StripPrefix("/filter/", NewFilterHandler(
root,
pool.CreateClient,
)))
mux.Handle("/responder/", http.StripPrefix("/responder/", NewResponderHandler(
entrypoint,
pool.CreateClient,
)))
return mux
}
108 changes: 108 additions & 0 deletions example/nodejs/nodejs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ func examplePath() string {
return filepath.Join(basePath, "src", "index.js")
}

func exampleAssetPath() string {
basePath, err := os.Getwd()
if err != nil {
panic(err)
}
return filepath.Join(basePath, "assets")
}

func waitConn(socket string) <-chan net.Conn {
chanConn := make(chan net.Conn)
go func() {
Expand Down Expand Up @@ -120,3 +128,103 @@ func TestHandler(t *testing.T) {

os.Remove(socket)
}

func TestMuxHandler(t *testing.T) {
root := exampleAssetPath() // the "assets" folder
webapp := examplePath() // the "src/index.js" file path
socket := filepath.Join(filepath.Dir(webapp), "test2.sock")

// define webapp.py command
cmd := exec.Command("node", webapp)
cmd.Env = append(os.Environ(), "TEST_FCGI_SOCK="+socket)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// start the command and wait for its exit
done := make(chan error, 1)
go func() {
if err := cmd.Start(); err != nil {
done <- err
return
}
// wait if the command started successfully
log.Printf("started successfully")
log.Printf("process=%#v", cmd.Process)
done <- cmd.Wait()
log.Printf("wait ended")
}()

// wait until socket ready
conn := <-waitConn(socket)
conn.Close()
log.Printf("socket ready")

// start the proxy handler
h := nodejs.NewMuxHandler(
root,
webapp,
"unix", socket,
)

get := func(path string) (w *httptest.ResponseRecorder, err error) {
r, err := http.NewRequest("GET", path, nil)
if err != nil {
return
}
w = httptest.NewRecorder()
h.ServeHTTP(w, r)
return
}

testDone := make(chan bool)
go func() {

w, err := get("/responder/")
if err != nil {
t.Errorf("unexpected error %v", err)
testDone <- false
return
}
if want, have := "hello index", w.Body.String(); want != have {
t.Errorf("expected %#v, got %#v", want, have)
testDone <- false
return
}

w, err = get("/filter/content.txt")
if err != nil {
t.Errorf("unexpected error %v", err)
testDone <- false
return
}
if want, have := "ereh dlrow olleh", w.Body.String(); want != have {
t.Errorf("expected %#v, got %#v", want, have)
testDone <- false
return
}
testDone <- true
}()

select {
case testSuccess := <-testDone:
if !testSuccess {
log.Printf("test failed")
}
case <-time.After(3 * time.Second):
log.Printf("test timeout")
case err := <-done:
if err != nil {
log.Printf("process done with error = %v", err)
} else {
log.Print("process done gracefully without error")
}
}

log.Printf("send SIGTERM")
if err := cmd.Process.Signal(syscall.SIGTERM); err != nil {
log.Fatal("failed to kill: ", err)
}
log.Println("process killed")

os.Remove(socket)
}
68 changes: 53 additions & 15 deletions example/nodejs/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,68 @@ var fcgi = require('node-fastcgi');

var listenTarget = process.env.TEST_FCGI_SOCK || './node-fastcgi.sock';

/**
* function createServer([responder], [authorizer], [filter], [config])
* Creates and returns a FastCGI server object. Compatible with http.createServer
*
* Arguments:
* - responder (optional): callback for FastCGI responder requests (normal HTTP requests, 'request' event)
* - authorizer (optional): callback for FastCGI authorizer requests ('authorize' event)
* - filter (optional): callback for FastCGI filter requests ('filter' event)
* - config (optional): server configuration (default: { maxConns: 2000, maxReqs: 2000, multiplex: true, valueMap: {} })
*/
fcgi.createServer(function(req, res) {
responder = function(req, res) {
if (req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end("hello index");
res.end('hello index');
} else if (req.method === 'POST') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
var body = "";
var body = '';

req.on('data', function(data) { body += data.toString(); });
req.on('end', function() {
res.end("Received data:\n" + body);
res.end('Received data:\n' + body);
});
} else {
res.writeHead(501);
res.end();
}
}).listen(listenTarget);
}
authorizer = function (req, res) {
// placeholder for now
res.writeHead(200);
res.end();
}
filter = function (req, res) {
// a simple filter to reverse the data string as
// response body
if (req.method === 'GET' || req.method === 'POST') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
var body = '';
var toFilter = '';

// read post data, if any
req.on('data', function(data) { body += data.toString(); });
res.on('end', function () {
res.end('error: no data to filter with')
});

// read on data stream until stream fulfilled
req.socket.dataStream.on('data', function (data) {
// the filter logic for test
// Note: you may do eval(data) if you want to run data as js
toFilter += data.toString();
});
req.socket.dataStream.on('end', function (data) {
// the filter logic for test
// Note: you may do eval(data) if you want to run data as js
res.end(toFilter.split('').reverse().join(''));
});

} else {
res.writeHead(501);
res.end();
}
}

/**
* function createServer([responder], [authorizer], [filter], [config])
* Creates and returns a FastCGI server object. Compatible with http.createServer
*
* Arguments:
* - responder (optional): callback for FastCGI responder requests (normal HTTP requests, 'request' event)
* - authorizer (optional): callback for FastCGI authorizer requests ('authorize' event)
* - filter (optional): callback for FastCGI filter requests ('filter' event)
* - config (optional): server configuration (default: { maxConns: 2000, maxReqs: 2000, multiplex: true, valueMap: {} })
*/
fcgi.createServer(responder, authorizer, filter).listen(listenTarget);

0 comments on commit 2be5b56

Please sign in to comment.