rs / cors Goto Github PK
View Code? Open in Web Editor NEWGo net/http configurable handler to handle CORS requests
License: MIT License
Go net/http configurable handler to handle CORS requests
License: MIT License
Hello!
I see in the docs/src that it's currently GET / POST by default, but https://www.w3.org/TR/cors/#simple-method includes HEAD in there as well. I can't imagine many use-cases for making a HEAD CORS call but hey haha.
Hi, I found that when I setup cors, I cannot handle POST requests. They seem to get translated to GET requests instead:
mux.Handle("/api/login", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Debug(r.Method)
}))
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowedHeaders: []string{"*"},
AllowCredentials: true,
Debug: true,
})
handler := c.Handler(mux)
// listen and serve...
The output is GET when I send a request using vue-resource:
this.$http.post('different-tld/api/login', {login: this.login, password: this.password})
.then(function(r){
console.log(r.data);
}, errHandler);
Viewing the network graph in Chrome, it says 301 Moved Permanently:
// general
Request URL:http://localhost:9200//api/login
Request Method:POST
Status Code:301 Moved Permanently
Remote Address:127.0.0.1:9200
Referrer Policy:no-referrer-when-downgrade
// response header
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost:9100
Content-Length:0
Content-Type:text/plain; charset=utf-8
Date:Fri, 16 Jun 2017 16:55:34 GMT
Location:/api/login
Vary:Origin
// request header
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, br
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,fr;q=0.2,nb;q=0.2,la;q=0.2
Connection:keep-alive
Content-Length:36
Content-Type:application/json;charset=UTF-8
Host:localhost:9200
Origin:http://localhost:9100
Referer:http://localhost:9100/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36
// request payload
{login: "foo", password: "bar"}
It makes some more requests (two times OPTION) and ends up using GET with a 200 response. What am I doing wrong?
github.com/rs/cors/vendor/github.com/rs/cors/cors.go:215: cannot convert func literal (type func("golang.org/x/net/context".Context, http.ResponseWriter, *http.Request)) to type xhandler.HandlerFunc
Hi there, would it be possible to release a patch version with the latest changes? :)
Thanks for this awesome library, it works like a charm.
In my case, the very end of the middleware chain, I have a jwt authorization middleware (https://github.com/pkieltyka/jwtauth). With your current cors
handler, when it's handling the preflight request, it will call h.ServeHTTP(), which runs the next middleware, however I think it should just stop the chain and leave the response at that. Otherwise, the clients will get an unauthorized token
response as the usual OPTIONS command does not include the "Authorization" header.
Thoughts?
There is an easy way to use echo v2 standard engine with cors.
But with the fasthttp engine, I had to create a fork and rewrite handlePreflight and handleActualRequest. It also add a dep to echo.
Do you want a PR or do I fork it?
According to this:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Simple_requests
A simple request should at the very least allow these two headers. Do you think it makes sense to whitelist those by default here?
Right now the Vary: Origin
response header is only applied when there is an Origin
header in the request. I believe this should be considered a bug because conditionally returning the header based on the presence of an Origin
will poison intermediate caches (Varnish, Akamai, any CDN, etc).
Since you can issue a request without an Origin an intermediate cache will hang on to the return value. Next time when a cross domain request comes in with an Origin
the intermediate cache won't know to Vary
by Origin
and will return the non-CORS response.
There hasn't been a release in over a year. Dependency managers like Glide push users to use tagged releases, which is resulting in users having outdated code.
I'm new to Go and CORS.
Reading Readme.md
I was impressed by the "Benchmarks" section.
I do not know if I understood correctly:
BenchmarkWithout
means without using CORS?BenchmarkDefault
means using CORS?I plan on sending a PR at some point, just opening this for reference or if someone else wants to grab it.
I think here if we respond with Access-Control-Allow-Origin
first it'll lead to nicer browser errors, and require fewer trips to enabling debugging with this middleware to discover what went wrong.
Currently this kind of masks issues with the other fields, so the browser basically just says you don't have access at all. Let me know if that sounds reasonable!
Currently, there is no way to return "Access-Control-Allow-Origin": "*"
using this library due to https://github.com/rs/cors/blob/master/cors.go#L333
When allowedOriginsAll = true
, it is perhaps better to do
headers.Set("Access-Control-Allow-Origin", "*")
instead of
origin := r.Header.Get("Origin")
headers.Set("Access-Control-Allow-Origin", origin)
In my case for example, I'm caching responses from multiple origins behind a CDN, and require "Access-Control-Allow-Origin": "*"
so that the cached response for origin A can be reused for origin B, which is not possible with this library.
I think it should be "v1.1" not "v.1.1" - because of typo dependency managers like glide have problems picking up latest version.
Since the context is now built-in in the request.
It would remove those dependencies:
github.com/rs/xhandler
golang.org/x/net/context
Find using https://go-critic.github.io/overview#ifElseChain-ref
$GOPATH/src/github.com/rs/cors/utils.go:42:3: ifElseChain: should rewrite if-else to switch statement
Just a suggestion, since wildcard for Access-Control-Expose-Headers isn't widely supported yet, I would recommend filling Access-Control-Expose-Headers in the AllowAll configuration with the result of whatever is set in the wrapped handler minus any of the 6 default values and other Access-Control-* headers.
The specification doesn't allow the "AllowedHeaders" to be a wildcard. But what do you think of the idea of checking to see if AllowedHeaders is just []string{"*"}
and, if so, simply echo back whatever Access-Control-Request-Headers
happens to be. This would just be a special case that would make it easier to avoid the trap of failing to anticipate all the headers that the client might throw into the request.
As you point out in the comments, the AllowedHeaders are unbounded. So you are already doing this after you check to make sure that the Requested headers are a subset of the Allowed headers. This would just short-circuit that step by treating this as a special case which would accept any requested headers. Of course, it could also be extended to a general capability that treats the AllowedHeaders as regexps and actually checks them. But that is more work and, frankly, greater chance for a mistake or misunderstanding.
I'm not sure if this is a good idea or not. That's why I'm asking.
In the example code provided in the README file, the handler variable has not been initialized before being assigned. line has to be changed as the gocode could be successfully compiled:
handler := cors.Default().Handler(h)
When issuing a GET
request everything is working fine:
winterp@WINTERP3 MINGW64 /
$ curl -v -X GET --noproxy localhost -D - -H 'Origin: http://localhost' http://localhost:41414
Note: Unnecessary use of -X or --request, GET is already inferred.
* Rebuilt URL to: http://localhost:41414/
* timeout on name lookup is not supported
* Trying ::1...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost
Content-Type: application/json
Vary: Origin
Date: Thu, 27 Jul 2017 11:40:47 GMT
Content-Length: 18
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 41414 (#0)
> GET / HTTP/1.1
> Host: localhost:41414
> User-Agent: curl/7.45.0
> Accept: */*
> Origin: http://localhost
>
< HTTP/1.1 200 OK
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Origin: http://localhost
< Content-Type: application/json
< Vary: Origin
< Date: Thu, 27 Jul 2017 11:40:47 GMT
< Content-Length: 18
<
{ [18 bytes data]
100 18 100 18 0 0 82 0 --:--:-- --:--:-- --:--:-- 88{"hello": "world"}
* Connection #0 to host localhost left intact
However, I'm currently having problems getting the CORS headers correctly to work for OPTIONS
requests:
winterp@WINTERP3 MINGW64 /
$ curl -v -X OPTIONS --noproxy localhost -D - -H 'Origin: http://localhost' http://localhost:41414
* Rebuilt URL to: http://localhost:41414/
* timeout on name lookup is not supported
* Trying ::1...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0HTTP/1.1 200 OK
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Date: Thu, 27 Jul 2017 11:40:40 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 41414 (#0)
> OPTIONS / HTTP/1.1
> Host: localhost:41414
> User-Agent: curl/7.45.0
> Accept: */*
> Origin: http://localhost
>
< HTTP/1.1 200 OK
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Date: Thu, 27 Jul 2017 11:40:40 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
<
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
* Connection #0 to host localhost left intact
Although I've allowed the use of all headers and request origins:
cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"OPTIONS", "GET", "HEAD", "POST", "DELETE", "PUT"},
AllowedHeaders: []string{"*"},
AllowCredentials: true,
Debug: true,
}
Removing OPTIONS
from the allowed methods doesn't help either.
The problem seems to be that the method is not correctly parsed and therefore substituted with an empty string:
[cors] 2017/07/27 13:46:36 Handler: Preflight request
[cors] 2017/07/27 13:46:36 Preflight aborted: method '' not allowed
When adding the empty string to the allowed headers everything works fine.
how to retrieve post petition from client side
[ec2-107-20-94-2.compute-1.amazonaws.com] out: src/github.com/rs/cors/cors.go:121: undefined: strings.IndexByte
[ec2-107-20-94-2.compute-1.amazonaws.com] out:
Fatal error: run() received nonzero return code 2 while executing!
Last commit introduced this error when trying to get my repo using godeps
Usage:
cors.AllowAll().Handler(...)
Any chance we could get an example integration for Gin?
I think that CORS design is completely unnecessary, and so should be abandoned. I have laid down my arguments, if you think you know this issue pretty well, would you please look at it, and comment it.
Seems this middleware happily add the headers even if they're present on the response.
I'm getting this from Chrome:
The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:8085, http://localhost:8085', but only one is allowed. Origin 'http://localhost:8085' is therefore not allowed access.
I'm looking to set this to be ["*"]
, but can't find a way to set this through the api. I'm wondering what the default value is for it when set
It's currently not possible to set CORS based on e.g. HTTP Authorization. This is because signature AllowOriginFunc func(origin string) bool
does no have r *http.Request
. This makes advanced use cases impossible and the function for anything else than maybe wildcard-based subdomain matching. There's actually a fork that does that (https://github.com/go-chi/cors) but this should really be added here.
I've got this implemented in a go library, and it seems to work when testing it in browser with JavaScript, but I would like to write go tests for it.
Specifically, testing whether http://hostname:port/health
works or not.
So far, the test that I have written, seems to always work (returns 200 status code), regardless that the origin should not be allowed.
func TestCORS(t *testing.T) {
// Hostname
hostname := getHostname()
if len(hostname) == 0 {
t.Error("The API address was empty")
return
}
// Prepare the URL
addr := "http://" + hostname + "/health"
// Make the OPTIONS request, which initialises CORS
req, err := http.NewRequest("OPTIONS", addr, nil)
if err != nil {
t.Error(err)
return
}
// Simulate an external origin
req.Header.Add("Origin", "http://example.com/")
req.Header.Add("Access-Control-Request-Method", "GET")
// Send the request
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
t.Error(err)
return
}
// Close everything
defer res.Body.Close()
// Check its result
// @todo this should actually fail due to only localhost and 127.0.0.1 being allowed `[http://localhost:* http://127.0.0.1:*]`
if res.StatusCode != http.StatusOK {
t.Error(fmt.Errorf("%s returned unexpected status: %d", addr, res.StatusCode))
return
}
}
Using this like so:
func DoServer () {
router := gin.Default()
router.Use(gincors.New(/* some redacted cors options that only allow localhost */))
// ...
}
I've based the tests from https://github.com/rs/cors/blob/694cf2ad010fcf91ee35161f5bfdad8f42ed1b9a/wrapper/gin/gin_test.go
In the last month (probably with the most recent commits):
$ go get github.com/rs/cors
# github.com/rs/cors
/opt/go/src/github.com/rs/cors/cors.go:185: undefined: http.MethodOptions
/opt/go/src/github.com/rs/cors/cors.go:207: undefined: http.MethodOptions
/opt/go/src/github.com/rs/cors/cors.go:218: undefined: http.MethodOptions
/opt/go/src/github.com/rs/cors/cors.go:242: undefined: http.MethodOptions
/opt/go/src/github.com/rs/cors/cors.go:300: undefined: http.MethodOptions
/opt/go/src/github.com/rs/cors/cors.go:376: undefined: http.MethodOptions
It looks that it now requires go 1.6?
$ go version
go version go1.5 linux/amd64
when I switch to POST, it works.
when set to "PUT", browser reports:
Fetch API cannot load http://127.0.0.1:8090/blabla. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8000' is therefore not allowed access. The response had HTTP status code 404. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
How do one use this package with gin-gonic?
I have this error: cannot use cors.Default() (type *cors.Cors) as type gin.HandlerFunc in argument to r.Use. My snippet below:
r := gin.Default()
r.Use(cors.Default())
v1 := r.Group("/v1")
{
item := v1.Group("/item")
{
item.POST("/new", controllers.AddItem)
item.GET("/", controllers.GetAllItems)
item.GET("/:id", controllers.GetItem)
item.PUT("/update", controllers.UpdateItem)
item.DELETE("/", controllers.DeleteItem)
}
verify := v1.Group("/verify")
{
verify.POST("/", controllers.VerifyItem)
}
}
return r
I need to expose Location header for only some responses (like to POST request), in most other responses there is no Location header in responses.
Should it filter ExposedHeaders by response headers already presented in? Or it depends on the order of middleware setup?
Also it will add a time penalty for processing that filter. So the main purpose of this issue is: is it OK to add Access-Control-Expose-Headers: XXX
header without XXX
header in the response?
When CORS policy is configured to origin:"*", current go CORS handler will actively convert it to reflect any Origin header value. This kind of behavior is dangerous and has caused many security problems in the past.
Some similar security issues:
cyu/rack-cors#126
https://nodesecurity.io/advisories/148
Some related blog posts:
http://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
https://ejj.io/misconfigured-cors/
Would it make sense to copy "Allow" to "Access-Control-Allow-Methods" when optionsPassthrough enabled? julienschmidt/httprouter
automatically generates OPTIONS reply based on the routes configured, but that's written to the "Allow" header. I don't know what's the right thing here.
I'm limiting my web service to only some origins. If I try to make a request from an origin not allowed I get the error Origin 'xxxx' is therefore not allowed access.
in the console, but I still get the response, is this normal? How to block the access to the further resources.
I try to install to in a docker container:
FROM golang:1.8
i get the following error
/go/src/github.com/rs/cors/wrapper/gin/gin.go:13: syntax error: unexpected = in type declaration
The command '/bin/sh -c go get github.com/rs/cors/wrapper/gin' returned a non-zero code: 2
do you have seen this?
go test
./cors_test.go:402: comparison of function logf == nil is always false
FAIL github.com/rs/cors [build failed]
Hi!
I'm trying to use CORS middleware with Buffalo.
I saw code snippet in the issue #609.
It works, but after OPTIONS call log prints warning "multiple response.WriteHeader calls":
[cors] 2018/01/17 15:35:08 Handler: Preflight request
[cors] 2018/01/17 15:35:08 Preflight response headers: map[Vary:[Origin Access-Control-Request-Method Access-Control-Request-Headers] Access-Control-Allow-Origin:[http://localhost:4200] Access-Control-Allow-Methods:[POST] Access-Control-Allow-Headers:[Content-Type] Access-Control-Allow-Credentials:[true]]
2018/01/17 15:35:08 http: multiple response.WriteHeader calls
[cors] 2018/01/17 15:35:08 Handler: Actual request
[cors] 2018/01/17 15:35:08 Actual response added headers: map[Access-Control-Allow-Credentials:[true] Vary:[Origin] Access-Control-Allow-Origin:[http://localhost:4200]]
Could you suggest me, how to fix this warning?
My code:
func App() *buffalo.App {
if app == nil {
app = buffalo.New(buffalo.Options{
Env: ENV,
SessionStore: sessions.NewCookieStore([]byte(SECRET)),
SessionName: "sessionid",
})
// Set the request content type to JSON
app.Use(middleware.SetContentType("application/json"))
if ENV == "development" {
app.Use(middleware.ParameterLogger)
app.PreWares = []buffalo.PreWare{cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowedHeaders: []string{"Content-Type", "Cookie"},
AllowCredentials: true,
Debug: true,
}).Handler}
}
// Set custom error response
// SOME STAFF
app.GET("/", HomeHandler)
app.POST("/session", AuthLogin)
auth := app.Group("/")
auth.Use(AuthRequired)
auth.DELETE("/session", AuthLogout)
auth.GET("/session", AuthSession)
}
return app
}
Hi there, thanks for the hard work on cors. Just trying to get it set up Socket.io:
http://stackoverflow.com/questions/27828052/go-socket-io-http-wss-on-one-port-with-cors
Any pointers on where I'm going wrong or how to fix? I'm seeing:
WebSocket connection to 'wss://api.domain.com/socket.io/?EIO=3&transport=websocket&sid=bWok_LmVKvvS-MxihuxE' failed: WebSocket is closed before the connection is established.
(index):1 XMLHttpRequest cannot load https://api.domain.com/socket.io/?EIO=3&transport=polling&t=1420671018767-829&sid=bWok_LmVKvvS-MxihuxE. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://fiddle.jshell.net' is therefore not allowed access.
The X-Requested-With
header is a very common non-standard header often sent by browsers. As it stands, this library will by default. currently reject OPTIONS
requests requesting to use this header.
While not part of the CORS spec, it feels like this header is so common that adding it as a default value in AllowedHeaders
would save a lot of confusion. Let me know if that's true and I'll submit a PR to do so.
I kept getting a Preflight response is not successful error from my React side.
After trying everything mentioned in the documentation I started digging into the code and I found that there is an AllowAll()
method that is not mentioned in the docs.
Any chance we could get an example integration with kami?
Tags and versions aid in packaging for things like .deb files, and for anyone wanting to use Glide and other version based package managers to version their dependencies. It would also help ensure that if you make a breaking change, we don't pull it down until we are ready.
I'm not sure how general this is, but in the framework I'm using (gin, which is very similar to martini), when I use c.HandlerFunc
I get 404s for OPTIONS requests, even with OptionsPassthrough
set to false
.
I was able to fix this in the short term by always setting the status code to 200 for valid OPTIONS requests.
Here's what I had before that failed with a 404
# Fails with 404 on OPTIONS request
r := gin.Default()
r.Use(gin.WrapF(c.HandlerFunc))
Here's what works for OPTIONS requests
makeCorsHandler := func(c *cors.Cors) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
c.HandlerFunc(w, r)
// Allow it to return to avoid a 404
if r.Method == "OPTIONS" && w.Header().Get("Access-Control-Allow-Origin") == r.Header.Get("Origin") {
w.WriteHeader(http.StatusOK)
}
}
}
Really I should be checking if optionPassthrough
is false
, but I don't have access to that property here. Also, checking that Access-Control-Allow-Origin
is set to the correct value is kind of a hack that just works because that header value isn't set until the request has made it past all the relevant cross domain checks.
However you want to handle it, making the OptionsPassthrough
option make more sense when using HandlerFunc
would be helpful. That may mean just documenting this case, or providing an option to handle this automatically by always returning 200s. I couldn't think of a very clean way to do it, hence the bug report and no PR. :)
Please let me know if there is any testing I can do to help. I certainly appreciate your work on this library!
I believe that the Authorization
header is used more often than not specially when using this package. I think it would be a sensible idea to add it to the default AllowedHeaders
. I made a pull request with the change.
Until this issue is resolved at the tooling level and in light of the SemVer spec, I (and I assume others) would appreciate a x.x.0 on the release tags.
I'm sure I'm just doing something wrong. Something completely wrong.
However, here's the issue:
var AllowedHeaders = []string{
"Range",
"Accept",
"Content-Type",
"Authorization",
"X-CA-Session",
"X-Requested-With",
}
func Init(session *mgo.Session) http.Handler {
router := mux.NewRouter()
router.Handle("/", notImplementedHandler()).Methods("GET")
router.Handle("/api", notImplementedHandler()).Methods("GET")
router.Handle("/api/stats/push", statsapi.NewStatsPushHandler(session)).Methods("GET", "OPTIONS")
router.Handle("/api/interactions/push", interactionsapi.NewInteractionPushHandler(session)).Methods("POST", "OPTIONS")
corsHandler := cors.New(cors.Options{
AllowCredentials: true,
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "OPTIONS"},
AllowedHeaders: AllowedHeaders,
Debug: true, // Enable Debugging for testing, consider disabling in production
})
return corsHandler.Handler(router)
}
The server keeps returning status 502
, with the error message:
Failed to load '<my_server>': Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '<the_origin>' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Is the config all wrong, or the server has a mind of its own?
The request to the server is relatively simple:
const request: Request = new Request(`${process.env.STATS_API}/api/stats/push`, {
method: 'GET',
headers: new Headers({
Accept: '*/*',
'Content-Type': 'application/json',
'X-CA-Session': sessionHashStr,
}),
});
await fetch(request);
There's also a POST request, that actually sends data through in the body as well, however, haven't gotten to that point yet.
Am I missing something?
I've setup cors with default server. When running:
curl -D - -X OPTIONS -H 'Origin: http://www.plot-my-trip.local.com:8080' http://www.plot-my-trip.local.com:4000/api/auth/get-user
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Date: Thu, 20 Aug 2015 00:05:01 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
Notice there's no Access-Control-Allow-Origin header.
When trying to access the above API using Javascript in Chrome, I see the error in the console:
http://www.plot-my-trip.local.com:4000/api/auth/get-user. No 'Access-Control-Allow-Origin' header
is present on the requested resource. Origin 'http://www.plot-my-trip.local.com:8080' is
therefore not allowed access.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.