Giter Club home page Giter Club logo

sockjs-go's Introduction

Build Status GoDoc Coverage Status

What is SockJS?

SockJS is a JavaScript library (for browsers) that provides a WebSocket-like object. SockJS gives you a coherent, cross-browser, Javascript API which creates a low latency, full duplex, cross-domain communication channel between the browser and the web server, with WebSockets or without. This necessitates the use of a server, which this is one version of, for GO.

SockJS-Go server library

SockJS-Go is a SockJS server library written in Go.

For latest v3 version of sockjs-go use:

github.com/igm/sockjs-go/v3/sockjs

For v2 version of sockjs-go use:

gopkg.in/igm/sockjs-go.v2/sockjs

Using version v1 is not recommended (DEPRECATED)

gopkg.in/igm/sockjs-go.v1/sockjs

Note: using github.com/igm/sockjs-go/sockjs is not recommended. It exists for backwards compatibility reasons and is not maintained.

Versioning

SockJS-Go project adopted gopkg.in approach for versioning. SockJS-Go library details can be found here

With the introduction of go modules a new version v3 is developed and maintained in the master and has new import part github.com/igm/sockjs-go/v3/sockjs.

Example

A simple echo sockjs server:

package main

import (
	"log"
	"net/http"

	"github.com/igm/sockjs-go/v3/sockjs"
)

func main() {
	handler := sockjs.NewHandler("/echo", sockjs.DefaultOptions, echoHandler)
	log.Fatal(http.ListenAndServe(":8081", handler))
}

func echoHandler(session sockjs.Session) {
	for {
		if msg, err := session.Recv(); err == nil {
			session.Send(msg)
			continue
		}
		break
	}
}

SockJS Protocol Tests Status

SockJS defines a set of protocol tests to quarantee a server compatibility with sockjs client library and various browsers. SockJS-Go server library aims to provide full compatibility, however there are couple of tests that don't and probably will never pass due to reasons explained in table below:

Failing Test Explanation
XhrPolling.test_transport does not pass due to a feature in net/http that does not send content-type header in case of StatusNoContent response code (even if explicitly set in the code), details
WebSocket. Sockjs Go version supports RFC 6455, draft protocols hixie-76, hybi-10 are not supported
JSONEncoding As menioned in browser quirks section: "it's advisable to use only valid characters. Using invalid characters is a bit slower, and may not work with SockJS servers that have a proper Unicode support." Go lang has a proper Unicode support
RawWebsocket. The sockjs protocol tests use old WebSocket client library (hybi-10) that does not support RFC 6455 properly

WebSocket

As mentioned above sockjs-go library is compatible with RFC 6455. That means the browsers not supporting RFC 6455 are not supported properly. There are no plans to support draft versions of WebSocket protocol. The WebSocket support is based on Gorilla web toolkit implementation of WebSocket.

For detailed information about browser versions supporting RFC 6455 see this wiki page.

sockjs-go's People

Contributors

aronatkins avatar bbigras avatar cenkalti avatar druska avatar dustin avatar eelcocramer avatar fatih avatar fivegreenapples avatar fzambia avatar gebi avatar gngeorgiev avatar horstmannmat avatar igm avatar jamesgroat avatar jcheng5 avatar mariokostelac avatar mweibel avatar paulhovey avatar rjeczalik avatar scottmmjackson avatar tegioz avatar thehippo avatar tmc avatar wavded avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sockjs-go's Issues

Tag for v3

Hello @igm !

Noticed that recently you did migration to v3. I believe that it was rather painful experience due to how go.mod works with dependencies > v2, glad you managed to do it pushing this library to an actual stack 👍

Just tried to introduce v3 dependency:

$ go mod init test_sockjs
go: creating new go.mod: module test_sockjs

$ go get github.com/igm/sockjs-go/v3/sockjs
go: downloading github.com/igm/sockjs-go v2.0.1+incompatible
go: downloading github.com/igm/sockjs-go/v3 v3.0.0-20200426155823-50a65008b72f
go: found github.com/igm/sockjs-go/v3/sockjs in github.com/igm/sockjs-go/v3 v3.0.0-20200426155823-50a65008b72f

$ cat go.mod
module test_sockjs

go 1.14

require github.com/igm/sockjs-go/v3 v3.0.0-20200426155823-50a65008b72f // indirect

I suppose v3.0.0 tag for v3 required so go.mod can use dependency without pointing to exact commit.

Issues with URL Path matching logic

Because of the way URL path matching is done in the library using Regular Expressions and MatchString against the HTTP request, the overall path matching in this library is defective.

