Giter Site home page Giter Site logo

ngx_brotli's People

Contributors

daniel15 avatar dvershinin avatar eustas avatar felixbuenemann avatar nottsunami avatar piotrsikora avatar sp1l avatar taomaree avatar tedkornish 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

ngx_brotli's Issues

Written data not fully flushed on socket

Hi eustas,

We were running ngx_brotli and saw some strange issues every maybe 0.01% of requests where nginx 'lied' about having completed a request (as in, log entry was written to access_log) in 4ms yet the connection was left open and written data was not fully 'flushed' to the client.

After 90 seconds (our proxy_*_timeout value) the connection was dropped.

Not really sure how to debug it since it was completely random. But removing ngx_brotli from our nginx build resolved the issue.

corrupt server response with brotli_comp_level 1 and lower

When I set the brotli_comp_level lower then 2, I have with most compressed files a corrupt response form my server. This is easily reproducible. It also sometimes happens with brotli_comp_level lower then 5, but only on really big text/plain or text/css files. But it seems this is not so easy reproducible.
This bug is maybe a duplicate of #7 , because it shows similar symptoms on Wordpress or other sites with big css and JavaScript files. A Browser does it similar to my first example and starts rendering the non corrupt parts. It seems that Firefox is a little bit more picky, but with a Browser it is easier to notice it with big text files. The "404 Not Found" example isn't always showing a corrupt response in a browser.

I tried it with nginx 1.13.9 and brotli 1.0.2 and 1.0.1. The Problem doesn't occur with the old google/ngx_brotli version.

An easy way to reproduce this problem with a simple default setup is to set brotli_comp_level to 1 and use curl to get a nonexistent file for the server.

$ curl -H 'Accept-encoding: br' http://debian-test.local/notfound | brotli -cd
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   115    0   115    0     0  16428      0 --:--:-- --:--:-- --:--:-- 16428
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
corrupt input [con]

There is no error in any nginx log. Only a normal one in the access.log

[28/Feb/2018:03:50:36 +0100] "GET /notfound HTTP/1.1" 404 126 gzip - br 1.01

(I'm also not sure why the $body_bytes_sent in nginx log and received bytes form curl don't match but it is the same different with gzip.)

I also tested a normal text file, with around 5000 words.

$ curl -o Lorem5000.txt.br -H 'Accept-encoding: br' http://debian-test.local/Lorem5000.txt
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 10104    0 10104    0     0  1644k      0 --:--:-- --:--:-- --:--:-- 1644k

brotli -t Lorem5000.txt.br
corrupt input [Lorem5000.txt.br]

Again no errors, only a 200.

[28/Feb/2018:03:45:28 +0100] "GET /Lorem5000.txt HTTP/1.1" 200 10117 gzip - br 3.24

My config:
https://gist.github.com/xabbu/467927f8c8bec3fe8bd8eed2cfea09f7

These examples with gzip and without compression
https://gist.github.com/xabbu/4b93253341e467ab13befc124c358be9

Append "Accept-Encoding" to an existing vary header, instead of always adding a new vary header.

The current mechanism of simply adding a vary header, can lead to something like this

vary: Accept-Encoding
vary: X-Requested-With

Which in turn, because of this bug https://trac.nginx.org/nginx/ticket/1423 leads to
proxy_cache_key not working correctly.

The gzip module has the same issue, but as a lot more browsers support gzip, it is not as often a visible problem.

Just to clarify what the result can look like:
image

As the Nginx ticket is not really worked on, maybe this can be fixed here, by appending Accept-Encoding to an existing vary header, if one exist?

vary: X-Requested-With, Accept-Encoding

Brotli requires nginx 1.16, I have nginx 1.17

Hi, I'm having a problem with yum install nginx nginx-module-nbr... it appears that I have nginx 1.17, but the Brotli plugin requires 1.16. Is that correct? And if so, is there a newer Brotli plugin I can install?

# yum install nginx nginx-module-nbr

Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: mirror.mojohost.com
 * epel: d2lzkl7pfhq30w.cloudfront.net
 * extras: mirror.mojohost.com
 * nux-dextop: mirror.li.nux.ro
 * updates: mirror.mojohost.com
 * webtatic: us-east.repo.webtatic.com
Package 1:nginx-1.17.2-1.el7.ngx.x86_64 already installed and latest version
Resolving Dependencies
--> Running transaction check
---> Package nginx-module-nbr.x86_64 1:1.16.0.0.1.2-1.el7.gps will be installed
--> Processing Dependency: nginx = 1:1.16.0-1.el7.ngx for package: 1:nginx-module-nbr-1.16.0.0.1.2-1.el7.gps.x86_64
--> Finished Dependency Resolution
Error: Package: 1:nginx-module-nbr-1.16.0.0.1.2-1.el7.gps.x86_64 (getpagespeed-extras)
           Requires: nginx = 1:1.16.0-1.el7.ngx
           Installed: 1:nginx-1.17.2-1.el7.ngx.x86_64 (@nginx)
               nginx = 1:1.17.2-1.el7.ngx

