Search

7/31/2013

How to get Node.JS Express to listen only on localhost? - Stack Overflow

How to get Node.JS Express to listen only on localhost? - Stack Overflow i'm running a nodejs application and listen to port 3002, and use nginx as reverse proxy to redirect foo.chunghe.me to chunghe.me:3002, but i don't want to expose chunghe.me:3002 from outside world change from:
app.listen(3002)
to
app.listen(3002, 'localhost')

7/25/2013

while(1) in front of the json resposne

attacker could use <script src="http://remote.com/data.json"></script> to get the remote json data using victim's session id and get the json data using following code, modern browser won't allow this any more:

   var captured = [];
   Object.prototype.__defineSetter__('model',
       function (str) {
           captured.push(str);
       });
this is called JSON hijacking. appending while(1) in front of the json response. The attacker own't be able to get past the first line if the data is loaded using a <script> tag, because the browser will freeze while running the loop.

7/24/2013

Sending preflight requests

Sending preflight requests With CORS, if your request method is something other than GET, POST, or HEAD, or if you’re sending a custom HTTP header, the browser will make what’s called a preflight request. A preflight request is a server verification mechanism that allows both parties to decide whether the attempt is legitimate before performing the actual request. To notify the server about the upcoming request and ask for permission, the client sends the following headers: Origin—The origin of the request Access-Control-Request-Method—The intended HTTP method of the request Access-Control-Request-Headers—A comma-separated list of custom headers that the request wants to use The server then communicates back to the client by sending the following headers with the response: Access-Control-Allow-Origin—The allowed origin (must match the Origin header from the request) Access-Control-Allow-Methods—A comma-separated list of allowed methods Access-Control-Allow-Headers—A comma-separated list of allowed headers Access-Control-Max-Age—The amount of time (in seconds) that this preflight request should be cached for Access-Control-Allow-Credentials—Indicates whether the requested resource supports credentialed requests (optional) Example: a failed CORS request with not-allowed custom header source code: $.ajax({ type: 'GET', url: 'http://json.chunghe.me/list/page/0', headers: {"hello": "world"} }).done(function(a){ console.log(a) }) request header Accept:*/* Accept-Encoding:gzip,deflate,sdch Accept-Language:zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 Access-Control-Request-Headers:accept, hello, origin Access-Control-Request-Method:GET Connection:keep-alive Host:json.chunghe.me Origin:http://fiddle.jshell.net Referer:http://fiddle.jshell.net/_display/ User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36 Response Headersview source response: Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:X-Requested-With Access-Control-Allow-Methods:GET, POST, OPTIONS, DELETE Access-Control-Allow-Origin:* Access-Control-Max-Age:1728000 Connection:keep-alive Content-Length:0 Content-Type:text/plain Date:Wed, 24 Jul 2013 06:47:48 GMT Server:nginx/1.2.1 X-Powered-By:Express console OPTIONS http://json.chunghe.me/list Request header field hello is not allowed by Access-Control-Allow-Headers. jquery-1.9.1.js:8526 XMLHttpRequest cannot load http://json.chunghe.me/list. Request header field hello is not allowed by Access-Control-Allow-Headers. Example: a success CORS request with not-allowed custom header source $.ajax({ type: 'GET', url: 'http://json.chunghe.me/list/page/0', headers:{'X-Requested-With':'XMLHttpRequest'} }).done(function(a){ console.log(a) }) first request (preflight) Request URL:http://json.chunghe.me/list/page/0 Request Method:OPTIONS Status Code:200 OK request: Accept:*/* Accept-Encoding:gzip,deflate,sdch Accept-Language:zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 Access-Control-Request-Headers:accept, origin, x-requested-with Access-Control-Request-Method:GET Connection:keep-alive Host:json.chunghe.me Origin:http://fiddle.jshell.net Referer:http://fiddle.jshell.net/_display/ User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36 response: Access-Control-Allow-Credentials:true Access-Control-Allow-Headers:X-Requested-With Access-Control-Allow-Methods:GET, POST, OPTIONS, DELETE Access-Control-Allow-Origin:* Access-Control-Max-Age:1728000 Connection:keep-alive Content-Length:0 Content-Type:text/plain Date:Wed, 24 Jul 2013 06:52:10 GMT Server:nginx/1.2.1 X-Powered-By:Express second request(actual request) Request URL:http://json.chunghe.me/list/page/0 Request Method:GET Status Code:200 OK request: Accept:*/* Accept-Encoding:gzip,deflate,sdch Accept-Language:zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 Connection:keep-alive Host:json.chunghe.me Origin:http://fiddle.jshell.net Referer:http://fiddle.jshell.net/_display/ User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36 X-Requested-With:XMLHttpRequest response: Access-Control-Allow-Origin:* Connection:keep-alive Content-Length:575 Content-Type:application/json Date:Wed, 24 Jul 2013 06:52:10 GMT Server:nginx/1.2.1 X-Powered-By:Express

