Last active
August 29, 2015 14:08
-
-
Save bolasblack/fd638e6d5716c9768bf9 to your computer and use it in GitHub Desktop.
wrap web service with websocket
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
nil |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
express = require 'express' | |
http = require 'http' | |
httpMocks = require 'node-mocks-http' | |
httpStatus = require 'statuses' | |
app = express() | |
server = http.createServer app | |
io = require('socket.io') server | |
io.on 'connection', (socket) -> | |
console.log 'io connected' | |
socket.on 'request', (data) -> | |
console.log 'request event', data | |
{callbackId, info} = data | |
req = httpMocks.createRequest( | |
url: info.url | |
method: info.method | |
headers: info.headers | |
body: info.body | |
) | |
res = httpMocks.createResponse() | |
# https://github.com/strongloop/express/blob/661435256384165bb656cb7b6046b4138ca24c9e/lib/express.js#L28 | |
app.handle req, res, -> | |
socket.emit 'response', { | |
callbackId: callbackId | |
info: | |
textStatus: httpStatus[res.statusCode] | |
status: res.statusCode | |
headers: res.headers | |
body: res._getData() | |
} | |
socket.on 'disconnect', -> | |
console.log 'io disconnected' | |
app.use express.static __dirname + '/' | |
app.post '/hello', (req, res, next) -> | |
console.log 'server get post /hello', req.body | |
res.status(201).send a: 1 | |
next() | |
server.listen 3000, -> | |
console.log 'socket.io server listening at 3000' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Test</title> | |
</head> | |
<body> | |
<script src="/socket.io/socket.io.js"></script> | |
<script> | |
(function() { | |
var socket = io() | |
socket.on('connect', function() { | |
console.log('io connected') | |
socket.emit('request', { | |
callbackId: 0, | |
info: { | |
url: '/hello', | |
method: 'post', | |
headers: {'Content-Type': 'application/json'}, | |
body: JSON.stringify({test: true}) | |
} | |
}) | |
socket.on('response', function(data) { | |
console.log('response event', data.info) | |
}) | |
}) | |
})() | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "test-ws", | |
"version": "1.0.0", | |
"description": "", | |
"main": "index.js", | |
"scripts": { | |
"express": "`npm bin`/coffee express.coffee", | |
"restify": "`npm bin`/coffee restify.coffee" | |
}, | |
"author": "", | |
"license": "ISC", | |
"dependencies": { | |
"coffee-script": "^1.8.0", | |
"express": "^4.10.1", | |
"lodash": "^2.4.1", | |
"node-mocks-http": "^1.2.1", | |
"restify": "2.8.2", | |
"socket.io": "^1.2.0", | |
"statuses": "^1.2.0" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
restify = require 'restify' | |
_ = require 'lodash' | |
http = require 'http' | |
httpMocks = require 'node-mocks-http' | |
httpStatus = require 'statuses' | |
server = restify.createServer() | |
io = require('socket.io') server.server | |
io.on 'connection', (socket) -> | |
socket.on 'request', (request) -> | |
{info, callbackId} = request | |
# https://github.com/mcavage/node-restify/blob/885f1b95cf97a144ad10778d9dfa00fdfca6878c/lib/request.js#L109 | |
# Restify read header in downcase | |
headers = _(info.headers).reduce (lowerHeader, value, key) -> | |
lowerHeader[key.toLowerCase()] = value | |
lowerHeader | |
, {} | |
req = httpMocks.createRequest( | |
url: info.url | |
# Restify read method in upcase | |
method: info.method.toUpperCase() | |
headers: headers | |
body: info.body | |
) | |
req.__proto__ = http.IncomingMessage.prototype | |
req.pause = -> | |
req.resume = -> | |
# https://github.com/mcavage/node-restify/commit/bcdbda4b97d6d4621b502c3bc1dc36e10c70fee8#L55 | |
# We didn't need bodyReader, you can complete it if you need | |
req.isChunked = -> req.contentType() in ['multipart/form-data', 'application/octet-stream'] | |
req.getContentLength = -> 0 | |
res = httpMocks.createResponse() | |
afterEventHandler = (inputReq, inputRes) -> | |
return if inputReq isnt req | |
server.removeListener 'after', afterEventHandler | |
statusCode = if res.statusCode >= 100 then res.statusCode else 200 | |
socket.emit 'response', { | |
callbackId: callbackId | |
info: | |
status: statusCode | |
textStatus: httpStatus[statusCode] | |
headers: res.headers | |
body: res._getData() | |
} | |
server.on 'after', afterEventHandler | |
# https://github.com/mcavage/node-restify/blob/3e38cec398841c64473ce534b2372aeb8a786868/lib/server.js#L250 | |
server.emit 'request', req, res | |
server._setupRequest req, res | |
server._handle req, res | |
socket.on 'disconnect', -> | |
console.log 'io disconnected' | |
server.get '/', restify.serveStatic( | |
directory: '.' | |
default: 'index.html' | |
) | |
server.post '/hello', (req, res, next) -> | |
console.log 'server get post /hello', req.body | |
res.send 201, a: 1 | |
next() | |
server.listen 3000, -> | |
console.log 'socket.io server listening at 3000' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
angular.module('ng') | |
.config(['$provide', ($provide) -> | |
wsURLRE = /^\$websocket\/?/ | |
globalCallbackId = 0 | |
isSocketReq = (url) -> | |
wsURLRE.test url | |
fakeResponseHeader = (headerObj) -> | |
_.map headerObj, (value, key) -> | |
"#{key}: #{value}" | |
.join '\n' | |
$provide.decorator('$httpBackend', [ | |
'$delegate', '$q', '$window', '$log', 'API_HOST' | |
(httpBackend, $q , $window , $log , API_HOST) -> | |
socket = $window.io API_HOST.replace wsURLRE, '' | |
waitSocketReady = (cb) -> | |
(args...) -> | |
return cb.apply(this, args) if socket.connected | |
socket.once 'connect', => cb.apply(this, args) | |
serializeUrl = (url) -> | |
return url if /^https?:/.test url | |
return url if /^\//.test url | |
"/#{url}" | |
socketBackend = waitSocketReady (method, url, post, callback, headers, timeout, withCredentials, responseType) -> | |
callbackId = globalCallbackId | |
globalCallbackId++ | |
reqData = { | |
callbackId: callbackId | |
info: | |
url: serializeUrl url.replace wsURLRE, '' | |
method: method | |
headers: headers | |
body: post | |
} | |
respReceiver = (resp) -> | |
return unless resp.callbackId is callbackId | |
$log.debug "[Socket]", "Request:", reqData.info, "Response:", resp.info | |
socket.off 'response', respReceiver | |
{status, textStatus, body, headers} = resp.info | |
callback status, JSON.stringify(body), fakeResponseHeader(headers), textStatus | |
socket.on 'response', respReceiver | |
socket.emit 'request', reqData | |
(method, url) -> | |
# if url start with "$websocket", switch to websocket backend | |
backend = if isSocketReq(url) then socketBackend else httpBackend | |
backend.apply this, arguments | |
]) | |
]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Could you describe in a bit more detail what happens in one cycle? I'm unsure whether the case I'm working on is analogous to this or not.