-
Notifications
You must be signed in to change notification settings - Fork 0
/
example.py
145 lines (105 loc) · 3.98 KB
/
example.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import os
from encodings.base64_codec import base64_decode
import uasyncio
from nanoweb import HttpError, Nanoweb, send_file
CREDENTIALS = ('foo', 'bar')
def authenticate(credentials):
async def fail(request):
await request.write("HTTP/1.1 401 Unauthorized\r\n")
await request.write('WWW-Authenticate: Basic realm="Restricted"\r\n\r\n')
await request.write("<h1>Unauthorized</h1>")
def decorator(func):
async def wrapper(request):
header = request.headers.get('Authorization', None)
if header is None:
return await fail(request)
# Authorization: Basic XXX
kind, authorization = header.strip().split(' ', 1)
if kind != "Basic":
return await fail(request)
authorization = base64_decode(authorization.strip()) \
.decode('ascii') \
.split(':')
if list(credentials) != list(authorization):
return await fail(request)
return await func(request)
return wrapper
return decorator
@authenticate(credentials=CREDENTIALS)
async def api_status(request):
"""API status endpoint"""
await request.write("HTTP/1.1 200 OK\r\n")
await request.write("Content-Type: application/json\r\n\r\n")
await request.write('{"status": "running"}')
@authenticate(credentials=CREDENTIALS)
async def api_ls(request):
await request.write("HTTP/1.1 200 OK\r\n")
await request.write("Content-Type: application/json\r\n\r\n")
await request.write('{"files": [%s]}' % ', '.join(
'"' + f + '"' for f in sorted(os.listdir('/'))
))
@authenticate(credentials=CREDENTIALS)
async def api_download(request):
await request.write("HTTP/1.1 200 OK\r\n")
filename = request.url[len(request.route.rstrip("*")) - 1:].strip("/")
await request.write("Content-Type: application/octet-stream\r\n")
await request.write("Content-Disposition: attachment; filename=%s\r\n\r\n"
% filename)
await send_file(request, filename)
@authenticate(credentials=CREDENTIALS)
async def api_upload(request):
if request.method != "PUT":
raise HttpError(request, 501, "Not Implemented")
bytesleft = int(request.headers.get('Content-Length', 0))
if not bytesleft:
await request.write("HTTP/1.1 204 No Content\r\n\r\n")
return
output_file = request.url[len(request.route.rstrip("*")) - 1:].strip("\/")
tmp_file = output_file + '.tmp'
try:
with open(tmp_file, 'wb') as o:
while bytesleft > 0:
chunk = await request.read(min(bytesleft, 64))
o.write(chunk)
bytesleft -= len(chunk)
o.flush()
except OSError as e:
raise HttpError(request, 500, "Internal error")
try:
os.remove(output_file)
except OSError as e:
pass
try:
os.rename(tmp_file, output_file)
except OSError as e:
raise HttpError(request, 500, "Internal error")
await send_response(request, 201, "Created")
@authenticate(credentials=CREDENTIALS)
async def api_delete(request):
if request.method != "DELETE":
raise HttpError(request, 501, "Not Implemented")
filename = request.url[len(request.route.rstrip("*")) - 1:].strip("\/")
try:
os.remove(filename)
except OSError as e:
raise HttpError(request, 500, "Internal error")
await send_response(request)
naw = Nanoweb()
# Declare route from a dict
naw.routes = {
'/status.html': {'name': 'Nanoweb'},
'/status/*': ('/status.html', {'name': 'Nanoweb'}),
'/api/status': api_status,
'/api/ls': api_ls,
'/api/download/*': api_download,
'/api/upload/*': api_upload,
'/api/delete/*': api_delete,
}
# Declare route directly with decorator
@naw.route("/ping")
def ping(request):
await request.write("HTTP/1.1 200 OK\r\n\r\n")
await request.write("pong")
loop = uasyncio.get_event_loop()
loop.create_task(naw.run())
loop.run_forever()