GithubHelp home page GithubHelp logo

saltyrtc / saltyrtc-server-python Goto Github PK

View Code? Open in Web Editor NEW
58.0 7.0 13.0 588 KB

SaltyRTC signalling server implementation.

License: MIT License

Python 99.47% Shell 0.53%
saltyrtc server python signaling

saltyrtc-server-python's Introduction

SaltyRTC Signalling Server

CircleCI codecov PyPI Gitter

This is a SaltyRTC server implementation for Python 3.6 or 3.7 using asyncio. (Note that currently Python 3.8+ is not supported! We recommend using Python 3.7.)

Note

On machines where Python 3 is not the default Python runtime, you should use pip3 instead of pip.

Prerequisites

We recommend using venv to create an isolated Python environment:

You can switch into the created virtual environment venv by running this command:

While the virtual environment is active, all packages installed using pip will be installed into this environment.

To deactivate the virtual environment, just run:

If you want easier handling of your virtualenvs, you might also want to take a look at virtualenvwrapper.

Installation

If you are using a virtual environment, activate it first.

Install the module by running:

The dependency libnacl will be installed automatically. However, you may need to install libsodium for libnacl to work.

Command Line Usage

The script saltyrtc-server will be automatically installed and provides a command line interface for the server.

Run the following command to see detailed usage information:

All command line options are also available as environment variables by prefixing them with SALTYRTC_SERVER_ and the upper case command name, followed by the option name in upper case. For example: SALTYRTC_SERVER_SERVE_PORT=8765.

Quick Start

Generate a new private permanent key:

Run the following command to start the server on any address with port `8765`:

Alternatively, provide the options via environment variables:

Docker

You can also use our official Docker images to run the server:

The above command maps port 8765 of the server within the container to port 8765 on the host machine.

Of course it is also possible to use environment variables to provide the options, as explained in the previous section.

Contributing

If you want to contribute to this project, you should install the optional dev requirements of the project in an editable environment:

Before creating a pull request, it is recommended to run the following commands to check for code style violations (flake8), optimise imports (isort), do a static type analysis and run the project's tests:

Reporting Security Issues

Please report security issues directly to one or both of the following contacts:

saltyrtc-server-python's People

Contributors

dbrgn avatar lgrahl avatar sauermann avatar threema-danilo 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

saltyrtc-server-python's Issues

Disconnected exception not caught

I found this in the log:

