Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add socket sharing #94

Merged
merged 23 commits into from
Nov 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ actix-web = "4.0.0-beta.8"
actix-files = "0.6.0-beta.4"
futures-util = "0.3.15"
matchit = "0.4.3"
actix-http = "3.0.0-beta.8"
socket2 = { version = "0.4.1", features = ["all"] }

[package.metadata.maturin]
name = "robyn"
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,19 @@ If you're feeling curious. You can take a look at a more detailed architecture [

## To Run

### Without hot reloading
`python3 app.py`
```
python3 app.py -h

usage: base_routes.py [-h] [--processes PROCESSES] [--workers WORKERS] [--dev DEV]

### With hot reloading
`python3 app.py --dev=true`
Robyn, a fast async web framework with a rust runtime.

optional arguments:
-h, --help show this help message and exit
--processes PROCESSES : allows you to choose the number of parallel processes
--workers WORKERS : allows you to choose the number of workers
--dev DEV : this flag gives the option to enable hot reloading or not
```


## Contributors/Supporters
Expand Down
40 changes: 31 additions & 9 deletions integration_tests/base_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
# robyn_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../robyn")
# sys.path.insert(0, robyn_path)

from robyn import Robyn, static_file, jsonify
from robyn import Robyn, static_file, jsonify, SocketHeld
import asyncio
import os
import pathlib

app = Robyn(__file__)


callCount = 0


@app.get("/")
async def h(requests):
print(requests)
async def hello(request):
global callCount
callCount += 1
message = "Called " + str(callCount) + " times"
return message
return jsonify(request)


@app.get("/test/:id")
Expand All @@ -41,21 +41,41 @@ async def json(request):
return jsonify({"hello": "world"})

@app.post("/post")
async def postreq(request):
async def post():
return "POST Request"

@app.post("/post_with_body")
async def postreq_with_body(request):
return bytearray(request["body"]).decode("utf-8")

@app.put("/put")
async def putreq(request):
async def put(request):
return "PUT Request"

@app.put("/put_with_body")
async def putreq_with_body(request):
print(request)
return bytearray(request["body"]).decode("utf-8")


@app.delete("/delete")
async def deletereq(request):
async def delete(request):
return "DELETE Request"

@app.delete("/delete_with_body")
async def deletereq_with_body(request):
return bytearray(request["body"]).decode("utf-8")


@app.patch("/patch")
async def patchreq(request):
async def patch(request):
return "PATCH Request"

@app.patch("/patch_with_body")
async def patchreq_with_body(request):
return bytearray(request["body"]).decode("utf-8")


@app.get("/sleep")
async def sleeper():
await asyncio.sleep(5)
Expand All @@ -71,5 +91,7 @@ def blocker():

if __name__ == "__main__":
app.add_header("server", "robyn")
app.add_directory(route="/test_dir",directory_path="./test_dir/build", index_file="index.html")
current_file_path = pathlib.Path(__file__).parent.resolve()
os.path.join(current_file_path, "build")
app.add_directory(route="/test_dir",directory_path=os.path.join(current_file_path, "build/"), index_file="index.html")
app.start(port=5000, url='0.0.0.0')
16 changes: 16 additions & 0 deletions integration_tests/test_delete_requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import requests

BASE_URL = "http://127.0.0.1:5000"

def test_delete(session):
res = requests.delete(f"{BASE_URL}/delete")
assert (res.status_code == 200)
assert res.text=="DELETE Request"

def test_delete_with_param(session):
res = requests.delete(f"{BASE_URL}/delete_with_body", data = {
"hello": "world"
})
assert (res.status_code == 200)
assert res.text=="hello=world"

11 changes: 11 additions & 0 deletions integration_tests/test_dir/build/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
Hello, World
</body>
</html>
8 changes: 5 additions & 3 deletions integration_tests/test_get_requests.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import requests

BASE_URL = "http://127.0.0.1:5000"

def test_index_request(session):
res = requests.get("http://127.0.0.1:5000/")
res = requests.get(f"{BASE_URL}")
assert(res.status_code == 200)

def test_jsonify(session):
r = requests.get("http://127.0.0.1:5000/jsonify")
r = requests.get(f"{BASE_URL}/jsonify")
assert r.json()=={"hello":"world"}
assert r.status_code==200

def test_html(session):
r = requests.get("http://127.0.0.1:5000/test/123")
r = requests.get(f"{BASE_URL}/test/123")
assert "Hello world. How are you?" in r.text

15 changes: 15 additions & 0 deletions integration_tests/test_patch_requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import requests

BASE_URL = "http://127.0.0.1:5000"

def test_patch(session):
res = requests.patch(f"{BASE_URL}/patch")
assert (res.status_code == 200)
assert res.text=="PATCH Request"

def test_patch_with_param(session):
res = requests.patch(f"{BASE_URL}/patch_with_body", data = {
"hello": "world"
})
assert (res.status_code == 200)
assert res.text=="hello=world"
22 changes: 22 additions & 0 deletions integration_tests/test_post_requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import requests

BASE_URL = "http://127.0.0.1:5000"