7/22/2013

CodeMirror

CodeMirror

CodeMirror is a JavaScript component that provides a code editor in the browser. When a mode is available for the language you are coding in, it will color your code, and optionally help with indentation. A rich programming API and a CSS theming system are available for customizing CodeMirror to fit your application, and extending it with new functionality.

7/16/2013

Create Keyboard Shortcuts with Mousetrap

Create Keyboard Shortcuts with Mousetrap

Mousetrap.bind("d a v i d", function() {
 // Alert "FTW"
});

// konami code!
Mousetrap.bind('up up down down left right left right b a enter', function() {
    highlight([21, 22, 23]);
});

Mousetrap.bind("shift+k", function() {
 // Yay for extra key control
});

愛一個能夠給你正面能量的人

愛一個能夠給你正面能量的人

在很遙遠的某一天,當我的孩子仰頭向我提出這個問題,我會微笑地回答他/她:去愛一個能夠給你正面能量的人。    每個人的生活都一樣,在細看是碎片遠看是長河的時間中間接地尋找著幸福,直接地尋找著能夠讓自己幸福的一切事物:物質、榮譽、成就、愛情、青春、陽光或者回憶。    既然你想幸福,就去找一個能夠讓你感到幸福的人吧。不要找一個沒有激情、沒有好奇心的人過日子,他們只會和你窩在家裡唉聲歎氣抱怨生活真沒勁,只會打開電視,翻來覆去的調轉頻道,好像除了看電視再也想不出其它的娛樂項目。人生就是在沒完沒了的工作和一樣沒完沒了的電視節目中度過的。    擁有正面能量的人,對很多事情充滿好奇,無論遇到什麼樣的新鮮事物都想嘗試一下,會帶你去嘗試一家新的餐廳,帶你去看一場口碑不錯的電影,帶你去體驗新推出的娛樂節目,帶你去下一個陌生的城市旅行。你會發現世界很大,值得用罄一生去不斷嘗試。    不要找一個沒有安全感的人過日子,他們一直在排查可能的不幸和焦慮未來的災難。他們一直在想該怎麼辦,一直擔心禍事即將降臨。他們命名自己為救火隊員,每天撲向那些或有或無、或虛或實的災情,不停算計、緊張和憂愁。    擁有正面能量的人,會對生活樂觀對自己信任。他們知道生活本來就悲喜交加,所以已經學會坦然面對。當快樂來臨時,會盡情享受,當煩擾來襲時,就理性解決。他們相信人定勝天,確實無法獲勝時,就坦然接受。他們能夠正確認識自己,有自知之明,不會自我貶損也不會自我膨脹,他們在該獨立的時候獨立,該求助的時候求助。樂觀和自信後面,深藏著對人生的豁達與包容。    不要找一個無知的人過日子,他們沒有樹立起完整的人生觀,或者對事情價值的判斷缺乏基準線。他們常會做出匪夷所思的決定,不能獨立思考或者過於固執己見。他們優柔寡斷或專橫無禮,他們扭捏作態或者刻板無情。不是因為別的,正是因為無知。   擁有正面能量的人,擁有大智慧,他們分得清世界的黑白曲直,不會在人生的道路上跑偏也不會隨波逐流。他們不會扭曲事物的本質,不會誇大事情的不利面。他們知道世界運作的原理,明白人人都有陰晴陽缺。他們在你需要時給你最中肯的建議,有原則卻又求新求變,有主見卻又聽得進勸。    不要找一個容易放棄的人過日子。他們得過且過永久性地安於現狀。他們沒有信仰,也沒有夢想。他們遇到挫折的第一反應和最終反應都是逃避,為了抵擋失敗或者因為怕麻煩,他們可以放棄一整個世界。    擁有正面能量的人,堅定自己的信念,擁有人生的目標,知道自己的所需並為之不斷努力。他們歡迎變化也製造進步。當困難來臨,他們不嫌麻煩或貪圖安逸,他們知道山丘後面會有道更美麗的風景。是的,去愛一個擁有正面能量的人吧。他們會讓你覺得人生有意思,會讓你覺得世界色彩斑斕。他們會給你驚喜,同時也會帶給你感悟。他們讓你把路走直,戒斷所有扭曲的價值觀。    如果你本身就不是一個擁有足夠正面能量的人,那麼就請你一定要愛一個擁有正面能量的人。在這道數學題裡,負負並不能得正,另一個同樣具有負面能量的人會把你的人生拖垮,不同空間的畸形與病態會讓你過得一團糟。讓這樣具有正面能量的人導正你的靈魂和行為,潛移默化中,你會變得更加開朗和幸福。這一定,比任何財富更能長久的滋養你的心靈。

