2016년 4월 11일 월요일

점진적 방법으로 async web server 만들기 - node.js

최소 라이브러리만 가지고 접근

require('http').createServer(function(req,res) {
  console.log(req.url, req.method);
  res.end(req.url+":"+req.method);
}).listen(3000);

# coffee
require('http').createServer((req,res)-> res.end "#{req.url}:#{req.method}").listen 3000

Router를 만들자.
if 나 switch 를 쓰는 것보다 object를 이용하자.
해당 패턴이 있으면 쓰고 없으면 404 처리를 하자.

var router={
  "/": function() {
    return "home";
  },
  "/list": function() {
    return "list";
  },
  "404": function() {
    return "not found";
  }
}

require('http').createServer(function(req,res) {
  res.end(router[router[req.url] && req.url || "404"]());
}).listen(3000);

# coffee
router =
  "/": -> "home"
  "/list": -> "list"
  "404": -> "not found"
require('http').createServer((req, res) ->
  res.end router[router[req.url]? and req.url or '404']()
).listen 3000

만일 Async가 있다면?

router = {
  "/" : function(act) {
    return function() {
      act("hello home");
    };
  },
  "/list": function(act) {
    return function() {
      setTimeout(function() {
        act("list");
      }, 1000);
    };
  },
  "404": function(act) {
    return function() {
     act("not found");
    };
  }
};
http.createServer(function(req, res) {
  router[router[req.url] && req.url || "404"](function(body) {
    res.end(body);
  })();
}).listen(3000);

# coffee
router =
  '/': (act) ->
    -> act 'hello home'
  '/list': (act) ->
    -> setTimeout (->
      act 'list'
    ), 1000
  '404': (act) ->
    -> act 'not found'
http.createServer((req, res) ->
  router[router[req.url]? and req.url or '404']((body) ->
    res.end body
  )()
).listen 3000

마지막에 res.end 를 해야하므로 최종적으로 비동기 작업을 끝내고 res.end를 하도록 함수를 리턴해야한다.

/list/:id 와 같은 패턴 매칭을 하려면?

params="/list/:id".match(/:([^\/]+)/g).map(v=>v.substr(1))

이런 식으로 접근해서 인자명을 뽑아둔다.
패턴을 생각해보자.

테스트는 이렇게 해본다.
웹브라우저에서도 해볼 수 있지만 

$ curl localhost:3000/list

이렇게 하거나 curl 이 없으면

$ telnet localhost 3000
혹은
$ nc localhost 3000
을 실행하여
GET /list 
입력 후 엔터 두번 해준다.

2016년 4월 10일 일요일

cloudflare+openshift 조합으로 무료 node.js https 서버 구축하기

요즘은 *.meteor.com 호스팅도 없어지고
기존 서비스들이 무료로 쓰기엔 시간 제한 같은 것들이 생겨서 간단하게 뭔가 만들어 보여주는 목적으로 쓸만한게 점점 줄어들고 있다.

하지만, 대인배 RedHat에선 PaaS 를 아직 무료로 제공하고 있다.
https://www.openshift.com/pricing/plan-comparison.html

24시간동안 놀리고 있지만 않으면 된다. 이거야 방법이 여러가지 있으니 생략하고.

일단 만들어 본다. 가입하고
https://openshift.redhat.com/app/console/applications 를 접근하면 새로운 앱을 만들 수 있다.
무료 계정은 Gear 라는 것을 3개까지 쓸 수 있는데 node.js / python 같은 언어도 1개. DB도 1개. jenkins도 1개씩 먹으니 잘 생각해서 계획을 짜야한다.
가령, node.js와 mongoDB를 각각 선택하면 2 gear를 사용하지만 둘을 합친 MEAN 을 사용하면 1gear만 사용한다.
대신, MEAN은 node.js/mongoDB 버전이 낮으므로 ecma2016이나 wiredTiger같은 최신 기술을 사용할 수 없다.

내 경우는 일반 js를 쓰는 것이 너무 괴로와서 https://github.com/icflorescu/openshift-cartridge-nodejs 커스텀 카트릿지로 생성했다.
그리고 이건 P짱O꿀W꿀E팁R! 인데 생성 후 https://openshift.redhat.com/app/console/applications 에서 방금 생성한 node.js 앱을 선택 후 해당 앱 상세에서 Or, see the entire list of cartridges you can add 이 부분을 클릭.
Choose a cartridge to add to your application. 화면에서 맨 아래로 스크롤 한 뒤.
Install your own cartridge 에서 텍스트 입력창에
https://raw.githubusercontent.com/icflorescu/openshift-cartridge-mongodb/master/metadata/manifest.yml (https://github.com/icflorescu/openshift-cartridge-mongodb 의 mongodb 카트릿지) 를 추가하면 최신 wiredTiger 를 지원하는 mongodb 3.2.x 버전을 설치할 수 있는데 Gear 소모가 0이다!

