actionhero

Actionhero is a realtime multi-transport nodejs API Server with integrated cluster capabilities and delayed tasks

APACHE-2.0 License

Downloads
14.5K
Stars
2.4K
Committers
127
actionhero - v7.5.1: connection.sendFile()

Published by evantahler almost 11 years ago

Sending files from Actions

You can send files from within actions using connection.sendFile(). Here's an example:

connection.rawConnection.responseHttpCode = 404; 
connection.sendFile('404.html');
next(connection, false);

Note that you can optionally modify responseCodes (for HTTP clients only). Be sure to set toRender = false in the callback, as you have already sent data to the client, and probably don't want to do so again on a file request.

actionhero - v7.5.0: actionHeroClient

Published by evantahler almost 11 years ago

actionHeroClient

Websocket clients can now request files

  • client.file('path/to/file', callback)
  • data in the callback is of the form:
{
  content: "<h1>ActionHero</h1>\nI am a flat file being served to you via the API from ./public/simple.html<br />",
  context: "response",
  error: null,
  length: 101,
  messageCount: 3,
  mime: "text/html"
}

http actions

  • The "actionHeroClient" client-side library can now be used to make http actions when not connected as a websocket (and websocket clients will fall back to http actions when disconnected)
var client = new actionHeroClient();
client.action('cacheTest', {key: 'k', value: 'v'}, function(err, data){
   // do stuff
}); 

client events

  • The client has been modified to emit events (like on the server side)
  • This replaces the client.events hash. Do not use it any more.
  • Logging has been removed from the client in favor of events
    • You can use client.on('message') to capture every message from the server
client = new actionHeroClient;

client.on('connected',    function(){ console.log('connected!') })
client.on('disconnected', function(){ console.log('disconnected :(') })

client.on('message',      function(message){ console.log(message) })

client.on('alert',        function(message){ alert(message) }) 
client.on('api',          function(message){ alert(message) })

client.on('welcome',      function(message){ appendMessage(message); })
client.on('say',          function(message){ appendMessage(message); })

Misc

  • The javascript files have been renamed to actionHeroClient.js
    • <script type="text/javascript" src="/public/javascript/actionHeroClient.js"></script>
    • <script type="text/javascript" src="/public/javascript/actionHeroClient.min.js"></script>

Misc

actionhero - v7.4.2: package upgrage

Published by evantahler almost 11 years ago

  • upgrade a number of dependancies
  • default new projects with cookies valid on the whole domain
actionhero - v7.4.1: fix and test long-lasting specHelper connections

Published by evantahler almost 11 years ago

  • correct a bug in specHelper so that spec connections can be reused between tests
  • remove actionHero info in favor of a simple 'echo' message to be more compatible across all operating systems on install.
actionhero - v7.4.0: specHelper and testing advice

Published by evantahler almost 11 years ago

Testing

actionHero provides test helpers so that you may try your actions and tasks within a headless environment. We do this by including a specHelper initializer which creates a server, testServer when running within the test environment. Via the testServer, you can easily call actions or tasks without making a real request.

We have chosen mocha as our test framework and should as our assertion tool which are included as dependancies within all new projects. You do not need to use these testing tools, but an example will be provided which makes use of them.

You also don't need to use these test helpers, and you may want to make a 'real' http or websocket request to test something specific. If this is the case, you can check out how actionHero tests its own servers for examples.

A new wiki page about testing has been created which contains much of this information

Test Methods

new api.specHelper.connection()

  • generate a new connection object for the testServer
  • this connection can run actions, chat, etc.
  • connection.messages will contain all messages the connection has been sent (welcome messages, action responses, say messages, etc)

api.specHelper.runAction(actionName, input, callback)

  • use this method to run an action
  • input can be either a api.specHelper.connection object, or simply a hash of params, IE: {key: 'value'}
  • the callback returns message and connection.
  • example use:
api.specHelper.runAction('cacheTest', {key: 'key', value: 'value'}, function(message, connection){
  // message is the normal API response;
  // connection is a new connection object
})

api.specHelper.getStaticFile(file, callback)

  • request a file in /public from the server
  • the callback returns message and connection where message is a hash:
var message = {
  error    : error,  // null if everything is OK
  content  : (string),  // string representation of the file's body
  mime     : mime,  // file mime
  length   : length  // bytes
}

api.specHelper.runTask(taskName, params, callback)

  • callback may or may not return anything depending on your task's makeup

Suggested Test Layout

process.env.NODE_ENV = 'test';

var should = require('should');
var actionHeroPrototype = require("actionHero").actionHeroPrototype;
var actionHero = new actionHeroPrototype();
var api;

