mongrel2 0.48.0 → 0.49.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +10 -2
- data/History.rdoc +7 -0
- data/Manifest.txt +0 -16
- data/README.rdoc +0 -4
- data/Rakefile +7 -7
- data/lib/mongrel2.rb +3 -28
- data/lib/mongrel2/connection.rb +9 -11
- data/lib/mongrel2/control.rb +6 -7
- data/lib/mongrel2/handler.rb +50 -18
- data/spec/mongrel2/connection_spec.rb +28 -50
- data/spec/mongrel2/control_spec.rb +49 -48
- data/spec/mongrel2/handler_spec.rb +52 -31
- metadata +54 -66
- metadata.gz.sig +4 -3
- data/data/mongrel2/bootstrap.html +0 -33
- data/data/mongrel2/config.rb.in +0 -71
- data/data/mongrel2/css/master.css +0 -77
- data/data/mongrel2/index.html.in +0 -51
- data/data/mongrel2/js/websock-test.js +0 -108
- data/data/mongrel2/websock-test.html +0 -33
- data/examples/Procfile +0 -7
- data/examples/README.txt +0 -6
- data/examples/async-upload.rb +0 -109
- data/examples/config.rb +0 -63
- data/examples/helloworld-handler.rb +0 -33
- data/examples/request-dumper.rb +0 -49
- data/examples/request-dumper.tmpl +0 -78
- data/examples/run +0 -22
- data/examples/sendfile.rb +0 -39
- data/examples/ws-echo.rb +0 -263
metadata.gz.sig
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
|
2
|
+
C��
|
3
|
+
}���S�۠1���T!�YV�^����[������||�hi��Fߺ�B��䲯9k݆���]�ķ����Y�=���
|
4
|
+
O������ָ��ZD��Z�f��1�F��I^�yPq�U�ѐ��������F��~?2�i�¡�9
|
@@ -1,33 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>Ruby-Mongrel2 Examples</title>
|
5
|
-
<link rel="stylesheet" href="/css/master.css" type="text/css" media="screen"
|
6
|
-
title="no title" charset="utf-8" />
|
7
|
-
<meta charset="utf-8" />
|
8
|
-
</head>
|
9
|
-
<body>
|
10
|
-
<header>
|
11
|
-
<hgroup>
|
12
|
-
<h1>Ruby-Mongrel2 Examples</h1>
|
13
|
-
</hgroup>
|
14
|
-
</header>
|
15
|
-
|
16
|
-
<section>
|
17
|
-
<ol>
|
18
|
-
<li><a href="/hello">Hello World</a> (<a href="/source/helloworld-handler.rb">source</a>) </li>
|
19
|
-
<li><a href="/async-upload">Async Upload Demo</a>
|
20
|
-
(<a href="/source/async-upload.rb">source</a>) </li>
|
21
|
-
<li><a href="/dump">Dump Request</a>
|
22
|
-
(<a href="/source/request-dumper.rb">source</a>) </li>
|
23
|
-
<li><a href="/websock-test.html">Web Socket Test</a>
|
24
|
-
(<a href="/source/ws-echo.rb">source</a>)</li>
|
25
|
-
</ol>
|
26
|
-
</section>
|
27
|
-
|
28
|
-
<footer>
|
29
|
-
<p>Copyright © 2011-2012 Michael Granger <<a href="mailto:ged@FaerieMUD.org">ged@FaerieMUD.org</a>>.</p>
|
30
|
-
<p><tt>$Id: bootstrap.html,v 4c64666f4255 2012/10/03 00:42:35 ged $</tt></p>
|
31
|
-
</footer>
|
32
|
-
</body>
|
33
|
-
</html>
|
data/data/mongrel2/config.rb.in
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#encoding: utf-8
|
3
|
-
|
4
|
-
require 'pathname'
|
5
|
-
require 'tmpdir'
|
6
|
-
|
7
|
-
# This is a Ruby script that will *generate* the SQLite database
|
8
|
-
# that Mongrel2 uses for its configuration. You can just as easily
|
9
|
-
# use Mongrel2's 'm2sh' and the Pythonish config syntax described
|
10
|
-
# in the manual if you prefer that.
|
11
|
-
#
|
12
|
-
# See the "Mongrel2 Config DSL" section of the API docs, and the "How A
|
13
|
-
# Config Is Structured" section of the manual for details
|
14
|
-
# on specific items:
|
15
|
-
#
|
16
|
-
# Mongrel2 Config DSL::
|
17
|
-
# http://deveiate.org/code/mongrel2/DSL_rdoc.html
|
18
|
-
#
|
19
|
-
# How A Config Is Structured::
|
20
|
-
# http://mongrel2.org/static/book-finalch4.html#x6-260003.4
|
21
|
-
#
|
22
|
-
# You can load this via the 'm2sh.rb' tool that comes with the 'mongrel2'
|
23
|
-
# gem:
|
24
|
-
#
|
25
|
-
# m2sh.rb -c config.sqlite load config.rb
|
26
|
-
|
27
|
-
# Establish some directories
|
28
|
-
base_dir = Pathname( '%% PWD %%' )
|
29
|
-
upload_dir = Pathname( Dir.tmpdir ) + 'm2spool'
|
30
|
-
|
31
|
-
# Main Mongrel2 server config
|
32
|
-
main = server 'main' do
|
33
|
-
|
34
|
-
name 'Main'
|
35
|
-
default_host 'localhost'
|
36
|
-
chroot base_dir
|
37
|
-
|
38
|
-
# All of these values are relative to the 'chroot' value if Mongrel2
|
39
|
-
# is started as root. If it's not, they're relative to the directory
|
40
|
-
# it's started in.
|
41
|
-
access_log '/logs/access.log'
|
42
|
-
error_log '/logs/error.log'
|
43
|
-
pid_file '/var/run/mongrel2.pid'
|
44
|
-
|
45
|
-
# This the address and port the server will listen on. You can
|
46
|
-
# use '0.0.0.0' as the bind_addr to listen on all interfaces/IPs.
|
47
|
-
bind_addr '127.0.0.1'
|
48
|
-
port 8113
|
49
|
-
|
50
|
-
host 'localhost' do
|
51
|
-
|
52
|
-
# Serve static content out of a 'public' subdirectory
|
53
|
-
route '/', directory( "public/", 'index.html', 'text/html' )
|
54
|
-
|
55
|
-
# Dynamic content is served via handler routes
|
56
|
-
route '/hello', handler( 'tcp://127.0.0.1:9999', 'helloworld' )
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
setting 'limits.content_length', 512 * 1024
|
63
|
-
setting 'control_port', 'ipc://var/run/mongrel2.sock'
|
64
|
-
setting 'upload.temp_store', upload_dir + 'mongrel2.upload.XXXXXX'
|
65
|
-
|
66
|
-
# Make relative directories so that starting as a regular user works
|
67
|
-
(base_dir + "./#{main.access_log}").dirname.mkpath
|
68
|
-
(base_dir + "./#{main.error_log}").dirname.mkpath
|
69
|
-
(base_dir + "./#{main.pid_file}").dirname.mkpath
|
70
|
-
mkdir_p( upload_dir )
|
71
|
-
|
@@ -1,77 +0,0 @@
|
|
1
|
-
/* @override http://pelimer.pg.laika.com:8113/css/master.css
|
2
|
-
http://localhost:8113/css/master.css */
|
3
|
-
|
4
|
-
/*
|
5
|
-
* Mongrel2 Examples Stylesheet
|
6
|
-
* $Id$
|
7
|
-
*/
|
8
|
-
|
9
|
-
html {
|
10
|
-
background: #333;
|
11
|
-
margin: 0;
|
12
|
-
}
|
13
|
-
|
14
|
-
body {
|
15
|
-
background: ##9d9d9d;
|
16
|
-
background: -moz-linear-gradient(bottom, #aaa 0%, #eee 30%);
|
17
|
-
background: -webkit-gradient(linear, left bottom, left top,
|
18
|
-
color-stop(0%,#9d9d9d), color-stop(30%,#eee));
|
19
|
-
background: linear-gradient(top, ##9d9d9d 0%,#eee 30%);
|
20
|
-
color: #333;
|
21
|
-
margin: 0;
|
22
|
-
padding: 4em;
|
23
|
-
font-family: Menlo, Monaco, monospace;
|
24
|
-
font-size: 76%;
|
25
|
-
min-width: 785px;
|
26
|
-
}
|
27
|
-
|
28
|
-
pre, table, code {
|
29
|
-
background-color: rgba( 25,25,200, 0.2 );
|
30
|
-
box-shadow: 0 2px 8px rgba( 50,50,50, 0.25 )
|
31
|
-
}
|
32
|
-
|
33
|
-
pre {
|
34
|
-
padding: 2px 8px;
|
35
|
-
font-family: Menlo, Monaco, monospace;
|
36
|
-
overflow: hidden;
|
37
|
-
}
|
38
|
-
table {
|
39
|
-
border-collapse: collapse;
|
40
|
-
}
|
41
|
-
th, td {
|
42
|
-
padding: 2px 8px;
|
43
|
-
border-bottom: 1px solid rgba( 150,150,150, 0.5 );
|
44
|
-
}
|
45
|
-
tbody th {
|
46
|
-
text-align: right;
|
47
|
-
vertical-align: top;
|
48
|
-
white-space: pre;
|
49
|
-
}
|
50
|
-
|
51
|
-
|
52
|
-
/*
|
53
|
-
* WebSocket tester styles
|
54
|
-
*/
|
55
|
-
|
56
|
-
.websocket-test form {
|
57
|
-
margin: 2em 0;
|
58
|
-
padding: 2em;
|
59
|
-
border-radius: 0.5em;
|
60
|
-
background: rgba( 50,50,50, 0.5 );
|
61
|
-
}
|
62
|
-
|
63
|
-
.websocket-test form.socket-connected {
|
64
|
-
background: rgba( 0,150,0, 0.25 );
|
65
|
-
}
|
66
|
-
|
67
|
-
.websocket-test form input {
|
68
|
-
font-size: 1.4em;
|
69
|
-
width: 300px;
|
70
|
-
border: 1px solid #aaa;
|
71
|
-
background: #ddd;
|
72
|
-
}
|
73
|
-
|
74
|
-
.websocket-test form.socket-connected input {
|
75
|
-
background: #eee;
|
76
|
-
}
|
77
|
-
|
data/data/mongrel2/index.html.in
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>Ruby-Mongrel2 QuickStart</title>
|
5
|
-
<style type="text/css" media="screen" title="no title" charset="utf-8">
|
6
|
-
html {
|
7
|
-
margin: 0;
|
8
|
-
}
|
9
|
-
|
10
|
-
body {
|
11
|
-
background: rgb(206,220,231); /* Old browsers */
|
12
|
-
background: linear-gradient(to bottom, rgba(206,220,231,1) 0%,rgba(89,106,114,1) 100%); /* W3C */
|
13
|
-
color: #333;
|
14
|
-
margin: 0;
|
15
|
-
padding: 4em;
|
16
|
-
font-family: Menlo, Monaco, monospace;
|
17
|
-
font-size: 76%;
|
18
|
-
min-width: 785px;
|
19
|
-
}
|
20
|
-
|
21
|
-
footer {
|
22
|
-
position: absolute;
|
23
|
-
bottom: 5px;
|
24
|
-
font-size: 9px;
|
25
|
-
color: #666;
|
26
|
-
}
|
27
|
-
|
28
|
-
</style>
|
29
|
-
<meta charset="utf-8" />
|
30
|
-
</head>
|
31
|
-
<body>
|
32
|
-
<header>
|
33
|
-
<hgroup>
|
34
|
-
<h1>Ruby-Mongrel2 Quickstart</h1>
|
35
|
-
</hgroup>
|
36
|
-
</header>
|
37
|
-
|
38
|
-
<section>
|
39
|
-
<p>It Works!™</p>
|
40
|
-
|
41
|
-
<p>If you have a Hello World application connected to <tt>%% HELLOWORLD_SEND_SPEC %%</tt> /
|
42
|
-
<tt>%% HELLOWORLD_RECV_SPEC %%</tt>, you can
|
43
|
-
<a href="%% HELLOWORLD_URI %%">run it here</a>. You should also try killing
|
44
|
-
the application, clicking the link, then restarting the app. Neat!</p>
|
45
|
-
</section>
|
46
|
-
|
47
|
-
<footer>
|
48
|
-
<p>Brought to you by %% VERSION %%.</p>
|
49
|
-
</footer>
|
50
|
-
</body>
|
51
|
-
</html>
|
@@ -1,108 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Ruby-Mongrel2 Websocket Demo
|
3
|
-
* $Id: websock-test.js,v fea3cd02d23c 2012/03/07 16:07:50 ged $
|
4
|
-
*
|
5
|
-
* Author:
|
6
|
-
* - Michael Granger <ged@FaerieMUD.org>
|
7
|
-
*
|
8
|
-
*/
|
9
|
-
|
10
|
-
var ws = null;
|
11
|
-
|
12
|
-
function writeToLog( msg ) {
|
13
|
-
$('#log').append( "<li>" + msg + "</li>\n");
|
14
|
-
}
|
15
|
-
|
16
|
-
function writeErrorLog( msg ) {
|
17
|
-
$('#log').append( "<li class=\"error\">" + msg + "</li>\n");
|
18
|
-
}
|
19
|
-
|
20
|
-
function onOpen( e ) {
|
21
|
-
console.debug( "WebSocket open." );
|
22
|
-
writeToLog( "Connected." );
|
23
|
-
|
24
|
-
$('form').addClass( 'socket-connected' );
|
25
|
-
$('#connect').unbind( 'click' ).attr( 'disabled', 'disabled' );
|
26
|
-
$('#disconnect').click( doDisconnect ).removeAttr('disabled');
|
27
|
-
$('#echo-body').removeAttr('disabled');
|
28
|
-
$('#send').click( doSend ).removeAttr('disabled');
|
29
|
-
}
|
30
|
-
|
31
|
-
function onClose( e ) {
|
32
|
-
console.debug( "WebSocket closed." );
|
33
|
-
writeToLog( "Disconnected." );
|
34
|
-
|
35
|
-
$('form').removeClass( 'socket-connected' );
|
36
|
-
$('#connect').click( doConnect ).removeAttr('disabled');
|
37
|
-
$('#disconnect,#echo-body,#send').unbind( 'click' ).attr( 'disabled', 'disabled' );
|
38
|
-
}
|
39
|
-
|
40
|
-
function onMessage( e ) {
|
41
|
-
console.debug( "WebSocket message: %s.", e.data );
|
42
|
-
writeToLog( "Response: <code>" + e.data + "</code>" );
|
43
|
-
}
|
44
|
-
|
45
|
-
function onError( e ) {
|
46
|
-
console.error( "WebSocket error: %o", e );
|
47
|
-
writeErrorLog( "WebSocket Error: " + e.data )
|
48
|
-
}
|
49
|
-
|
50
|
-
function doConnect( e ) {
|
51
|
-
console.debug( "Connecting WebSocket." );
|
52
|
-
writeToLog( "Connecting." );
|
53
|
-
var ws_uri = $('meta[name=socket-uri]').attr( 'content' );
|
54
|
-
ws_uri = 'ws://' + window.location.host + ws_uri;
|
55
|
-
|
56
|
-
console.debug( " Socket URI is: %s", ws_uri );
|
57
|
-
|
58
|
-
ws = new WebSocket( ws_uri, 'echo' );
|
59
|
-
|
60
|
-
ws.onopen = onOpen;
|
61
|
-
ws.onclose = onClose;
|
62
|
-
ws.onmessage = onMessage;
|
63
|
-
ws.onerror = onError;
|
64
|
-
}
|
65
|
-
|
66
|
-
function doDisconnect( e ) {
|
67
|
-
console.debug( "Closing WebSocket." );
|
68
|
-
writeToLog( "Disconnecting." );
|
69
|
-
ws.close();
|
70
|
-
}
|
71
|
-
|
72
|
-
function doSend( e ) {
|
73
|
-
e.preventDefault();
|
74
|
-
e.stopPropagation();
|
75
|
-
|
76
|
-
var data = $('#echo-body').val();
|
77
|
-
$('#echo-body').val('');
|
78
|
-
|
79
|
-
console.debug( "Sending message: %s.", data );
|
80
|
-
writeToLog( "Sent: <code>" + data + "</code>." );
|
81
|
-
|
82
|
-
ws.send( data );
|
83
|
-
}
|
84
|
-
|
85
|
-
$(document).ready( function() {
|
86
|
-
log = $('#log');
|
87
|
-
|
88
|
-
if (window.MozWebSocket) {
|
89
|
-
console.debug( 'Using MozWebSocket' );
|
90
|
-
window.WebSocket = window.MozWebSocket;
|
91
|
-
} else if (!window.WebSocket) {
|
92
|
-
console.error( "This browser doesn't support WebSocket. ;_;" );
|
93
|
-
return;
|
94
|
-
}
|
95
|
-
|
96
|
-
// Check for recent implementations of the WebSocket API
|
97
|
-
if ( typeof WebSocket.CONNECTING == 'undefined' ) {
|
98
|
-
console.error( "WebSocket implementation is too old." );
|
99
|
-
writeErrorLog( "WebSocket implementation too old. Sorry, you'll need a newer browser." );
|
100
|
-
return;
|
101
|
-
}
|
102
|
-
|
103
|
-
$('#connect').click( doConnect );
|
104
|
-
$('#disconnect,#echo-body,#send').attr( 'disabled', 'disabled' );
|
105
|
-
|
106
|
-
writeToLog( "Ready." );
|
107
|
-
});
|
108
|
-
|
@@ -1,33 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
|
5
|
-
<script type="text/javascript" src="/js/websock-test.js"></script>
|
6
|
-
|
7
|
-
<link rel="stylesheet" href="/css/master.css" type="text/css" media="screen"
|
8
|
-
title="no title" charset="utf-8" />
|
9
|
-
|
10
|
-
<meta name="socket-uri" content="/ws" />
|
11
|
-
<meta charset="utf-8" />
|
12
|
-
|
13
|
-
</head>
|
14
|
-
<body class="websocket-test">
|
15
|
-
<h1>WebSockets Echo Test</h1>
|
16
|
-
|
17
|
-
<form action="#" method="get" accept-charset="utf-8">
|
18
|
-
<label for="echo-body">Echo message:</label>
|
19
|
-
<input type="text" name="echo-body" value="" id="echo-body" disabled="disabled" />
|
20
|
-
<button id="send" disabled="disabled">Send</button>
|
21
|
-
</form>
|
22
|
-
|
23
|
-
<section id="connection-controls">
|
24
|
-
<button id="connect">Connect</button>
|
25
|
-
<button id="disconnect">Disconnect</button>
|
26
|
-
</section>
|
27
|
-
|
28
|
-
<section id="log-box">
|
29
|
-
<h2>Log:</h2>
|
30
|
-
<ul id="log"></ul>
|
31
|
-
</section>
|
32
|
-
</body>
|
33
|
-
</html>
|
data/examples/Procfile
DELETED
data/examples/README.txt
DELETED
data/examples/async-upload.rb
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'erb'
|
4
|
-
require 'loggability'
|
5
|
-
require 'mongrel2/config'
|
6
|
-
require 'mongrel2/handler'
|
7
|
-
|
8
|
-
# A example of how to allow Mongrel2's async uploads.
|
9
|
-
class AsyncUploadHandler < Mongrel2::Handler
|
10
|
-
|
11
|
-
# App ID
|
12
|
-
ID = 'async-upload'
|
13
|
-
|
14
|
-
### Load up the ERB template from the DATA section on instantiation.
|
15
|
-
def initialize( * )
|
16
|
-
super
|
17
|
-
@template = self.load_template
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
### Load the ERB template from this file's DATA section.
|
22
|
-
def load_template
|
23
|
-
raw = IO.read( __FILE__ ).split( /^__END__$/m, 2 ).last
|
24
|
-
return ERB.new( raw, nil, '<%>' )
|
25
|
-
end
|
26
|
-
|
27
|
-
|
28
|
-
### Mongrel2 async upload callback -- allow uploads to proceed.
|
29
|
-
def handle_async_upload_start( request )
|
30
|
-
self.log.info "Upload started: %s" % [ request.header.x_mongrel2_upload_start ]
|
31
|
-
return nil # Do nothing
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
|
-
### Mongrel2 async upload callback -- finish the upload.
|
36
|
-
def handle_upload_done( request )
|
37
|
-
self.log.warn "Upload finished: %s (%0.2fK, %s)" %
|
38
|
-
[ request.uploaded_file, request.content_length, request.content_type ]
|
39
|
-
|
40
|
-
response = request.response
|
41
|
-
response.puts "Upload complete: %p" % [ request.uploaded_file ]
|
42
|
-
response.content_type = 'text/plain'
|
43
|
-
|
44
|
-
return response
|
45
|
-
rescue Mongrel2::UploadError => err
|
46
|
-
self.log.error "%s when finishing an upload: %s" % [ err.class, err.message ]
|
47
|
-
self.log.debug { err.backtrace.join("\n\t") }
|
48
|
-
|
49
|
-
finish_with HTTP::BAD_REQUEST, 'malformed upload headers'
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
### Regular request -- show the upload form.
|
54
|
-
def handle( request )
|
55
|
-
# If it's a finished upload, use that handler method
|
56
|
-
if request.upload_done?
|
57
|
-
return self.handle_upload_done( request )
|
58
|
-
|
59
|
-
else
|
60
|
-
response = request.response
|
61
|
-
|
62
|
-
settings = Mongrel2::Config.settings
|
63
|
-
|
64
|
-
response.headers.content_type = 'text/html'
|
65
|
-
response.puts( @template.result(binding()) )
|
66
|
-
|
67
|
-
return response
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
end # class HelloWorldHandler
|
72
|
-
|
73
|
-
configdb = ARGV.shift || 'examples.sqlite'
|
74
|
-
|
75
|
-
# Log to a file instead of STDERR for a bit more speed.
|
76
|
-
# Loggability.output_to( 'hello-world.log' )
|
77
|
-
Loggability.level = :debug
|
78
|
-
|
79
|
-
# Point to the config database, which will cause the handler to use
|
80
|
-
# its ID to look up its own socket info.
|
81
|
-
Mongrel2::Config.configure( configdb: configdb )
|
82
|
-
AsyncUploadHandler.run( 'async-upload' )
|
83
|
-
|
84
|
-
__END__
|
85
|
-
<!DOCTYPE html>
|
86
|
-
<html lang="en">
|
87
|
-
<head>
|
88
|
-
<title>Ruby-Mongrel2 Async Upload Demo</title>
|
89
|
-
<meta charset="utf-8">
|
90
|
-
</head>
|
91
|
-
|
92
|
-
<body>
|
93
|
-
|
94
|
-
<h1>Ruby-Mongrel2 Async Upload Demo</h1>
|
95
|
-
|
96
|
-
<form action="<%= request.headers.uri %>" method="post" accept-charset="utf-8"
|
97
|
-
enctype="multipart/form-data">
|
98
|
-
|
99
|
-
<label for="file">Choose a file</label>
|
100
|
-
<input type="file" name="uploaded-file" value="">
|
101
|
-
|
102
|
-
<p>Upload will be async if it's larger than:
|
103
|
-
<%= settings['limits.content_length'] %> bytes.</p>
|
104
|
-
|
105
|
-
<p><input type="submit" value="Upload →"></p>
|
106
|
-
</form>
|
107
|
-
|
108
|
-
</body>
|
109
|
-
</html>
|