Jul 31 10:07:35 vs-wcs2 python[6075]: future: <Task finished coro=<ServerProtocol.initiator_receive_loop() done, defined at /srv/saltyrtc-server/saltyrtc/server/server.py:536> exception=Disconnected(3003,) created at /srv/saltyrtc-server/saltyrtc/server/server.py:368>
Jul 31 10:07:35 vs-wcs2 python[6075]: source_traceback: Object created at (most recent call last):
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "start_saltyrtc.py", line 115, in <module>
Jul 31 10:07:35 vs-wcs2 python[6075]:     main()
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "start_saltyrtc.py", line 91, in main
Jul 31 10:07:35 vs-wcs2 python[6075]:     loop.run_forever()
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/usr/lib/python3.5/asyncio/base_events.py", line 345, in run_forever
Jul 31 10:07:35 vs-wcs2 python[6075]:     self._run_once()
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/usr/lib/python3.5/asyncio/base_events.py", line 1304, in _run_once
Jul 31 10:07:35 vs-wcs2 python[6075]:     handle._run()
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/usr/lib/python3.5/asyncio/events.py", line 125, in _run
Jul 31 10:07:35 vs-wcs2 python[6075]:     self._callback(*self._args)
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/usr/lib/python3.5/asyncio/tasks.py", line 307, in _wakeup
Jul 31 10:07:35 vs-wcs2 python[6075]:     self._step()
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
Jul 31 10:07:35 vs-wcs2 python[6075]:     result = coro.send(None)
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/srv/saltyrtc-server/saltyrtc/server/server.py", line 216, in handler
Jul 31 10:07:35 vs-wcs2 python[6075]:     yield from self.handle_client(task_loop_task)
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/srv/saltyrtc-server/saltyrtc/server/server.py", line 368, in handle_client
Jul 31 10:07:35 vs-wcs2 python[6075]:     for coroutine in coroutines]
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/srv/saltyrtc-server/saltyrtc/server/server.py", line 368, in <listcomp>
Jul 31 10:07:35 vs-wcs2 python[6075]:     for coroutine in coroutines]
Jul 31 10:07:35 vs-wcs2 python[6075]: Traceback (most recent call last):
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/srv/saltyrtc-server/saltyrtc/server/protocol.py", line 559, in receive
Jul 31 10:07:35 vs-wcs2 python[6075]:     data = yield from self._connection.recv()
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/var/venv/saltyrtc-server/lib/python3.5/site-packages/websockets/protocol.py", line 298, in recv
Jul 31 10:07:35 vs-wcs2 python[6075]:     raise ConnectionClosed(self.close_code, self.close_reason)
Jul 31 10:07:35 vs-wcs2 python[6075]: websockets.exceptions.ConnectionClosed: WebSocket connection is closed: code = 3003, no reason.
Jul 31 10:07:35 vs-wcs2 python[6075]: The above exception was the direct cause of the following exception:
Jul 31 10:07:35 vs-wcs2 python[6075]: Traceback (most recent call last):
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
Jul 31 10:07:35 vs-wcs2 python[6075]:     result = coro.send(None)
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/srv/saltyrtc-server/saltyrtc/server/server.py", line 541, in initiator_receive_loop
Jul 31 10:07:35 vs-wcs2 python[6075]:     message = yield from initiator.receive()
Jul 31 10:07:35 vs-wcs2 python[6075]:   File "/srv/saltyrtc-server/saltyrtc/server/protocol.py", line 562, in receive
Jul 31 10:07:35 vs-wcs2 python[6075]:     raise Disconnected(exc.code) from exc
Jul 31 10:07:35 vs-wcs2 python[6075]: saltyrtc.server.exception.Disconnected: 3003

Version: 2.0.0

isort false positives (?)

We always get this output from isort -rc -df but I think the sorting is actually fine and the proposed changes of isort are ugly:

ERROR: /saltyrtc-server-python/saltyrtc/server/message.py Imports are incorrectly sorted.
ERROR: /saltyrtc-server-python/tests/test_cli.py Imports are incorrectly sorted.
--- /saltyrtc-server-python/saltyrtc/server/message.py:before	2018-07-09 17:18:19.324564
+++ /saltyrtc-server-python/saltyrtc/server/message.py:after	2018-08-29 18:01:28.881520
@@ -7,7 +7,6 @@
 import libnacl
 import umsgpack
 
