Giter Club home page Giter Club logo

tornadio's Introduction

Tornadio

If you're looking for socket.io 0.7+ integration library, check TornadIO2

Contributors

Credits

Authors of SocketTornad.IO project:

This is implementation of the Socket.IO realtime transport library on top of the Tornado framework.

Short Background

There's a library which already implements Socket.IO integration using Tornado framework - SocketTornad.IO, but it was not finished, has several known bugs and not very well structured.

TornadIO is different from SocketTornad.IO library in following aspects:

  • Simpler internal design, easier to maintain/extend
  • No external dependencies (except of the Tornado itself and simplejson on python < 2.6)
  • Properly handles on_open/on_close events for polling transports
  • Proper Socket.IO protocol parser
  • Proper unicode support
  • Actively maintained

Introduction

In order to start working with the TornadIO library, you need to know some basic concepts on how Tornado works. If you don't, please read Tornado tutorial, which can be found here.

If you're familiar with Tornado, do following to add support for Socket.IO to your application:

1. Derive from tornadio.SocketConnection class and override on_message method (on_open/on_close are optional): :

class MyConnection(tornadio.SocketConnection):
  def on_message(self, message):
    pass

2. Create handler object that will handle all socket.io transport related functionality: :

MyRouter = tornadio.get_router(MyConnection)

3. Add your handler routes to the Tornado application: :

application = tornado.web.Application(
  [MyRouter.route()],
  socket_io_port = 8000)
  1. Start your application
  2. You have your socket.io server running at port 8000. Simple, right?

Goodies

SocketConnection class provides three overridable methods:

  1. on_open called when new client connection was established.
  2. on_message called when message was received from the client. If client sent JSON object, it will be automatically decoded into appropriate Python data structures.
  3. on_close called when client connection was closed (due to network error, timeout or just client-side disconnect)

Each SocketConnection has send() method which is used to send data to the client. Input parameter can be one of the:

  1. String/unicode string - sent as is (though with utf-8 encoding)
  2. Arbitrary python object - encoded as JSON string automatically
  3. List of python objects/strings - encoded as series of the socket.io messages using one of the rules above.

Configuration

You can configure your handler by passing settings to the get_router function as a dict object.

  • enabled_protocols: This is a list of the socket.io protocols the server will respond requests for. Possibilities are:
  • websocket: HTML5 WebSocket transport
  • flashsocket: Flash emulated websocket transport. Requires Flash policy server running on port 843.
  • xhr-multipart: Works with two connections - long GET connection with multipart transfer encoding to receive updates from the server and separate POST requests to send data from the client.
  • xhr-polling: Long polling AJAX request to read data from the server and POST requests to send data to the server. If message is available, it will be sent through open GET connection (which is then closed) or queued on the server otherwise.
  • jsonp-polling: Similar to the xhr-polling, but pushes data through the JSONp.
  • htmlfile: IE only. Creates HTMLFile control which reads data from the server through one persistent connection. POST requests are used to send data back to the server.
  • session_check_interval: Specifies how often TornadIO will check session container for expired session objects. In seconds.
  • session_expiry: Specifies session expiration interval, in seconds. For polling transports it is actually maximum time allowed between GET requests to consider virtual connection closed.
  • heartbeat_interval: Heartbeat interval for persistent transports. Specifies how often heartbeat events should be sent from the server to the clients.
  • xhr_polling_timeout: Timeout for long running XHR connection for xhr-polling transport, in seconds. If no data was available during this time, connection will be closed on server side to avoid client-side timeouts.

Resources

You're not limited with one connection type per server - you can serve different clients in one server instance.

By default, all socket.io clients use same resource - 'socket.io'. You can change resource by passing resource parameter to the get_router function: :

ChatRouter = tornadio.get_router(MyConnection, resource='chat')

In the client, provide resource you're connecting to, by passing resource parameter to io.Socket constructor: :

sock = new io.Socket(window.location.hostname, {
             port: 8001,
             resource: 'chat',
           });

As it was said before, you can have as many connection types as you want by having unique resources for each connection type: :