For example, if I register a handler with the prefix "echo", all of below match and work even though only the first one should be working. As long at the last part of the URL ends with the prefix string, the library will allow that URL to be handled.

http://server:port/echo/
http://server:port/test/echo/
http://server:port/echo/test/test/echo/

CheckOrigin doesn't work , how to set cors

//here is my options
opts := sockjs.DefaultOptions
opts.ResponseLimit = 4096
opts.RawWebsocket = true
opts.CheckOrigin = func(r *http.Request) bool {
fmt.Printf("check origin request %v\n", r.URL)

	return true
}
opts.Origin = "*"
opts.WebsocketUpgrader = &websocket.Upgrader{}
opts.WebsocketUpgrader.CheckOrigin = func(r *http.Request) bool { return true }

wsHandler := &WSockHandler{prefix, sockjs.NewHandler(prefix, opts, handler)}

Getting session id from Conn object

sockjs-go works fine.

but I think it needs a method to get session id.

cause some apps need to make channel to group connections.

in that case, that method required to group and share with other programs (like web) as a key for each connections.

CORS errors when trying to connect sockjs server from a local (file://) resource

PR #73 broke the ability to connect to a sockjs server based on this library from a html page launched as a local resource (file://) without use of web server.

This looks to be due to the addition of the check for incoming origin header "null" and then changing it to *. Earlier, when origin from client side is null, Access-Control-Allow-Origin was also set to "null" . But since its changed to *, Chrome does not allow it as a match for Origin "null"

I understand that sending "null" as allowed origin is not a good idea, but is there an alternative way get sockjs working form file:// resources. There are situations where we launch a html directly without web server.

Add context support in the socket session

I was wondering whether you'd accept a pull request for some sort of context support in the socket session. Or at least some way of being notified of the session's end.

My use case is that sometimes an incoming websocket message kicks off a long running process. If the socket session dies, I'd like to cancel that process. I tried to pass session.Request().Context() down the line but now realise this doesn't work in fallback modes as session.Request() is only the first request.

I hope I have explained myself clearly enough!

Fix tests

Some tests fail occasionally.

$ while true; do go test >> out.txt; done

shows failing tests

Raw Websocket endpoint

Hello! First of all thanks for a great work on implementing SockJS server in Go.

I looked through code but have not found raw WebSocket endpoint - i.e. something like ws://localhost:8000/echo/websocket to use SockJS server without SockJS client.

Is this not implemented?

Message read format

In order to read a message and convert it to JSON, I need to strip the surrounding quotes and remove \ escaping on the internal quotes.

msg, _ := conn.ReadMessage()
// Message is in the format: `"{\"fruit\":\"apple\"}"`.
// To convert to JSON it must be in the format `{"fruit":"apple"}`.
msg = []byte(strings.Replace(string(msg[1:len(msg)-1]), `\"`, `"`, -1))

I'm just sending a normal JSON request from a sockjs web client to the server. Is there something I'm doing incorrectly? This seems like a hack.

Interrupting Recv

Thank you for nice lib.

I have implemented authentication wrapper that requires user to authenticate before he can do anything. I want to close a session that has not been authenticated for 5 seconds, so i added timeout to session handler. What i'm trying to understand is what happens when calling session Close while Recv blocked. Or if there is some better way to interrupt Recv?

CloseNotify issue.

Running against latest master and on go version go1.9.2 linux/amd64, I get the following panic. I don't know what causes it yet, perhaps xhr? Any help would be really appreciated as it only seems to show up in production :(

panic: net/http: CloseNotify called after ServeHTTP finished
goroutine 4804667 [running]:
net/http.(*response).CloseNotify(0xc42361a000, 0x9b4240)
#011/usr/lib/go-1.8/src/net/http/server.go:1902 +0xa3
/vendor/github.com/urfave/negroni.(*responseWriterCloseNotifer).CloseNotify(0xc42420a008, 0xc422d182a0)
#011/vendor/github.com/urfave/negroni/response_writer.go:112 +0x5a
/vendor/github.com/igm/sockjs-go/sockjs.newHTTPReceiver.func1(0x7ffb3d6dc530, 0xc42420a008, 0xc420ad85f0)
#011/vendor/github.com/igm/sockjs-go/sockjs/httpreceiver.go:46 +0x49
created by /vendor/github.com/igm/sockjs-go/sockjs.newHTTPReceiver
#011/vendor/github.com/igm/sockjs-go/sockjs/httpreceiver.go:57 +0x172

Race conditions

Should we fix that or I'm doing something wrong?
xhr:30 tries to access the map without any locks so it seems we have to protect it with sessionMux.
I could make a PR.

==================
WARNING: DATA RACE
Write by goroutine 51:
  runtime.mapdelete()
      /usr/lib/go/src/pkg/runtime/hashmap.c:1152 +0x0
  gopkg.in/igm/sockjs-go.v2/sockjs.func·001()
      /home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:115 +0x113

Previous read by goroutine 49:
  runtime.mapaccess2_faststr()
      /usr/lib/go/src/pkg/runtime/hashmap_fast.c:129 +0x0
  gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).xhrSend()
      /home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/xhr.go:36 +0x459
  gopkg.in/igm/sockjs-go.v2/sockjs.*handler.(gopkg.in/igm/sockjs-go.v2/sockjs.xhrSend)·fm()
      /home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:42 +0x51
  gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).ServeHTTP()
      /home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:73 +0x1be
  net/http.serverHandler.ServeHTTP()
      /usr/lib/go/src/pkg/net/http/server.go:1597 +0x1ca
  net/http.(*conn).serve()
      /usr/lib/go/src/pkg/net/http/server.go:1167 +0xc00

