changeset 1295:9dca1e912658

improve web shell
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 10 Jan 2019 21:42:11 -0700
parents 2555154ad19f
children 040c98e1f837
files src/luan/lib/json/JsonToString.java src/luan/modules/Html.luan src/luan/modules/http/tools/Shell.luan
diffstat 3 files changed, 91 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/lib/json/JsonToString.java	Thu Jan 10 18:22:52 2019 -0700
+++ b/src/luan/lib/json/JsonToString.java	Thu Jan 10 21:42:11 2019 -0700
@@ -29,6 +29,12 @@
 		return sb.toString();
 	}
 
+	public static String encodeString(String s) {
+		StringBuilder sb = new StringBuilder();
+		encodeString(s,sb);
+		return sb.toString();
+	}
+
 	private void toString(Object obj,StringBuilder sb,int indented) throws JsonException {
 		if( obj == null || obj instanceof Boolean || obj instanceof Number ) {
 			sb.append(obj);
@@ -49,14 +55,22 @@
 		throw new JsonException("can't handle type "+obj.getClass().getName());
 	}
 
-	private void toString(final String s,StringBuilder sb) {
+	private static void toString(final String s,StringBuilder sb) {
+		sb.append('"');
+		encodeString(s,sb);
 		sb.append('"');
+	}
+
+	private static void encodeString(final String s,StringBuilder sb) {
 		for( int i=0; i<s.length(); i++ ) {
 			char c = s.charAt(i);
 			switch(c) {
 			case '"':
 				sb.append("\\\"");
 				break;
+			case '\'':
+				sb.append("\\'");
+				break;
 			case '\\':
 				sb.append("\\\\");
 				break;
@@ -79,7 +93,6 @@
 				sb.append(c);
 			}
 		}
-		sb.append('"');
 	}
 
 	private void toString(List list,StringBuilder sb,int indented) {
--- a/src/luan/modules/Html.luan	Thu Jan 10 18:22:52 2019 -0700
+++ b/src/luan/modules/Html.luan	Thu Jan 10 21:42:11 2019 -0700
@@ -2,6 +2,7 @@
 local HtmlLuan = require "java:luan.modules.HtmlLuan"
 local HtmlParser = require "java:luan.modules.parsers.Html"
 local URLEncoder = require "java:java.net.URLEncoder"
+local JsonToString = require "java:luan.lib.json.JsonToString"
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local ipairs = Luan.ipairs or error()
@@ -14,6 +15,7 @@
 local Html = {}
 
 Html.encode = HtmlLuan.encode
+Html.javascript_encode = JsonToString.encodeString
 
 local quote = HtmlLuan.quote
 Html.quote = quote
--- a/src/luan/modules/http/tools/Shell.luan	Thu Jan 10 18:22:52 2019 -0700
+++ b/src/luan/modules/http/tools/Shell.luan	Thu Jan 10 21:42:11 2019 -0700
@@ -3,15 +3,17 @@
 local load = Luan.load or error()
 local to_string = Luan.to_string or error()
 local try = Luan.try or error()
-local String = require "luan:String.luan"
-local concat = String.concat or error()
+local range = Luan.range or error()
 local Table = require "luan:Table.luan"
+local concat = Table.concat or error()
 local pack = Table.pack or error()
 local unpack = Table.unpack or error()
 local Time = require "luan:Time.luan"
 local Thread = require "luan:Thread.luan"
+local Html = require "luan:Html.luan"
+local html_encode = Html.encode or error()
+local javascript_encode = Html.javascript_encode or error()
 local Io = require "luan:Io.luan"
-local print = Io.print or error()
 local Http = require "luan:http/Http.luan"
 local Logging = require "luan:logging/Logging.luan"
 local logger = Logging.logger "Shell"
@@ -26,23 +28,13 @@
 	return to_string(count)
 end}).next
 
-local history = ""
 local env = {}
 Shell.env = env
 
 local fns = {}
 
-function fns.history()
-	return history
-end
-
 function fns.run(cmd)
-	Io.stdout = {}
-	Io.stdout.write = function(...)
-		history = concat(history,...)
-	end
-	print( "% "..cmd )
-	try {
+	return try {
 		function()
 			local line
 			try {
@@ -53,14 +45,11 @@
 					line = load(cmd,"<web_shell>",env)
 				end
 			}
-			local rtn = pack( line() )
-			if rtn.n > 0  then
-				print( unpack(rtn) )
-			end
+			return line()
 		end
 		catch = function(e)
-			Io.print_to(Io.stderr,e)
-			print(e)
+--			Io.print_to(Io.stderr,e)
+			return to_string(e)
 		end
 	}
 end
@@ -71,74 +60,91 @@
 	return Thread.global_callable("shell.session"..session_id,timeout,fns)
 end
 
-local function remove_session(session_id)
-	return Thread.remove_global_callable("shell.session"..session_id)
-end
-
 function Shell.respond()
-	local session_id = Http.request.cookies["session"]
-	if session_id == nil then
-		session_id = new_session()
-		Http.response.set_cookie("session",session_id)
+	Io.stdout = Http.response.text_writer()
+	local cmd = Http.request.parameters.cmd
+	if cmd ~= nil then
+		Http.response.headers["content-type"] = "application/javascript"
+		local session_id = Http.request.parameters.session or error()
+		local session = get_session(session_id)
+		local rtn = pack( session.run(cmd) )
+%>
+		var pre = document.querySelector('pre');
+		pre.innerHTML += '\n&gt; <%=javascript_encode(html_encode(cmd))%>';
+<%
+		if rtn.n > 0 then
+			local t = {}
+			for i in range(1,rtn.n) do
+				t[#t+1] = javascript_encode(html_encode(to_string(rtn[i])))
+			end
+%>
+			pre.innerHTML += '\n<%=concat(t,"\t")%>';
+<%
+		end
+		return
 	end
-	local session = get_session(session_id)
-
-	if Http.request.parameters.clear ~= nil then
-		remove_session(session_id)
-		Http.response.send_redirect(Http.request.path)  -- reload page
-		return
-	else
-		local cmd = Http.request.parameters.cmd
-		if cmd ~= nil then
-			session.run(cmd)
-		end
-	end
-
-	Io.stdout = Http.response.text_writer()
 %>
 <!doctype html>
 <html>
 	<head>
 		<title>Luan Shell</title>
+		<script>
+
+			function ajax(url) {
+				var request = new XMLHttpRequest();
+				request.open( 'GET', url );
+				request.onload = function() {
+					if( request.status !== 200 ) {
+						console.log( 'ajax failed: ' + request.status );
+						return;
+					}
+					//console.log( request.responseText );
+					eval( request.responseText );
+				};
+				request.send();
+			}
+
+			function submitted() {
+				var input = document.querySelector('input');
+				ajax( '?session=<%=new_session()%>&cmd=' + encodeURIComponent(input.value) );
+				input.value = '';
+			}
+
+		</script>
 		<style>
 			body {
 				font-family: sans-serif;
-				margin: 2em 5% 0 5%;
 			}
 			pre {
 				font: inherit;
+				margin-bottom: 0;
 			}
-			input[type="text"] {
-				font: inherit;
-				padding: .5em .8em;
-				border-radius: 8px;
-				border-style: groove;
-			}
-			input[type="text"]:focus {
-				border-color: #66afe9;
-				outline: none;
+			table {
+				width: 100%;
+				border-collapse: collapse;
 			}
-			input[type="submit"] {
-				color: white;
-				background: #337ab7;
-				border-color: #337ab7;
+			td {
+				padding: 0;
+			}
+			td:last-child {
+				width: 100%;
+			}
+			input {
+				width: 100%;
 				font: inherit;
-				padding: .5em;
-				border-radius: 4px;
-			}
-			input[type="submit"]:hover {
-				background: #236aa7 !important;
+				outline: none;
 			}
 		</style>
 	</head>
 	<body>
-		<h2>Luan Shell</h2>
-		<p>This is a command shell.  Enter commands below.</p>
-		<pre><%= session.history() %></pre>
-		<form method='post'>
-			% <input type="text" name='cmd' size="80" autofocus>
-			<input type="submit" value="run">
-			<input type="submit" name="clear" value="clear">
+		<pre>Luan <%=Luan.VERSION%></pre>
+		<form autocomplete=off onsubmit="submitted(); return false">
+			<table>
+				<tr>
+					<td>&gt;&nbsp;</td>
+					<td><input name=cmd autofocus></td>
+				</tr>
+			</table>
 		</form>
 	</body>
 </html>