ChatRouter = tornadio.get_router(ChatConnection, resource='chat')
PingRouter = tornadio.get_router(PingConnection, resource='ping')
MapRouter = tornadio.get_router(MapConnection, resource='map')

application = tornado.web.Application(
  [ChatRouter.route(), PingRouter.route(), MapRouter.route()],
  socket_io_port = 8000)

Extra parameters

If you need some kind of user authentication in your application, you have two choices:

  1. Send authentication token as a first message from the client
  2. Provide authentication token as part of the resource parameter

TornadIO has support for extra data passed through the socket.io resources.

You can provide regexp in extra_re parameter of the get_router function and matched data can be accessed in your on_open handler as kwargs['extra']. For example: :

class MyConnection(tornadio.SocketConnection):
  def on_open(self, *args, **kwargs):
    print 'Extra: %s' % kwargs['extra']

ChatRouter = tornadio.get_router(MyConnection, resource='chat', extra_re='\d+', extra_sep='/')

and on the client-side: :

sock = new io.Socket(window.location.hostname, {
             port: 8001,
             resource: 'chat/123',
           });

If you will run this example and connect with sample client, you should see 'Extra: 123' printed out.

Starting Up

Best Way: SocketServer

We provide customized version (shamelessly borrowed from the SocketTornad.IO library) of the HttpServer, which simplifies start of your TornadIO server.

To start it, do following (assuming you created application object before):

if __name__ == "__main__":
  socketio_server = SocketServer(application)

SocketServer will automatically start Flash policy server, if required.

SocketServer by default will also automatically start ioloop. In order to prevent this behaviour and perform some additional action after socket server is created you can use auto_start param. In this case you should start ioloop manually:

if __name__ == "__main__":
  socketio_server = SocketServer(application, auto_start=False)
  logging.info('You can perform some actions here')    
  ioloop.IOLoop.instance().start()

Going big

So, you've finished writting your application and want to share it with rest of the world, so you started thinking about scalability, deployment options, etc.

Most of the Tornado servers are deployed behind the nginx, which also used to serve static content. This won't work very well with TornadIO, as nginx does not support HTTP/1.1, does not support websockets and XHR-Multipart transport just won't work.

So, to load balance your TornadIO instances, use alternative solutions like HAProxy. However, HAProxy does not work on Windows, so if you plan to deploy your solution on Windows platform, you might want to take look into MLB.

Scalability is completely different beast. It is up for you, as a developer, to design scalable architecture of the application.

For example, if you need to have one large virtual server out of your multiple physical processes (or even servers), you have to come up with some kind of the synchronization mechanism. This can be either common meeting point (and also point of failure), like memcached, redis, etc. Or you might want to use some transporting mechanism to communicate between servers, for example something AMQP based, ZeroMQ or just plain sockets with your protocol.

For example, with message queues, you can treat TornadIO as a message gateway between your clients and your server backend(s).

Examples

Chatroom Example

There is a chatroom example application from the SocketTornad.IO library, contributed by swanson. It is in the examples/chatroom directory.

Ping Example

Simple ping/pong example to measure network performance. It is in the examples/ping directory.

Transports Example

Simple ping/pong example with chat-like interface with selectable transports. It is in the examples/transports directory.

tornadio's People

Contributors

mrjoes avatar kachayev avatar kmike avatar chengfred avatar statico avatar rckclmbr avatar peterbe avatar psobot avatar hamax avatar

Stargazers