$brotli_ratio is always empty

Since the update of brotli to v1.0.1, the $brotli_ratio variable is always empty. It should contain the compression ratio achieved by the brotli filter module. It should work similar to $gzip_ratio. The old google/ngx_brotli module works as accepted.

I created a simple "log_format" which shows $gzip_ratio and $brotli_ratio.

log_format compressed '[$time_local] "$request" $status '
'$body_bytes_sent gzip $gzip_ratio br $brotli_ratio';

It shows on a simple HTTP connection, that gzip is used. Since there is no brotli, the $brotli_ratio is empty. This is expected.

[26/Feb/2018:20:20:19 +0100] "GET / HTTP/1.1" 200 396 gzip 1.59 br -

On a HTTP/2 connection is brotli used, so there is no gzip. But the $brotli_ratio variable is still empty.

[26/Feb/2018:20:23:44 +0100] "GET / HTTP/2.0" 200 289 gzip - br -

I did test it with nginx 1.13.6 and 1.13.9, also with brotli v1.0.1 and v1.0.2

BREACH concern: 'text/html' is always compressed

By having text/html "always compressed" you introduce a persistent BREACH risk to your data.

It would be better if you have text/html as the 'default' option for brotli_types, but allow people to override it to exclude text/html MIMEtypes.

By forcing text/html to be compressed all the time, a compressed HTML body containing some secret information could be vulnerable to BREACH. Adjusting the defaults to allow brotli_types to be overridden to exclude text/html would help to mitigate this risk.

(NGINX upstream has gzip_types always compress text/html which is its own issue, and should probably be overrideable, yet it isn't - this is https://trac.nginx.org/nginx/ticket/1083 to some extent)

Javascript not fully loading

Hi!

After upgrading to latest build, we are seeing issues with Javascript files not fully loading in WordPress dashboard: Uncaught SyntaxError: Unexpected end of input

Testing from Chrome 64.0.3282.167 but users on Firefox and Safari also reported it.

Build was done on Nginx 1.13.8:

./configure --with-ld-opt='-ljemalloc -Wl,-z,relro -Wl,-rpath,/usr/local/lib' --with-cc-opt='-DTCP_FASTOPEN=23 -m64 -march=x86-64 -g -O3 -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wimplicit-fallthrough=0 -fcode-hoisting -Wno-cast-function-type -Wp,-D_FORTIFY_SOURCE=2' --prefix=/usr/share --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --user=nginx --group=nginx --with-file-aio --with-http_ssl_module --with-http_realip_module --with-http_sub_module --with-http_dav_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_v2_module --with-pcre=/root/sources/pcre-8.41 --with-pcre-jit --add-module=/root/sources/ngx_brotli

Please let me know should you require any additional information.

100% nginx usage

Hi, on debian/buster,
nginx 1.14.2
libbrotli 1.0.7
your latest ngx_brotli module, configured with

brotli on;

brotli_comp_level 6;
brotli_buffers 16 8k;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

After several requests (browsing several web sites) nginx quickly goes to 100% cpu load and stays there... If i set brotli off it doesn't happen.

The full configuration is
https://github.com/pageboard/proxy/tree/master/nginx

Releases missing submodules

Hi,

Let me start by saying, thanks a tonne for keeping this fork alive.

It seems like the "release" build doesn't contain what's needed to compile. Specifically I'm seeing this error:

./configure: error: Brotli library is missing from the ./ngx_brotli-0.1.2/deps/brotli/c directory.

Please make sure that the git submodule has been checked out:

    cd ./ngx_brotli-0.1.2 && git submodule update --init && cd /nginx-libressl/src/nginx-1.14.0

It reads like it has an obvious fix, but the advice given requires we be working with

$ wget https://github.com/eustas/ngx_brotli/archive/v0.1.2.tar.gz
$ tar zxvf v0.1.2.tar.gz
$ cd ngx_brotli-0.1.2/
$ git submodule update --init
fatal: not a git repository (or any of the parent directories): .git
$ git status
fatal: not a git repository (or any of the parent directories): .git

I'm not sure what the trick to solving this is but apparently Micropython solved it:

micropython/micropython#2952 (comment)

Accept-Encoding q-value of 0 is not honored

When the request includes for example

accept-encoding: gzip, deflate, br;q=0

the answer is still using brotli, even though the accept-encoding header says don't send me brotli.

ngx_brotli won't work in nginx-1.16.1

@eustas

I tried to debug problems with ngx_brotli modules at 1.16.1. This is my configuration :

nginx version: blinx/1.16.1
built by gcc 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) 
built with OpenSSL 1.1.0 (compatible; BoringSSL) (running with BoringSSL)
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --add-module=ngx_brotli --with-cc-opt='-g -O2 -fdebug-prefix-map=/home/mockbuild/nginx-1.16.1=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-specs=/usr/share/dpkg/no-pie-link.specs -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' --with-openssl=boringssl --with-openssl-opt=enable-tls1_3

