Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
bogart-edge
Advanced tools
A blazing fast rapid application development web framework using JSGI for node.
Bogart can be installed via npm.
npm install bogart
Alternatively, clone the git repository.
git clone git://github.com/nrstott/bogart.git
Make a directory: mkdir hello-world
Create the following file:
var bogart = require('bogart');
var router = bogart.router();
router.get('/', function(req) {
return "hello world";
});
router.get('/:name', function(req) {
return 'hello '+req.params.name;
});
var app = bogart.app();
app.use(bogart.batteries({ secret: 'do-not-share' })); // A batteries included JSGI stack including streaming request body parsing, session, flash, and much more.
app.use(router); // Our router
app.start();
Start your app: node app.js
Visit it in a web browser at http://localhost:8080. Visit the route that says hello to you by name at http://localhost:8080/bob
If you can't run on 8080, change the app.start
call e.g. app.start(9090, '127.0.0.1')
Routing in Bogart is simple and intuitive. A route is an HTTP method paired with an URL matching pattern and a handler function.
var router = bogart.router();
router.get('/', function(req) {
return bogart.html('Hello World');
});
Routes are tested for matches in the order in which they are defined.
Route patterns are matched against URLs. They may include named parameters that will
be accessible via the params
object of the req
object passed to the route handler.
var router = bogart.router();
router.get('/hello/:name', function(req) {
var greeting = 'Hello '+req.params.name;
return bogart.html(greeting);
});
Route patterns support wildcards. Wildcards will match anything whereas regular named parameters
will not match beyond a path separator ("/"). Multiple wildcards may be used in a route pattern.
The values for the wildcard matches are placed in an Array
available from
the request params at req.params.splat
.
var router = bogart.router();
router.get('/hello/*', function(req) {
return bogart.html('Hello '+req.params.splat[0]);
});
In the above example, everything after /hello/
will be in req.params.splat[0].
A request to /hello/how/are/you
would have a splat
of [ 'how/are/you' ]
.
When a route pattern is not powerful enough, regular expressions may be used to specify which URLs are to be matched.
var router = bogart.router();
router.get(/\/posts?/, function(req) {
// Matches 'post' or 'posts'
return bogart.html('Regex Route');
});
Parameters are via regular expression groups in regular expression routes. The parameter values
are put in an Array
in req.params.splat
of the req
object passed to the route handler.
var router = bogart.router();
router.get(/hello-(.*)/, function(req) {
var name = req.params.splat[0];
return bogart.html('Hello '+name);
});
When no route pattern matches the request, a next
jsgi application to be called may
be specified either with the Router#notFound
method or via a parameter to the function
returned by bogart:router
. If both techniques are used, then the parameter overrides
the app specified by the Router#notFound
method.
Specified by calling the Router#notFound
method
var myRouter = bogart.myRouter();
router.get('/', index);
myRouter.notFound(function (req) {
return bogart.html('Not Found', { status: 404 });
});
Specified as a parameter to the router
var myRouter = bogart.router();
myRouter.get('/', index);
var app = bogart.app();
app.use(myRouter(function (req) {
return {
status: 404,
body: [ 'Not Found' ]
};
}))
To handle HTTP Verbs other than get
, post
, put
, and delete
, the route
method of
Router
can be invoked.
var router = bogart.router();
router.route('options', function (req) {
// Logic for handling the request goes here.
});
bogart.app
makes it easy to setup a middleware chain and start coding. Combined with
bogart.batteries
(See Below), you can setup a full-stack JSGI application in two lines of code.
var app = bogart.app();
app.use(bogart.batteries);
After adding bogart.batteries
, you will normally want to add a Router. This is also done
with app.use
. To start the application, use the start
method.
var app = bogart.app();
app.use(bogart.batteries);
var router = bogart.router();
// NOTE: Here you would normally add some routes.
app.use(router);
app.start();
The Bogart view engine is a tempalting language agnostic tool to resolve and render
templates. Bogart comes with a mustache
renderer included. Other renderers can
be added via the plugin infrastructure.
Example:
var viewEngine = bogart.viewEngine('mustache');
// Render `views/index.html` with values `{ name: 'Nathan Stott' }`
viewEngine.render('index.html', { locals: { name: 'Nathan Stott' } });
More information can be found in docs/view-engine.md.
Bogart includes helpers for creating JSGI Responses. The helpers, by convention, take a final parameter of an options object that allows the caller to override defaults. The options object is merged with the default JSGI Response values of the helper before the JSGI Response is returned.
Helper to create an HTTP 200 Success response with a Content-Type of application/json.
Sample Route:
var router = bogart.router();
router.get('/', function(req) {
return bogart.json({ framework: 'Bogart' });
});
This route yields the following JSGI Response:
{
status: 200,
headers: { 'Content-Type': 'application/json' },
body: [ '{ "framework": "Bogart" }' ]
}
Helper to send a JSON response with 'Access-Control-Allow-Origin', 'Access-Control-Allow-Methods', and 'Access-Control-Allow-Headers' headers to faciliate Cross-Origin Resource Sharing (CORS).
Sample Route:
var router = bogart.router();
router.get('/', function(req) {
return bogart.cors({ framework: 'Bogart' });
});
This route yields the following JSGI Response:
{
status: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE',
'Access-Control-Allow-Headers': 'x-requested-with,*'
},
body: [ '{ "framework": "Bogart" }' ]
}
Helper to create an HTTP 302 Temporary Redirect response.
Sample Route:
var router = bogart.router();
router.get('/', function(req) {
return bogart.redirect('/some/other/url');
});
This route yields the following JSGI Response:
{
status: 302,
headers: { 'Location': '/some/other/url' },
body: []
}
Helper to create an HTTP 500 Internal Server Error response.
Sample Route:
var router = bogart.router();
router.get('/', function(req) {
return bogart.error('<html>...</html>');
});
This route yields the following JSGI Response:
{
status: 500,
headers: { 'Content-Type': 'text/html' },
body: [ '<html>...</html>' ]
}
Helper to create an HTTP 304 Not Modified response.
Sample Route:
var router = bogart.router();
router.get('/', function(req) {
return bogart.notModified();
});
This route yields the following JSGI Response:
{
status: 304,
headers: {},
body: []
}
Helper to create an HTTP 200 Success response with a body streamed from the contents of a file. The Content-Type of the response is determined by the mime type of the file.
Sample Route:
var path = require('path');
var router = bogart.router();
router.get('/download/*', function(req) {
var filePath = path.join(__dirname, 'public', req.params.splat[0]);
return bogart.file(filePath);
});
This route yields the following JSGI Response:
{
status: 200,
headers: { 'Content-Type': '<mimetype of the file>' },
body: fileStream // <-- A file stream
}
Helper to create a response that proxies the response from a URL.
Sample Route:
var router = bogart.router();
router.get('/google', function(req) {
return bogart.proxy('http://www.google.com');
});
This route yields a JSGI Response that matches the response from the proxied URL.
Bogart includes the ResponseBuilder
helper to provide an imperative interface. While not
recommended as a goto style of programming in Bogart, there are times when buiding a
response imperatively makes for cleaner, better code. This is true especially
when working with callback based functions that cannot be wrapped by bogart.promisify
.
var router = bogart.router();
router.get('/', function(req) {
// Get a ResponseBuilder
var res = bogart.res();
doSomethingAsync(function(err, messageStr) {
res.setHeader('Content-Type', 'text/plain');
if (err) {
res.status(500);
res.send('Error');
return res.end(); // We use return to break out of the function, do not want to continue executing after res.end()
}
res.status(200);
res.send(messageStr);
res.end(); // End the Response. This is analagous to resolving a promise for a JSGI Response.
});
return res;
});
The session middleware can be included individually with
app.use(bogart.middleware.session({ secret: 'do-not-share' }))
or
by using batteries app.use(bogart.batteries({ secret: 'do-not-share' }))
which includes a default stack of middleware including session.
The secret
parameter is the encryption secret. Do not use a secret from an example
you find online, including in this document. Do not share your secret.
A session
function will be available on the request object passed to your route handlers. This
function follows the jQuery style of arity determining if it is getting or setting a key/value pair.
A call to session with one argument is a get to the value of the key referenced by the argument.
req.session('name'); // => value associated with 'name'
A call to session with two arguments is a set.
req.session('name', 'Nathan'); // sets the value of 'name' to 'Nathan'
A set of two routes that use session:
router.get('/:name', function(req) {
req.session('name', req.params.name);
return bogart.redirect('/');
});
router.get('/', function(req) {
return bogart.html('Hello ' +req.session('name'));
});
Visiting '/:name' ('/Nathan', '/Bob', etc...) will set a session key that will be displayed by the root route '/', after the redirect.
In the 'examples' directory of the cloned source, there are several examples of bogart applications.
The hello world example demonstrates a basic bogart applications. The application has a route, '/:name', that takes a name
as a parameter in the request URL. The application responds with 'hello '. It also has a route, '/', that responds with hello world
.
> cd examples
> node hello-world.js
Visit the application in your web browser at http://localhost:8080/Jim
This example demonstrates usage of the mustache templating engine and a mustache layout. A layout is a page designed to hold other templates to avoid duplication of content.
> cd examples/mustache-layout
> node app.js
Visit the application in a web browser at http://localhost:8080/
Bogart comes with a variety of JSGI middleware appliances. The batteries
appliance includes a recommended JSGI application chain for
normal usage.
Frameworks are better when they come batteries included. Bogart ships with the batteries, it's just up to you to put them in if you want to use them.
The batteries middleware creates the following JSGI chain where nextApp
is the JSGI appliance passed as the first parameter
to batteries
:
error -> validateResponse -> directory -> parted -> methodOverride
-> session -> flash -> bodyAdapter -> stringReturnAdapter -> nextApp
The static example demonstrates using bogarts Directory middleware to serve an image.
> cd examples/static-server
> node app.js
Visit the application in a web browser at http://localhost:8080/. You should see the image.
Translates rejected promises to a JSGI error response.
Provides Ruby-esque 'flash' variables, good only for the next request cycle. Flash session tracking and flash data is stored via encrypted cookies by default. These providers can be overriden with the Flash middleware options. An example flash data provider implementation with a Redis backend is available here: https://github.com/jdc0589/bogart-flash-redis
Checks for the _method parameter or X-HTTP-METHOD-OVERRIDE header to override the HTTP method.
Parses the body of any request with an "application/json" content-type. The value of request body is reassigned with the parsed value. Use the Parted middleware instead unless you have a specific reason not to as it contains a superset of this functionality and also performs streaming parsing.
Parses the body of any request with an "application/x-www-form-urlencoded" content-type. The value of request body is reassigned with the parsed value. Use the Parted middleware instead unless you have a specific reason not to as it contains a superset of this functionality and also performs streaming parsing.
A JSGI wrapper around the excellent Parted middleware for Connect. Parted is a streaming multipart, json, and urlencoded parser.
Standard flash middleware for bogart. Session id and data are stored in encrypted cookies by default, however this can be overridden with custom storage providers. A redis backed session data store is available here: https://github.com/jdc0589/bogart-session-redis
bogart.app()
to new bogart.Application()
.Promises provide a well-defined interface for interacting with an object that represents the result of an action that is performed asynchronously. Why does Bogart use Promises instead of Callbacks for an interface? Because it's prettier and easier to reason about!
In all seriousness, I don't want to get into large arguments about the merits of Promises. Node.JS uses callbacks for its API; however, user-land applications and frameworks are free to employ higher-level abstractions and Bogart uses Promises.
Promises may only be resolved one time. In the future, when success listeners are added to a promise that has already been resolved, the success listener will be invoked with the previously resolved value. Each success listener or error listener is invoked one time and one time only.
Promises are not EventEmitters. Many times when describing promises, other coders ask why not just use an EventEmitter. Promises have a different contract. The fact that promises are resolved or rejected only one time is powerful. EventEmitters have their place; however, they do not take the place of Promises and Promises do not take the place of EventEmitters.
function helloWorld() {
// Retrieve the q promise utility
var q = require('bogart').q;
// Create a deferred, a wrapper around a Promise.
var deferred = q.defer();
// Do something async
setTimeout(function() {
// Resolve the promise, this will cause success listeners to be invoked.
deferred.resolve('hello world');
}, 100);
// Return the promise that the deferred wraps to the client.
return deferred.promise;
}
// Consuming the helloWorld function
var p = helloWorld();
p.then(function(msg) {
// This function will be invoked on success
console.log(msg);
}, function(err) {
// This function will be invoked on error.
console.log(err);
});
then
Methodpromise.then(callback, errback, progress)
A promise will have a then
method which takes up to three parameters. The three parameters are all optional.
The first parameter, callback
, is executed if the Promise is successfully resolved. The second parameter, errback
, is
executed if the Promise is rejected. The third parameter, progress
, is used to provide intermediate feedback on the
asynchronous operation. This parameter is rarely used. Most promises do not report progress.
when
Functionbogart.q.when(promiseOrValue, callback, errback, progress)
The when
function in the bogart.q
namespace is helpful when you do not know if what you have is a value or a promise for a
value. The callback will be executed for success for a resoled promise or for the value passed if it is a value and not a promise.
// The following two lines are equivalent with the exception that the `when` can
// handle values that are not promises.
q.when(p, function() { console.log('Success'); });
p.then(function() { console.log('Success'); });
Promises can be 'bubbled'. The return value of a callback becomes the value of an external promise. The same is true of errbacks.
function bubble(p) {
return p.then(function() {
// Assume makePromise is a function that returns a promise for an asyncronous operation.
// The value of makePromise when resolved becomes the resolution of the `bubble` function as well.
return makePromise();
});
}
Bubbling errbacks is parallel to having a try/catch at a higher level handle errors at a lower level.
p.then(function(url) {
return request(url).then(function() {
throw 'error';
});
}, function(err) {
// Will handle the error that occurs in the callback of `request.then`.
console.log(err);
});
Node.JS uses a style of callback with the following signuare: function(err, result)
. Bogart includes a utility function,
bogart.promisify
, to adapt these Node.JS-style functions to return a Promise.
var fs = require('fs'); // Node File System Module
var bogart = require('bogart'); // Include Bogart
// A promise-based version of fs.readFile.
var readFile = bogart.promisify(fs.readFile);
readFile('test.txt').then(function(data) {
console.log(data);
});
NOTE: A Node.JS-style function which calls its callback multiple times is not compatible with promises.
A Promise may be resolved only once. Node.JS style functions that call their callback multiple times are
uncommon. The vast majority of Node.JS style functions can be safely translated using bogart.promisify
.
There are a lot of critiques of Promises. These usually come from programmers who have not used a proper Promise library.
Turst me, promises are 'A Good Thing'. Your code will be more composable, readable, and maintainable if you choose to use
Promises instead of Callbacks for your API. It is also quite easy to transalate Node.JS style callbacks into promises using
bogart.promisify
so working wtih callback based APIs is still simple.
JSGI stands for JavaScript Gateway Interface. It is an interface between web applications and web servers. It is similar to Ruby's Rack and Python's WSGI.
Bogart makes it easy to create JSGI Applications and Middleware. Bogart is a high-level wrapper around JSGI.
Valid JSGI responses are always valid returns from Bogart. Bogart, via its helpful Middleware, also allows you to return Node.JS Streams, Node.JS Buffers, and JavaScript Strings as responses that will be translated into valid JSGI responses.
Bogart also provides helper functions to make creating JSGI responses easier. Prime examples of this are bogart.file
which
returns a JSGI response that serves a file and bogart.proxy
which returns a response that proxies another URL.
npm install bogart-jade
Copyright (c) 2009 Nathan Stott <nathan.whiteboard-it.com>
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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
FAQs
Fast JSGI web framework taking inspiration from Sinatra
The npm package bogart-edge receives a total of 17 weekly downloads. As such, bogart-edge popularity was classified as not popular.
We found that bogart-edge demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.