FR avatar BugMan avatar Bhavya Peshavaria avatar  avatar Jorge Gomez avatar Erik Nomitch avatar Rafid Aslam avatar sefanll avatar  avatar kevinduke avatar Shuxiao Wang avatar Adriano Gil avatar  avatar  avatar  avatar Eddie Wu avatar jwchen avatar Nick Klein avatar  avatar  avatar Chaney Zorn avatar titanjf avatar mingz avatar Kai Li avatar Tianle Chen avatar Justin Faler avatar Meta avatar schangech avatar  avatar huangnauh avatar llgoo avatar Adam Swanda avatar Victor Login avatar Jim Liu 宝玉 avatar Oktay Sancak avatar Chelder avatar  avatar Tian avatar  avatar Mingze Zhang avatar Angus H. avatar 三月沙 avatar  avatar Eric Zhao avatar YAJIE avatar Gregory Tereshko avatar Mason Sun avatar Xiadong Zhu avatar  avatar  avatar  avatar AssassinPig avatar Sim Sun avatar saeed avatar khahux avatar Jim Zhan avatar  avatar yflau avatar Lloyd Zhou avatar messense avatar  avatar parachvte avatar cabinw avatar Kiran Gangadharan avatar Vaidik Kapoor avatar Kyrre Wahl Kongsgård avatar  avatar  avatar RenderCoder avatar zoowii avatar Glavin Wiechert avatar ShiningChan avatar TUPUNCO avatar Hannes Stöven avatar xuxiandi avatar xiangyuan avatar fan avatar GuohuiZhou avatar  avatar Marc Adam avatar Max avatar byeval avatar Steve Peak avatar Gab Stehr avatar Blake VandeMerwe avatar winiex avatar  avatar Kun R avatar Marcel Tschopp avatar Yaşar İÇLİ avatar LEE Jaeyoung avatar Actionless Loveless avatar  avatar Lorren Biffin avatar Ivo Flipse avatar Fredrik Möllerstrand avatar Zhida avatar  avatar  avatar Yan Yu avatar

Watchers

 avatar  avatar ww2000e avatar Beck Xu avatar Aaron  avatar Wei Lin avatar Antonio Ignacio Campos Ruiz avatar  avatar  avatar  avatar Mason Sun avatar Eric Zhao avatar

tornadio's Issues

IOErrors with websockets on Tornado 2.1.1

I get a log full of:

2011-10-06 15:09:27,355 root ERROR Error in periodic callback
Traceback (most recent call last):
File "build/bdist.linux-i686/egg/tornadio/periodic.py", line 42, in _run
next_call = self.callback()
File "build/bdist.linux-i686/egg/tornadio/conn.py", line 141, in _heartbeat
self.close()
File "build/bdist.linux-i686/egg/tornadio/conn.py", line 85, in close
self._protocol.close()
File "/home/ubuntu/env/local/lib/python2.7/site-packages/tornado-2.1.1-py2.7.egg/tornado/websocket.py", line 121, in close
self.ws_connection.close()
File "/home/ubuntu/env/local/lib/python2.7/site-packages/tornado-2.1.1-py2.7.egg/tornado/websocket.py", line 497, in close
self._write_frame(True, 0x8, b(""))
File "/home/ubuntu/env/local/lib/python2.7/site-packages/tornado-2.1.1-py2.7.egg/tornado/websocket.py", line 400, in _write_frame
self.stream.write(frame)
File "/home/ubuntu/env/local/lib/python2.7/site-packages/tornado-2.1.1-py2.7.egg/tornado/iostream.py", line 213, in write
self._check_closed()
File "/home/ubuntu/env/local/lib/python2.7/site-packages/tornado-2.1.1-py2.7.egg/tornado/iostream.py", line 504, in _check_closed
raise IOError("Stream is closed")
IOError: Stream is closed

heartbeat not getting cancelled?

FlashPolicyServer intermittent hangups

hey -- I've been debugging an issue where in firefox it sometimes reverts to xhr-polling instead of flashsocket.

It seems like the problem might be with the FlashPolicyServer, which sometimes returns the policy and other times disconnects after the client sends

I'm able to repro as follows:

telnet server 843
Connected
Escape character is '^]'.

Connection closed by foreign host.

WebSocket ietf-hybi-07 support in Tornadio

Hi!

I just noticed that Firefox 6 was just released, with WebSockets re-enabled.
It seems to support WebSockets protocol version draft-ietf-hybi-thewebsocketprotocol-07.
Would it be possible for Tornadio to also support this protocol version?

I'm using Tornadio in my project, http://www.freeciv.net/ and it seems to work very nicely so far.
Good work! :)

Andreas R.
www.freeciv.net

HTTPError: HTTP 401: Unauthorized (Invalid session)

polling.py, line 66, raises this error if the XHR long-polling session expires.

Can you elaborate on this a bit more? How does this occur and why is the default 30 seconds? This seems to cause strange behavior on the client-side — how can my clients handle this gracefully?