Goroutine 51 (running) created at:
  gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).sessionByRequest()
      /home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:117 +0x4d6
  gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).xhrPoll()
      /home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/xhr.go:53 +0x8b
  gopkg.in/igm/sockjs-go.v2/sockjs.*handler.(gopkg.in/igm/sockjs-go.v2/sockjs.xhrPoll)·fm()
      /home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:44 +0x51
  gopkg.in/igm/sockjs-go.v2/sockjs.(*handler).ServeHTTP()
      /home/vagrant/go/src/gopkg.in/igm/sockjs-go.v2/sockjs/handler.go:73 +0x1be
  net/http.serverHandler.ServeHTTP()
      /usr/lib/go/src/pkg/net/http/server.go:1597 +0x1ca
  net/http.(*conn).serve()
      /usr/lib/go/src/pkg/net/http/server.go:1167 +0xc00

Goroutine 49 (running) created at:
  net/http.(*Server).Serve()
      /usr/lib/go/src/pkg/net/http/server.go:1644 +0x2c1
  net/http.Serve()
      /usr/lib/go/src/pkg/net/http/server.go:1561 +0xac
  github.com/backplane/platform/clientcomm.func·003() 

RFC: Add the possibility set allowed methods on NewHandler func

ATM, there is no way of restricting Methods for socksJS (e.g. If I don't want my application to use XHR)

My idea is to add a bitmask (using the ReceiverType)on the NewHandler function, the default would be all, if not default, then it would be only the types within the bitmask

sockjs server not working with gorilla/websocket

I have an existing Go application which uses gorilla/websocket. I also have a test suite written in Go which uses gorilla/websocket's websocket.Dialer to connect to an instance of the application and exercise a bunch of functionality. This all works.

I replaced gorilla/websocket with sockjs in the application. Now my test client, using gorilla/websocket can no longer connect to it. I always get bad handshake. My actual handler never gets called -- I have a log statement as the first line, and it never logs anything.

I've tried these variations:

socketJSHandler := sockjs.NewHandler("/chat", sockjs.DefaultOptions, socketHandler)
socketJSHandler := sockjs.NewHandler("/", sockjs.DefaultOptions, socketHandler)
socketJSHandler := sockjs.NewHandler("/chat/", sockjs.DefaultOptions, socketHandler)

I've also tried using custom options instead of the defaults.

On the client side, I've tried to connect to:

ws://ip_address:9999/
ws://ip_address:9999/chat
ws://ip_address:9999/chat/
ws://ip_address:9999/chat/websocket
ws://ip_address:9999/chat/websocket/

http://ip_address:9999/
http://ip_address:9999/chat
http://ip_address:9999/chat/
http://ip_address:9999/chat/websocket
http://ip_address:9999/chat/websocket/

So, my socketHandler function never gets called, and gorilla/websockets gets a "bad handshake" message every time I connect to sockjs. I'm using the same version of gorilla/websockets in all places.

Am I doing something wrong, or is this possibly a bug?

Handler prefix does not allow grouping expressions

Creating a sockjs handler with a prefix that involves a regular expression group breaks parseSessionID because it uses constant match offsets.

It feels like there are two options:

  1. Trim the prefix-match from the incoming url.Path before attempting the session match.
  2. Take advantage of the named matches (assuming the user-provided prefix does not include the same names).

In my situation, I have two prefix paths that I want to be treated equivalently. Let's call them:

  /foo/bar
  /foo

I am attempting to adjust the client, but that's a little out of my control; I originally tried to work around this situation with:

sockjs.NewHandler(
  "/foo(/bar)?",
  ...
)

Multiple prefixes always in sync?

If I have something like the following:

http.Handle("/channel/", sockjs.NewHandler("/channel", sockjs.DefaultOptions, ChannelHandler))
http.Handle("/room/", sockjs.NewHandler("/room", sockjs.DefaultOptions, RoomHandler))
http.Handle("/all/", sockjs.NewHandler("/all", sockjs.DefaultOptions, AllHandler))

and I have some state which each route can read and write to, is a race condition applied? When I run go build -race it doesn't goive race errors, but I am not completely certain of this. Are the routes for example combined to that it is always run in one goroutine? So are these prefixes always in sync.

Do sockjs-go sessions support XDR transports?

According to sockjs-client, XDR transports are used for IE8-9. Since the site I'm using this library for needs to be able to support these browsers and I need to maintain compatibility with sockjs-node, will they still be able to connect using XHR, considering XDR is just a modified version of it? If they can't, would it be possible to add support for it? I could give a hand if you wish.

JSessionID not actually being called.

For some reason sockjs.Options.JSessionID or Options.cookie is never gets called. From what I gather the client never sends OPTIONS /options, so it never really starts. The GET version of option is called however.

To replicate this situation just run: https://github.com/igm/sockjs-go/blob/master/testserver/server.go and run sockjs-client (https://github.com/sockjs/sockjs-client) with url: /cookie_needed_echo with no extra options. It should set a JSessionID cookie with value dummy.

Any pointers would be very welcome :)

