uploadserver 5.2.2__tar.gz → 6.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {uploadserver-5.2.2 → uploadserver-6.0.1}/PKG-INFO +69 -15
- uploadserver-5.2.2/uploadserver.egg-info/PKG-INFO → uploadserver-6.0.1/README.md +57 -26
- {uploadserver-5.2.2 → uploadserver-6.0.1}/setup.py +2 -2
- {uploadserver-5.2.2 → uploadserver-6.0.1}/uploadserver/__init__.py +31 -13
- uploadserver-5.2.2/README.md → uploadserver-6.0.1/uploadserver.egg-info/PKG-INFO +80 -12
- {uploadserver-5.2.2 → uploadserver-6.0.1}/LICENSE +0 -0
- {uploadserver-5.2.2 → uploadserver-6.0.1}/setup.cfg +0 -0
- {uploadserver-5.2.2 → uploadserver-6.0.1}/uploadserver/__main__.py +0 -0
- {uploadserver-5.2.2 → uploadserver-6.0.1}/uploadserver/cgi.py +0 -0
- {uploadserver-5.2.2 → uploadserver-6.0.1}/uploadserver.egg-info/SOURCES.txt +0 -0
- {uploadserver-5.2.2 → uploadserver-6.0.1}/uploadserver.egg-info/dependency_links.txt +0 -0
- {uploadserver-5.2.2 → uploadserver-6.0.1}/uploadserver.egg-info/entry_points.txt +0 -0
- {uploadserver-5.2.2 → uploadserver-6.0.1}/uploadserver.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: uploadserver
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.1
|
|
4
4
|
Summary: Python's http.server extended to include a file upload page
|
|
5
5
|
Home-page: https://github.com/Densaugeo/uploadserver
|
|
6
6
|
Author: Densaugeo
|
|
@@ -8,9 +8,18 @@ Author-email: author@example.com
|
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
Dynamic: requires-python
|
|
22
|
+
Dynamic: summary
|
|
14
23
|
|
|
15
24
|
# uploadserver
|
|
16
25
|
|
|
@@ -23,8 +32,8 @@ Python's http.server extended to include a file upload page
|
|
|
23
32
|
|
|
24
33
|
| Platform | Supported? | Notes |
|
|
25
34
|
|-|-|-|
|
|
26
|
-
| Python 3.
|
|
27
|
-
| Python 3.6-3.
|
|
35
|
+
| Python 3.9+ | Yes | Tested on 3.9 through 3.14 every release. |
|
|
36
|
+
| Python 3.6-3.8 | No | Was supported by previous versions. |
|
|
28
37
|
| Python 3.5- | No | |
|
|
29
38
|
| Linux | Yes | Tested on Fedora and Ubuntu every release. |
|
|
30
39
|
| Windows | Yes | Occasional manual testing. Haven't noticed any obvious problems. |
|
|
@@ -32,13 +41,13 @@ Python's http.server extended to include a file upload page
|
|
|
32
41
|
|
|
33
42
|
## Installation
|
|
34
43
|
|
|
35
|
-
~~~
|
|
44
|
+
~~~bash
|
|
36
45
|
python3 -m pip install --user uploadserver
|
|
37
46
|
~~~
|
|
38
47
|
|
|
39
48
|
## Usage
|
|
40
49
|
|
|
41
|
-
~~~
|
|
50
|
+
~~~bash
|
|
42
51
|
python3 -m uploadserver
|
|
43
52
|
~~~
|
|
44
53
|
|
|
@@ -49,18 +58,18 @@ After the server starts, the upload page is at /upload. For example, if the serv
|
|
|
49
58
|
Warning: This is an upload server, and running it will allow uploads.
|
|
50
59
|
|
|
51
60
|
Now supports uploading multiple files at once! Select multiple files in the web page's file selector, or upload with cURL:
|
|
52
|
-
~~~
|
|
61
|
+
~~~bash
|
|
53
62
|
curl -X POST http://127.0.0.1:8000/upload -F 'files=@multiple-example-1.txt' -F 'files=@multiple-example-2.txt'
|
|
54
63
|
~~~
|
|
55
64
|
|
|
56
65
|
## Basic Authentication (downloads and uploads)
|
|
57
66
|
|
|
58
|
-
~~~
|
|
67
|
+
~~~bash
|
|
59
68
|
python3 -m uploadserver --basic-auth hello:world
|
|
60
69
|
~~~
|
|
61
70
|
|
|
62
71
|
Now you can upload with basic authentication. For example:
|
|
63
|
-
~~~
|
|
72
|
+
~~~bash
|
|
64
73
|
curl -X POST http://127.0.0.1:8000/upload -F 'files=@basicauth-example.txt' -u hello:world
|
|
65
74
|
~~~
|
|
66
75
|
|
|
@@ -68,7 +77,7 @@ All requests without authentication will be rejected. Note that basic authentica
|
|
|
68
77
|
|
|
69
78
|
## Basic Authentication (uploads only)
|
|
70
79
|
|
|
71
|
-
~~~
|
|
80
|
+
~~~bash
|
|
72
81
|
python3 -m uploadserver --basic-auth-upload hello:world
|
|
73
82
|
~~~
|
|
74
83
|
|
|
@@ -79,18 +88,18 @@ If both `--basic-auth` and `--basic-auth-upload` are specified, all requests wil
|
|
|
79
88
|
## Theme Option
|
|
80
89
|
|
|
81
90
|
The upload page supports a dark mode for showing white text on black background. If no option is specified, the color scheme is chosen from the client’s browser’s preference (which typically matches their operating system’s setting, if light or dark mode is supported by the OS). To enforce the light or dark theme, the CLI parameter `--theme` can be used:
|
|
82
|
-
~~~
|
|
91
|
+
~~~bash
|
|
83
92
|
python3 -m uploadserver --theme light
|
|
84
93
|
~~~
|
|
85
94
|
or
|
|
86
|
-
~~~
|
|
95
|
+
~~~bash
|
|
87
96
|
python3 -m uploadserver --theme dark
|
|
88
97
|
~~~
|
|
89
98
|
|
|
90
99
|
## HTTPS Option
|
|
91
100
|
|
|
92
101
|
Run with HTTPS and without client authentication:
|
|
93
|
-
~~~
|
|
102
|
+
~~~bash
|
|
94
103
|
# Generate self-signed server certificate
|
|
95
104
|
openssl req -x509 -out server.pem -keyout server.pem -newkey rsa:2048 -nodes -sha256 -subj '/CN=server'
|
|
96
105
|
|
|
@@ -103,7 +112,7 @@ curl -X POST https://localhost:8000/upload --insecure -F files=@simple-example.t
|
|
|
103
112
|
~~~
|
|
104
113
|
|
|
105
114
|
Run with HTTPS and with client authentication:
|
|
106
|
-
~~~
|
|
115
|
+
~~~bash
|
|
107
116
|
# Generate self-signed server certificate
|
|
108
117
|
openssl req -x509 -out server.pem -keyout server.pem -newkey rsa:2048 -nodes -sha256 -subj '/CN=server'
|
|
109
118
|
|
|
@@ -123,6 +132,51 @@ curl -X POST https://localhost:8000/upload --insecure --cert client.pem -F files
|
|
|
123
132
|
|
|
124
133
|
Note: This uses a self-signed server certificate which clients such as web browser and cURL will warn about. Most browsers will allow you to proceed after adding an exception, and cURL will work if given the -k/--insecure option. Using your own certificate from a certificate authority will avoid these warnings.
|
|
125
134
|
|
|
135
|
+
## Available Options
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
usage: __main__.py [-h] [--cgi] [--allow-replace] [--bind ADDRESS]
|
|
139
|
+
[--directory DIRECTORY] [--theme {light,auto,dark}]
|
|
140
|
+
[--server-certificate SERVER_CERTIFICATE]
|
|
141
|
+
[--client-certificate CLIENT_CERTIFICATE]
|
|
142
|
+
[--basic-auth BASIC_AUTH]
|
|
143
|
+
[--basic-auth-upload BASIC_AUTH_UPLOAD]
|
|
144
|
+
[port]
|
|
145
|
+
|
|
146
|
+
positional arguments:
|
|
147
|
+
port Specify alternate port [default: 8000]
|
|
148
|
+
|
|
149
|
+
options:
|
|
150
|
+
-h, --help show this help message and exit
|
|
151
|
+
--cgi Run as CGI Server
|
|
152
|
+
--allow-replace Replace existing file if uploaded file has the same
|
|
153
|
+
name. Auto rename by default.
|
|
154
|
+
--bind, -b ADDRESS Specify alternate bind address [default: all
|
|
155
|
+
interfaces]
|
|
156
|
+
--directory, -d DIRECTORY
|
|
157
|
+
Specify alternative directory [default:current
|
|
158
|
+
directory]
|
|
159
|
+
--theme {light,auto,dark}
|
|
160
|
+
Specify a light or dark theme for the upload page
|
|
161
|
+
[default: auto]
|
|
162
|
+
--server-certificate, --certificate, -c SERVER_CERTIFICATE
|
|
163
|
+
Specify HTTPS server certificate to use [default:
|
|
164
|
+
none]
|
|
165
|
+
--client-certificate CLIENT_CERTIFICATE
|
|
166
|
+
Specify HTTPS client certificate to accept for mutual
|
|
167
|
+
TLS [default: none]
|
|
168
|
+
--basic-auth BASIC_AUTH
|
|
169
|
+
Specify user:pass for basic authentication (downloads
|
|
170
|
+
and uploads)
|
|
171
|
+
--basic-auth-upload BASIC_AUTH_UPLOAD
|
|
172
|
+
Specify user:pass for basic authentication (uploads
|
|
173
|
+
only)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Breaking Changes in 6.0.0
|
|
177
|
+
|
|
178
|
+
- Support for Python 3.8 dropped.
|
|
179
|
+
|
|
126
180
|
## Breaking Changes in 5.0.0
|
|
127
181
|
|
|
128
182
|
- Support for Python 3.6-7 dropped.
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: uploadserver
|
|
3
|
-
Version: 5.2.2
|
|
4
|
-
Summary: Python's http.server extended to include a file upload page
|
|
5
|
-
Home-page: https://github.com/Densaugeo/uploadserver
|
|
6
|
-
Author: Densaugeo
|
|
7
|
-
Author-email: author@example.com
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.8
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
|
|
15
1
|
# uploadserver
|
|
16
2
|
|
|
17
3
|
Python's http.server extended to include a file upload page
|
|
@@ -23,8 +9,8 @@ Python's http.server extended to include a file upload page
|
|
|
23
9
|
|
|
24
10
|
| Platform | Supported? | Notes |
|
|
25
11
|
|-|-|-|
|
|
26
|
-
| Python 3.
|
|
27
|
-
| Python 3.6-3.
|
|
12
|
+
| Python 3.9+ | Yes | Tested on 3.9 through 3.14 every release. |
|
|
13
|
+
| Python 3.6-3.8 | No | Was supported by previous versions. |
|
|
28
14
|
| Python 3.5- | No | |
|
|
29
15
|
| Linux | Yes | Tested on Fedora and Ubuntu every release. |
|
|
30
16
|
| Windows | Yes | Occasional manual testing. Haven't noticed any obvious problems. |
|
|
@@ -32,13 +18,13 @@ Python's http.server extended to include a file upload page
|
|
|
32
18
|
|
|
33
19
|
## Installation
|
|
34
20
|
|
|
35
|
-
~~~
|
|
21
|
+
~~~bash
|
|
36
22
|
python3 -m pip install --user uploadserver
|
|
37
23
|
~~~
|
|
38
24
|
|
|
39
25
|
## Usage
|
|
40
26
|
|
|
41
|
-
~~~
|
|
27
|
+
~~~bash
|
|
42
28
|
python3 -m uploadserver
|
|
43
29
|
~~~
|
|
44
30
|
|
|
@@ -49,18 +35,18 @@ After the server starts, the upload page is at /upload. For example, if the serv
|
|
|
49
35
|
Warning: This is an upload server, and running it will allow uploads.
|
|
50
36
|
|
|
51
37
|
Now supports uploading multiple files at once! Select multiple files in the web page's file selector, or upload with cURL:
|
|
52
|
-
~~~
|
|
38
|
+
~~~bash
|
|
53
39
|
curl -X POST http://127.0.0.1:8000/upload -F 'files=@multiple-example-1.txt' -F 'files=@multiple-example-2.txt'
|
|
54
40
|
~~~
|
|
55
41
|
|
|
56
42
|
## Basic Authentication (downloads and uploads)
|
|
57
43
|
|
|
58
|
-
~~~
|
|
44
|
+
~~~bash
|
|
59
45
|
python3 -m uploadserver --basic-auth hello:world
|
|
60
46
|
~~~
|
|
61
47
|
|
|
62
48
|
Now you can upload with basic authentication. For example:
|
|
63
|
-
~~~
|
|
49
|
+
~~~bash
|
|
64
50
|
curl -X POST http://127.0.0.1:8000/upload -F 'files=@basicauth-example.txt' -u hello:world
|
|
65
51
|
~~~
|
|
66
52
|
|
|
@@ -68,7 +54,7 @@ All requests without authentication will be rejected. Note that basic authentica
|
|
|
68
54
|
|
|
69
55
|
## Basic Authentication (uploads only)
|
|
70
56
|
|
|
71
|
-
~~~
|
|
57
|
+
~~~bash
|
|
72
58
|
python3 -m uploadserver --basic-auth-upload hello:world
|
|
73
59
|
~~~
|
|
74
60
|
|
|
@@ -79,18 +65,18 @@ If both `--basic-auth` and `--basic-auth-upload` are specified, all requests wil
|
|
|
79
65
|
## Theme Option
|
|
80
66
|
|
|
81
67
|
The upload page supports a dark mode for showing white text on black background. If no option is specified, the color scheme is chosen from the client’s browser’s preference (which typically matches their operating system’s setting, if light or dark mode is supported by the OS). To enforce the light or dark theme, the CLI parameter `--theme` can be used:
|
|
82
|
-
~~~
|
|
68
|
+
~~~bash
|
|
83
69
|
python3 -m uploadserver --theme light
|
|
84
70
|
~~~
|
|
85
71
|
or
|
|
86
|
-
~~~
|
|
72
|
+
~~~bash
|
|
87
73
|
python3 -m uploadserver --theme dark
|
|
88
74
|
~~~
|
|
89
75
|
|
|
90
76
|
## HTTPS Option
|
|
91
77
|
|
|
92
78
|
Run with HTTPS and without client authentication:
|
|
93
|
-
~~~
|
|
79
|
+
~~~bash
|
|
94
80
|
# Generate self-signed server certificate
|
|
95
81
|
openssl req -x509 -out server.pem -keyout server.pem -newkey rsa:2048 -nodes -sha256 -subj '/CN=server'
|
|
96
82
|
|
|
@@ -103,7 +89,7 @@ curl -X POST https://localhost:8000/upload --insecure -F files=@simple-example.t
|
|
|
103
89
|
~~~
|
|
104
90
|
|
|
105
91
|
Run with HTTPS and with client authentication:
|
|
106
|
-
~~~
|
|
92
|
+
~~~bash
|
|
107
93
|
# Generate self-signed server certificate
|
|
108
94
|
openssl req -x509 -out server.pem -keyout server.pem -newkey rsa:2048 -nodes -sha256 -subj '/CN=server'
|
|
109
95
|
|
|
@@ -123,6 +109,51 @@ curl -X POST https://localhost:8000/upload --insecure --cert client.pem -F files
|
|
|
123
109
|
|
|
124
110
|
Note: This uses a self-signed server certificate which clients such as web browser and cURL will warn about. Most browsers will allow you to proceed after adding an exception, and cURL will work if given the -k/--insecure option. Using your own certificate from a certificate authority will avoid these warnings.
|
|
125
111
|
|
|
112
|
+
## Available Options
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
usage: __main__.py [-h] [--cgi] [--allow-replace] [--bind ADDRESS]
|
|
116
|
+
[--directory DIRECTORY] [--theme {light,auto,dark}]
|
|
117
|
+
[--server-certificate SERVER_CERTIFICATE]
|
|
118
|
+
[--client-certificate CLIENT_CERTIFICATE]
|
|
119
|
+
[--basic-auth BASIC_AUTH]
|
|
120
|
+
[--basic-auth-upload BASIC_AUTH_UPLOAD]
|
|
121
|
+
[port]
|
|
122
|
+
|
|
123
|
+
positional arguments:
|
|
124
|
+
port Specify alternate port [default: 8000]
|
|
125
|
+
|
|
126
|
+
options:
|
|
127
|
+
-h, --help show this help message and exit
|
|
128
|
+
--cgi Run as CGI Server
|
|
129
|
+
--allow-replace Replace existing file if uploaded file has the same
|
|
130
|
+
name. Auto rename by default.
|
|
131
|
+
--bind, -b ADDRESS Specify alternate bind address [default: all
|
|
132
|
+
interfaces]
|
|
133
|
+
--directory, -d DIRECTORY
|
|
134
|
+
Specify alternative directory [default:current
|
|
135
|
+
directory]
|
|
136
|
+
--theme {light,auto,dark}
|
|
137
|
+
Specify a light or dark theme for the upload page
|
|
138
|
+
[default: auto]
|
|
139
|
+
--server-certificate, --certificate, -c SERVER_CERTIFICATE
|
|
140
|
+
Specify HTTPS server certificate to use [default:
|
|
141
|
+
none]
|
|
142
|
+
--client-certificate CLIENT_CERTIFICATE
|
|
143
|
+
Specify HTTPS client certificate to accept for mutual
|
|
144
|
+
TLS [default: none]
|
|
145
|
+
--basic-auth BASIC_AUTH
|
|
146
|
+
Specify user:pass for basic authentication (downloads
|
|
147
|
+
and uploads)
|
|
148
|
+
--basic-auth-upload BASIC_AUTH_UPLOAD
|
|
149
|
+
Specify user:pass for basic authentication (uploads
|
|
150
|
+
only)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Breaking Changes in 6.0.0
|
|
154
|
+
|
|
155
|
+
- Support for Python 3.8 dropped.
|
|
156
|
+
|
|
126
157
|
## Breaking Changes in 5.0.0
|
|
127
158
|
|
|
128
159
|
- Support for Python 3.6-7 dropped.
|
|
@@ -5,7 +5,7 @@ with open('README.md', 'r') as fh:
|
|
|
5
5
|
|
|
6
6
|
setuptools.setup(
|
|
7
7
|
name='uploadserver',
|
|
8
|
-
version='
|
|
8
|
+
version='6.0.1',
|
|
9
9
|
author='Densaugeo',
|
|
10
10
|
author_email='author@example.com',
|
|
11
11
|
description='Python\'s http.server extended to include a file upload page',
|
|
@@ -18,7 +18,7 @@ setuptools.setup(
|
|
|
18
18
|
'License :: OSI Approved :: MIT License',
|
|
19
19
|
'Operating System :: OS Independent',
|
|
20
20
|
],
|
|
21
|
-
python_requires='>=3.
|
|
21
|
+
python_requires='>=3.9',
|
|
22
22
|
entry_points = {
|
|
23
23
|
'console_scripts': ['uploadserver=uploadserver:main'],
|
|
24
24
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import http.server, http, pathlib, sys, argparse, ssl, os, builtins, tempfile
|
|
2
|
+
import ipaddress
|
|
2
3
|
import base64, binascii, functools, contextlib
|
|
3
4
|
|
|
4
5
|
# Does not seem to do be used, but leaving this import out causes uploadserver
|
|
@@ -18,7 +19,7 @@ COLOR_SCHEME = {
|
|
|
18
19
|
'dark': 'dark',
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
def get_upload_page(theme):
|
|
22
|
+
def get_upload_page(theme: str) -> bytes:
|
|
22
23
|
return bytes('''<!DOCTYPE html>
|
|
23
24
|
<html>
|
|
24
25
|
<head>
|
|
@@ -74,7 +75,7 @@ document.getElementsByTagName('form')[0].addEventListener('submit', async e => {
|
|
|
74
75
|
</script>
|
|
75
76
|
</html>''', 'utf-8')
|
|
76
77
|
|
|
77
|
-
def get_directory_head_injection(theme):
|
|
78
|
+
def get_directory_head_injection(theme: str) -> bytes:
|
|
78
79
|
return bytes('''<!-- Injected by uploadserver -->
|
|
79
80
|
<meta name="viewport" content="width=device-width" />
|
|
80
81
|
<meta name="color-scheme" content="''' + COLOR_SCHEME.get(theme) + '''">
|
|
@@ -88,7 +89,7 @@ DIRECTORY_BODY_INJECTION = b'''<!-- Injected by uploadserver -->
|
|
|
88
89
|
<!-- End injection by uploadserver -->
|
|
89
90
|
'''
|
|
90
91
|
|
|
91
|
-
def send_upload_page(handler):
|
|
92
|
+
def send_upload_page(handler: http.server.BaseHTTPRequestHandler):
|
|
92
93
|
handler.send_response(http.HTTPStatus.OK)
|
|
93
94
|
handler.send_header('Content-Type', 'text/html; charset=utf-8')
|
|
94
95
|
handler.send_header('Content-Length', len(get_upload_page(args.theme)))
|
|
@@ -99,7 +100,7 @@ class PersistentFieldStorage(cgi.FieldStorage):
|
|
|
99
100
|
# Override cgi.FieldStorage.make_file() method. Valid for Python 3.1 ~ 3.10.
|
|
100
101
|
# Modified version of the original .make_file() method (base copied from
|
|
101
102
|
# Python 3.10)
|
|
102
|
-
def make_file(self):
|
|
103
|
+
def make_file(self) -> object:
|
|
103
104
|
if self._binary_file:
|
|
104
105
|
return tempfile.NamedTemporaryFile(mode = 'wb+',
|
|
105
106
|
dir = args.directory, delete = False)
|
|
@@ -107,7 +108,9 @@ class PersistentFieldStorage(cgi.FieldStorage):
|
|
|
107
108
|
return tempfile.NamedTemporaryFile("w+", dir = args.directory,
|
|
108
109
|
delete = False, encoding = self.encoding, newline = '\n')
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
# True argument/return type is str | pathlib.Path, but Python 3.9 doesn't
|
|
112
|
+
# support |
|
|
113
|
+
def auto_rename(path: pathlib.Path) -> pathlib.Path:
|
|
111
114
|
if not os.path.exists(path):
|
|
112
115
|
return path
|
|
113
116
|
(base, ext) = os.path.splitext(path)
|
|
@@ -117,7 +120,8 @@ def auto_rename(path):
|
|
|
117
120
|
return renamed_path
|
|
118
121
|
raise FileExistsError(f'File {path} already exists.')
|
|
119
122
|
|
|
120
|
-
def receive_upload(handler
|
|
123
|
+
def receive_upload(handler: http.server.BaseHTTPRequestHandler,
|
|
124
|
+
) -> tuple[http.HTTPStatus, str]:
|
|
121
125
|
result = (http.HTTPStatus.INTERNAL_SERVER_ERROR, 'Server error')
|
|
122
126
|
name_conflict = False
|
|
123
127
|
|
|
@@ -153,16 +157,19 @@ def receive_upload(handler):
|
|
|
153
157
|
os.rename(source, destination)
|
|
154
158
|
# class '_io.BytesIO', small file (< 1000B, in cgi.py), in-memory
|
|
155
159
|
# buffer
|
|
156
|
-
else:
|
|
160
|
+
else:
|
|
157
161
|
with open(destination, 'wb') as f:
|
|
158
162
|
f.write(field.file.read())
|
|
159
|
-
handler.log_message(
|
|
163
|
+
handler.log_message('[Uploaded] "%s" --> %s', filename, destination)
|
|
160
164
|
result = (http.HTTPStatus.NO_CONTENT, 'Some filename(s) changed '
|
|
161
165
|
'due to name conflict' if name_conflict else 'Files accepted')
|
|
162
166
|
|
|
163
167
|
return result
|
|
164
168
|
|
|
165
|
-
|
|
169
|
+
# True return type is tuple[bool, str | None], but Python 3.9 doesn't support |
|
|
170
|
+
def check_http_authentication_header(
|
|
171
|
+
handler: http.server.BaseHTTPRequestHandler, auth: str,
|
|
172
|
+
) -> tuple[bool, str]:
|
|
166
173
|
auth_header = handler.headers.get('Authorization')
|
|
167
174
|
if auth_header is None:
|
|
168
175
|
return (False, 'No credentials given')
|
|
@@ -186,7 +193,8 @@ def check_http_authentication_header(handler, auth):
|
|
|
186
193
|
|
|
187
194
|
return (True, None)
|
|
188
195
|
|
|
189
|
-
def check_http_authentication(handler
|
|
196
|
+
def check_http_authentication(handler: http.server.BaseHTTPRequestHandler
|
|
197
|
+
) -> bool:
|
|
190
198
|
"""
|
|
191
199
|
This function should be called in at the beginning of HTTP method handler.
|
|
192
200
|
It validates Authorization header and sends back 401 response on failure.
|
|
@@ -219,7 +227,7 @@ def check_http_authentication(handler):
|
|
|
219
227
|
valid, message = check_http_authentication_header(handler, args.basic_auth_upload)
|
|
220
228
|
|
|
221
229
|
if not valid:
|
|
222
|
-
handler.log_message(
|
|
230
|
+
handler.log_message('Request rejected (%s)', message)
|
|
223
231
|
handler.send_response(http.HTTPStatus.UNAUTHORIZED, message)
|
|
224
232
|
handler.send_header('WWW-Authenticate', 'Basic realm="uploadserver"')
|
|
225
233
|
handler.end_headers()
|
|
@@ -251,7 +259,8 @@ class ListDirectoryInterception:
|
|
|
251
259
|
content = content.replace(b'<ul>', DIRECTORY_BODY_INJECTION + b'<ul>')
|
|
252
260
|
outputfile.write(content)
|
|
253
261
|
|
|
254
|
-
|
|
262
|
+
# True argument type is str | pathlib.Path, but Python 3.9 doesn't support |
|
|
263
|
+
def list_directory(self, path: pathlib.Path) -> object:
|
|
255
264
|
setattr(self, 'flush_headers', self.flush_headers_interceptor)
|
|
256
265
|
setattr(self, 'copyfile', self.copyfile_interceptor)
|
|
257
266
|
|
|
@@ -323,7 +332,7 @@ def intercept_first_print():
|
|
|
323
332
|
builtins.print = old_print
|
|
324
333
|
builtins.print = new_print
|
|
325
334
|
|
|
326
|
-
def ssl_wrap(socket):
|
|
335
|
+
def ssl_wrap(socket: socket.socket) -> ssl.SSLSocket:
|
|
327
336
|
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
|
328
337
|
server_root = pathlib.Path(args.directory).resolve()
|
|
329
338
|
|
|
@@ -444,5 +453,14 @@ def main():
|
|
|
444
453
|
|
|
445
454
|
args = parser.parse_args()
|
|
446
455
|
if not hasattr(args, 'directory'): args.directory = os.getcwd()
|
|
456
|
+
if args.bind:
|
|
457
|
+
try:
|
|
458
|
+
ipaddress.ip_address(args.bind)
|
|
459
|
+
except ValueError:
|
|
460
|
+
parser.error(
|
|
461
|
+
'Invalid -b/--bind address. Expected an IP address (no port). '
|
|
462
|
+
'Example: -b 192.168.1.10 and pass the port as a separate '
|
|
463
|
+
'argument.'
|
|
464
|
+
)
|
|
447
465
|
|
|
448
466
|
serve_forever()
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uploadserver
|
|
3
|
+
Version: 6.0.1
|
|
4
|
+
Summary: Python's http.server extended to include a file upload page
|
|
5
|
+
Home-page: https://github.com/Densaugeo/uploadserver
|
|
6
|
+
Author: Densaugeo
|
|
7
|
+
Author-email: author@example.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
Dynamic: requires-python
|
|
22
|
+
Dynamic: summary
|
|
23
|
+
|
|
1
24
|
# uploadserver
|
|
2
25
|
|
|
3
26
|
Python's http.server extended to include a file upload page
|
|
@@ -9,8 +32,8 @@ Python's http.server extended to include a file upload page
|
|
|
9
32
|
|
|
10
33
|
| Platform | Supported? | Notes |
|
|
11
34
|
|-|-|-|
|
|
12
|
-
| Python 3.
|
|
13
|
-
| Python 3.6-3.
|
|
35
|
+
| Python 3.9+ | Yes | Tested on 3.9 through 3.14 every release. |
|
|
36
|
+
| Python 3.6-3.8 | No | Was supported by previous versions. |
|
|
14
37
|
| Python 3.5- | No | |
|
|
15
38
|
| Linux | Yes | Tested on Fedora and Ubuntu every release. |
|
|
16
39
|
| Windows | Yes | Occasional manual testing. Haven't noticed any obvious problems. |
|
|
@@ -18,13 +41,13 @@ Python's http.server extended to include a file upload page
|
|
|
18
41
|
|
|
19
42
|
## Installation
|
|
20
43
|
|
|
21
|
-
~~~
|
|
44
|
+
~~~bash
|
|
22
45
|
python3 -m pip install --user uploadserver
|
|
23
46
|
~~~
|
|
24
47
|
|
|
25
48
|
## Usage
|
|
26
49
|
|
|
27
|
-
~~~
|
|
50
|
+
~~~bash
|
|
28
51
|
python3 -m uploadserver
|
|
29
52
|
~~~
|
|
30
53
|
|
|
@@ -35,18 +58,18 @@ After the server starts, the upload page is at /upload. For example, if the serv
|
|
|
35
58
|
Warning: This is an upload server, and running it will allow uploads.
|
|
36
59
|
|
|
37
60
|
Now supports uploading multiple files at once! Select multiple files in the web page's file selector, or upload with cURL:
|
|
38
|
-
~~~
|
|
61
|
+
~~~bash
|
|
39
62
|
curl -X POST http://127.0.0.1:8000/upload -F 'files=@multiple-example-1.txt' -F 'files=@multiple-example-2.txt'
|
|
40
63
|
~~~
|
|
41
64
|
|
|
42
65
|
## Basic Authentication (downloads and uploads)
|
|
43
66
|
|
|
44
|
-
~~~
|
|
67
|
+
~~~bash
|
|
45
68
|
python3 -m uploadserver --basic-auth hello:world
|
|
46
69
|
~~~
|
|
47
70
|
|
|
48
71
|
Now you can upload with basic authentication. For example:
|
|
49
|
-
~~~
|
|
72
|
+
~~~bash
|
|
50
73
|
curl -X POST http://127.0.0.1:8000/upload -F 'files=@basicauth-example.txt' -u hello:world
|
|
51
74
|
~~~
|
|
52
75
|
|
|
@@ -54,7 +77,7 @@ All requests without authentication will be rejected. Note that basic authentica
|
|
|
54
77
|
|
|
55
78
|
## Basic Authentication (uploads only)
|
|
56
79
|
|
|
57
|
-
~~~
|
|
80
|
+
~~~bash
|
|
58
81
|
python3 -m uploadserver --basic-auth-upload hello:world
|
|
59
82
|
~~~
|
|
60
83
|
|
|
@@ -65,18 +88,18 @@ If both `--basic-auth` and `--basic-auth-upload` are specified, all requests wil
|
|
|
65
88
|
## Theme Option
|
|
66
89
|
|
|
67
90
|
The upload page supports a dark mode for showing white text on black background. If no option is specified, the color scheme is chosen from the client’s browser’s preference (which typically matches their operating system’s setting, if light or dark mode is supported by the OS). To enforce the light or dark theme, the CLI parameter `--theme` can be used:
|
|
68
|
-
~~~
|
|
91
|
+
~~~bash
|
|
69
92
|
python3 -m uploadserver --theme light
|
|
70
93
|
~~~
|
|
71
94
|
or
|
|
72
|
-
~~~
|
|
95
|
+
~~~bash
|
|
73
96
|
python3 -m uploadserver --theme dark
|
|
74
97
|
~~~
|
|
75
98
|
|
|
76
99
|
## HTTPS Option
|
|
77
100
|
|
|
78
101
|
Run with HTTPS and without client authentication:
|
|
79
|
-
~~~
|
|
102
|
+
~~~bash
|
|
80
103
|
# Generate self-signed server certificate
|
|
81
104
|
openssl req -x509 -out server.pem -keyout server.pem -newkey rsa:2048 -nodes -sha256 -subj '/CN=server'
|
|
82
105
|
|
|
@@ -89,7 +112,7 @@ curl -X POST https://localhost:8000/upload --insecure -F files=@simple-example.t
|
|
|
89
112
|
~~~
|
|
90
113
|
|
|
91
114
|
Run with HTTPS and with client authentication:
|
|
92
|
-
~~~
|
|
115
|
+
~~~bash
|
|
93
116
|
# Generate self-signed server certificate
|
|
94
117
|
openssl req -x509 -out server.pem -keyout server.pem -newkey rsa:2048 -nodes -sha256 -subj '/CN=server'
|
|
95
118
|
|
|
@@ -109,6 +132,51 @@ curl -X POST https://localhost:8000/upload --insecure --cert client.pem -F files
|
|
|
109
132
|
|
|
110
133
|
Note: This uses a self-signed server certificate which clients such as web browser and cURL will warn about. Most browsers will allow you to proceed after adding an exception, and cURL will work if given the -k/--insecure option. Using your own certificate from a certificate authority will avoid these warnings.
|
|
111
134
|
|
|
135
|
+
## Available Options
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
usage: __main__.py [-h] [--cgi] [--allow-replace] [--bind ADDRESS]
|
|
139
|
+
[--directory DIRECTORY] [--theme {light,auto,dark}]
|
|
140
|
+
[--server-certificate SERVER_CERTIFICATE]
|
|
141
|
+
[--client-certificate CLIENT_CERTIFICATE]
|
|
142
|
+
[--basic-auth BASIC_AUTH]
|
|
143
|
+
[--basic-auth-upload BASIC_AUTH_UPLOAD]
|
|
144
|
+
[port]
|
|
145
|
+
|
|
146
|
+
positional arguments:
|
|
147
|
+
port Specify alternate port [default: 8000]
|
|
148
|
+
|
|
149
|
+
options:
|
|
150
|
+
-h, --help show this help message and exit
|
|
151
|
+
--cgi Run as CGI Server
|
|
152
|
+
--allow-replace Replace existing file if uploaded file has the same
|
|
153
|
+
name. Auto rename by default.
|
|
154
|
+
--bind, -b ADDRESS Specify alternate bind address [default: all
|
|
155
|
+
interfaces]
|
|
156
|
+
--directory, -d DIRECTORY
|
|
157
|
+
Specify alternative directory [default:current
|
|
158
|
+
directory]
|
|
159
|
+
--theme {light,auto,dark}
|
|
160
|
+
Specify a light or dark theme for the upload page
|
|
161
|
+
[default: auto]
|
|
162
|
+
--server-certificate, --certificate, -c SERVER_CERTIFICATE
|
|
163
|
+
Specify HTTPS server certificate to use [default:
|
|
164
|
+
none]
|
|
165
|
+
--client-certificate CLIENT_CERTIFICATE
|
|
166
|
+
Specify HTTPS client certificate to accept for mutual
|
|
167
|
+
TLS [default: none]
|
|
168
|
+
--basic-auth BASIC_AUTH
|
|
169
|
+
Specify user:pass for basic authentication (downloads
|
|
170
|
+
and uploads)
|
|
171
|
+
--basic-auth-upload BASIC_AUTH_UPLOAD
|
|
172
|
+
Specify user:pass for basic authentication (uploads
|
|
173
|
+
only)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Breaking Changes in 6.0.0
|
|
177
|
+
|
|
178
|
+
- Support for Python 3.8 dropped.
|
|
179
|
+
|
|
112
180
|
## Breaking Changes in 5.0.0
|
|
113
181
|
|
|
114
182
|
- Support for Python 3.6-7 dropped.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|