The module should be loaded correctly with nginx, but the browser encoding still gzip instead of br. I am using Google Chrome latest stable OSX. Is it normal @eustas ?

Checked out submodule deps using commit d6d98957ca8ccb1ef45922e978bb10efca0ea541

unbrotli?

Would it be possible to achieve/implement the same thing as what gunzip module does, in regards with Brotli?

Just some idea how gunzip can be immediately useful first:

Say, you proxy a site with NGINX, and overwrite Accept-Encoding with gzip in order for the upstream to always return compress data
Then the value of proxy_buffers can be greatly reduced because you always know the response is rather small, essentially reducing RAM usage.
And with gunzip applied in NGINX, you still get to serve the clients who requested non-compressed resource.

We can't do the same trick with Brotli, as there is no equivalent module which would auto-decompress upstream response for clients which indicate their preference in no compression.

E.g. for a Magento 2 page which emits 512K worth of HTML, which compresses to under 50K with Brotli:

location / {
    proxy_pass foo;
    proxy_set_header Accept-Encoding br;
    unbrotli on;
    proxy_buffers 12 4k;  
    proxy_http_version 1.1;
}

instead of:

location / {
    proxy_pass foo;
    proxy_buffers 128 4k; # has to be set high because we don't know if upstream returns compressed/not
    proxy_http_version 1.1;
}

Sure, it implies delivering uncompressed response to gzip clients, but that may be OK to same folks (myself included), considering the vast support of Brotli in browsers nowadays.

Conflict with nginx-module-vts

I just found a very peculiar conflict between eustas/ngx_brotli (v0.1 and latest) and vozlt/nginx-module-vts. When rendering the VTS status page (/status/format/html), it just displays a blank page. That is, the HTML has things in the <head> tag but nothing in the <body>. However, rendering as JSON or Prometheus works fine.

This issue is present when compiling with nginx-modules/ngx_brotli and eustas/ngx_brotli but not with google/ngx_brotli. I am compiling Nginx 1.16.1 with the following modules:

git clone --recurse-submodules -j8 https://github.com/eustas/ngx_brotli.git /usr/src/nginx/modules/ngx_brotli
git clone --recurse-submodules -j8 https://github.com/openresty/headers-more-nginx-module.git /usr/src/nginx/modules/headers-more-nginx-module
git clone --recurse-submodules -j8 https://github.com/openresty/echo-nginx-module.git /usr/src/nginx/modules/echo-nginx-module
git clone --recurse-submodules -j8 https://github.com/vozlt/nginx-module-vts.git /usr/src/nginx/modules/nginx-module-vts
git clone --recurse-submodules -j8 https://github.com/nginx-modules/nginx_upstream_check_module.git /usr/src/nginx/modules/nginx_upstream_check_module

And the following configuration:

curl -sS https://nginx.org/download/nginx-1.16.1.tar.gz | sudo tar -xz --strip 1 -C /usr/src/nginx
cd /usr/src/nginx
sudo patch -p1 < /usr/src/nginx/modules/nginx_upstream_check_module/check_1.14.0+.patch
./configure --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx.lock --with-threads --with-file-aio --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_image_filter_module --with-http_geoip_module --with-http_sub_module --with-http_dav_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_slice_module --with-http_stub_status_module --http-client-body-temp-path=/var/lib/nginx/body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --http-scgi-temp-path=/var/lib/nginx/scgi --with-mail --with-mail_ssl_module --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --add-module=/usr/src/nginx/modules/ngx_brotli --add-module=/usr/src/nginx/modules/headers-more-nginx-module --add-module=/usr/src/nginx/modules/echo-nginx-module --add-module=/usr/src/nginx/modules/nginx-module-vts --add-module=/usr/src/nginx/modules/nginx_upstream_check_module --with-compat --with-debug --with-cc-opt="-g -w -fPIC -O2 -fstack-protector-strong --param=ssp-buffer-size=4 -Wp,-D_FORTIFY_SOURCE=2 -Wp,-DFD_SETSIZE=65536 -I/usr/local/pcre/include -I/usr/local/zlib-ng/include -I/usr/local/openssl/include" --with-ld-opt="-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -ljemalloc -L/usr/local/jemalloc/lib -Wl,-rpath=/usr/local/jemalloc/lib -L/usr/local/pcre/lib -Wl,-rpath=/usr/local/pcre/lib -L/usr/local/zlib-ng/lib -Wl,-rpath=/usr/local/zlib-ng/lib -L/usr/local/openssl/lib -Wl,-rpath=/usr/local/openssl/lib"
sudo make -j 16 && sudo make install