Data race issue found in session.go: GetSessionState()

Hello,

I met a data race issue when I used the master branch in my code.

Related code in file: session.go
func (s *session) GetSessionState() sessionState { return s.state }

Could you please add s.Lock()/s.Unlock for this method? Or change the sync.Mutex to sync.RWMutex, and something like follow:
func (s *session) GetSessionState() sessionState {
s.RLock()
defer s.RUnlock()
return s.state
}

Thanks a lot.

eventsource needs to escape messages

The EventSource frame writer does no escaping of its messages:

return fmt.Fprintf(w, "data: %s\r\n\r\n", frame)

Compare that to how sockjs-node handles its EventSource payload:
https://github.com/sockjs/sockjs-node/blob/9efed2c754226b702eb468ef0643b316b91bf37f/lib/transport/eventsource.js#L13-L17

This has caused a problem in our update from sockjs-client-0.3.4 to a more modern sockjs-1.5.x.

In 0.3.4, the EventStream client code decoded the data with unescape (which does not err on unescaped % symbols).
https://github.com/sockjs/sockjs-client/blob/0c70698bddcfab826c7b241ed709f69b5b0d41f7/lib/trans-receiver-eventsource.js#L12-L15

In sockjs-1.5.x, data is decoded with decodeURI, which errs on unescaped % symbols.
https://github.com/sockjs/sockjs-client/blob/71876b30849299255f34131af6474d959d61cbb2/lib/transport/receiver/eventsource.js#L19-L22

unescape('%')
# => '%'
decodeURI('%')
# => URI error

This problem can be seen using the webecho example when modified so it only uses the eventsource or iframe-eventsource protocol.

// Update the SockJS initialization in
// https://github.com/igm/sockjs-go/blob/master/examples/webecho/web/app.js
// No other modifications are necessary.

// options usage example
var options = {
		debug: true,
		devel: true,
                transports: ["eventsource"]
};

Visit the echo app and send the % character. This causes an error in the JS console after the client receives the message:

"a[\"%\"]"

The message should be:

"a[\"%25\""

This problem exists both in v2 and v3.

websocket timeout

In the web socket mode, the send function hangs up at different times. In the library code comes to

w.conn.WriteMessage (websocket.TextMessage, [] byte (frame));

helps deadline w.conn.SetWriteDeadline(time.Now().Add(time.Second))

Why can this happen it? Add deadline please as soon as possible.

Exposing http.Request

Is there any way the http.Request used to establish a SockJS connection could be exposed? I don't even need the full Request actually. All I really want is to be able to use path variables from the SockJS uri. For example, if my SockJS handler is wired up like:

func main() {
    sockjsHandler := sockjs.NewHandler("/echo/{foo}", sockjs.DefaultOptions, echoHandler)
    http.ListenAndServe("8081", sockjsHandler)
}

func echoHandler(session sockjs.Session) {
    // I'd like to access the value of "foo" here.
}

Basically like how mux.Vars works.

Thoughts?

Avoiding using mutex for prefixRegex

