seanmonstar / warp Goto Github PK
View Code? Open in Web Editor NEWA super-easy, composable, web server framework for warp speeds.
Home Page: https://seanmonstar.com/post/176530511587/warp
License: MIT License
A super-easy, composable, web server framework for warp speeds.
Home Page: https://seanmonstar.com/post/176530511587/warp
License: MIT License
I took a shot at adding support for Authorization header filters. I would like to know whether you like the approach I took: https://github.com/algermissen/warp/blob/add-auth-headers/src/filters/authorization.rs
If so, I'll work on making the code better and submit a PR after that - given I am relatively new to Rust, there is a lot of stuff that needs improvement (eg reduce String usage), so please do not judge that yet.
Some questions:
let text3 = warp::path("protected").
and(warp::authorization::basic("myRealm"))
.map(|b: warp::authorization::BasicCredentials| {
format!("User: {} Passwd: {}", b.user,b.password)
});
If we were implementing something like a proxy (which might be a good example to add), you would want to issue a request that would return a Future. I'm not exactly sure which combinator to use with it. I thought it would be .and_then
, but the example is geared towards validation and returning a CombineReject
, and I believe that map
doesn't accept a future.
curl localhost:3030/bye/tö -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3030 (#0)
> GET /bye/tö HTTP/1.1
> Host: localhost:3030
> User-Agent: curl/7.61.0
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< content-length: 0
< date: Sun, 19 Aug 2018 15:38:41 GMT
<
* Connection #0 to host localhost left intact
Output from warp
:
RUST_LOG=debug cargo run --example routing
...
Finished dev [unoptimized + debuginfo] target(s) in 0.11s
Running `target/debug/examples/routing`
INFO warp::server > warp drive engaged: listening on 127.0.0.1:3030
DEBUG tokio_reactor::background > starting background reactor
DEBUG tokio_reactor::background > starting background reactor
DEBUG tokio_reactor > loop process - 1 events, 0.000s
DEBUG tokio_reactor > loop process - 1 events, 0.000s
DEBUG hyper::proto::h1::io > read 85 bytes
DEBUG hyper::proto::h1::conn > parse error (invalid Header provided) with 85 bytes
DEBUG hyper::proto::h1::role > sending automatic response (400 Bad Request) for parse error
DEBUG hyper::proto::h1::io > flushed 84 bytes
DEBUG hyper::server::conn::upgrades > error polling connection protocol: invalid Header provided
DEBUG hyper::server::conn > conn error: invalid Header provided
DEBUG tokio_reactor > dropping I/O source: 0
Hi!
Thanks for your hard work on this! I'm enjoying the filters api.
I have a use case for nested querystrings (or some way to create a list out of repeated querystring keys). I don't believe that serde_urlencoded supports this, while I think serde_qs does.
Would you want to support this in warp, or is there a way for me to write a custom filter?
On https://crates.io/crates/warp, I see the following example:
extern crate warp;
use warp::Filter;
fn main() {
// GET /hello/warp => 200 OK with body "Hello, warp!"
let hello = warp::path!("hello" / String)
.map(|name| format!("Hello, {}!", name));
warp::serve(hello)
.run(([127, 0, 0, 1], 3030));
}
This does not compile for me using rust 1.28.0. It looks like it is a cross between using the path!
macro and using the warp::path
function.
Is there a way to debug routes? I'm 99% sure my route is correct, yet it gets rejected with 204 No Content.
I'm missing the following to effectively debug the routes:
Is there something along those lines available?
I'm seeing seemingly-good requests be rejected. Looks like this might happen when the client specifies a charset in the content-type. Relevant log output:
TRACE warp::filters::body > is_content_type "application/x-www-form-urlencoded"? "application/x-www-form-urlencoded; charset=utf-8"
DEBUG warp::filters::body > content-type doesn't match "application/x-www-form-urlencoded"
I'm not an http expert and am unaware whether it's technically correct for charset to be included here, but random googling indicates it's not rare.
Maybe is_content_type
should just check what the header starts with? or split on semicolons?
When writing a router for an API with a lot of endpoints, compilation crawls to a halt.
The code in question can be found here: http://gist.github.com/kiljacken/866f6617b2998d672ac8f67cb6037186
When compiling with cargo build
and only one route enabled the compilation takes around ~5s. With all routes enabled this increases to 1m 12s. This obviously makes iteration time quite high, and thus decreases productivity quite a bit.
It would be nice if there was more effective way to do this.
I was curious how this was done and it didn't seem to be in an intuitive place.
We could use the mime_guess
crate to add the content-type
header to the File
reply.
Hey,
Really like what you did !
Say I want to define very rich, specific errors, I usually do this with failure
.
I'd like to be able to return my own custom failures in the and_then
combinator, and then either simply log them or present a detailed error page.
Do you currently have a way to do this ? Is it a goal at all ?
Maybe I just have the wrong approach, what would you recommend to deal with this ?
These would likely need to be with
filters.
Some hypothetical example:
let route = warp::fs::dir("static")
.with(warp::compress::gzip());
I'd like to use a path parameter from the update request inside the websocket handling code, but the current API doesn't provide an easy way to do that:
warp::path("example")
.and(warp::path::param())
.and(warp::ws(|websocket| {
// cannot access path parameter easily here
}))
It would be useful to be able to access a file's contents from a filter so that It could be potentially modified or handled in some way.
Needing to output HTML using a templating system is required by many applications. warp should provide an easy default way to do so (clearly, the filter system allows someone to plug in any output they want). It seems like handlebars is the most popular.
I don't know if this is a genuine problem, but I thought I'd share that my first thought on learning about this exciting new project was, wait, "warp" is already the name of a popular Haskell Web server: https://hackage.haskell.org/package/warp
I know, name collisions are hard to avoid these days.
Near as I can tell there's no way to get the address of the client. Something like
let ip = warp::ip()
.map(|addr| format!("your address is {}", addr.ip()));
where addr
is a std::net::SocketAddr
. Alternatively, do some trait fanciness to allow specifying IpAddr
or SocketAddr
in the closure.
A user may wish to access multiple headers at once, or dynamically determine which headers to look at. There isn't yet a way, but there should be! Some options to implement this:
warp::headers()
that just clones the HeaderMap
. It's the easiest thing to do, but it's also the costliest.warp::headers(|headers| do_something(headers))
would allow accessing &HeaderMap
, so you don't need to clone, but filters of this design feel less natural. It's also harder to determine what the return type can be (the Filter
being Copy
depends on the closure being Copy
).async
syntax, however.It would be nice to include some built-in filters in warp::multipart
(or warp::body
?). I'm not sure what the API should look like. Some things that come to mind that a user could want to do:
serde::Deserialize
.It's a goal to allow turning a warp::Filter
into a tower::Service
, and to allow for warp::Server
to be given a Service
(or NewService
) as its argument. This would facilitate making use of the wider tower middleware directly.
The Filter::or
combinator returns an Either<A, B>
type, but it currently isn't exported publicly, making it impossible to match on the variants. It does implement Reply
, making the full framework function, but it's currently impossible to use or
to retrieve either A or B.
Some reasons why it hasn't been exposed yet:
Either<A, B>
to instead more like Coprod<A, Coprod<B, Never>>
. The benefit of doing this is that nesting Either<A, B>
will result in the enums getting fatter and fatter, Coprod
approach makes unlimited nested or
s still only need space for 1 variant tagCoprod
, it also grows the size).I just read your blog post that introduces the warp library, so I'm quite intrigued now as to how things will develop now with tower and tower-web quickly entering the stage. I am certainly attracted by the design philosophy of warp, but I do want to know how you see it in relation to tower-web, which you mentioned in passing. From an issue you have open here, it would seem the Filter
and Service
traits are somehow analogous? I'm presuming neither tower-web nor warp will ever include the other as a dependency, but what will the relation between them be, going forwards? Direct competitors?
Side question: does warp support async workflows like Gotham or such?
I'm not really sure how much of this is an artifact of warp
's design and how much is just my lack of experience, but in a small test project, I found that attempting to abstract parts of filter/server construction in separate functions seems to produce some complex issues. For instance, consider the following reduced example:
extern crate warp;
pub fn get_routes() -> impl warp::Filter {
warp::path::index()
}
fn main() {
warp::serve(routes);
}
This produces the following error:
error[E0277]: the trait bound `<impl warp::Filter as warp::filter::FilterBase>::Extract: warp::reply::sealed::ReplySealed` is not satisfied
--> src/main.rs:8:5
|
8 | warp::serve(get_routes());
| ^^^^^^^^^^^ the trait `warp::reply::sealed::ReplySealed` is not implemented for `<impl warp::Filter as warp::filter::FilterBase>::Extract`
|
= note: required because of the requirements on the impl of `warp::Reply` for `<impl warp::Filter as warp::filter::FilterBase>::Extract`
= note: required because of the requirements on the impl of `warp::server::IntoWarpService` for `impl warp::Filter`
= note: required by `warp::serve`
I haven't found a good workaround for the error yet; just extending the signature of get_routes
to return impl warp::Filter + Sync + Send + 'static
isn't enough to satisfy the bounds on the impl
of IntoWarpService
for Filter
s in src/filter/service.rs
.
Basically, it seems like there are so many trait bounds that trying to modularize a large application's route structure across multiple functions could get interesting fast. Most of the filter combinators (or
, etc.) seem to include trait bounds that would cause similar issues to this one.
I don't really have a proposed solution (if this is even really a practical issue), but I guess it's something to think about…
Under the current api, Filter::with
requires that the type be W: Wrap
however the Wrap
trait is sealed making it difficult to write custom wrapping filters. For instance, I'm trying to write a filter that will forward a header from a request to the response. So far I've come up with the filter to generate the either grab the request id or generate a new one:
let req_id = warp::header::<String>("x-request-id").or_else(|_| Ok(("generated".to_owned(),)))
But I can't figure out using the combinators provided how to store the request-id in the filter and use it on the response.
It also seems that you cannot call map
after returning a warp::reply
in the combinator chain so I can't even make the above work with boiler plate something like
let my_route = req_id.map(warp::reply).map(|id: String, reply: warp::Reply| {
reply.set_header("x-request-id", id)
})
I was looking over the routing example, and a part of me was wondering if there might be some standardized macros that could help with very common filter patterns. An example:
fn main() {
let routes = routes!{
route!{
warp::index(),
||{
"Welcome!"
}
},
route!{
path!("hello"/String),
|name|{
format!("Hello {}",name)
}
}
route!{
warp::any(),
||{
"How'd you get here?"
}
}
};
warp::serve(routes)
.run(([0, 0, 0, 0], 3030));
}
routes! would .or(...)
a chain of filters
route! would .and(...)
a chain of filters and .map(...)
to the last param function
Please keep in mind i'm a Rust noob so I might be missing some capabilities of macros right now :)
It might be that there's a way to do this by composing filters, but it seems to escape me for the moment.
If I have a route of /:something
, is there a way to provide a pattern to filter? For example, say I wanted to accept /:something
, but only where :something
is value1
or value2
. It seems I could maybe join some handlers up together to make this happen, but not in a way that would provide access to the value of :something
?
I'm posting this as an issue because some people here may be interested in exploring an idea like this one. If you feel that this is not an appropriate place, please feel free to close the issue.
My intention is not change the code in the warp crate, but to discuss the idea of using a DSL to write the routes.
As someone who used spray-routing (which was integrated in Akka HTTP a few years ago) for 5 years, it was a pleasant surprise to discover this project in the Reddit post.
I miss the routing DSL of Spray/Akka-HTTP, and when I saw the warp documentation I thought that it could be a perfect foundation to write something like that.
During the last couple of days I made a crate that implements a DSL to write routes. The code is a bit junky, there are a lot of missing features, and I'm pretty sure that there are some edge cases that will generate broken code, but at least it works well enough to experiment with it. It also works on Rust stable.
As an example, it converts the following code:
let routes = router!(
path("blogs") {
get & index {
complete { "Get all blogs" }
}
path(u32) |blog_id| {
get {
complete { format!("Details for blog {}", blog_id) }
}
delete {
complete { format!("Delete blog {}", blog_id) }
}
}
}
path("blog" / u32 / "posts") |blog_id| {
post & index & (body::json()) |post: Post| {
complete {
format!("Publish post '{}' with body '{}' in blog {}", post.title, post.body, blog_id)
}
}
get & path(u32) & index |post_id| {
complete {
format!("Read post {} in blog {}", post_id, blog_id)
}
}
}
);
To this:
let routes = (((::warp::get((path!("blogs")).and(::warp::index()).map(
|| "Get all blogs",
)))).or(
((::warp::get((path!("blogs")).and(path!(u32)).map(|blog_id| {
format!("Details for blog {}", blog_id)
})))).or(
(::warp::delete((path!("blogs")).and(path!(u32)).map(|blog_id| {
format!("Delete blog {}", blog_id)
}))),
),
)).or(
((::warp::post(
(path!("blog" / u32 / "posts"))
.and(::warp::index())
.and(body::json())
.map(|blog_id, post: Post| {
format!(
"Publish post '{}' with body '{}' in blog {}",
post.title,
post.body,
blog_id
)
}),
))).or(
(::warp::get(
(path!("blog" / u32 / "posts"))
.and(path!(u32))
.and(::warp::index())
.map(|blog_id, post_id| {
format!("Read post {} in blog {}", post_id, blog_id)
}),
)),
),
);
This example is available in the repository of the crate, and can be tested with:
$ cargo run --example blog_api
It relies on proc-macro-hack to use procedural macros in Rust stable.
The DSL is heavily inspired by Spray/Akka.
The rules to combine filters are simple:
and
.or
.So, a declaration like:
path("p") {
foo { ... }
bar { ... }
}
Emits the code:
(
path!("p").and(foo()).and(...)
).or(
path!("p").and(bar()).and(...)
)
The operator &
can be used to combine filters when there is only one child (from Spray/Akka).
A declaration like:
a {
b {
c {
complete { ... }
}
}
}
Can be written as:
a & b & c {
complete { ... }
}
The handler of the request is defined with the complete
directive (also from Spray/Akka).
Closure arguments are accumulated until the complete
block is found.
For example, a route like:
path("x" / usize) |x| {
(warp::header::<SocketAddr>("host")) |host| {
complete {
do_stuff(x, host)
}
}
}
Will emit something like:
path!("x" / usize)
.and(warp::header::<SocketAddr>("host"))
.map(|x, host| {
do_stuff(x, host)
});
HTTP methods are moved to the top of the filter, since the current functions in warp::filters::method
expect a filter as an argument.
The DSL is able to detect when a route contain multiple HTTP methods. A declaration like
post {
get {
...
}
}
Produces an error during compilation.
If you don't do anything with rejections, the default response rendering will apply, which is just an empty response with an appropriate status code. It's possible to convert rejections into custom responses using or_else
, but that may be less obvious. There should be an example!
let simple = warp::get(warp::path("hello").map(warp::reply));
let route = simple.or_else(|rejection| match rejection.status() {
StatusCode::NOT_FOUND => {
Ok(my_custom_404_response)
},
_ => {
// convert all?? others into just 400s
Ok(Response::builder()
.status(400)
.body("i swallowed an error!")
.unwrap())
}
});
Looking over the list of implementations for Reply, there doesn't seem to be a way to reply with a Stream
.
I am basically trying to port the following code from using hyper
to warp
:
fn main () {
let addr = ([127, 0, 0, 1], 8000).into();
let server = Server::bind(&addr)
.serve(|| hyper::service::service_fn(handler))
.map(|r| {
println!("Response: {:?}", r);
}).map_err(|e| {
println!("Error: {}", e);
});
hyper::rt::run(server);
}
type BoxedFuture = Box<Future<Item = Response<Body>, Error = hyper::Error> + Send>;
fn handler(_request: Request<Body>) -> BoxedFuture {
let mut response = Response::new(Body::empty());
*response.body_mut() =
Body::wrap_stream(<something that implements Stream>);
Box::new(future::ok(response))
}
I'm not sure what the right line is for which ones merit convenience filters, but at least PATCH
I'd think. Alternatively, given the small number, all could be exposed.
List of methods:
GET
HEAD
this could be promoted, I thinkPOST
PUT
DELETE
CONNECT
(pretty niche for use cases of warp?)OPTIONS
(again, niche for use cases of warp)TRACE
don't think this is worth it, unless exposing all methodsPATCH
I think this one should be promoted(I'm ignoring webdav and any other such protocols on top of HTTP here.)
Me a noob: /sum/1/2 doesn't work, let's get off rust nightly and see if that's the issue, let's rebuild the app in debug and non debug
You the author: "math routes should be mounted to a different path"
Me: that implies a copy not a move otherwise why would the example imply the original two routes work...
Turns out they don't which is fine but I was about 1 click away to just abandoning everything until I tried it. I would suggest showing 'can be moved to a new route' separate from 'here are the 2 examples folks will try to see the neat pattern matching'
First of all, this is a very cool project, thanks! It feels a bit like Flask for Python, in the sense that it is non-nonsense and lets you get on with writing the code.
In Flask, there is a method called url_for, which can be used both inside and outside templates. An example would be url_for("index"), which resolves to a the URL the index function (view) is mounted at.
Is this possible in warp? I've looked at the code but couldn't see anything obvious. It would be nice to have this functionality as part of the handlebars example.
Provide a content-negotiation filter with these objectives:
content-type
headersaccept
header.T: Serialize
, or Negotiate(Response<T: Serialize>)
, which will automatically be serialized by the "best" format.Example:
let negotiation = warp::reply::with::negotiate()
.format(json)
.format(xml);
let list = warp::path("todos")
.and_then(|| DB::load_todos());
let one = path!("todos" / u64)
.and_then(|id| Db::get_todo(id));
let api = list.or(one)
.with(negotiate);
Hi there,
I'm fiddling with warp and I'm having trouble redirecting the root route. I basically have 3 routes defined.
Here is what I'm attempting:
...
let asset_routes = generate_asset_routes();
let index = warp::get2().and(warp::index()).and_then(index_page);
let login = warp::get2()
.and(warp::path("login"))
.map(|| "login")
.map(render);
let routes = asset_routes.or(login).or(index);
...
fn index_page() -> Result<impl warp::Reply, warp::Rejection> {
Ok(Response::builder()
.status(StatusCode::FOUND)
.header("Location", "/login/")
.body("")
.expect("response is valid"))
}
fn generate_asset_routes() -> BoxedFilter<(impl Reply,)> {
let css = warp::path("css").and(warp::fs::dir("./content/css/"));
let fonts = warp::path("fonts").and(warp::fs::dir("./content/fonts/"));
let js = warp::path("js").and(warp::fs::dir("./content/js/"));
let images = warp::path("images").and(warp::fs::dir("./content/images/"));
css.or(fonts).or(js).or(images).boxed()
}
So this happily redirects the root route to /login
. However, the assets are also being redirected to /login
. If I remove the index route, this works. I can browse to /login
and the page and its assets render as expected.
What am I doing wrong here?
I am working on case, where one can look up an item from a database. The get_item(id) function returns a future because the lookup is async, of course.
I had some substantial trouble figuring out how to work with the future inside the and_then() combinator, mostly due to the lack of impls of Rejection for common error (and other types).
This is where I ended up:
let get_item = path!("item" / i32 ).and_then(|id| {
repository::get_item(id).map(|item:String| {
http::Response::builder().status(http::StatusCode::OK)
.body(format!("This is item {}", item)).unwrap()
}).map_err(|_| warp::reject())
});
Note the use of map_err(|_| warp::reject())
to have an error-type that the filter chain can work with.
So, even if I return HTTP responses for success and error cases (and explicitly not a rejection), I need to convert to Rejection so that the types match.
Two solutions would have helped me (I think):
Am I making sense? Did I miss something? Or is it just due to missing examples?
I am also curious whether you plan to add another combinator like and_then but which allows me to supply an explicit reactor to run the supplied future on. (eg see https://docs.rs/futures/0.2.1/futures/trait.FutureExt.html#method.with_executor )
I'd like to be able to reject requests with status codes currently not available, like 401 Unauthorized; is this possible?
Hi,
I'm attempting to use this crate in a project where some of the paths to route aren't known until runtime, as they're acquired from the CLI via clap
. Unfortunately, this appears to mean that I cannot use warp
at all as warp::path
requires p
to be &'static str
.
I'm wondering if warp
will ever support the use case where it's simply given a &str
to route.
Thanks.
above
thanks
Hello,
Sorry if this is kind of a basic question, but I'm trying to figure out how to return an error instead of going the or
path.
For example, if a querystring fails to parse, the way I have it set up now, the router will fallback to another or
branch. But what if I want to return an error immediately?
I'm probably overlooking something obvious...
(I'll update with a code snippet when I have time in a bit, sorry if the question is too vague)
While warp usually handles unit ()
from built-in Filter
s quite well, if a user tries to map
(or and_then
) a Filter
and return nothing, the unit is no longer dropped.
Example
warp::any()
// unit was dropped, no arg needed
.map(|| {
println!("no returning anything");
})
// return of `map` was auto-wrapped in `(T,)`, becoming `((),)`
.map(|((),))| {
println!("gross");
})
The Content-type
seems suspicious, since we are returning warp::reply::json(&employee)
:
examples$ curl -v -X POST -d '{"name":"frodo","rate":33}' http://localhost:3030/employees/44
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3030 (#0)
> POST /employees/44 HTTP/1.1
> Host: localhost:3030
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 26
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 26 out of 26 bytes
< HTTP/1.1 415 Unsupported Media Type
< content-length: 0
< date: Sat, 04 Aug 2018 07:22:08 GMT
Edit: Never mind, being stoopid - forget to say -H "Content-Type: application/json"
:)
I've run into a couple of situations where it would be convenient to construct a Filter
that expects additional input beyond the request. The main use case right now is for having resources that define their own "root route" in a helper function and then rely on a higher level of logic to integrate that Filter
into a higher context where it's easier to get to the context objects (like database connection pools). There are some pretty reasonable ways to handle many of those cases right now, like passing connection pools straight into the helper functions that create the lower-level Filter
s or having the low-level Filter
extract a function that maps the additional data to a Future
, but those both have their own trade-offs. I don't know how feasible it is, but having an ergonomic way to build a filter that expects more context information (i.e. some kind of "Inject
" associated type) might be a neat feature.
Hi!
I have added a warp example in the ructe repository: https://github.com/kaj/ructe/tree/master/examples/warp (ructe is Rust Compiled Templates , a web page template system where as much work as possible is done compile-time).
I welcome any suggestions for things that could be done more "warp-like". In particular, I'm a bit shady about error handling, and I have not implemented a proper far-expires header yet. I really like the routing / parameter passing to handlers, but at the same time, I think it would be nice to be able to also get the full url and other details of the current request, especially in the custom error handling routine.
Feel free to mention this in any documentation as you may see fit, or you can just close this PR.
I'm curious about why we write
warp::get(
warp::path("a")
.and(warp::path("b"))
);
instead of
warp::get()
.and(warp::path("a"))
.and(warp::path("b"))
;
I'm looking at writing functions that return filters, so that I can more easily write integration tests and split my routes across multiple modules. I want to start by doing something like:
main.rs
use myapp::routes;
fn main() {
warp::serve(routes()).run(([127, 0, 0, 1], 3030));
}
lib.rs
pub fn routes() -> // ??? {
path!("ping").map(|| "pong")
}
What type should my routes()
function return?
Hello,
with the following code, I would get a 405 error while I expect a 404:
fn main() {
let routes = warp::post2().map(warp::reply).or(warp::get2().and(warp::path("test").and_then(send_test)));
warp::serve(routes).run(([127, 0, 0, 1], 3000));
}
fn send_test() -> Result<String, warp::Rejection> {
Err(warp::reject::not_found())
}
Is there something I am doing wrong?
It would be nice if the rejections allowed passing an error message, so the end user of an API has a chance of knowing what they did wrong (for example on bad request).
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.