7/15/2013

Polaroid 推出 Polamatic 應用:讓你在 Android 裝置上做出老照片的味道

Polaroid 推出 Polamatic 應用:讓你在 Android 裝置上做出老照片的味道

如果你覺得市面上那些製作照片懷舊效果的應用還不夠多的話,Polaroid 今天又帶來了一個新的選擇。他們在 Android 平台上推出了一款名為 Polamatic 的照片編輯 App,它擁有 36 種不同的邊框(其中包括了 Polaroid 經典的白色邊框,見上圖左),同時還可使用 20 種特有的濾鏡,字型選擇也多達 24 種。 用戶在 App 內可透過電郵、Facebook、Twitter、Flickr、Tumblr 甚至是 Instagram 來分享製作完成的照片,這其中 Instagram 無疑是當下照片編輯、分享類應用的佼佼者,但實話說,它的不少懷舊濾鏡效果也都是在向老前輩 Polaroid 致敬啊。有興趣的朋友,訪問來源去看一下吧。

7/08/2013

socket.io + express.js common error

if you see this error: GET http://localhost:8000/socket.io/1/?t=1373270667022 404 (Not Found) should be server.listen(80) instead of app.listen(80)

var app = require('express')()
  , server = require('http').createServer(app)
  , io = require('socket.io').listen(server);

server.listen(80);

app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');
});

io.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

nodejs pubsub

var events = require('events');
var eventEmitter = new events.EventEmitter();

function mainLoop () {
    console.log('starting application');
    eventEmitter.emit('ApplicationStart');
    console.log('Running application');
    eventEmitter.emit('ApplicationRun');
    console.log('Stooping application');
    eventEmitter.emit('ApplicationStop');
}
function onApplicationStart() {
    console.log('Handling Application Start Event');
}
function onApplicationRun () {
    console.log('Handling Application Run Event');
}
function onApplicationStop () {
    console.log('Handling Application Stop Event');
}
eventEmitter.on('ApplicationStart', onApplicationStart);
eventEmitter.on('ApplicationRun', onApplicationRun);
eventEmitter.on('ApplicationStop', onApplicationStop);

7/05/2013

How to code a URL shortener?

How to code a URL shortener?