handler.go has a global map for compiled regexes.

Can we change it to compile regex upfront and attach it to the handler itself?
There are several benefits:

  • it avoids using mutex for every session parsing. High throughput systems will be thankful for having one fewer lock
  • we are not using global map and we do not have a memory leak. We never remove these compiled regexes from that global map. I doubt somebody will have increasing number of handler with different prefixes, but it's nice to have it done the right way.

I am happy to take this one if there are not some reasons that would blow my story away.

DecodeFrames in WriteMessage

I'm not sure if DecodeFrames in WriteMessage this makes sense. You can't write a JSON message to clients now or else it will double-encode it. Plus the config setting is called decode, not encode. WriteMessage encoding should probably be left to the user.

sockjs: session not in open state

when i use websocket not sockjs, i'm confused,

2020/11/18 19:16:14.854 [D] [asm_amd64.s:1373]  A new sockjs session established ...
2020/11/18 19:16:14.854 [D] [asm_amd64.s:1373]  l1kg4bq2
2020/11/18 19:16:14.854 [D] [asm_amd64.s:1373]  &{{0 0} l1kg4bq2 1 0xc000020190 [] 0xc000010018 0xc000010020 0xc0000761e0 0xc00040a180  5000000000 25000000000 0xc0004180f0 0xc0000741e0}
&{{0 0} l1kg4bq2 1 0xc000020190 [] 0xc000010018 0xc000010020 0xc0000761e0 0xc00040a180  5000000000 25000000000 0xc0004180f0 0xc0000741e0}
****************

****************
2020/11/18 19:16:14.866 [E] [handler.go:31]  sockjs: session not in open state
2020-11-18T19:16:14.866+0800	ERROR	logger/zap.go:31	[session error]
git.sxjicheng.com/jicheng/trend_wsim/logger.Error
	/Users/lcp0578/go/src/git.sxjicheng.com/jicheng/trend_wsim/logger/zap.go:31
github.com/lcp0578/trend_wsim/websocket.initConnection
	/Users/lcp0578/go/src/git.sxjicheng.com/jicheng/trend_wsim/websocket/handler.go:258
github.com/lcp0578/trend_wsim/websocket.Handler
	/Users/lcp0578/go/src/git.sxjicheng.com/jicheng/trend_wsim/websocket/handler.go:31
2020/11/18 19:16:14.866 [D] [asm_amd64.s:1373]  init

go
handler := sockjs.NewHandler("/ws", sockjs.DefaultOptions, websocket.Handler)

func Handler(session sockjs.Session) {
	logs.Debug("A new sockjs session established ...")
	logs.Debug(session.ID())
	logs.Debug(session)
	sessionId := session.ID()
	if sessionId == "" {
		logs.Error("session id error")
		return
	}
	//inline
	key, err := initConnection(session)
	logs.Debug("init")
	logs.Debug(key)
	logs.Debug(err)
	if err != nil {
		logs.Error(err)
		return
	}

	//offline
	defer closeConnect(key, sessionId)
	// sub message
	go subMessage(session, sessionId, key)
	for {
		if msg, err := session.Recv(); err == nil {
			messageCenter(msg, session)
			continue
		}
		break
	}
}

func initConnection(session sockjs.Session) (key string, err error) {
	msg, err := session.Recv()
	if err != nil {
		logs.Error(err)
		logger.Error("session error")
		return
	}
	logger.Debug("init msg")
	logger.Debug(msg)
	recvMsg, err := decodeMsg(msg)
	logger.Debug(recvMsg)
	if err != nil {
		return
	}
	if recvMsg.Type != "connection" && recvMsg.Type != "reconnection" {
		return "", errors.New("init connection type error")
	}

	key = strconv.Itoa(recvMsg.FromType) + "_" + strconv.Itoa(recvMsg.Data.Mine.Id) + "_" + strconv.Itoa(recvMsg.ToType) + "_" + strconv.Itoa(recvMsg.Data.To.Id)
	logger.Debug(key)
	chat.SetUserMap(key, session.ID())
	logger.Debug("set user map")
	return
}

uni-app:

                   uni.connectSocket({
					url:"ws://127.0.0.1:8088/ws/92/l1kg4bq2/websocket",
				
					header:{
						"Connection": "Upgrade",
						//"Sec-WebSocket-Accept": "J04QneyO/SnbBD1HyZJePaOLoFA=",
						"Upgrade": "websocket"
					},
					complete: function(res) {
						console.log(res);
					},
					success:function(res){
						console.log(res);
						console.log('WebSocket success!');
						
					}
				});

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.