Thanks :)

on_close not called on firefox and opera

Thanks for sharing this great library,

I have tested your chatroom example simultanously in three browser : chrome, opera and firefox.
It's work well until i noticed that if i close the app on opera and firefox, on_close function not called ( " A User has Left" not displayed),
but if i closed the app on chrome, on_close function is called.
do you know what happened to it?

Thanks.

One-step installation fails if tornado is not installed

This is a quite minor issue but it can break automatic deployment in some cases.

(myenv)> pip install tornado -e git+git://github.com/MrJoes/tornadio.git#egg=tornadio

Downloading/unpacking tornado
  Running setup.py egg_info for package tornado
    warning: no files found matching '*.png' under directory 'demos'
Obtaining tornadio from git+git://github.com/MrJoes/tornadio.git#egg=tornadio
  Cloning git://github.com/MrJoes/tornadio.git to ./envs/myend/src/tornadio
  Running setup.py egg_info for package tornadio
    Traceback (most recent call last):
      File "<string>", line 14, in <module>
      File "/Users/kmike/envs/myenv/src/tornadio/setup.py", line 9, in <module>
        import tornadio
      File "tornadio/__init__.py", line 15, in <module>
        from tornadio.router import get_router
      File "tornadio/router.py", line 13, in <module>
        from tornado import ioloop
    ImportError: No module named tornado
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 14, in <module>

  File "/Users/kmike/envs/myenv/src/tornadio/setup.py", line 9, in <module>

    import tornadio

  File "tornadio/__init__.py", line 15, in <module>

    from tornadio.router import get_router

  File "tornadio/router.py", line 13, in <module>

    from tornado import ioloop

ImportError: No module named tornado

----------------------------------------
Command python setup.py egg_info failed with error code 1
Storing complete log in /Users/kmike/.pip/pip.log

That's obviously because of imports in tornadio/init.py and the fact tornado version is defined there.

Pip installs packages only as a last step. It runs 'setup.py egg_info' before installing so it is not safe to assume tornado is installed in tornadio's setup.py.

I wasn't able to come up with a good fix. The easiest way seems to be to duplicate the version definition. Another possibility is to remove 'shortcuting' imports from init.py. I think catching import errors in init.py is much worse.

WebSocketException: Handshake Status 200 when talking to a Tornadio server

Hi,

I have a tornadio server used in a HTML/JS app working fine. I'd like to test this using a Python script using websocket-client.

When I connect to the example tornadio chatroom server using websocket-client as follows:

import websocket
ws = websocket.create_connection("ws://192.168.2.101:8001")

I get the following error:

WebSocketException: Handshake Status 200

I can see that the client is looking for a status of 101 back from the server but it gets 200. I can't see anywhere in tornadio or tornado where it would get back a 101.

Any suggestions as to where to look would be appreciated.

Derek.

Chrome SSL Websockets

I dont understand why i am getting this error after switching to SSL..

Can anyone help???
im serving completely from a SSL enabled server.. :/

Error during WebSocket handshake: location mismatch: wss://dev.website.de/socket.io/websocket != ws://dev.website.de/socket.io/websocket

Missing connection when trying to close it

I don't know exactly what happened for the connection to disappear. In my log files I get this:

[E 110606 10:44:18 iostream:237] Uncaught exception, closing connection.
    Traceback (most recent call last):
      File "/usr/local/lib/python2.6/dist-packages/tornado-1.2.1-py2.6.egg/tornado/iostream.py", line 234, in wrapper
        callback(*args)
      File "/usr/local/lib/python2.6/dist-packages/tornado-1.2.1-py2.6.egg/tornado/stack_context.py", line 156, in wrapped
        callback(*args, **kwargs)
      File "/usr/local/lib/python2.6/dist-packages/tornado-1.2.1-py2.6.egg/tornado/websocket.py", line 206, in on_connection_close
        self.on_close()
      File "/usr/local/lib/python2.6/dist-packages/TornadIO-0.0.4-py2.6.egg/tornadio/persistent.py", line 86, in on_close
        self.connection.is_closed = True
    AttributeError: 'TornadioWebSocketHandler' object has no attribute 'connection'