def test_post(session):
res = requests.post(f"{BASE_URL}/post")
assert (res.status_code == 200)
assert res.text=="POST Request"

def test_post_with_param(session):
res = requests.post(f"{BASE_URL}/post_with_body", data = {
"hello": "world"
})
assert res.text=="hello=world"
assert (res.status_code == 200)


def test_jsonify_request(session):
res = requests.post(f"{BASE_URL}/jsonify/123")
assert(res.status_code == 200)
assert res.json()=={"hello":"world"}

16 changes: 16 additions & 0 deletions integration_tests/test_put_requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import requests

BASE_URL = "http://127.0.0.1:5000"

def test_put(session):
res = requests.put(f"{BASE_URL}/put")
assert (res.status_code == 200)
assert res.text=="PUT Request"

def test_put_with_param(session):
res = requests.put(f"{BASE_URL}/put_with_body", data = {
"hello": "world"
})

assert (res.status_code == 200)
assert res.text=="hello=world"
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,12 @@ build-backend = "poetry.core.masonry.api"
[project]
name = "robyn"
dependencies = [
"watchdog>=2.1.3,<3"
'watchdog == 2.1.3',
'multiprocess == 0.70.12.2',
# conditional
'uvloop == 0.16.0; sys_platform == "darwin"',
'uvloop == 0.16.0; platform_machine == "x86_64"',
'uvloop == 0.16.0; platform_machine == "i686"'
]


3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
requests==2.26.0
pytest==6.2.5
maturin
uvloop
watchdog
multiprocess==0.70.12.2
44 changes: 31 additions & 13 deletions robyn/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
# default imports
import os
import argparse
import asyncio
from inspect import signature
import multiprocessing as mp
mp.allow_connection_pickling()

from .robyn import Server
# custom imports and exports
from .robyn import Server, SocketHeld
from .argument_parser import ArgumentParser
from .responses import static_file, jsonify
from .dev_event_handler import EventHandler
from .processpool import spawn_process
from .log_colors import Colors


# 3rd party imports and exports
from multiprocess import Process
from watchdog.observers import Observer


Expand All @@ -22,12 +28,14 @@ def __init__(self, file_object):
self.file_path = file_object
self.directory_path = directory_path
self.server = Server(directory_path)
self.dev = self._is_dev()

def _is_dev(self):
parser = argparse.ArgumentParser()
parser.add_argument('--dev', default=False, type=lambda x: (str(x).lower() == 'true'))
return parser.parse_args().dev
self.parser = ArgumentParser()
self.dev = self.parser.is_dev()
self.processes = self.parser.num_processes()
self.workers = self.parser.workers()
self.routes = []
self.headers = []
self.routes = []
self.directories = []


def add_route(self, route_type, endpoint, handler):
Expand All @@ -42,15 +50,15 @@ def add_route(self, route_type, endpoint, handler):
""" We will add the status code here only
"""
number_of_params = len(signature(handler).parameters)
self.server.add_route(
route_type, endpoint, handler, asyncio.iscoroutinefunction(handler), number_of_params
self.routes.append(
( route_type, endpoint, handler, asyncio.iscoroutinefunction(handler), number_of_params)
)

def add_directory(self, route, directory_path, index_file=None, show_files_listing=False):
self.server.add_directory(route, directory_path, index_file, show_files_listing)
self.directories.append(( route, directory_path, index_file, show_files_listing ))

def add_header(self, key, value):
self.server.add_header(key, value)
self.headers.append(( key, value ))

def remove_header(self, key):
self.server.remove_header(key)
Expand All @@ -61,8 +69,18 @@ def start(self, url="127.0.0.1", port=5000):

:param port [int]: [reperesents the port number at which the server is listening]
"""
socket = SocketHeld(f"0.0.0.0:{port}", port)
workers = self.workers
if not self.dev:
self.server.start(url, port)
for process_number in range(self.processes):
copied = socket.try_clone()
p = Process(
target=spawn_process,
args=(url, port, self.directories, self.headers, self.routes, copied, f"Process {process_number}", workers),
)
p.start()

input("Press Cntrl + C to stop \n")
else:
event_handler = EventHandler(self.file_path)
event_handler.start_server_first_time()
Expand Down
22 changes: 22 additions & 0 deletions robyn/argument_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import argparse

class ArgumentParser(argparse.ArgumentParser):
def __init__(self):
self.parser = argparse.ArgumentParser(description="Robyn, a fast async web framework with a rust runtime.")
self.parser.add_argument('--processes', type=int, default=1, required=False)
self.parser.add_argument('--workers', type=int, default=1, required=False)
self.parser.add_argument('--dev', default=False, type=lambda x: (str(x).lower() == 'true'))
self.args = self.parser.parse_args()

def num_processes(self):
return self.args.processes

def workers(self):
return self.args.workers

def is_dev(self):
_is_dev = self.args.dev
if _is_dev and ( self.num_processes() != 1 or self.workers() != 1 ):
raise Exception("--processes and --workers shouldn't be used with --dev")
return _is_dev

Loading