-from .common import sign_keys as sign_keys_
 from .common import (
     COOKIE_LENGTH,
     DATA_LENGTH_MIN,
@@ -17,6 +16,9 @@
     CloseCode,
     MessageType,
     OverflowSentinel,
+)
+from .common import sign_keys as sign_keys_
+from .common import (
     validate_client_id,
     validate_cookie,
     validate_drop_reason,
--- /saltyrtc-server-python/tests/test_cli.py:before	2018-08-27 18:03:56.773474
+++ /saltyrtc-server-python/tests/test_cli.py:after	2018-08-29 18:01:28.916764
@@ -6,11 +6,9 @@
 
 import pytest
 
+from saltyrtc.server import Server
 from saltyrtc.server import __version__ as _version
-from saltyrtc.server import (
-    Server,
-    util,
-)
+from saltyrtc.server import util
 
 
 class TestCLI:
Skipped 2 files

Task exception never retrieved: Disconnected

[2017-07-06 11:30:25.653956] ERROR: asyncio: Task exception was never retrieved
future: <Task finished coro=<responder_receive_loop() done, defined at /.../saltyrtc-server-python
/saltyrtc/server/server.py:495> exception=Disconnected(1006,)>
Traceback (most recent call last):
  File "/.../saltyrtc-server-python/saltyrtc/server/protocol.py", line 528, in receive
    data = yield from self._connection.recv()
  File "/.../.virtualenvs/saltyrtc-py34/lib/python3.4/site-packages/websockets/protocol.py", line 299, in r
ecv
    raise ConnectionClosed(self.close_code, self.close_reason)
websockets.exceptions.ConnectionClosed: WebSocket connection is closed: code = 1006, no reason.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/.../python3.4/asyncio/tasks.py", line 238, in _step
    result = next(coro)
  File "/.../saltyrtc-server-python/saltyrtc/server/server.py", line 500, in responder_receive_loo
p
    message = yield from responder.receive()
  File "/.../saltyrtc-server-python/saltyrtc/server/protocol.py", line 531, in receive
    raise Disconnected(exc.code) from exc
saltyrtc.server.exception.Disconnected: 1006

AttributeError after relaying failed

Just managed to trigger an internal server error:

[2016-07-12 15:37:09.292546] INFO: saltyrtc.path.828.client.805ea2ea8: Connection established
[2016-07-12 15:37:09.859110] INFO: saltyrtc.path.828.client.805ea2ea8.0x02: Handshake completed
[2016-07-12 15:37:10.394731] INFO: saltyrtc.path.828.client.805ea2ea8.0x02: Sending relayed message failed, receiver 0x01 is gone
[2016-07-12 15:37:10.395454] INFO: saltyrtc.path.828.client.805ea2ea8.0x02: Relaying failed, enqueuing send-error
[2016-07-12 15:37:10.405469] INFO: saltyrtc.path.828.client.805ea2ea8.0x02: Sending relayed message failed, receiver 0x01 is gone
[2016-07-12 15:37:10.406046] INFO: saltyrtc.path.828.client.805ea2ea8.0x02: Relaying failed, enqueuing send-error
[2016-07-12 15:37:10.410673] ERROR: saltyrtc.path.828.client.805ea2ea8.0x02: Closing due to exception:
Traceback (most recent call last):
  File "/usr/home/lg/saltyrtc-server-python/saltyrtc/server.py", line 133, in handler
    yield from self.handle_client()
  File "/usr/home/lg/saltyrtc-server-python/saltyrtc/server.py", line 227, in handle_client
    raise exc
  File "/usr/local/lib/python3.4/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "/usr/home/lg/saltyrtc-server-python/saltyrtc/server.py", line 397, in responder_receive_loop
    yield from self.relay_message(initiator, message)
  File "/usr/home/lg/saltyrtc-server-python/saltyrtc/server.py", line 422, in relay_message
    source.log.info(error_message, destination.id)
AttributeError: 'NoneType' object has no attribute 'id'

Log events

It would be nice to be able to log certain events, like connection start and connection end, to tools like http://grafana.org/.

To keep things generic, the user could specify a script on the commandline that is invoked as a "callback" with the event data (e.g. {"name": "connect"} or {"name": "disconnect"}) to stdin whenever an event happens.

@lgrahl probably wouldn't be a lot of effort, right?

Path already removed

I get this a lot, especially when running tests:

WARNING: saltyrtc.paths: Path x has already been removed

It doesn't seem much of a problem, so this is a minor one.

Add more usage hints in readme

Please add more information on how to actually run the python-server!
Example:
$ saltyrtc-server serve gives me:

It is REQUIRED to provide a SSL certificate and a server permanent key unless the environment variable 'SALTYRTC_SAFETY_OFF' is set to 'yes-and-i-know-what-im-doing'

send-error message not always sent / wrong order

When a relay message is being enqueued (as a task from id 0x01) and the subsequent ping (to 0x02) times out, the resulting PingTimeoutError (on a task of 0x02) for some reason prevents the send-error routine from being called.

The relay send task on 0x02 is cancelled and the relay task waiting for the send task to complete on 0x01 doesn't get that information.

Set websocket close reason

When closing due to a protocol error, the server emits the following log (example):

[2016-10-31 16:52:36.572995] NOTICE: saltyrtc.path.142.client.7f9c1786fa08: Closing due to protocol error: Invalid ping interval

The WebSocket close event that I receive on the client side looks like this:

{
    code: 3001,
    reason: "",
    // ...
}

Would it be possible to add the reason text to the close event, or do you think that might be a security issue?

TypeError: Future or coroutine is required

When timing out after not receiving an answer to a server-auth message:

[2016-06-01 07:42:28.623226] ERROR: saltyrtc.path.8.client.7f1743e77e10.0x01: Closing due to exception:
Traceback (most recent call last):
  File "/home/danilo/saltyrtc/saltyrtc-server-python/saltyrtc/server.py", line 129, in handler
    yield from self.handle_client()
  File "/home/danilo/saltyrtc/saltyrtc-server-python/saltyrtc/server.py", line 215, in handle_client
    raise exc
  File "/usr/lib/python3.4/asyncio/tasks.py", line 237, in _step
    result = next(coro)
  File "/home/danilo/saltyrtc/saltyrtc-server-python/saltyrtc/server.py", line 458, in keep_alive
    pong_future, KEEP_ALIVE_TIMEOUT, loop=self._loop)
  File "/usr/lib/python3.4/asyncio/tasks.py", line 364, in wait_for
    fut = async(fut, loop=loop)
  File "/usr/lib/python3.4/asyncio/tasks.py", line 511, in async
    raise TypeError('A Future or coroutine is required')

