PageRenderTime 65ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/server.js

https://gitlab.com/jiewenli/nodejs-c4
JavaScript | 542 lines | 486 code | 47 blank | 9 comment | 116 complexity | 67a8f30774fff5bd6c9a375b36771947 MD5 | raw file
  1. /**
  2. * New node file
  3. */
  4. var http = require('http');
  5. var url = require("url");
  6. var fs = require('fs');
  7. var net = require('net');
  8. var HashMap = require('./hashmap.js').HashMap;
  9. var ev = require('./event.js');
  10. var VERSION="0.20.0"
  11. var userMap = new HashMap();
  12. function newSession(user, hash){
  13. var obj = new Object();
  14. obj.remoteHost = '';
  15. obj.remotePort = 80;
  16. obj.sid = hash;
  17. obj.user = user;
  18. obj.socket = null;
  19. obj.sequence = 0;
  20. obj.cachedEvents = [];
  21. obj.closed = false;
  22. obj.pausecb = null;
  23. obj.paused = false;
  24. obj.passiveread = true;
  25. obj.closecb = null;
  26. obj.close = function() {
  27. if(null != obj.socket){
  28. obj.socket.destroy();
  29. obj.socket = null;
  30. }
  31. obj.closed = true;
  32. if(userMap.has(obj.user)){
  33. var userSessions = userMap.get(obj.user);
  34. userSessions.remove(obj.sid);
  35. }
  36. obj.cachedEvents= [];
  37. if(obj.closecb != null){
  38. obj.closecb(obj);
  39. }
  40. }
  41. obj.endWriter = function(){
  42. if(null != obj.writer && obj.passiveread){
  43. obj.writer.end();
  44. obj.writer = null;
  45. }
  46. if(null != obj.socket && obj.passiveread){
  47. obj.socket.pause();
  48. }
  49. }
  50. obj.cacheData = function(data){
  51. obj.cachedEvents.push(data);
  52. }
  53. obj.pause = function(){
  54. if(null != obj.socket){
  55. obj.socket.pause();
  56. if(null != obj.pausecb)
  57. {
  58. obj.pausecb();
  59. }
  60. }
  61. obj.paused = true;
  62. }
  63. obj.checkCachedData = function(){
  64. if(null == obj.writer || obj.closed)
  65. {
  66. return;
  67. }
  68. for(var i = 0; i < obj.cachedEvents.length; i++){
  69. if(!obj.writer.write(obj.cachedEvents[i])){
  70. console.log("##########Busy again@@");
  71. }
  72. }
  73. obj.cachedEvents= [];
  74. };
  75. return obj;
  76. }
  77. function getCreateSession(user, hash, writer, passiveread){
  78. if(!userMap.has(user)){
  79. userMap.set(user, new HashMap());
  80. }
  81. var userSessions = userMap.get(user);
  82. if(!userSessions.has(hash)){
  83. userSessions.set(hash, newSession(user, hash));
  84. }
  85. var session = userSessions.get(hash);
  86. if(null != writer)
  87. {
  88. session.writer = writer;
  89. }
  90. session.passiveread = passiveread;
  91. return session;
  92. }
  93. function onIndex(request, response) {
  94. fs.readFile('./index.html', function (err, html) {
  95. if (err) {
  96. throw err;
  97. }
  98. response.writeHead(200, {"Content-Type": "text/html"});
  99. response.write(html.toString().replace("${version}",VERSION).replace("${version}",VERSION));
  100. response.end()
  101. });
  102. }
  103. var HTTP_REQUEST_EVENT_TYPE=1000;
  104. var ENCRYPT_EVENT_TYPE=1501;
  105. var EVENT_TCP_CONNECTION_TYPE = 12000;
  106. var EVENT_USER_LOGIN_TYPE=12002;
  107. var EVENT_TCP_CHUNK_TYPE=12001;
  108. var EVENT_SOCKET_READ_TYPE=13000;
  109. var TCP_CONN_OPENED = 1;
  110. var TCP_CONN_CLOSED = 2;
  111. var ENCRYPTER_NONE = 0;
  112. var ENCRYPTER_SE1 = 1;
  113. function handleEvent(evv, user, writer, passiveread, allsessions){
  114. //console.log("Session[" + evv.hash + "] handle event:" + evv.type);
  115. switch(evv.type){
  116. case EVENT_USER_LOGIN_TYPE:
  117. {
  118. if(userMap.has(user)){
  119. userMap.get(user).forEach(function(sess, hash) {
  120. sess.close();
  121. });
  122. userMap.get(user).clear();
  123. }
  124. userMap.remove(user);
  125. return null;
  126. }
  127. case HTTP_REQUEST_EVENT_TYPE:
  128. {
  129. //var writer = ispull?response:null;
  130. var session = getCreateSession(user, evv.hash, writer, passiveread);
  131. currentSession = session;
  132. var host = evv.host;
  133. var port = 80;
  134. if(evv.method.toLowerCase() == 'connect'){
  135. port = 443;
  136. }
  137. var ss = host.split(":");
  138. if(ss.length == 2){
  139. host = ss[0];
  140. port = parseInt(ss[1]);
  141. }
  142. if(null != session.socket && session.remoteHost == host && session.remotePort == port){
  143. session.socket.write(evv.rawContent);
  144. return;
  145. }
  146. session.remoteHost = host;
  147. session.remotePort = port;
  148. if(null != session.socket){
  149. session.socket.destroy();
  150. }
  151. var remoteaddr = host+":" + port;
  152. var client = net.connect(port, host , function() {
  153. console.log("####Connected:" + remoteaddr + " for hash:" + evv.hash);
  154. session.socket = client;
  155. if(passiveread || session.paused){
  156. session.pause();
  157. }
  158. session.sequence = 0;
  159. if(evv.method.toLowerCase() == 'connect'){
  160. var established = ev.newEvent(EVENT_TCP_CHUNK_TYPE, 1, session.sid);
  161. established.seq = session.sequence++;
  162. established.content=new Buffer("HTTP/1.1 200 OK\r\n\r\n");
  163. if(null != session.writer)
  164. {
  165. session.writer.write(ev.encodeChunkTcpChunkEvent(established));
  166. }else{
  167. session.cacheData(ev.encodeChunkTcpChunkEvent(established));
  168. }
  169. }else{
  170. session.socket.write(evv.rawContent);
  171. //console.log("####writed:" + evv.rawContent.toString());
  172. }
  173. });
  174. client.on('data', function(data) {
  175. var chunk = ev.newEvent(EVENT_TCP_CHUNK_TYPE, 1, session.sid);
  176. chunk.seq = session.sequence++;
  177. chunk.content=data;
  178. console.log("###[" + session.sid + "]recv chunk:[" + chunk.seq + ":" + data.length + "]");
  179. if(null != session.writer && !session.paused){
  180. session.checkCachedData();
  181. if(!session.writer.write(ev.encodeChunkTcpChunkEvent(chunk))){
  182. session.pause();
  183. }
  184. }else{
  185. session.pause();
  186. session.cacheData(ev.encodeChunkTcpChunkEvent(chunk));
  187. }
  188. });
  189. client.on('end', function() {
  190. });
  191. client.on('error', function(err) {
  192. console.log("####Failed to connect:" + remoteaddr + " :" + err);
  193. });
  194. client.on('close', function(had_error) {
  195. if(remoteaddr == (session.remoteHost + ":" + session.remotePort)){
  196. console.log("####Close connection for " + remoteaddr);
  197. var closed = ev.newEvent(EVENT_TCP_CONNECTION_TYPE, 1, session.sid);
  198. closed.status = TCP_CONN_CLOSED;
  199. closed.addr = remoteaddr;
  200. if(null != session.writer){
  201. session.checkCachedData();
  202. session.writer.write(ev.encodeChunkConnectionEvent(closed));
  203. }else{
  204. //cache this event.
  205. session.cacheData(ev.encodeChunkConnectionEvent(closed));
  206. }
  207. session.endWriter();
  208. session.close();
  209. }
  210. });
  211. return session;
  212. }
  213. case EVENT_TCP_CONNECTION_TYPE:
  214. {
  215. var session = getCreateSession(user, evv.hash, writer, passiveread);
  216. currentSession = session;
  217. session.close();
  218. return null;
  219. }
  220. case EVENT_TCP_CHUNK_TYPE:
  221. {
  222. var session = getCreateSession(user, evv.hash, writer, passiveread);
  223. currentSession = session;
  224. if(null == session.socket){
  225. session.close();
  226. return null;
  227. }
  228. session.socket.write(evv.content);
  229. return session;
  230. }
  231. case EVENT_SOCKET_READ_TYPE:
  232. {
  233. if(evv.hash == 0 && null != allsessions){
  234. allsessions.forEach(function(sess, hash) {
  235. sess.checkCachedData();
  236. });
  237. return null;
  238. }
  239. var session = getCreateSession(user, evv.hash, writer, passiveread);
  240. var check = function(){
  241. for(var i = 0; i < session.cachedEvents.length; i++){
  242. writer.write(session.cachedEvents[i]);
  243. }
  244. session.cachedEvents= [];
  245. if(session.closed){
  246. session.endWriter();
  247. return;
  248. }
  249. if(session.writer == null){
  250. return;
  251. }
  252. if(session.socket != null)
  253. {
  254. session.socket.resume();
  255. }else{
  256. setTimeout(check, 1);
  257. }
  258. };
  259. check();
  260. return session;
  261. }
  262. default:
  263. {
  264. console.log("################Unsupported type:" + evv.type);
  265. break;
  266. }
  267. }
  268. return null;
  269. }
  270. function now(){
  271. return Math.round((new Date()).getTime()/ 1000);
  272. }
  273. function onInvoke(request, response) {
  274. var length = parseInt(request.headers['content-length']);
  275. var user = request.headers['usertoken'];
  276. var ispull = (url.parse(request.url).pathname == '/pull');
  277. var c4miscinfo = request.headers['c4miscinfo'];
  278. if(c4miscinfo != null){
  279. var miscInfo = c4miscinfo.split('_');
  280. var timeout = parseInt(miscInfo[0]);
  281. var maxread = parseInt(miscInfo[1]);
  282. }
  283. var postData = new Buffer(length);
  284. var recvlen = 0;
  285. var responsed = false;
  286. var startTime = now();
  287. var currentSession = null;
  288. if(ispull){
  289. setTimeout(function(){
  290. if(null != currentSession && currentSession.writer != null){
  291. currentSession.endWriter();
  292. }else{
  293. response.end();
  294. }
  295. }, timeout*1000);
  296. }
  297. response.writeHead(200, {"Content-Type": "image/jpeg", "C4LenHeader":1, "Connection":"keep-alive"});
  298. request.addListener("data", function(chunk) {
  299. chunk.copy(postData, recvlen, 0);
  300. recvlen += chunk.length;
  301. });
  302. response.on('drain', function () {
  303. if(null != currentSession && null != currentSession.socket && null != currentSession.writer){
  304. currentSession.socket.resume();
  305. }
  306. });
  307. request.addListener("end", function() {
  308. if(recvlen == length && length > 0){
  309. var readBuf = ev.newReadBuffer(postData);
  310. var events = ev.decodeEvents(readBuf);
  311. //console.log("Total events is "+events.length);
  312. for (var i = 0; i < events.length; i++)
  313. {
  314. var evv = events[i];
  315. //console.log("Decode event " + evv.type + ":" + evv.version + ":" + evv.hash);
  316. var writer = response;
  317. if(!ispull){
  318. writer = null;
  319. }
  320. currentSession = handleEvent(evv, user, writer, true, null);
  321. }
  322. }else{
  323. console.log("Request not full data ");
  324. }
  325. if(!ispull){
  326. response.end();
  327. }
  328. });
  329. }
  330. var handle = {}
  331. handle["/"] = onIndex;
  332. handle["/pull"] = onInvoke;
  333. handle["/push"] = onInvoke;
  334. function route(pathname, request, response) {
  335. if (typeof handle[pathname] === 'function') {
  336. handle[pathname](request, response);
  337. } else {
  338. response.writeHead(404, {"Content-Type": "text/plain"});
  339. response.end();
  340. }
  341. }
  342. function onRequest(request, response) {
  343. var pathname = url.parse(request.url).pathname;
  344. route(pathname, request, response)
  345. }
  346. var ipaddr = process.env.OPENSHIFT_NODEJS_IP || "0.0.0.0";
  347. var port = process.env.VCAP_APP_PORT ||process.env.OPENSHIFT_NODEJS_PORT || process.env.PORT || 8080;
  348. var server = http.createServer(onRequest);
  349. server.listen(port, ipaddr);
  350. console.log('Server running at '+ ipaddr + ":" + port);
  351. var userConnTable = new HashMap();
  352. var userConnBufTable = new HashMap();
  353. var userConnCacheTable = new HashMap();
  354. function getUserConnSessionGroup(user, index){
  355. if(!userConnTable.has(user)){
  356. userConnTable.set(user, new HashMap());
  357. }
  358. var sessions = userConnTable.get(user);
  359. if(!sessions.has(index)){
  360. sessions.set(index, new HashMap());
  361. }
  362. return sessions.get(index);
  363. }
  364. function getUserConnBuffer(user, index){
  365. if(!userConnBufTable.has(user)){
  366. userConnBufTable.set(user, new HashMap());
  367. }
  368. var bufs = userConnBufTable.get(user);
  369. if(!bufs.has(index)){
  370. bufs.set(index, new Buffer(0));
  371. }
  372. return bufs.get(index);
  373. }
  374. function getUserConnCache(user, index){
  375. if(!userConnCacheTable.has(user)){
  376. userConnCacheTable.set(user, new HashMap());
  377. }
  378. var caches = userConnCacheTable.get(user);
  379. if(!caches.has(index)){
  380. caches.set(index, []);
  381. }
  382. return caches.get(index);
  383. }
  384. function resumeCachedConn(ss, writer){
  385. ss.forEach(function(session, sid) {
  386. session.writer = writer;
  387. session.paused = false;
  388. session.checkCachedData();
  389. if(null != session.socket){
  390. session.socket.resume();
  391. }
  392. });
  393. }
  394. server.on('upgrade', function(req, connection, head) {
  395. var user = req.headers['usertoken'];
  396. var localConn = connection;
  397. var index = req.headers['connectionindex'];
  398. var keepalive = parseInt(req.headers['keep-alive']);
  399. console.log('Websocket establis with index:' + index);
  400. localConn.write(
  401. 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
  402. 'Upgrade: WebSocket\r\n' +
  403. 'Connection: Upgrade\r\n' +
  404. '\r\n'
  405. );
  406. var cumulateBuf = getUserConnBuffer(user, index);
  407. var chunkLen = -1;
  408. var sesss = getUserConnSessionGroup(user, index);
  409. var connCache = getUserConnCache(user, index);
  410. resumeCachedConn(sesss, localConn);
  411. var localConnReady = true;
  412. setTimeout(function(){
  413. localConn.destroy();
  414. }, keepalive*1000);
  415. localConn.on("data", function(data) {
  416. if(null == cumulateBuf || cumulateBuf.length == 0){
  417. cumulateBuf = data;
  418. }else{
  419. cumulateBuf = Buffer.concat([cumulateBuf, data])
  420. }
  421. for(;;){
  422. if(chunkLen == -1){
  423. if(cumulateBuf.length >= 4){
  424. var tmplen = cumulateBuf.length;
  425. chunkLen = cumulateBuf.readInt32BE(0);
  426. cumulateBuf = cumulateBuf.slice(4, tmplen);
  427. }else{
  428. return;
  429. }
  430. }
  431. if(chunkLen > 0){
  432. if(cumulateBuf.length >= chunkLen){
  433. var chunk = new Buffer(chunkLen);
  434. cumulateBuf.copy(chunk, 0, 0, chunkLen);
  435. var tmplen = cumulateBuf.length;
  436. cumulateBuf = cumulateBuf.slice(chunkLen, tmplen);
  437. chunkLen = -1;
  438. var readBuf = ev.newReadBuffer(chunk);
  439. var sess = handleEvent(ev.decodeEvent(readBuf), user, localConn, false, sesss);
  440. if(null != sess){
  441. sesss.set(sess.sid, sess);
  442. sess.closecb = function(session){
  443. sesss.remove(session.sid);
  444. };
  445. sess.pausecb = function(session){
  446. localConnReady = false;
  447. sesss.forEach(function(session, sid) {
  448. if(null != session.socket){
  449. session.socket.pause();
  450. session.writer = null;
  451. }
  452. session.paused = true;
  453. });
  454. };
  455. sess.cacheData = function(data){
  456. connCache.push(data);
  457. };
  458. sess.checkCachedData = function(){
  459. if(!localConnReady){
  460. return;
  461. }
  462. for(var i = 0; i < connCache.length; i++){
  463. if(!localConn.write(connCache[i])){
  464. console.log('########busy again');
  465. }
  466. }
  467. connCache = [];
  468. };
  469. }
  470. }else{
  471. return;
  472. }
  473. }
  474. }
  475. });
  476. localConn.on("end", function() {
  477. });
  478. localConn.on("close", function() {
  479. console.log('@@@@@@@@@@@@websocket closed');
  480. sesss.forEach(function(session, sid) {
  481. if(null != session.socket){
  482. session.socket.pause();
  483. session.writer = null;
  484. }
  485. });
  486. userConnBufTable.get(user).set(index, cumulateBuf);
  487. userConnCacheTable.get(user).set(index, connCache);
  488. });
  489. localConn.on("error", function() {
  490. });
  491. localConn.on("drain", function() {
  492. localConnReady = true;
  493. resumeCachedConn(sesss, localConn);
  494. });
  495. });