describe('Action: Random Number', function(){

  before(function(done){
    actionHero.start(function(err, a){
      api = a;
      done();
    })
  });

  after(function(done){
    actionHero.stop(function(err){
      done();
    });
  });

  var firstNumber = null;
  it('generates random numbers', function(done){
    api.specHelper.runAction('randomNumber', function(response, connection){
      response.randomNumber.should.be.a.Number;
      response.randomNumber.should.be.within(0,1);
      firstNumber = response.randomNumber;
      done();
    });
  });

  it('is unique / random', function(done){
    api.specHelper.runAction('randomNumber', function(response, connection){
      response.randomNumber.should.be.a.Number;
      response.randomNumber.should.not.equal(firstNumber);
      done();
    });
  });

});

Notes

  • be sure to run your tests in the test environment, setting the shell's env with NODE_ENV=test. You can alternatively set this explicitly in your tests with process.env.NODE_ENV = 'test'

Misc

  • removed grunt+mocha helpers to return to a cleaner npm test using only mocha. This makes the test framework easier to understand
  • in the actionProcessor and cache, we have changed from process.nextTick to setImmediate in an attempt to throttle incoming requests under heavy load
  • misc cleanup
actionhero - v7.3.1: Bin Update

Published by evantahler almost 11 years ago

simply remove the explicit call to 'node' in package.json's scripts. This should help with cross-platform compatibility.

actionhero - v7.3.0: Grunt

Published by evantahler almost 11 years ago

actionHero now uses grunt as its script-runner rather than Jake. We made the switch due to Grunt's overwhelming popularity in the node/js community so that it would be easier for new developers to feel comfortable with the actionHero ecosystem. Grunt and Jake serve a similar purpose (with similar functionality) within actionHero to run tests and provide a CLI for various commands (saving and loading the cache, enqeuing periodic tasks, etc).

A huge shoutout to Github user @nullivex for doing most of the work in this pull request. He's added Grunt's linting tools and other helpers to the project which will make actionHero even better.

actionhero - v7.2.0: cache to use a redis key per object

Published by evantahler almost 11 years ago

Move api.cache storage to individual redis keys rather than keeping all of the cache objects in a hash.

NONE OF YOU PREVIOUSLY CACHED OBJECTS WILL WORK WITH THIS NEW VERSION. THEY WILL BE LOST

  • we can remove the cache sweeper and use redis' built in expire commands
  • allows for redis shading for large key spaces
  • remove stats around cahce
  • api.config.general.cachePrefix is now configurable
  • api.cache.clear can be used to clear all items in the cache
  • api.cache.dumpWrite and api.cache.dumpRead can be used to export and import the cache
    • jake commands now wrap these methods
actionhero - v7.1.1: Package Update

Published by evantahler almost 11 years ago

Time for a dependent package update!

Updates

  • "redis": "0.9.x"
  • "optimist": "0.6.x"
  • "mocha": "1.x.x",
  • "should": "2.x.x"
  • "uglify-js": "2.x.x"

Removals

  • validator

Misc

  • fix a parsing issue with ipv6 forwarders reporting ipv4 addresses in ipv6 style
actionhero - v7.1.0: Chat Framework, env-based config, and Cleanup

Published by evantahler almost 11 years ago

New Chat Authentication Pattern

By popular request, we are changing the chat sub-system to provide more fine-grain control over access to chat rooms to API developers.

Philosophical Changes

  • chat rooms now must be created before anyone can join them
    • api.chatRoom.add can be used for this, or you can append the array api.configData.general.startingChatRooms
    • you can remove chatRooms with api.chatRoom.del
  • the authentication pattern for chat rooms is now about access rather than per message. All members in all rooms will see all messages
    • access is still defined by looking at attributes of a connection, but only when the connection first joins a room. This means that if a connection's attributes change, it will not be kicked out of the room
    • api.chatRoom.setAuthenticationPatern is used to define a room's access rules. 2 examples:
    • api.chatRoom.setAuthenticationPatern('myRoom', 'type', 'websocket') would only allow websocket clients in
    • api.chatRoom.setAuthenticationPatern('myRoom', 'auteneticated', true) would only allow clients in which have previously been modified by connection.auteneticated = true; connection._original_connection.authenticated = true; probably in an action or middleware.
  • connections start without being in any chat room at all and need to explicitly join a room
  • connections can leave the room they are in with the new verb roomLeave (socket and websocket)
  • connections may still listenToRoom and silenceRoom as they had before, but listening to a room will follow the same authentication logic as joining a room (listen can now return an error)

API Changes

api.chatRoom.add(room, callback)

  • callback will return 1 if you created the room, 0 if it already existed