이제 도메인쪽 이야기를 하면 DNS용으로 cloudflare를 선택한 것은 정적 자원의 캐슁이랑 HTTP SSL 때문인데 openshift에선 HTTP SSH이 유료 옵션이라 죽이 척척 맞는다.

세가지만 신경써 주면 되는데
첫째, 도메인 서비스를 받는 곳에 Name server 설정을 도메인을 구매한 곳에 업데이트한다.
aragorn.ns.cloudflare.com
wally.ns.cloudflare.com
이런 식으로 두 개를 넣어주면 끝.
둘째, openshift에서 도메인과 연결할 App이름 오른쪽 옆에 조그맣게 써있는 change를 선택하고 다음화면에서 도메인 명을 넣어준다. sub-domain이 아닐 경우 nake domain 을 위해 www.yourdomain.com 과 www가 없는 yourdomain.com 식으로 두개를 써준다. (아마 두 번 들락날락 해줘야할 것이다.)
SSL Certificate 부분은 쿨하게 무시. 안쓸거다.
셋째, cloudflare 로 가서 DNS 쪽에 A를 선택하고
A  yourdomain.com xx.xx.xx.xx (nake domain을 안쓸거면 생략 가능)
A  www                         xx.xx.xx.xx

이와 같이 두개를 입력한다. 여기서 xx.xx.xx.xx는 해당 앱의 최초 Alias인 xxx-yyy.rhcloud.com 형식인 도메인의 IP를 ping을 때리던가 해서 찾아서 넣으면 된다.

여기까지 완료되면 http와 https로 각각 접속해보자. www가 있고 없고 다 잘되는지 확인해보고 이쁜 녹색 자물쇠(크롬기준)이 떴는지도 체크해보자.

2016년 4월 4일 월요일

lua table의 serialization 구현


table은 lua의 재밌는 특징 중 하나인데 간단하게 JSON화 하는 것을 구현해보았다.

a={a=1, b={c=2, d=3}}  

serialize=function(a) 
  local res=""
  for k,v in pairs(a) do
    if res~="" then res = res .. "," end
    res = res .. k .. ":" .. (type(v)=="table" and serialize(v) or v)
  end
  res="{" .. res .. "}" 
  return res 
end

print(serialize(a))

잘 된다.
moonscript로 써보면

a=
  a:1
  b:
    c:2
    d:3

serialize=(a)->
  res=""
  res="#{res}#{res~="" and "," or ""}#{k}:#{type(v)=="table" and serialize(v) or v}" for k,v in pairs(a)
  "{#{res}}"

print serialize a

더 좋다.

문자 처리까지하면

a = {
  a = 1,
  b = {
    c = 2,
    d = "a"
  }
}

serialize = function(a)
  local res = ""
  for k, v in pairs(a) do
    res = res .. (res ~= "" and "," or "") .. k .. ":" .. (type(v) == "table" and serialize(v) or (type(v) == "string" and "\"" .. v .. "\"" or v))
  end
  return "{" .. res .. "}"
end

print(serialize(a))

마찬가지로 moonscript로는

a=
  a:1
  b:
    c:2
    d:"a"

serialize=(a)->
  res=""
  res="#{res}#{res~="" and "," or ""}#{k}:#{type(v)=="table" and serialize(v) or (type(v)=="string" and "\""..v.."\"" or v)}" for k,v in pairs(a)
  "{#{res}}"

print serialize a

조금만 더 마사지 하면 된다.

cURL로 cookie를 다루는 법

http://stackoverflow.com/questions/22252226/passport-local-strategy-and-curl

레거시 소스를 보다보면 인증 관련해서 cookie를 사용하는 경우가 있는데

가령 REST 서버인 경우

curl -H "Content-Type: application/json" -X POST -d '{"email": "aaa@bbb.com", "pw": "cccc"}' "http://localhost/login"

이렇게 로그인이 성공이 했더라도


curl -H "Content-Type: application/json" -X GET -d '' "http://localhost/accounts/"

이런 식으로 했을 때 쿠키를 사용한다면 당연히 인증 오류가 날 것이다.

curl의 --cookie-jar 와 --cookie 옵션을 사용해서 cookie를 저장하고 꺼내쓰자.
각각 옵션 뒤엔 저장하고 꺼내쓸 파일이름을 임의로 지정하면 된다.

위의 과정을 다시 수정해서 적용하면

curl -H --cookie-jar jarfile "Content-Type: application/json" -X POST -d '{"email": "aaa@bbb.com", "pw": "cccc"}' "http://localhost/login"


curl -H --cookie jarfile "Content-Type: application/json" -X GET -d '' "http://localhost/accounts/"

이렇게 사용하면 된다.
물론 새로 만든다면 OAuth 같은 방식을 사용하는 것이 좋다.