Streamed forwarding

There's no need to ever receive forwarded messages completely. Streamed forwarding would decrease RAM usage and potentially improve throughput.

Better logging of unsupported subprotocol

When specifying an unsupported subprotocol, the error msg is as follows:

[2017-02-07 08:06:45.943390] NOTICE: saltyrtc.server: Unsupported sub-protocol 'None', dropping client

Would it be possible to actually print the string specified by the client? Or is that functionality hidden in the websockets implementation? (I tried to fix it for 10 minutes, but didn't find the correct place where the subprotocol string is parsed...)

Max message size

Currently when sending 4 MiB messages over the websocket, the connection is closed with status code 1009.

If you take a look at the WS server docs, you can see that the limit is 1 MiB by default.

The max_size parameter enforces the maximum size for incoming messages in bytes. The default value is 1MB. None disables the limit. If a message larger than the maximum size is received, recv() will raise ConnectionClosed and the connection will be closed with status code 1009.

The max_queue parameter sets the maximum length of the queue that holds incoming messages. The default value is 32. 0 disables the limit. Messages are added to an in-memory queue when they’re received; then recv() pops from that queue. In order to prevent excessive memory consumption when messages are received faster than they can be processed, the queue must be bounded. If the queue fills up, the protocol stops processing incoming data until recv() is called. In this situation, various receive buffers (at least in asyncio and in the OS) will fill up, then the TCP receive window will shrink, slowing down transmission to avoid packet loss.

Since Python can use up to 4 bytes of memory to represent a single character, each websocket connection may use up to 4 * max_size * max_queue bytes of memory to store incoming messages. By default, this is 128MB. You may want to lower the limits, depending on your application’s requirements.

I would suggest to document this limit, and to make the two values configurable by the user, since the limits should depend on the use case. For the WebRTC Task, 1 MiB is more than enough. For Threema Web + Relayed Data, we'd like to support messages up to maybe 50 MiB.

The problem is that according to the docs above, 50 MiB limits @ queue size 32 might result in memory use up to 6.4 GiB per process (worst case).

Chunking might be a possibility, but it's a bit stupid to do chunking on top of a chunked protocol.

@lgrahl RFC :)