api.chatRoom.del(room, callback)

  • callback is empty

api.chatRoom.exists(room, callback)

  • callback returns (error, found); found is a boolean

api.chatRoom.setAuthenticationPatern(room, key, value, callback)

  • callback returns (error)

api.chatRoom.roomStatus(room, callback)

  • callback return (error, data)
  • data is of the form:
{
  room: "myRoom",
  membersCount: 2,
  members: {
    aaa: {id: "aaa", joinedAt: 123456789 },
    bbb: {id: "bbb", joinedAt: 123456789 },
  }
}

api.chatRoom.addMember(connection, room, callback)

  • callback is of the form (error, wasAdded)

api.chatRoom.removeMember(connection, callback)

  • callback is of the form (error, wasRemoved)

env-based config

This allows actionHero developers to crete environment-specific modifications to a general config.js. This mirrors the config pattern in Rails and Express.

  • change api.configData to api.config
  • move config.js into a config directory, rename it to config/config.js
  • allow for environment-specific overrides to confg.js
    • use NODE_ENV to choose environment
    • allow for ENV specific overrides via files (config/development.js, config/production.js, etc)
  • update tests
  • update generators
    • provide a slug production.json

General

  • Change the default route for HTTP clients in config.js to "file" rather than "api" so folks who boot the server will have a nicer experience. This also helps out demo.actionherojs.com

Fixes

A special call out is earned by @Spudz76 who took the time to go though all of acitonHero's source and clean it up for typos, formatting, readability, and other js-lint type bugs.

  • Detecting connection.remoteIp and connection.remotePort should now work behind most proxies, including those which append the port to the forwarded IP address
  • Detecting connection.remoteIp and connection.remotePort should now work much better for IPv6 addresses, and also work behind proxies (thanks @yshaul, @genexp @macrauder, and @nullivex)
  • Fixed a bug where api.utils.randomString would sometimes return 'undefined' within the random string (@sylock)
  • General clean up to wiki documentation (@nullivex)
  • Fix a bug which would potentially cause a domain-caught error in an action to still throw in node v8.x.x
actionhero - v7.0.0: Resque

Published by evantahler almost 11 years ago

actionHero version 7.0.0 changes the task sub-sysem to be compatible with resque.

Why?

  • actionHero's task system was perviously ok, but was custom designed and integrating with other systems/ecosystems was hard. Resque's ecosystem is the largest redis-based task ecosystem so it was a natural fit
  • it is now simple to implement priority queues for your jobs
  • you can now work jobs from Rails and other projects which also share a resque-base backend (or vice-versa)
  • you can now make use of great UIs, monitoring tools, etc which already exist in the resque ecosystem to manage your actionHero queues.

node-resque

  • node-resque was created as a stand-alone resque package to allow fo this upgrade.
  • This package contains node js implementations of:
    • resque
    • resque scheduler
    • resque-lock (plugin)
    • worker-lock (plugin)
  • a simple invocation of the ruby resque UI is included in this project
    • gem install bundler
    • bundle install
    • rackup

Warnings

  • NO EXISTING JOBS WILL BE COMPATIBLE FROM EARLIER ACTIONHERO VERSIONS. Your enqueued jobs will be lost

Major Notes

  • actionHero now uses resque as its task backend
  • the philosophy of any and all tasks has been sacrificed in order to move to resque. All tasks are now any tasks, one and only one server will process that job
  • recurring tasks are still allowed
  • tasks now have a default queue which they will be enqueued into. You can override this at enqueue time
  • tasks may now also opt into specific resque plugins (like worker-lock, which will ensure that only one instance of a job is being processed system-wide at any given time)
  • workers are now defined in config.js by queue name(s) to allow for more detailed control. You can still run more than 1 worker per actionHero process
  • If you wish to use delayed or recurrent tasks, you need to run at least once instance of the scheduler daemon within actionHero. This can be toggled on in config.js. You may run more than one scheduler in your cluster .
  • when actionHero is signaled to shut down, workers will be allowed to complete what they are currently working on (up to 30 seconds) before the server fully exits
  • with the removal of all tasks, there is no longer a reason for actionHero to keep an active watch on other peers in the cluster. The redis initializer has been greatly simplified. There is no longer an explicit notion of peers, and the related methods (api.redis.checkForDroppedPeers and api.redis.ping) are removed
  • a resque initializer has been added, and the task and taskProcessor initializers have been removed
  • periodic tasks will now automatically be enqueued at boot
  • jake helpers for tasks have been added
    • jake actionHero:tasks:enqueueAllPeriodicTasks # This will enqueue all periodic tasks (could lead to duplicates)
    • jake actionHero:tasks:enqueuePeriodicTask # This will enqueue a periodic task
    • jake actionHero:tasks:stopPeriodicTask # This will remove an enqueued periodic task
  • significant changes to api.tasks. The complete set of public methods is now:
    • api.tasks.enqueue(taskName, params, queue, callback)
    • api.tasks.enqueueAt(timestamp, taskName, params, queue, callback)
    • api.tasks.enqueueIn(time, taskName, params, queue, callback)
    • api.tasks.del(queue, taskName, args, count, callback)
    • api.tasks.delDelayed(queue, taskName, args, callback)
    • api.tasks.enqueueRecurrentJob(taskName, callback)
    • api.tasks.stopRecurrentJob(taskName, callback)
    • api.tasks.details(callback)

