Browse files

Refactored 'http' module implementation API; Refactored helloworld an…

…d README accordingly
  • Loading branch information...
1 parent e3922e5 commit 6c0aa943a4e1dea205a03e0b8559d9e16115bf42 @tristanls committed Dec 18, 2011
Showing with 379 additions and 124 deletions.
  1. +18 −14 README.md
  2. +11 −14 examples/helloworld.example.coffee
  3. +1 −1 package.json
  4. +349 −95 src/http.coffee
View
32 README.md
@@ -28,25 +28,29 @@ anode = require 'anode'
cnf = new anode.Configuration()
-http_actor = cnf.actor anode.http.http_beh()
-helloworld = cnf.actor anode.behavior(
- 'http, #created, server' : ->
- @send( @, '#listen', 8080, '127.0.0.1' ).to @server
- 'server, #listening, port, host' : ->
- @send( 'Server running at http://' + @host + ':' + @port + '/' ).to cnf.console.log
- 'server, #request, request, response' : ->
- @send( null, '#end', 'Hello Actor World\n' ).to @response
-)()
-
-cnf.send( helloworld, '#createServer' ).to http_actor
+httpServer = cnf.actor anode.http.server_beh()
+helloworld = cnf.actor anode.behavior( 'httpServer'
+
+ '#start' : ->
+ @send( @, '#listen', 8080, '127.0.0.1' ).to @httpServer
+
+ '$httpServer, #listen' : ->
+ @send( 'Server running at http://127.0.0.1:8080/' ).to cnf.console.log
+
+ '$httpServer, #request, request, response' : ->
+ @send( null, '#end', 'Hello Actor World\n' ).to @response
+
+)( httpServer ) # helloworld
+
+cnf.send( '#start' ).to helloworld
```
To run the server, go into the `examples` directory and execute it with `coffee`:
- '% coffee helloworld.example.coffee
+ % coffee helloworld.example.coffee
Server running at http://127.0.0.1:8080/
-*note: not all of the `node` `'http'` functionality has been wrapped yet.
+*note: not all of the Node.js `'http'` functionality has been wrapped yet.
### Actors
@@ -82,7 +86,7 @@ Other examples show simpler functionality:
(The MIT License)
-Copyright (c) 2011 Tristan Slominski <tristan.slominski@gmail.com>
+Copyright (c) 2011 Tristan Slominski
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
View
25 examples/helloworld.example.coffee
@@ -9,23 +9,20 @@ anode = require '../lib/anode'
cnf = new anode.Configuration()
# create the http actor
-http_actor = cnf.actor anode.http.http_beh()
+httpServer = cnf.actor anode.http.server_beh()
-# create the hello world actor
-helloworld = cnf.actor anode.beh(
+# create the hello world application actor
+helloworld = cnf.actor anode.beh( 'httpServer'
- 'http, #created, server' : ->
- @send( @, '#listen', 8080, '127.0.0.1' ).to @server
-
- 'server, #listening, port, host' : ->
- @send( 'Server running at http://' + @host + ':' + @port + '/' ).to \
- cnf.console.log
+ '#start' : ->
+ @send( @, '#listen', 8080, '127.0.0.1' ).to @httpServer
+
+ '$httpServer, #listen' : ->
+ @send( 'Server running at http://127.0.0.1:8080/' ).to cnf.console.log
- 'server, #request, request, response' : ->
+ '$httpServer, #request, request, response' : ->
@send( null, '#end', 'Hello Actor World\n' ).to @response
-)() # helloworld
+)( httpServer ) # helloworld
-# send the #createServer message to http actor with helloworld as the customer
-# for the '#created' message
-cnf.send( helloworld, '#createServer' ).to http_actor
+cnf.send( '#start' ).to helloworld
View
2 package.json
@@ -1,7 +1,7 @@
{
"name" : "anode",
"description" : "Humus inspired actor framework for Node.js",
- "version" : "0.1.5",
+ "version" : "0.2.0",
"author" : "Tristan Slominski <tristan.slominski@gmail.com>",
"contributors" : [
"Dale Schumacher <dale.schumacher@gmail.com>"
View
444 src/http.coffee
@@ -1,161 +1,415 @@
#
-# http.coffee : anode wrapper around nodejs http library
+# http.coffee : anode wrapper around Node.js 'http' module
#
# (C) 2011 Tristan Slominski
#
anode = require '../lib/anode'
nodehttp = require 'http'
#
-# Behavior controlling the server
+# Anode wrapper around the http.clientRequest
#
-httpServer_beh = anode.beh 'server'
-
- # issue 'listen' command to the server
- 'cust, #listen, port, [host]' : ->
+clientRequest_beh = anode.beh 'request'
+
+ 'cust, #abort' : ->
- __cust = @cust
- __host = @host
- __httpServer = @
- __port = @port
- __send = @send
+ @request.abort()
+ if @cust # ack requested
+ @send( @, '#abort' ).to @cust
+
+ 'cust, #end, [data], [encoding]' : ->
+ @request.end @data, @encoding
if @cust # ack requested
- if @host
- __callback = () ->
- __send( __httpServer, '#listening', __port, __host ).to __cust
- else
- __callback = () ->
- __send( __httpServer, '#listening', __port ).to __cust
-
- @host = if @host then @host else undefined
- __callback = if __callback then __callback else undefined
- @server.listen @port, @host, __callback
-
-#
-# The main http_beh wrapping node.js 'http' functionality
-#
-exports.http_beh = anode.beh
-
- 'cust, #createServer' : ->
-
- __create = @create
- __cust = @cust
- __send = @send
-
- server = nodehttp.createServer()
-
- server_actor = @create httpServer_beh server, @cust
-
- server.on 'request', ( req, res ) ->
-
- request_actor = __create request_beh __cust, req
- response_actor = __create response_beh res
-
- # all request binding need to happen here before nextTick
- req.on 'data', ( chunk ) ->
- __send( req, '#data', chunk ).to request_actor
- req.on 'end', ->
- __send( req, '#end' ).to request_actor
- req.on 'close', ->
- __send( req, '#close' ).to request_actor
-
- __send( server_actor, '#request', request_actor, response_actor ).to __cust
-
- server.on 'connection', ( socket ) ->
- # TODO review what should happen here
- __send( server_actor, '#socket', socket ).to __cust
-
- server.on 'close', ->
- # TODO
-
- server.on 'checkContinue', ( req, res ) ->
- # TODO
-
- server.on 'upgrade', ( request, socket, head ) ->
- # TODO
-
- server.on 'clientError', ( exception ) ->
- # TODO
-
- @send( @, '#created', server_actor ).to @cust
+ @send( @, '#end' ).to @cust
+
+ 'cust, #setNoDelay, noDelay' : ->
+
+ @request.setNoDelay @noDelay
+ if @cust # ack requested
+ @send( @, '#setNoDelay' ).to @cust
+
+ 'cust, #setSocketKeepAlive, enable, [initialDelay]' : ->
+
+ @request.setSocketKeepAlive @enable, @initialDelay
+ if @cust # ack requested
+ @send( @, '#setSocketKeepAlive' ).to @cust
+
+ 'cust, #setTimeout, timeout' : ->
+
+ @request.setTimeout @timeout
+ if @cust # ack requested
+ @send( @, '#setTimeout' ).to @cust
+
+ 'cust, #write, chunk, [encoding]' : ->
+
+ @request.write chunk, @encoding
+ if @cust # ack requested
+ @send( @, '#write' ).to @cust
+
+#
+# Anode wrapper around the http.clientResponse
+#
+clientResponse_beh = anode.beh '_cust', 'response'
+
+ 'cust, #headers' : ->
+
+ @send( @, '#headers', @response.headers ).to @cust
+
+ 'cust, #httpVersion' : ->
+
+ @send( @, '#httpVersion', @response.httpVersion ).to @cust
+
+ 'cust, #pause' : ->
+
+ @response.pause()
+ if @cust # ack requested
+ @send( @, '#pause' ).to @cust
+
+ 'cust, #resume' : ->
+
+ @response.resume()
+ if @cust # ack requested
+ @send( @, '#resume' ).to @cust
+
+ 'cust, #setEncoding, [encoding]' : ->
+
+ @response.setEncoding( @encoding )
+ if @cust # ack requested
+ @send( @, '#setEncoding' ).to @cust
+
+ 'cust, #statusCode' : ->
+
+ @send( @, '#statusCode', @response.statusCode ).to @cust
+
+ 'cust, #trailers' : ->
+
+ @send( @, '#trailers', @response.trailers ).to @cust
+
+ '$response, #close, err' : ->
+
+ @send( @, '#close', @err ).to @_cust
+
+ '$response, #data, chunk' : ->
+
+ @send( @, '#data', @chunk ).to @_cust
+
+ '$response, #end' : ->
+
+ @send( @, '#end' ).to @_cust
+
+#
+# Initialized http server beh wrapped around a created server
+#
+initializedServer_beh = anode.beh 'server',
+
+ # stops the server from accepting new connections
+ 'cust, #close' : ->
+ @server.close()
+ if @cust # ack requested
+ @send( @, '#close' ).to @cust
#
# Actor wrapper around the request object that attaches the actor identity
# with each message for distinction between messages from different
# simultaneous requests
#
-request_beh = anode.beh 'cust', 'req'
+request_beh = anode.beh '_cust', 'req'
- '$req, #data, chunk' : ->
-
- @send( @, '#data', @chunk ).to @cust
-
- '$req, #end' : ->
+ 'cust, #connection' : ->
- @send( @, '#end' ).to @cust
+ # TODO: implement net.Socket as actor
+ @send( @, '#connection', @req.connection ).to @cust
- '$req, #close' : ->
+ # headers
+ 'cust, #headers' : ->
- @send( @, '#close' ).to @cust
+ @send( @, '#headers', @req.headers ).to @cust
- 'cust, #method' : ->
+ 'cust, #httpVersion' : ->
+ @send( @, '#httpVersion', @req.httpVersion ).to @cust
+
+ # the request method as a string
+ 'cust, #method' : ->
+
@send( @, '#method', @req.method ).to @cust
+ # pauses request from emitting events
+ 'cust, #pause' : ->
+
+ @req.pause()
+ if @cust # ack requested
+ @send( @, '#pause' ).to @cust
+
+ 'cust, #resume' : ->
+
+ @req.resume()
+ if @cust # ack requested
+ @send( @, '#resume' ).to @cust
+
+ 'cust, #setEncoding, encoding' : ->
+
+ @req.setEncoding @encoding
+ if @cust # ack requested
+ @send( @, '#setEncoding' ).to @cust
+
+ # trailers
+ 'cust, #trailers' : ->
+
+ @send( @, '#trailers', @req.trailers ).to @cust
+
+ # requested URL string
+ 'cust, #url' : ->
+
+ @send( @, '#url', @req.url ).to @_cust
+
+ '$req, #close' : ->
+
+ @send( @, '#close' ).to @_cust
+
+ '$req, #data, chunk' : ->
+
+ @send( @, '#data', @chunk ).to @_cust
+
+ '$req, #end' : ->
+
+ @send( @, '#end' ).to @_cust
+
#
-# What can I say, it's a wrapper for response object
+# An actor wrapper for the response object
#
response_beh = anode.beh 'res'
'cust, #addTrailers, headers' : ->
-
+
@res.addTrailers @headers
if @cust # ack requested
@send( @, '#addTrailers' ).to @cust
-
+
'cust, #end, [data], [encoding]' : ->
-
+
@res.end @data, @encoding
if @cust # ack requested
@send( @, '#end' ).to @cust
-
+
'cust, #getHeader, name' : ->
-
+
@send( @, '#header', @res.getHeader @name ).to @cust
-
+
'cust, #removeHeader, name' : ->
-
+
@res.removeHeader @name
if @cust # ack requested
@send( @, '#removeHeader' ). to @cust
'cust, #setHeader, name, value' : ->
-
+
@res.setHeader @name, @value
if @cust # ack requested
@send( @, '#setHeader' ).to @cust
'cust, #statusCode, statusCode' : ->
-
+
@res.statusCode @statusCode
if @cust # ack requested
@send( @, '#statusCode' ).to @cust
-
+
'cust, #write, chunk, [encoding]' : ->
-
+
@res.write @chunk, @encoding
if @cust # ack requested
@send( @, '#write' ).to @cust
'cust, #writeContinue' : ->
-
+
@res.writeContinue()
if @cust # ack requested
@send( @, '#writeContinue' ).to @cust
-
+
'cust, #writeHead, statusCode, [reasonPhrase], [headers]' : ->
-
+
@res.writeHead @statusCode, @reasonPhrase, @headers
if @cust # ack requested
- @send( @, '#writeHead' ).to @cust
+ @send( @, '#writeHead' ).to @cust
+
+#
+# An http client beh that will execute client requests
+#
+client_beh = anode.beh
+
+ # get convenience method, will create a request, set method to 'GET'
+ # and automatically send '#end' message to request actor
+ 'cust, #get, options' : ->
+
+ # set method to GET
+ @options.method = 'GET'
+
+ # local aliases for use inside closures
+ __httpClient = @
+ __cust = @cust
+ __send = @send
+
+ clientRequest = nodehttp.request @options
+
+ request_actor = @create clientRequest_beh clientRequest
+
+ # all clientRequest event bindings need to happen before nextTick
+
+ clientRequest.on 'response', ( response ) ->
+
+ response_actor = __create clientResponse_beh __cust, response
+
+ response.on 'data', ( chunk ) ->
+ __send( response, '#data', chunk ).to response_actor
+ response.on 'end', ->
+ __send( response, '#end' ).to response_actor
+ response.on 'close', ( err ) ->
+ __send( response, '#close', err ).to response_actor
+
+ __send( __httpClient, '#response', response_actor ).to __cust
+
+ clientRequest.on 'socket', ( socket ) ->
+ # TODO
+
+ clientRequest.on 'upgrade', ( response, socket, head ) ->
+ # TODO
+
+ clientRequest.on 'continue', ->
+ __send( __httpClient, '#continue' ).to __cust
+
+ # create 'callback' ack actor to execute once the 'get' request has been
+ # sent, it matches this specific request_actor instance via $_request
+ if @cust # ack requested
+ ack = @create anode.beh( '_request'
+
+ '$_request, #end' : ->
+
+ __send( __httpClient, '#get', @_request ).to __cust
+
+ )( request_actor )
+
+ ack = if ack then ack else undefined
+
+ # send '#end' to request actor, executing the request, but give it
+ # 'ack' actor ( if ack requested ) to listen for acknowledgment once '#end'
+ # is executed, at which point, 'ack' will send an acknowledgment to the
+ # customer of original '#get' request
+ @send( ack, '#end' ).to request_actor
+
+ # creates the request client
+ 'cust, #request, options' : ->
+
+ # local aliases for use inside closures
+ __httpClient = @
+ __cust = @cust
+ __send = @send
+
+ clientRequest = nodehttp.request @options
+
+ request_actor = @create clientRequest_beh clientRequest
+
+ # all clientRequest event bindings need to happen before nextTick
+
+ clientRequest.on 'response', ( response ) ->
+
+ response_actor = __create clientResponse_beh __cust, response
+
+ response.on 'data', ( chunk ) ->
+ __send( response, '#data', chunk ).to response_actor
+ response.on 'end', ->
+ __send( response, '#end' ).to response_actor
+ response.on 'close', ( err ) ->
+ __send( response, '#close', err ).to response_actor
+
+ __send( __httpClient, '#response', response_actor ).to __cust
+
+ clientRequest.on 'socket', ( socket ) ->
+ # TODO
+
+ clientRequest.on 'upgrade', ( response, socket, head ) ->
+ # TODO
+
+ clientRequest.on 'continue', ->
+ __send( __httpClient, '#continue' ).to __cust
+
+ @send( @, '#request', request_actor ).to @cust
+
+#
+# An uninitialized http server beh that will lazy-initialize the server
+# and then become initialized http server beh
+#
+uninitializedServer_beh = anode.beh
+
+ # creates the server and attempts to listen on given port and host
+ 'cust, #listen, port, [host]' : ->
+
+ # local aliases for use inside closures
+ __create = @create
+ __cust = @cust
+ __host = @host
+ __http = @
+ __port = @port
+ __send = @send
+
+ server = nodehttp.createServer()
+
+ # all server event bindings need to happen before nextTick
+
+ # on 'request'
+ server.on 'request', ( req, res ) ->
+
+ request_actor = __create request_beh __cust, req
+ response_actor = __create response_beh res
+
+ # all request binding needs to happen here before nextTick
+ req.on 'data', ( chunk ) ->
+ __send( req, '#data', chunk ).to request_actor
+ req.on 'end', ->
+ __send( req, '#end' ).to request_actor
+ req.on 'close', ->
+ __send( req, '#close' ).to request_actor
+
+ __send( __http, '#request', request_actor, response_actor ).to __cust
+
+ # on 'connection'
+ server.on 'connection', ( socket ) ->
+ # TODO
+
+ # on 'close'
+ server.on 'close', ->
+ # TODO
+
+ # on 'checkContinue'
+ server.on 'checkContinue', ( req, res ) ->
+ # TODO
+
+ # on 'upgrade'
+ server.on 'upgrade', ( request, socket, head ) ->
+ # TODO
+
+ # on clientError
+ server.on 'clientError', ( exception ) ->
+ # TODO
+
+ # start listening
+ if @cust # ack requested
+ __callback = ->
+ __send( __http, '#listen' ).to __cust
+
+ @host = if @host then @host else undefined
+ __callback = if __callback then __callback else undefined
+
+ server.listen @port, @host, __callback
+
+ @become initializedServer_beh server
+
+#
+# The request behavior for making http requests
+#
+exports.client_beh = client_beh
+
+#
+# The server behavior for serving http requests
+#
+exports.server_beh = uninitializedServer_beh

0 comments on commit 6c0aa94

Please sign in to comment.