I'm not sure why one module would conflict with another, or even if this is an issue with ngx_brotli, nginx-module-vts or just my configuration. Any help would be appreciated.

Libbrotlienc not found

Was trying to build nginx with this fork, unsuccessfully.

What i did:

cd /opt/lib && git clone https://github.com/eustas/ngx_brotli.git && cd ngx_brotli && git submodule update --init
cd /opt/lib && git clone https://github.com/bagder/libbrotli.git && cd libbrotli
./autogen.sh
./configure
make && make install

then make nginx with

--prefix=/usr/share/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--http-client-body-temp-path=/var/lib/nginx/tmp/client_body \
--http-proxy-temp-path=/var/lib/nginx/tmp/proxy \
--http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi \
--http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi \
--http-scgi-temp-path=/var/lib/nginx/tmp/scgi \
--pid-path=/run/nginx.pid \
--lock-path=/run/lock/subsys/nginx \
--user=nginx \
--group=nginx \
--with-file-aio \
--with-threads \
--with-http_v2_module \
--with-http_ssl_module \
--with-http_sub_module \
--with-http_gzip_static_module \
--with-http_stub_status_module \
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -DTCP_FASTOPEN=23' \
--with-ld-opt='-Wl,-z,relro -Wl,-E' \
--with-openssl=/opt/lib/openssl-1.0.2o	 \
--with-openssl-opt='enable-tlsext zlib no-ssl2 no-shared -fpic' \
--add-module=/opt/lib/ngx_pagespeed \
--add-module=/opt/lib/ngx_brotli

after install nginx can't start because of

/usr/sbin/nginx: error while loading shared libraries: libbrotlienc.so.0: cannot open shared object file: No such file or directory

With original google/ngx_brotli all this works smoothy

Random segfaults

My environment:

  • Debian stretch
  • libbrotli 1.0.3 from Debian backports
  • nginx 1.13.12 backported by me from Debian testing
  • ngx_brotli at f1fcc84 using the system libbrotli

While in general it works, nginx workers segfault randomly. Unfortunately I am unable to reliably reproduce it myself yet, only throwing some real traffic at it manifests the problem.

Disabling brotli solved the problem with this build for me. I'll try to debug this further.

How to install brotly_FILTER_module?

I am trying to add brotly to nginx using the FreeBSD www/nginx port (https://www.freshports.org/www/nginx/). I selected 'brotli' in port configuration and got compiled nginx with the following flags:

# nginx -V
nginx version: nginx/1.14.2
built with OpenSSL 1.0.2p 14 Aug 2018
TLS SNI support enabled
configure arguments: --prefix=/usr/local/etc/nginx --with-cc-opt='-I /usr/local/include' --with-ld-opt='-L /usr/local/lib' --conf-path=/usr/local/etc/nginx/nginx.conf --sbin-path=/usr/local/sbin/nginx --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --user=www --group=www --add-module=/usr/ports/www/nginx/work/ngx_cache_purge-2.3 --with-debug --modules-path=/usr/local/libexec/nginx --http-client-body-temp-path=/var/tmp/nginx/client_body_temp --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp --http-proxy-temp-path=/var/tmp/nginx/proxy_temp --http-scgi-temp-path=/var/tmp/nginx/scgi_temp --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp --http-log-path=/var/log/nginx/access.log --with-http_v2_module --with-http_auth_request_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-pcre --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --without-mail_imap_module --without-mail_pop3_module --without-mail_smtp_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_geoip_module=dynamic --with-stream=dynamic --add-dynamic-module=/usr/ports/www/nginx/work/ngx_brotli-8104036 --add-dynamic-module=/usr/ports/www/nginx/work/echo-nginx-module-c65f5c6

As you can see, the ngx_brotli module is here. But when I trying to add brotli on; directive to my 'server' section, I getting message nginx: [emerg] unknown directive "brotli" in /www/htdocs/nginx.conf:166.

It looks like I have only static version of module but not filter. What can I do to fix this?

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.