I can accept that a connection breaks every now and then. Especially with the brittle Flash stuff and, after all, WebSocket is something really new. However, this error indicates something is fundamentally wrong. It shouldn't try to do things to the connection if it's no longer there.

Because my code is not involved in the traceback, as you can see, there's no way for me to catch the disconnection and do something like "Sorry, you're disconnected yada yada yada"

Possible memory leak in send

Hi,
I've been stress testing a chat server based on the chatroom example. Here's the client code:

import threading
import time
import websocket

threads = dict()

class PingThread(threading.Thread):
    def run(self):
        ws = websocket.create_connection("ws://192.168.2.119:8001/chat/%s/websocket" % self.name)
        while True:
            response = ws.recv()
            ws.send("~m~32~m~Blah hello from %s" % self.name)
            time.sleep(2)

if __name__=="__main__":

    threadcount = 100
    print "Starting %s threads" % (threadcount,)
    for i in range(threadcount):
        t = PingThread()
        t.name = "%d" % i
        t.start()
    print "Threads started"

The only mod to the chat server I've made is naming the resource 'chat' and allowing for extra args:

-ChatRouter = tornadio.get_router(ChatConnection)
+ChatRouter = tornadio.get_router(ChatConnection,
+        resource='chat', 
+        extra_re='[^\/]*', extra_sep='/')

When I run the client for a long time I notice the memory taken up by the Python chat server process just keeps increasing. If I change the chat server to return a very large string on each message instead of the original message sent, then the effect is noticeable after a few seconds.

Any ideas on where I could look to see where it might be leaking?

Derek.

session_id blank

I'm trying to swap in tornadio as a replacement to SocketTornad.io, but the session_id appears to be blank (at least in chrome websocket).

With SocketTornad.io, the handler gets a session object (self.session.id which points to the session id)

In tornadio, I see kwargs['session_id'] with the handler, but it is always blank.

Any help here would be great, thanks

WebSockets transport doesn't work with tornado 1.2

This can be reproduced by running examples/tranports/transports.py.
It works fine for WebSockets with tornado 1.1 installed.
After updating to 1.2 example falls back to xhr-polling.
Browser is Chrome 9.0.597.102.

Clean stop of the SocketServer

Hello,
First of all, thank you very much tornadio. I like it very much.

I don't find a way to make a clean stop of the SocketServer. I would like to be able to start/stop/restart but i don't have access to the internal io_loop instance. Is it possible to make it accessible?

Is there a better way to make a clean stop?

Best
luc

Tornado iostream error with tornadio

randomly found the following error, current socket.io session not respond, heartbeat is ok. It able to accept new socket.io connections.