Update CLI

The CLI is outdated and does not work with the current version. We should also add logging verbosity to the CLI as an option.

Memory Leaks

This project seems to have a small memory leak: RAM usage increased by ~300MiB over the last 7 days on a production server with 4 parallel SaltyRTC processes.

img

Nothing terrible though.

Revise cancellation of futures

Cancellation is propagated if not handled/shielded.

For example, when cancelling a Server.wait_closed, the handler tasks one is waiting for will be cancelled. We should carefully examine the public API and prevent futures from cancellation which shouldn't be cancelled.

Edit: This also affects the internal API. And coroutines are often moved into tasks, so we basically have to look at every yield from. An alternative to add tons of asyncio.shield is to mark a specific internal coroutine/task/future as don't cancel unless you really want to exit now. Candidates for this are, among others, ServerProtocol.handler and ServerProtocol.handle_client.

Edit 2: Wow, this issue sounds more scary than it actually is. Don't worry. The only critical operations affected here are enqueues into the task queue. But since the queue is unlimited, it will never block at the moment.

test_drop_responder results in uncaught exception

Even though it doesn't affect the test, the exception needs to be caught:

.[2018-02-28 17:34:08.236700] ERROR: asyncio: Task exception was never retrieved
future: <Task finished coro=<ServerProtocol.responder_receive_loop() done, defined at /saltyrtc/saltyrtc-server-python/saltyrtc/server/server.py:522> exception=Disconnected(1000,)>
Traceback (most recent call last):
  File "/saltyrtc/saltyrtc-server-python/saltyrtc/server/protocol.py", line 528, in receive
    data = yield from self._connection.recv()
  File "/.virtualenvs/saltyrtc-server-py35/lib/python3.5/site-packages/websockets/protocol.py", line 299, in recv
    raise ConnectionClosed(self.close_code, self.close_reason)
websockets.exceptions.ConnectionClosed: WebSocket connection is closed: code = 1000, no reason.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "/saltyrtc/saltyrtc-server-python/saltyrtc/server/server.py", line 527, in responder_receive_loop
    message = yield from responder.receive()
  File "/saltyrtc/saltyrtc-server-python/saltyrtc/server/protocol.py", line 531, in receive
    raise Disconnected(exc.code) from exc
saltyrtc.server.exception.Disconnected: 1000

Disconnected message not always sent

It seems that the "disconnected" message is not always sent.

Here's a responder that disconnects improperly (websocket close code 1006):

