Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 3b491d21bdee108dcf5251e026960695173ae341 @danmactough committed Nov 25, 2011
Showing with 262 additions and 0 deletions.
  1. +107 −0 README.markdown
  2. +24 −0 examples/module.js
  3. +7 −0 index.js
  4. +22 −0 package.json
  5. +102 −0 server.js
@@ -0,0 +1,107 @@
+# Parserproxy - A JSON-over-HTTP proxy for node-feedparser and node-opmlparser
+
+This module acts as a proxy that fetches an RSS/Atom/RDF or OPML url that you
+request, parses it -- using
+[node-feedparser](https://github.com/danmactough/node-feedparser) or
+[node-opmlparser](https://github.com/danmactough/node-opmlparser) -- and
+responds with the parsed JSON representation of the RSS/Atom/RDF or OPML you
+requested.
+
+This module was created so that I could easily transform what is frequently a
+time-intensive, blocking operation -- i.e., XML parsing -- into an I/O operation
+(in a separate process, maybe on another server), which means the parsing will
+no longer be blocking.
+
+## Requirements
+
+- [node-feedparser](https://github.com/danmactough/node-feedparser)
+- [node-opmlparser](https://github.com/danmactough/node-opmlparser)
+- [request](https://github.com/mikeal/request)
+
+## Installation
+
+Via npm:
+
+ npm install parserproxy
+
+Manually: (A fine idea if you're not going to use in programatically in your node.js program)
+
+ git clone git://github.com/danmactough/node-parserproxy.git parserproxy
+
+## Example
+
+### Manually
+
+ $ cd parserproxy
+ $ node server.js
+
+Then in your node app:
+
+ var request = require('request');
+ request({ uri : 'http://localhost:3030/parseFeed',
+ body : { url: 'http://cyber.law.harvard.edu/rss/examples/rss2sample.xml' },
+ json : true },
+ function (err, response, body){
+ if (!err && response.statusCode == 200) {
+ console.log('%s [%s]', body.meta.title || body.meta.xmlUrl, body.meta.link);
+ body.articles.forEach(function (article) {
+ console.log('%s - %s', article.pubDate, article.title || article.description.substring(0,50));
+ });
+ }
+ });
+
+### Programatically
+
+ var parserproxy = require('parserproxy')
+ , forever = require('forever')
+ , request = require('request');
+
+ forever.start(parserproxy).on('start', function (process, data){
+
+ // It takes a few milliseconds for the http server to spin up, so for the purposes of this
+ // example, we wrap the request in a setTimeout(). You DON'T need to do this in your code unless
+ // you are going to make one or more requests immediately upon start up.
+
+ setTimeout(function(){
+ request({ uri : 'http://localhost:3030/parseFeed',
+ body : { url: 'http://scripting.com/rss.xml' },
+ json : true },
+ function (err, response, body){
+ if (!err && response.statusCode == 200) {
+ console.log('%s [%s]', body.meta.title || body.meta.xmlUrl, body.meta.link);
+ body.articles.forEach(function (article) {
+ console.log('%s - %s', article.pubDate, article.title || article.description.substring(0,50));
+ });
+ }
+ });
+ }, 1000);
+ });
+
+## To-do
+
+Obviously, a pluggable memcache-like ability would be great. Pull requests always welcome. :-)
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2011 Dan MacTough <danmactough@gmail.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 OR COPYRIGHT HOLDERS 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.
@@ -0,0 +1,24 @@
+var parserproxy = require('parserproxy')
+ , forever = require('forever')
+ , request = require('request');
+
+forever.start(parserproxy).on('start', function (process, data){
+
+ // It takes a few milliseconds for the http server to spin up, so for the purposes of this
+ // example, we wrap the request in a setTimeout(). You DON'T need to do this in your code unless
+ // you are going to make one or more requests immediately upon start up.
+
+ setTimeout(function(){
+ request({ uri : 'http://localhost:3030/parseFeed',
+ body : { url: 'http://cyber.law.harvard.edu/rss/examples/rss2sample.xml' },
+ json : true },
+ function (err, response, body){
+ if (!err && response.statusCode == 200) {
+ console.log('%s [%s]', body.meta.title || body.meta.xmlUrl, body.meta.link);
+ body.articles.forEach(function (article) {
+ console.log('%s - %s', article.pubDate, article.title || article.description.substring(0,50));
+ });
+ }
+ });
+ }, 1000);
+});
@@ -0,0 +1,7 @@
+/*!
+* node-parserproxy
+* Copyright(c) 2011 Dan MacTough <danmactough@gmail.com>
+* MIT Licensed
+*/
+
+module.exports = require(__dirname + '/server');
@@ -0,0 +1,22 @@
+{
+ "name": "parserproxy",
+ "author": "Dan MacTough <danmactough@gmail.com>",
+ "description": "A JSON-over-HTTP proxy for node-feedparser and node-opmlparser",
+ "version": "0.1.0",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/danmactough/node-parserproxy.git"
+ },
+ "keywords": ["http", "proxy", "json", "rss", "feed", "atom", "rdf", "opml", "outline", "xml", "syndication"],
+ "main": "./server.js",
+ "engines": {
+ "node": ">= 0.4.0"
+ },
+ "dependencies": {
+ "feedparser": "0.4.x",
+ "opmlparser": "0.2.x",
+ "request": "2.1.x"
+ },
+ "devDependencies": {}
+}
+
102 server.js
@@ -0,0 +1,102 @@
+/*!
+ * node-parserproxy - A JSON-over-HTTP proxy for node-feedparser and node-opmlparser
+ * Copyright(c) 2011 Dan MacTough <danmactough@gmail.com>
+ * All rights reserved.
+ */
+
+/**
+ * Module dependencies.
+ */
+var http = require('http')
+ , url = require('url')
+ , request = require('request')
+ , FeedParser = require('feedparser')
+ , OpmlParser = require('opmlparser')
+ ;
+
+if (!module.parent) {
+
+ var port = process.env['PARSER_PROXY_PORT'] || 3030;
+
+ process.on('uncaughtException', function (err) {
+ console.error('Caught exception: ' + err);
+ });
+
+ function parseFeed (url, callback){
+ request(url, function (err, response, body){
+ if (err) callback(err);
+ else if (response.statusCode >= 400) callback(response.statusCode);
+ else {
+ var parser = new FeedParser();
+ parser.parseString(body, callback);
+ }
+ });
+ }
+
+ function parseOpml (url, callback){
+ request(url, function (err, response, body){
+ if (err) callback(err);
+ else if (response.statusCode >= 400) callback(response.statusCode);
+ else {
+ var parser = new OpmlParser();
+ parser.parseString(body, callback);
+ }
+ });
+ }
+
+ var server = http.createServer(function (req, res) {
+ var data = '';
+ req.body = {};
+
+ req.on('data', function (buffer){
+ data += (buffer.toString('utf8'));
+ });
+ req.on('end', function (){
+ req.body = JSON.parse(data);
+ switch (req.url) {
+ case '/parseFeed':
+ parseFeed(req.body.url, function (err, meta, articles){
+ if (err) {
+ if (+err >= 400) {
+ res.statusCode = err;
+ } else {
+ res.writeHead(500, err.message || err);
+ }
+ res.end();
+ } else {
+ res.writeHead(200, {'Content-Type': 'application/json'});
+ res.end(JSON.stringify({ meta: meta, articles: articles }));
+ }
+ });
+ break;
+ case '/parseOpml':
+ parseOpml(req.body.url, function (err, meta, feeds, outline){
+ if (err) {
+ if (+err >= 400) {
+ res.statusCode = err;
+ } else {
+ res.writeHead(500, err.message || err);
+ }
+ res.end();
+ } else {
+ res.writeHead(200, {'Content-Type': 'application/json'});
+ res.end(JSON.stringify({ meta: meta, feeds: feeds, outline: outline }));
+ }
+ });
+ break;
+ default:
+ res.statusCode = 501; // Not implemented
+ res.end();
+ break;
+ }
+ });
+ req.on('error', function (err){
+ res.writeHead(500, err.message || err);
+ res.end();
+ });
+ }).listen(port, function(){
+ console.log("Proxy parser server listening on port %d", port);
+ });
+} else {
+ module.exports = __dirname + '/server.js';
+}

0 comments on commit 3b491d2

Please sign in to comment.