ERROR:root:Uncaught exception POST /post/98/ (192.168.60.60)
HTTPRequest(protocol='http', host='192.168.60.60:40000', method='POST', uri='/post/98/', version='HTTP/1.1', remote_ip='192.168.60.60', remote_ip='192.168.60.60', body='msg=webgameTips::hello', headers={'Host': '192.168.60.60:40000', 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': '91', 'Accept': '/', 'User-Agent': 'curl/7.19.7 (i486-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15'})
Traceback (most recent call last):
File "/usr/local/lib/python2.6/dist-packages/tornado-1.1-py2.6.egg/tornado/web.py", line 814, in _stack_context
yield
File "/usr/local/lib/python2.6/dist-packages/tornado-1.1-py2.6.egg/tornado/stack_context.py", line 77, in StackContext
yield
File "/usr/lib/python2.6/contextlib.py", line 113, in nested
yield vars
File "/usr/local/lib/python2.6/dist-packages/tornado-1.1-py2.6.egg/tornado/stack_context.py", line 126, in wrapped
callback(_args, *_kwargs)
File "/usr/local/lib/python2.6/dist-packages/tornado-1.1-py2.6.egg/tornado/httpserver.py", line 300, in _on_write_complete
self._finish_request()
File "/usr/local/lib/python2.6/dist-packages/tornado-1.1-py2.6.egg/tornado/httpserver.py", line 319, in _finish_request
self.stream.read_until("\r\n\r\n", self._on_headers)
File "/usr/local/lib/python2.6/dist-packages/tornado-1.1-py2.6.egg/tornado/iostream.py", line 99, in read_until
self._add_io_state(self.io_loop.READ)
File "/usr/local/lib/python2.6/dist-packages/tornado-1.1-py2.6.egg/tornado/iostream.py", line 315, in _add_io_state
self.io_loop.update_handler(self.socket.fileno(), self._state)
AttributeError: 'NoneType' object has no attribute 'fileno'
ERROR:root:Cannot send error response after headers written

partial code can be found at gist: https://gist.github.com/992750

Resumable Sessions

I'm not sure if this is a bug, a feature request or if I'm just doing this wrong as I'm new to socket.io. What I'd like to do is use tornadio with xhr-polling to create some kind of chatroom like application. This is working fine so far except when a user just hits reload in his browser or simply closes / opens browser again. In that case I'd like to send him back automatically to his current channel. So I open a new socket.io connection for the previous channel after hes back. However, this will get me a new session ID and shortly after I can see the old session ID timing out and have me broadcasting a "member left" message although the member is still back in the channel.

Is there any way to specify a session Id on my own (e.g. by using a user ID) and make tornadio recognize it so I can really make sure that the same user will not have multiple sessions? I gave it a try by just hard coding the session_id value in the Session class but got very strange results.

Best way to pass settings dictionary to torandio.SocketConnection?

I would like to pass a settings dictionary to tornadio.SocketConnection. What is the best way?

Right now, I'm passing a settings dictionary to the tornado Application, like below.

application = tornado.web.Application([
    MyRouter.route()
], **settings);

and then I access this settings dictionary inside a subclass of tornadio.SocketConnection with

self._protocol.handler.settings

This seems hacky, Is there a better way to do it?

How should I stop the server?

I'm wanting to run this from a program instead on from command line. This requires me to be able to stop it, which means I cant just close the cmd window or hit ctrl c. Could some explain to me how I should go about stopping the server and the io_loop.

WebSocket connection error: Handshake status 200 OK

Our WebSocket client developed in Python Version python3 connects to our internal WebSocket server using ://wss protocol and we are able to exchange data via WebSocket. Works well.
However, when our WebSocket client is connecting to our internal WebSocket server (or) external WebSocket server using ://ws protocol, the handshake exceptions. The error that we receive:

2024-03-11 18:19:12,166 [INFO] # connectWS Exception.. wsConnect count
2024-03-11 18:19:12,166 [ERROR] Try: 4, ConnectWs :Handshake status 200 OK

Code Snippet that initiates the WebSocket connection:
ws = websocket.create_connection(configFile['WebSocketBackend'])

The URL of the WebSocket server is retrieved from the configfile. Details of these can be provided upon the engagement.

Is there a way to access secure cookies from a SocketConnection?

Is there a way to get/set cookies from within a SocketConnection class? Since the secure cookie handling methods are a part of the RequestHandler object in Tornado, I'm not sure this is possible. When on_open is called, I see the encrypted secure cookie contents in the HTTPRequest object, but I'd like to operate on them using Tornado's get/set_secure_cookie methods so everything is consistent.

gzip support

WebSockets version draft-ietf-hybi-thewebsocketprotocol-07 supports gzip,
would it be possible for Tornadio to support compression as well?

Thanks!

Andreas R.
www.freeciv.net

Websocket connection error Handshake status 200 OK

Hi,
I am also facing same issue when i tried to connect web socket, the issue is "Handshake status 200 OK",
My code is:
import websocket from websocket._exceptions
import WebSocketConnectionClosedException

ws = websocket.create_connection("ws://dev.charger.com/chargerWebService/connect/")

here
"dev.charger.com" is my domain,
"chargerWebService/connect/" is my path

I get this error like "Handshake status 200 OK" #21

Tornado 2 compatibility issue

Hey, just a heads up that I had to change the route regex for tornadio to work with tornado 2.

They don't allow unnamed groups in the routes regex.

My change billychasen@bda7f20

It seems to work fine, but could definitely use a code review

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.