[2018-06-14 15:40:38.750663] INFO: asyncio: poll 55403.143 ms took 4153.605 ms: 1 events
[2018-06-14 15:40:38.751094] DEBUG: asyncio: <_SelectorSocketTransport fd=9 read=polling write=<idle, bufsize=0>> received EOF
[2018-06-14 15:40:38.751525] DEBUG: asyncio: <asyncio.sslproto.SSLProtocol object at 0x7f0265772198> received EOF
[2018-06-14 15:40:38.752640] INFO: websockets.protocol: Failing the WebSocket connection: 1006 
[2018-06-14 15:40:38.755240] DEBUG: saltyrtc.path.2.client.7f026620fee8.0x02: Connection closed while receiving
[2018-06-14 15:40:38.755858] DEBUG: saltyrtc.path.2.client.7f026620fee8.0x02: Task done {<Task finished coro=<ServerProtocol.responder_receive_loop() done, defined at /home/danilo/Projects/saltyrtc/server-python/venv/lib/python3.6/site-packages/saltyrtc/server/server.py:496> exception=Disconnected(1006,) created at /home/danilo/Projects/saltyrtc/server-python/venv/lib/python3.6/site-packages/saltyrtc/server/server.py:305>}
[2018-06-14 15:40:38.755955] DEBUG: saltyrtc.path.2.client.7f026620fee8.0x02: Cancelling task <Task pending coro=<ServerProtocol.keep_alive_loop() running at /home/danilo/Projects/saltyrtc/server-python/venv/lib/python3.6/site-packages/saltyrtc/server/server.py:569> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f02660aaca8>()] created at /usr/lib/python3.6/asyncio/base_events.py:276> created at /home/danilo/Projects/saltyrtc/server-python/venv/lib/python3.6/site-packages/saltyrtc/server/server.py:305>
[2018-06-14 15:40:38.756146] DEBUG: saltyrtc.path.2.client.7f026620fee8.0x02: Cancelling task <Task pending coro=<ServerProtocol.task_loop() running at /home/danilo/Projects/saltyrtc/server-python/venv/lib/python3.6/site-packages/saltyrtc/server/server.py:455> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f02660aa618>()] created at /usr/lib/python3.6/asyncio/base_events.py:276> created at /home/danilo/Projects/saltyrtc/server-python/venv/lib/python3.6/site-packages/saltyrtc/server/server.py:283>
[2018-06-14 15:40:38.756377] INFO: saltyrtc.path.2.client.7f026620fee8.0x02: Connection closed
[2018-06-14 15:40:38.756465] DEBUG: saltyrtc.path.2: Removed responder
[2018-06-14 15:40:38.756534] DEBUG: saltyrtc.server: Protocol unregistered: <saltyrtc.server.server.ServerProtocol object at 0x7f02657cb898>
[2018-06-14 15:40:38.756606] DEBUG: saltyrtc.path.2.client.7f026620fee8.0x02: Worker stopped

s/SSL/TLS/

We should rename the --sslcert and --sslkey options to --tlscert and --tlskey. Similarly, the -sk and -tk options could be renamed too. SSL is dead :)

To provide backwards compatibility the options could still be supported, but not documented in the --help output.

Write documentation

  • Build and validate docs
  • Upload docs to saltyrtc.readthedocs.org (+instructions)
  • Add a section for nginx configuration (mentioned in #17)
  • Add type annotations

Restarting restartable.py

  1. Start server: python examples/restartable.py
  2. Send SIGHUP: kill -HUP <pid>. Nothing happens.
  3. Send signal again. This stack trace is the result:
Traceback (most recent call last):
  File "examples/restartable.py", line 79, in <module>
    main()
  File "examples/restartable.py", line 53, in main
    loop.run_until_complete(restart_signal)
  File "/usr/lib64/python3.5/asyncio/base_events.py", line 375, in run_until_complete
    self.run_forever()
  File "/usr/lib64/python3.5/asyncio/base_events.py", line 345, in run_forever
    self._run_once()
  File "/usr/lib64/python3.5/asyncio/base_events.py", line 1257, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib64/python3.5/selectors.py", line 441, in select
    fd_event_list = self._epoll.poll(timeout, max_ev)
  File "examples/restartable.py", line 46, in restart_signal_handler
    restart_signal.set_result(True)
  File "/usr/lib64/python3.5/asyncio/futures.py", line 329, in set_result
    raise InvalidStateError('{}: {!r}'.format(self._state, self))
asyncio.futures.InvalidStateError: FINISHED: <Future finished result=True created at examples/restartable.py:43>

Task returning unexpectly

If one runs the tests from the saltyrtc-client-js implementation, the following error can be found in the log:

[2018-07-17 15:32:10.100117] ERROR: saltyrtc.path.6.client.7fc0f46d31f8.0x01: Task <Task finished coro=<ServerProtocol.initiator_receive_loop() done, defined at /home/lenny/Projects/saltyrtc/saltyrtc-server-python/saltyrtc/server/server.py:536> result=None> returned unexpectedly
[2018-07-17 15:32:10.100270] NOTICE: saltyrtc.path.6.client.7fc0f46d31f8.0x01: Closing due to protocol error: A task returned unexpectedly

Still need to pin down what test this reproduces.

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.