How to convert the ID to a shortened URL Think of an alphabet we want to use. In your case that's [a-zA-Z0-9]. It contains 62 letters. Take an auto-generated, unique numerical key (the auto-incremented id of a MySQL table for example). For this example I will use 12510 (125 with a base of 10). Now you have to convert 12510 to X62 (base 62). 12510 = 2×621 + 1×620 = [2,1] This requires use of integer division and modulo. A pseudo-code example: digits = [] while num > 0 remainder = modulo(num, 62) digits.push(remainder) num = divide(num, 62) digits = digits.reverse Now map the indices 2 and 1 to your alphabet. This is how your mapping (with an array for example) could look like: 0 → a 1 → b ... 25 → z ... 52 → 0 61 → 9 With 2 → c and 1 → b you will receive cb62 as the shortened URL. http://shor.ty/cb How to resolve a shortened URL to the initial ID The reverse is even easier. You just do a reverse lookup in your alphabet. e9a62 will be resolved to "4th, 61st, and 0th letter in alphabet". e9a62 = [4,61,0] = 4×622 + 61×621 + 0×620 = 1915810 Now find your database-record with WHERE id = 19158 and do the redirect.
manufacturing flic.kr style photo URLs

function base_encode($num, $alphabet) {
    $base_count = strlen($alphabet);
    $encoded = '';
    while ($num >= $base_count) {
        $div = $num / $base_count;
        $mod = ($num - ($base_count * intval($div)));
        $encoded = $alphabet[$mod].$encoded;
        $num = intval($div);
    }

    if ($num) $encoded = $alphabet[$num].$encoded;

    return $encoded;
}

function base_decode($num, $alphabet) {
    $decoded = 0;
    $multi = 1;
    while (strlen($num) > 0) {
        $digit = $num[strlen($num) - 1];
        $decoded += $multi * strpos($alphabet, $digit);
        $multi = $multi * strlen($alphabet);
        $num = substr($num, 0, -1);
    }

    return $decoded;
}

7/02/2013

get user profile avatar from facebook/google plus/twitter/sina weibo, etc

weibo via api

$ curl 'https://api.weibo.com/2/users/show.json?uid=1717675430&access_token=2.008JbFTD0aByTEcd6d0b28a30plcC6'

{"id":1717675430,"idstr":"1717675430","screen_name":"蕭敬騰","name":"蕭敬騰","province":"71","city":"1","location":"台湾 台北市","description":"喜鵲娛樂+886-2-2782-3000 http://www.chinafun.com.tw/","url":"http://www.jamsclub.asia/","profile_image_url":"http://tp3.sinaimg.cn/1717675430/50/5629611856/1","profile_url":"iamxiaojingteng","domain":"iamxiaojingteng","weihao":"","gender":"m","followers_count":10316393,"friends_count":131,"statuses_count":2843,"favourites_count":0,"created_at":"Wed Mar 24 14:09:44 +0800 2010","following":false,"allow_all_act_msg":false,"geo_enabled":false,"verified":true,"verified_type":0,"remark":"","status":{"created_at":"Tue Jul 02 14:11:37 +0800 2013","id":3595617584807400,"mid":"3595617584807400","idstr":"3595617584807400","text":"[嘻嘻][嘻嘻][嘻嘻]//@黃子佼:[哈哈][哈哈][哈哈]","source":"HTC","favorited":false,"truncated":false,"in_reply_to_status_id":"","in_reply_to_user_id":"","in_reply_to_screen_name":"","pic_urls":[],"geo":null,"reposts_count":0,"comments_count":0,"attitudes_count":0,"mlevel":0,"visible":{"type":0,"list_id":0}},"allow_all_comment":true,"avatar_large":"http://tp3.sinaimg.cn/1717675430/180/5629611856/1","avatar_hd":"","verified_reason":"台灣歌手","follow_me":false,"online_status":0,"bi_followers_count":128,"lang":"zh-tw","star":0,"mbtype":12,"mbrank":4,"block_word":0}
guess the path: http://www.zhihu.com/question/19630156

get avatar from google profiles, facebook, gravatar, twitter, tumblr

