Index: /luci/trunk/libs/web/luasrc/http.lua
===================================================================
--- /luci/trunk/libs/web/luasrc/http.lua	(revision 5140)
+++ /luci/trunk/libs/web/luasrc/http.lua	(revision 6391)
@@ -13,7 +13,7 @@
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
-You may obtain a copy of the License at 
-
-	http://www.apache.org/licenses/LICENSE-2.0 
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
@@ -30,6 +30,8 @@
 local string = require "string"
 local coroutine = require "coroutine"
-
-local pairs, tostring, error = pairs, tostring, error
+local table = require "table"
+
+local ipairs, pairs, next, type, tostring, error =
+	ipairs, pairs, next, type, tostring, error
 
 --- LuCI Web Framework high-level HTTP functions.
@@ -46,5 +48,5 @@
 	-- File handler
 	self.filehandler = function() end
-	
+
 	-- HTTP-Message table
 	self.message = {
@@ -53,5 +55,5 @@
 		params = protocol.urldecode_params(env.QUERY_STRING or ""),
 	}
-	
+
 	self.parsed_input = false
 end
@@ -61,5 +63,5 @@
 		self:_parse_input()
 	end
-	
+
 	if name then
 		return self.message.params[name]
@@ -72,9 +74,9 @@
 	local vals = {}
 	prefix = prefix and prefix .. "." or "."
-	
+
 	if not self.parsed_input then
 		self:_parse_input()
 	end
-	
+
 	local void = self.message.params[nil]
 	for k, v in pairs(self.message.params) do
@@ -83,5 +85,5 @@
 		end
 	end
-	
+
 	return vals
 end
@@ -91,5 +93,5 @@
 		self:_parse_input()
 	end
-	
+
 	return self.message.content, self.message.content_length
 end
@@ -129,5 +131,5 @@
 		coroutine.yield(3)
 	end
-	
+
 	if not context.closed then
 		context.closed = true
@@ -165,5 +167,5 @@
 end
 
---- Get the value of a certain HTTP environment variable 
+--- Get the value of a certain HTTP environment variable
 -- or the environment table itself.
 -- @param name		Environment variable
@@ -249,6 +251,6 @@
 				header("Expires", "0")
 			end
-			
-			
+
+
 			context.eoh = true
 			coroutine.yield(3)
@@ -277,12 +279,16 @@
 -- @param table		Query string source table
 -- @return			Encoded HTTP query string
-function build_querystring(table)
-	local s="?"
-	
-	for k, v in pairs(table) do
-		s = s .. urlencode(k) .. "=" .. urlencode(v) .. "&"
-	end
-	
-	return s
+function build_querystring(q)
+	local s = { "?" }
+
+	for k, v in pairs(q) do
+		if #s > 1 then s[#s+1] = "&" end
+
+		s[#s+1] = urldecode(k)
+		s[#s+1] = "="
+		s[#s+1] = urldecode(v)
+	end
+
+	return table.concat(s, "")
 end
 
@@ -299,2 +305,36 @@
 -- @see urldecode
 urlencode = protocol.urlencode
+
+--- Send the given data as JSON encoded string.
+-- @param data		Data to send
+function write_json(x)
+	if x == nil then
+		write("null")
+	elseif type(x) == "table" then
+		local k, v
+		if type(next(x)) == "number" then
+			write("[ ")
+			for k, v in ipairs(x) do
+				write_json(v)
+				if next(x, k) then
+					write(", ")
+				end
+			end
+			write(" ]")
+		else
+			write("{ ")
+			for k, v in pairs(x) do
+			write("%q: " % k)
+				write_json(v)
+				if next(x, k) then
+					write(", ")
+				end
+			end
+			write(" }")
+		end
+	elseif type(x) == "number" or type(x) == "boolean" then
+		write(tostring(x))
+	elseif type(x) == "string" then
+		write("%q" % tostring(x))
+	end
+end
