8 Commits

Author SHA1 Message Date
Godzil
53e12f21f1 Change text/plain to application/octet-stream 2019-10-31 02:04:27 +00:00
Fractal147
9f4d7a9988 Fixed unnecessary globals as in issue #113. (#122)
* Fixed global assignment that should be local

Made result variable be local, see Issue #113

* Made global variable local

Made ASCII variable be local, see Issue #113

* Made more variables local

Related to Issue #113. questionMarkPos, and b,c,d,e,f all are global in scope, and are not cleared from memory, so leak.
Frankly, b, c, d, e, and f are not used either, but will now get GC'd later, if they ever were assigned, so not problematic
line 114 also has _ and i to make local too, so were put on their own line.
i on line 24 also was unnecessarily global, and undetected in issue #113

* Made module more local

Made the basicAuth table local in scope. Since it is returned when dofile is called in httpserver.lua, that already has a correctly scoped table, 'auth'. This is related to issue #113, and should reduce memory loss to globals

* Made bufferedConnection local

bufferedConnection was global and didn't have to be. Part of issue #113.
Now no longer remains in _G (globals table) after a connection has closed.
2018-01-14 22:03:37 -06:00
Gregor Hartmann
7c8fe9c164 Upload for all files fix 119 (#120)
* allow uploading files not only to http + replace symbol images by unicode symbols

allow uploading files to root dir and http.
+ replace symbol images by unicode symbols

* backend for uploading arbitrary files

* fix uploading to http
2018-01-14 22:02:51 -06:00
Gregor Hartmann
5cf303de79 make cars dynamic to load 1 to 6 cars (#114)
For lazy people like me for not having to change the page by hand every
time.
+ adding 2 more cars.
2017-10-02 11:15:12 -05:00
Manoël Trapier
ab9a5d365f Use file.exists instead of open/close when relevant (#112) 2017-10-02 11:14:26 -05:00
Gregor Hartmann
6158d4b5ba support changes json API (#116)
use sjson instead of cjson
Also take care of backward compatibility before 2.1
2017-10-02 11:13:39 -05:00
Gregor Hartmann
c67ed16e10 Support long filenames in upload (#115)
appending '.dnl' to the filename shortens the available filename length by 4 chars. Now a long filename ist cut off first and then the dnl is appended resulting in a unique but short enough temp filename.
2017-10-02 08:50:47 -05:00
Dmitry
a70fa745b0 Small fix to upload form (#117)
Fixed bug when you cannot browse or D&D same file multiple times without reloading the whole page.
2017-10-02 08:49:49 -05:00
13 changed files with 161 additions and 61 deletions

BIN
http/cars-bugatti.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
http/cars-mercedes.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

51
http/cars.lua Normal file
View File

@@ -0,0 +1,51 @@
return function (connection, req, args)
local function showCars(nr)
if not nr then return end
connection:send([===[<figure><img src="cars-ferrari.jpg" /><figcaption>Ferrari</figcaption></figure>]===])
if nr == "1" then return end
connection:send([===[<figure><img src="cars-lambo.jpg" /><figcaption>Lamborghini</figcaption></figure>]===])
if nr == "2" then return end
connection:send([===[<figure><img src="cars-mas.jpg" /><figcaption>Maserati</figcaption></figure>]===])
if nr == "3" then return end
connection:send([===[<figure><img src="cars-porsche.jpg" /><figcaption>Porsche</figcaption></figure>]===])
if nr == "4" then return end
connection:send([===[<figure><img src="cars-bugatti.jpg" /><figcaption>Bugatti</figcaption></figure>]===])
if nr == "5" then return end
connection:send([===[<figure><img src="cars-mercedes.jpg" /><figcaption>Mercedes</figcaption></figure>]===])
end
dofile("httpserver-header.lc")(connection, 200, 'html')
connection:send([===[
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Nice cars</title>
</head>
<body>
<h1>Nice cars!</h1>
<p>
This page loads "large" images of fancy cars. It is meant to serve as a stress test for nodemcu-httpserver.<br>
It works with three embedded images of cars, but the server crashes with four. Select the number of cars you want to see below.<br>
Whoever manages to modify nodemcu-httpserver to load all four images without crashing wins a prize!
</p>
<p>
choose: <a href="?n=1">show one car</a>
<a href="?n=2">show two cars</a>
<a href="?n=3">show three cars</a>
<a href="?n=4">show four cars</a>
<a href="?n=5">show five cars</a>
<a href="?n=6">show six cars</a>
</p>
]===])
showCars(args.n)
connection:send([===[
</body>
</html>
]===])
end

View File

@@ -22,6 +22,7 @@
<li><a href="index.html">Index</a>: This page (static)</li>
<li><a href="hello_world.txt">A text file</a>: Simple text, to verify MIME type is ok. (static)</li>
<li><a href="cars.html">Nice cars</a>: Stress test, loads several "large" images. Makes the chip panic and restart :( (static)</li>
<li><a href="cars.lua">Nice cars</a>: Stress test, loads several "large" images. Makes the chip panic and restart :( (dynamic to change number of cars)</li>
<li><a href="counter.html">Count Requests</a>: Stress test, loads the same page over and over, counting every load. (static)</li>
<li><a href="zipped.html">Zipped</a>: File is actually saved as zipped.html.gz. A compressed file! (static but gzipped)</li>
<li><a href="zipped.html.gz">Zipped</a>: Same exact file as served above. Server is smart enough to treat the .gz extension correctly (static but gzipped)</li>

View File

@@ -33,6 +33,29 @@
.dropBox {width:100vw; height:100vh; margin-top: -200px; padding-top: 200px;}
#uploaddir{
background-color: #2E3134;
font-size:16px;
font-weight:bold;
color:#7f858a;
padding: 40px 50px;
margin-bottom: 30px;
}
#uploaddir a{
background-color:#007a96;
padding:12px 26px;
color:#fff;
font-size:14px;
border-radius:2px;
cursor:pointer;
margin-top:12px;
line-height:1;
margin-left: 10px;
}
#selectedDir {
margin-top:20px;
}
#upload{
font-family:'PT Sans Narrow', sans-serif;
background-color:#373a3d;
@@ -140,14 +163,24 @@
position: absolute;
}
.delete:after{
color: #ff0000;
content: "\2718";
}
.uploaded:after{
color: #00ff00;
content: "\2714";
}
#upload ul li span{
width: 15px;
height: 12px;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAaCAYAAABozQZiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpFQUY1MzY0QUU3QjdFMjExODE0NkUyMUJBNERDNDk0NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCOTc5MTBDQ0I3RUYxMUUyOUVBQkNFOURERDIzQkU4NSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCOTc5MTBDQkI3RUYxMUUyOUVBQkNFOURERDIzQkU4NSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFQUY1MzY0QUU3QjdFMjExODE0NkUyMUJBNERDNDk0NyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFQUY1MzY0QUU3QjdFMjExODE0NkUyMUJBNERDNDk0NyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvX6SiYAAAGjSURBVHjanJRPRMRREMfbNtE19hQlolNdomsRaZUiIlHKdiml7CpFolU6pEOWTUTRJZKIakmHsqQ99UedOiT2lG6xRPSdzFtjGi+/ho99836/78z8Zt7bUN3VfElAKwcRkC/9h/AAPIKmsgDCMNgB3ezvB8m8BQZ4/QkmSVwJbsCcR7gOYrz+Av0gQ2WfgWaGvimphEsgLvwRcEQLypxVLy4KP678cbDnHMqc4GZMiQBU2huX62wWbMqSXLenWeDKW1alr4A13QjZ7YT1AmwDLFhd1KOi0naFf8lVmWYdklHuQTXo8g3eOiT07UOgFXy4zcPB2wpTjAdpcA8iVgbs0yTe8dsi90N3NdEUfifYfwBtPCZn9CzF6wJXlGt8Of3JXCVebAAXfOXIxoTQfVJYlk3n9NgIQGc9LfYpaxRcSzHdkD4jwKoStqujXOy2FUBnzPpGRQHODfErePprzjHVHGf1qom/xCTcVlXkPFMoiocNYQ/PM+MLQOIZJexlUUGsZYBOKaYRPAvhieq0DJCUt45uVZ5LrLXGIQJ0uP8uZ98CDADM9WkEBoK0xwAAAABJRU5ErkJggg==') no-repeat;
cursor:pointer;
position: absolute;
top: 34px;
right: 33px;
cursor:pointer;
font-size:18px;
}
#upload ul li.working span{
@@ -189,7 +222,7 @@
var chunkSize = 128;
var totalUploaded = 0;
var tpl = '<li class="working" id="file%filenum%"><div class="chart" id="graph%filenum%" data-percent="0"></div><p>%filename%<i>%filesize%</i></p><span id="fileStatus%filenum%" onclick="DeleteFiles(%filenum%);"></span></li>';
var tpl = '<li class="working" id="file%filenum%"><div class="chart" id="graph%filenum%" data-percent="0"></div><p>%filename%<i>%filesize%</i></p><span class="delete" id="fileStatus%filenum%" onclick="DeleteFiles(%filenum%);"></span></li>';
document.addEventListener("DOMContentLoaded", function() {
var dropbox;
@@ -200,6 +233,8 @@
dropbox.addEventListener("drop", drop, false);
UpdateFileList();
UploadDir("http");
});
function dragenter(e) {
@@ -235,13 +270,13 @@
var fileNames = {};
for (var i = 0; i < filesCount; i++) {
fileNames[tfiles[i].name] = i;
fileNames[uploadDir + tfiles[i].name] = i;
}
Keys(fileNames).sort(function(a,b){var c=a.toLowerCase(),d=b.toLowerCase();return c<d?-1:c>d?1:0}).forEach(function(item) {
var i = fileNames[item];
var append = tpl.replace(/%filename%/g, tfiles[i].name);
var append = tpl.replace(/%filename%/g, uploadDir + tfiles[i].name);
append = append.replace(/%filesize%/g, formatFileSize(tfiles[i].size));
append = append.replace(/%filenum%/g, i);
@@ -380,7 +415,7 @@
totalUploaded = 0;
if (statusElement) {
statusElement.style["background-position"] = "0 3px";
statusElement.classList.add("uploaded");
}
if (currentUploadingFile < files.length) {
@@ -396,7 +431,7 @@
fileUploadRequest = 0;
}
lastRequest = 'upload.lua?cmd=upload&filename=' + file.name + '&filesize=' + file.size + '&len=' + chunkLen + '&offset=' + sendingOffset + '&data=' + filedata;
lastRequest = 'upload.lua?cmd=upload&filename=' + uploadDir + file.name + '&filesize=' + file.size + '&len=' + chunkLen + '&offset=' + sendingOffset + '&data=' + filedata;
fileUploadRequest.timeout = 5000;
fileUploadRequest.open('GET', lastRequest, true);
@@ -406,6 +441,16 @@
fr.readAsArrayBuffer(file);
}
function UploadDir(dir) {
if (uploadingInProgress == 0) {
document.getElementById('dir').innerHTML = "/" + dir;
uploadDir = dir;
if (!(uploadDir == "")) {
uploadDir += "/";
}
}
}
function formatFileSize(bytes) {
if (typeof bytes !== 'number') {
return '';
@@ -494,14 +539,14 @@
document.getElementById('fileInfo').innerHTML = '';
var tpl = '<li class="working"><p style="left: 30px;">%filenamelink%<i>%filesize%</i></p><span id="fileStatus" onclick="RemoveFile(\'%filename%\');"></span></li>';
var tpl = '<li class="working"><p style="left: 30px;">%filenamelink%<i>%filesize%</i></p><span class="delete" id="fileStatus" onclick="RemoveFile(\'%filename%\');"></span></li>';
var tplTotal = '<li class="working"><p style="left: 30px;">Used:<i>%used%</i></p></li><li class="working"><p style="left: 30px;">Free:<i>%free%</i></p></li><li class="working"><p style="left: 30px;">Total:<i>%total%</i></p></li>';
var append, link;
Keys(fileList).sort(function(a,b){var c=a.toLowerCase(),d=b.toLowerCase();return c<d?-1:c>d?1:0}).forEach(function(item) {
if (!(item.match(/\.lc$/ig))) {
link = item.replace(/\.gz$/g, '');
if (!item.match(/\.lc$/ig) && item.match(/^http\//ig)) {
link = item.replace(/\.gz$/g, '').replace(/^http\//g, '');
append = tpl.replace(/%filenamelink%/g, '<a href="' + link + '" target="_blank">' + item + '</a>');
}
else {
@@ -549,12 +594,16 @@
<body>
<div id="dropbox" class="dropBox">
<div id="upload">
<div id="uploaddir" class="uploadDir">
<a onclick='UploadDir("");'>/</a>
<a onclick='UploadDir("http");'>/http</a>
<div id="selectedDir">selected Directory: <div id = "dir">bla</div></div></div>
<div id="drop">
Drop Here
<a onclick='document.getElementById("browseInput").click();'>Browse</a>
<a onclick='UploadFiles();'>Upload</a>
<input id="browseInput" type="file" name="upl" onchange="handleFiles(this.files);" multiple />
<form><input id="browseInput" type="file" name="upl" onclick="this.form.reset();" onchange="handleFiles(this.files);" multiple /></form>
</div>
<ul id="fileList">

View File

@@ -48,23 +48,22 @@ return function (connection, req, args)
if (mbCmd == 'upload') then
if (fieldsCount > 5) then
if (mbFilename ~= 'upload.lua') then
if (mbFilename ~= 'http/upload.lua') then
connection:send('"offset":"' .. mbOffset .. '",')
connection:send('"len":"' .. mbLen .. '",')
connection:send('"filename":"' .. mbFilename .. '"')
mbFilename = 'http/' .. mbFilename
for i=1,string.len(mbData),2 do
currentByte = tonumber(string.sub(mbData, i, i + 1), 16)
binaryData = binaryData .. string.char(currentByte)
end
local mbTmpFilename = string.sub(mbFilename, 0, 27) .. '.dnl'
if (mbOffset > 0) then
file.open(mbFilename .. '.dnl','a+')
file.open(mbTmpFilename,'a+')
else
file.remove(mbFilename .. '.dnl')
file.open(mbFilename .. '.dnl','w+')
file.remove(mbTmpFilename)
file.open(mbTmpFilename,'w+')
end
file.seek("set", mbOffset)
file.write(binaryData)
@@ -74,8 +73,8 @@ return function (connection, req, args)
if (fileSize == mbLen + mbOffset) then
file.remove(mbFilename)
file.rename(mbFilename .. '.dnl', mbFilename)
file.remove(mbFilename .. '.dnl')
file.rename(mbTmpFilename, mbFilename)
file.remove(mbTmpFilename)
if (string.sub(mbFilename, -4) == '.lua') then
file.remove(string.sub(mbFilename, 0, -3) .. "lc")
@@ -93,19 +92,15 @@ return function (connection, req, args)
connection:send('"files":{')
for name, size in pairs(file.list()) do
local isHttpFile = string.match(name, "(http/)") ~= nil
if (headerExist > 0) then
connection:send(',')
end
if isHttpFile then
if (headerExist > 0) then
connection:send(',')
end
local url = string.match(name, ".*/(.*)")
url = name
connection:send('"' .. url .. '":"' .. size .. '"')
local url = string.match(name, ".*/(.*)")
connection:send('"' .. url .. '":"' .. size .. '"')
headerExist = 1
end
headerExist = 1
end
connection:send('},')
@@ -115,8 +110,8 @@ return function (connection, req, args)
connection:send('"free":"' .. remaining .. '"')
elseif (mbCmd == 'remove') then
if (fieldsCount > 1) then
if (mbFilename ~= 'upload.lua') and (mbFilename ~= 'upload.lc') and (mbFilename ~= 'upload.html.gz') then
file.remove('http/' .. mbFilename)
if (mbFilename ~= 'http/upload.lua') and (mbFilename ~= 'http/upload.lc') and (mbFilename ~= 'http/upload.html.gz') then
file.remove(mbFilename)
end
end
end
@@ -124,4 +119,3 @@ return function (connection, req, args)
connection:send('}')
collectgarbage()
end

View File

@@ -37,14 +37,14 @@ end
-- logic OR for number values
local function lor(x,y)
result = 0
local result = 0
for p=1,8 do result = result + (((bit(x,p) or bit(y,p)) == true) and uipow(2, (p-1)) or 0) end
return result
end
-- Character decoding table
local function toBase64Byte(char)
ascii = string.byte(char, 1)
local ascii = string.byte(char, 1)
if ascii >= string.byte('A', 1) and ascii <= string.byte('Z', 1) then return ascii - string.byte('A', 1)
elseif ascii >= string.byte('a', 1) and ascii <= string.byte('z', 1) then return ascii - string.byte('a', 1) + 26
elseif ascii >= string.byte('0', 1) and ascii <= string.byte('9', 1) then return ascii + 4

View File

@@ -2,7 +2,7 @@
-- Part of nodemcu-httpserver, authenticates a user using http basic auth.
-- Author: Sam Dieck
basicAuth = {}
local basicAuth = {}
-- Returns true if the user/password match one of the users/passwords in httpserver-conf.lua.
-- Returns false otherwise.

View File

@@ -3,8 +3,7 @@
-- Author: Marcos Kirsch
local compileAndRemoveIfNeeded = function(f)
if file.open(f) then
file.close()
if file.exists(f) then
print('Compiling:', f)
node.compile(f)
file.remove(f)

View File

@@ -5,7 +5,7 @@
-- flush() and for closing the connection.
-- Author: Philip Gladstone, Marcos Kirsch
BufferedConnection = {}
local BufferedConnection = {}
-- parameter is the nodemcu-firmware connection
function BufferedConnection:new(connection)

View File

@@ -15,7 +15,7 @@ return function(connection, code, extension, isGzipped, extraHeaders)
-- A few MIME types. Keep list short. If you need something that is missing, let's add it.
local mt = {css = "text/css", gif = "image/gif", html = "text/html", ico = "image/x-icon", jpeg = "image/jpeg",
jpg = "image/jpeg", js = "application/javascript", json = "application/json", png = "image/png", xml = "text/xml"}
if mt[ext] then return mt[ext] else return "text/plain" end
if mt[ext] then return mt[ext] else return "application/octet-stream" end
end
local mimeType = getMimeType(extension)

View File

@@ -21,7 +21,8 @@ local function uri_decode(input)
end
local function parseArgs(args)
local r = {}; i=1
local r = {}
local i = 1
if args == nil or args == "" then return r end
for arg in string.gmatch(args, "([^&]+)") do
local name, value = string.match(arg, "(.*)=(.*)")
@@ -46,6 +47,10 @@ local function getRequestData(payload)
local requestData
return function ()
--print("Getting Request Data")
-- for backward compatibility before v2.1
if (sjson == nil) then
sjson = cjson
end
if requestData then
return requestData
else
@@ -60,7 +65,7 @@ local function getRequestData(payload)
--print("body = [" .. body .. "]")
if mimeType == "application/json" then
--print("JSON: " .. body)
requestData = cjson.decode(body)
requestData = sjson.decode(body)
elseif mimeType == "application/x-www-form-urlencoded" then
requestData = parseFormData(body)
else
@@ -79,7 +84,7 @@ local function parseUri(uri)
if uri == nil then return r end
if uri == "/" then uri = "/index.html" end
questionMarkPos, b, c, d, e, f = uri:find("?")
local questionMarkPos, b, c, d, e, f = uri:find("?")
if questionMarkPos == nil then
r.file = uri:sub(1, questionMarkPos)
r.args = {}
@@ -111,6 +116,7 @@ return function (request)
if not e then return nil end
local line = request:sub(1, e - 1)
local r = {}
local _, i
_, i, r.method, r.request = line:find("^([A-Z]+) (.-) HTTP/[1-9]+.[0-9]+$")
if not (r.method and r.request) then
--print("invalid request: ")

View File

@@ -61,19 +61,19 @@ return function (port)
uri.args = {code = 400, errorString = "Bad Request", logFunction = log}
fileServeFunction = dofile("httpserver-error.lc")
else
local fileExists = file.open(uri.file, "r")
file.close()
local fileExists = false
if not fileExists then
if not file.exists(uri.file) then
-- print(uri.file .. " not found, checking gz version...")
-- gzip check
fileExists = file.open(uri.file .. ".gz", "r")
file.close()
if fileExists then
--print("gzip variant exists, serving that one")
if file.exists(uri.file .. ".gz") then
-- print("gzip variant exists, serving that one")
uri.file = uri.file .. ".gz"
uri.isGzipped = true
fileExists = true
end
else
fileExists = true
end
if not fileExists then