function get_avatar_from_service(service, userid, size) {
    // this return the url that redirects to the according user image/avatar/profile picture
    // implemented services: google profiles, facebook, gravatar, twitter, tumblr, default fallback
    // for google   use get_avatar_from_service('google', profile-name or user-id , size-in-px )
    // for facebook use get_avatar_from_service('facebook', vanity url or user-id , size-in-px or size-as-word )
    // for gravatar use get_avatar_from_service('gravatar', md5 hash email@adress, size-in-px )
    // for twitter  use get_avatar_from_service('twitter', username, size-in-px or size-as-word )
    // for tumblr   use get_avatar_from_service('tumblr', blog-url, size-in-px )
    // everything else will go to the fallback
    // google and gravatar scale the avatar to any site, others will guided to the next best version
    var url = '';
 
    switch (service) {
 
    case "google":
        // see http://googlesystem.blogspot.com/2011/03/unedited-google-profile-pictures.html (couldn't find a better link)
        // available sizes: all, google rescales for you
        url = "http://profiles.google.com/s2/photos/profile/" + userid + "?sz=" + size;
        break;
 
    case "facebook":
        // see https://developers.facebook.com/docs/reference/api/
        // available sizes: square (50x50), small (50xH) , normal (100xH), large (200xH)
        var sizeparam = '';
        if (isNumber(size)) {
            if (size >= 200) {
                sizeparam = 'large'
            };
            if (size >= 100 && size < 200) {
                sizeparam = 'normal'
            };
            if (size >= 50 && size < 100) {
                sizeparam = 'small'
            };
            if (size < 50) {
                sizeparam = 'square'
            };
        } else {
            sizeparam = size;
        }
        url = "https://graph.facebook.com/" + userid + "/picture?type=" + sizeparam;
        break;
 
    case "gravatar":
        // see http://en.gravatar.com/site/implement/images/
        // available sizes: all, gravatar rescales for you
        url = "http://www.gravatar.com/avatar/" + userid + "?s=" + size
        break;
 
    case "twitter":
        // see https://dev.twitter.com/docs/api/1/get/users/profile_image/%3Ascreen_name
        // available sizes: bigger (73x73), normal (48x48), mini (24x24), no param will give you full size
        var sizeparam = '';
        if (isNumber(size)) {
            if (size >= 73) {
                sizeparam = 'bigger'
            };
            if (size >= 48 && size < 73) {
                sizeparam = 'normal'
            };
            if (size < 48) {
                sizeparam = 'mini'
            };
        } else {
            sizeparam = size;
        }
 
        url = "http://api.twitter.com/1/users/profile_image?screen_name=" + userid + "&size=" + sizeparam;
        break;
 
    case "tumblr":
        // see http://www.tumblr.com/docs/en/api/v2#blog-avatar
        //TODO do something smarter with the ranges
        // available sizes: 16, 24, 30, 40, 48, 64, 96, 128, 512
        var sizeparam = '';
        if (size >= 512) {
            sizeparam = 512
        };
        if (size >= 128 && size < 512) {
            sizeparam = 128
        };
        if (size >= 96 && size < 128) {
            sizeparam = 96
        };
        if (size >= 64 && size < 96) {
            sizeparam = 64
        };
        if (size >= 48 && size < 64) {
            sizeparam = 48
        };
        if (size >= 40 && size < 48) {
            sizeparam = 40
        };
        if (size >= 30 && size < 40) {
            sizeparam = 30
        };
        if (size >= 24 && size < 30) {
            sizeparam = 24
        };
        if (size < 24) {
            sizeparam = 16
        };
 
        url = "http://api.tumblr.com/v2/blog/" + userid + "/avatar/" + sizeparam;
        break;
 
    default:
        // http://www.iconfinder.com/icondetails/23741/128/avatar_devil_evil_green_monster_vampire_icon
        // find your own
        url = "http://i.imgur.com/RLiDK.png"; // 48x48
    }
 
 
    return url;
}
 
 
// helper methods
 
function isNumber(n) {
    // see http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric
    return !isNaN(parseFloat(n)) && isFinite(n);
}