Minor Notes

  • When inspecting the status of your queue, the new method api.tasks.details should be used. api.tasks.getAllTasks and api.tasks.getWorkerStatuses are removed
  • the task generator has been updated to reflect the above changes
  • a number of config.js changes including
    • a new tasks section for the resque workers
    • in the redis config settings, DB has been renamed database

Other Changes

  • support HTTP verbs HEAD, OPTIONS and TRACE in the web server
  • the validators for actions and tasks will no longer process.exit() when a warning is thrown (ie: a missing description). These errors will be logged and the action or task will not be loaded
  • the sweeper to delete expired api.cache objects now will only run once a minute (previously 10 seconds)
  • minor updates to the developerMode tools, including that the server will now reload on an initializer update
  • actionHero generate will now also include the minimized version of actionHeroWebsocket.js in the public directory
  • x-forwarded-for detection for remote IP addresses has been improved (thanks @yshaul)
  • the websocket server is now enabled by default in config.js
  • generateAction will include the variable toDocument allowing specific actions to be hidden from the documentation API response
actionhero - v6.4.4: buffered stats (and fixes)

Published by evantahler almost 11 years ago

buffered stats

Having many calls to redis on each connection (to increment stats) significantly slowed down actionHero, even in an async world. We have changed how recording stats works to be on a timed buffer. We will store stat deltas locally and then write those deltas to redis at a fixed, configurable interval.

In this update, we remove the notion of 'global' and 'local' stats in favor of configurable stats storage hashes in redis. You can still emulate the behavior of 'global' and 'local' stats by supplying 2 backends of the form:

configData.stats = {
  witeFrequency: 1000,
  keys: [    
    'actionHero:stats',
    'actionHero:stats:' + api.id
  ], 

Method Updates:

  • api.stats.increment = function(key, count) ( no longer takes a callback )
  • api.stats.get = function(key, collection, next) (can optionally provide which backend hash to lookup from, collection)
  • api.stats.getAll = function(collections, next) (can optionally provide which backend(s) hash to lookup from, collections)

api.stats.set is removed

In simple tests, this speeds up the performance of actions about ~2x

general improvements

  • if your action or task file only contains 1 action, you don't need to name the exports statement "action" any more. exports.action and exports.myTask will each work.
  • generators (generateAction, generateTask, etc) will now attempt to check with config.js to inspect your project's paths so files are generated into the proper directory.

bug fixes

  • v6.4.3 introduced a bug where actions and tasks in subdirectories would fail to load and crash the application. This is now solved
  • fixed a bug where actionHero would attempt to load non-javascript files that were kept in the project if they were of the form myAction.js.coffee. Only .js files should be loaded in
  • fixed many typos
actionhero - v6.4.3: Move mime modification to actions

Published by evantahler almost 11 years ago

This small release changes the behavior of mime modification released yesterday. 2 problems arose:

  • mime-modification should be be action-specific and not global
  • removing extensions from params caused problems for legitimate use cases where there was a "." in provided data (perhaps sending URLs or sentences)
  • api.configData.servers.web.matchExtensionMime is removed as an option from config.js
  • it is replaced with the option action.matchExtensionMimeType, to be set per action you wish to modify response mime information.

We have stopped removing extensions from posted params. Param sanitization can be done in a middleware or within the action. Behavior is now:

  • { path: "/images/:imageName", action: "renderImage" }
    • site.com/images/profile.jpg => connection.params.imageName = "profile.jpg" and connection.extension = "jpg"
  • { path: "/ping/:url", action: "pingCheck" }
    • site.com/ping/google.com => connection.params.url = "google.com", and connection.extension = "com"
actionhero - v6.4.2: file extensions and Faye fixes

Published by evantahler almost 11 years ago

  • Fix an issue with faye/websockets where clients could constantly be reconnecting after sending a request for an action to be filled
    • client logic for handling disconnections improved to rely more on Faye's internal status
    • reconnection logic improved to not change client.id
    • there is an update to the client library actionHeroWebSocket.js. Be sure to update your projects.
  • File Extension parsing for web (http) clients
    • treat extensions as a special type of data, and append it to the connections as connection.extension. You can access this in the action
    • (optionally) auto-set the mime type based on the extension (api.configData.servers.web.matchExtensionMime)
    • ensure file extensions are ignored when building params from routes
  • Remove XML output as an option in actionHero (previously ?OutputType)
    • OutputType removed as a reserved param
  • Update the default http headers in config.js for new projects to automatically allow cross-origin requests
  • Allow for the returning of metadata to web (http) clients to be optional
    • 2 new options added in config.js to enable this:
      • api.configData.servers.web. serverInformation
      • api.configData.servers.web. requestorInformation
  • connection.error can now optionally be a hash or a string. This type will be maintained in the presentation back to the client for all server types

Thanks to @jfgodoy for helping out with code in this release.

actionhero - v6.4.1: Bug fixes

Published by evantahler about 11 years ago

  • Fixes a bug so that you can now load more than one initializer in your projects
  • Update defaults for pidfile locations within project
  • Update help.txt to explain environment variable options for project name and config.
actionhero - v6.4.0: Custom Paths

Published by evantahler about 11 years ago

This release creates the option to break away from the traditional actionHero project structure by enabling custom paths for your project. Don't want your actions to live in __dirname + "/actions"? No problem! You can update api.configData.general.paths.

The default project layout doesn't change, and remains:

paths: {
    "action":      __dirname + "/actions",
    "task":        __dirname + "/tasks",
    "public":      __dirname + "/public",
    "pids":        __dirname + "/pids",
    "log":         __dirname + "/log",
    "server":      __dirname + "/servers",
    "initializer": __dirname + "/initializers",
}

This replaces flatFileDirectory and pidFileDirectory as configuration options.

note: You will need to add the new paths section to your config.json for your older actionHero projects to boot.

We are now using faye version 2.0.x and faye-redis version 0.2.x

This release also fixes some spelling mistakes (thanks Matt) and an issue booting the cluster on windows.

actionhero - v6.3.4: Path Overrides

Published by evantahler about 11 years ago

This tiny release allows you to manually set PROJECT_ROOT as an environment variable to force actionHero to run from a specific directory rather than proces.cwd(). This is useful when deploying actionHero applications on a server where symlinks will change under a running process.

actionhero - v6.3.3: Cluster and Websocket Fixes

Published by evantahler about 11 years ago

This release is a collection of minor fixes:

  • fix path for help documentation when running actionHero help
  • fix typo in generators
  • websocket clients will have thier id set by the server on connection, and not rely on uuid generation.
  • websocket
  • be more explicit in not propagating child's streams to the parent when running actionHero startCuster in dameon mode. This fixes some bugs where you cluster-workers would fail to boot in certain OSs
actionhero - v6.3.2: websocket and parsing fixes along with connection middleware

Published by evantahler about 11 years ago

As this release contains security updates, it is recommended that all actionHero users upgrade ASAP.


Features:

  • connection middleware
    • like middleware with actions, you can create callbacks to be invoked when any connection is created or destroyed.
    • unlike action middleware, connection middleware is non-blocking and connection logic will continue as normal regardless of what you do in this type of middleware.
    • api.connections.createCallback is an array of functions to call on a new connection and api.connections.destroyCallbacks will be called on destruction. Keep in mind that some connections persist (webSocket, socket) and some only exist for the duration of a single request. You will likely want to inspect connection.type in this middleware.

Bug Fixes:

  • actionHero will not properly parse form data for HTTP methods other than POST, including DELETE and PUT. GET will still properly ignore form data
  • when websocket clients (faye) degrade to long-polling as their transport and connect to another actionHero node in a cluster, many communication issues have been fixed. Previously clients in this state would timeout and (eventually) reconnect.
  • an issue where it was possible for the websocket client (faye) to drop the count of how many callbacks it had pending and not return (for actions-in-progress) has been fixed.

Security:

  • now only the params defined in an action (and a small set of global params) will be present in connection.params. Previously, any param allowed for any action would have made it into your action's run method.
  • internal faye channels actionHero uses for server-to-server communication are now protected from messages from a client. We will use api.configData.general.serverToken as a key.
actionhero - v6.3.1: node 0.8.x fix and log levles

Published by evantahler about 11 years ago

This release contains a small fix to bring back support for node v0.8.x. This also introduces the logLevel option on actions to set how verbose the server is when reporting the call of that action