Skip to content

Commit 2b002bb

Browse files
committed
ANDROID: file manager webui (wip)
1 parent 2b901db commit 2b002bb

File tree

2 files changed

+112
-74
lines changed
  • src/platform/android

2 files changed

+112
-74
lines changed

src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java

Lines changed: 99 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,33 @@
55
import java.io.ByteArrayInputStream;
66
import java.io.ByteArrayOutputStream;
77
import java.io.DataInputStream;
8-
import java.io.File;
98
import java.io.IOException;
109
import java.io.InputStream;
1110
import java.io.OutputStream;
1211
import java.io.UnsupportedEncodingException;
1312
import java.net.ServerSocket;
1413
import java.net.Socket;
1514
import java.net.URLDecoder;
16-
import java.nio.file.Files;
17-
import java.nio.file.attribute.BasicFileAttributes;
18-
import java.nio.file.attribute.FileTime;
19-
import java.text.DateFormat;
2015
import java.util.ArrayList;
2116
import java.util.Collection;
2217
import java.util.Collections;
2318
import java.util.HashMap;
24-
import java.util.Locale;
19+
import java.util.List;
2520
import java.util.Map;
2621
import java.util.zip.ZipEntry;
2722
import java.util.zip.ZipOutputStream;
2823

2924
/**
30-
* WebServer
25+
* Minimal AppServer with adequate performance for a single user
3126
*
3227
* @author chrisws
3328
*/
3429
public abstract class WebServer {
3530
private static final int BUFFER_SIZE = 32768;
3631
private static final int SEND_SIZE = BUFFER_SIZE / 4;
32+
private static final int LINE_SIZE = 128;
3733
private static final String UTF_8 = "utf-8";
34+
private static final String TOKEN = "token";
3835

3936
public WebServer() {
4037
super();
@@ -62,6 +59,49 @@ public void run() {
6259
protected abstract void log(String message, Exception exception);
6360
protected abstract void log(String message);
6461

62+
/**
63+
* Returns the HTTP headers from the given stream
64+
*/
65+
private List<String> getHeaders(InputStream stream) throws IOException {
66+
List<String> result = new ArrayList<>();
67+
ByteArrayOutputStream line = new ByteArrayOutputStream(LINE_SIZE);
68+
final char[] endHeader = {'\r', '\n', '\r', '\n'};
69+
int index = 0;
70+
for (int b = stream.read(); b != -1; b = stream.read()) {
71+
if (b == endHeader[index]) {
72+
index++;
73+
if (index == endHeader.length) {
74+
// end of headers
75+
break;
76+
} else if (index == 2) {
77+
// end of line
78+
result.add(line.toString());
79+
line.reset();
80+
}
81+
} else {
82+
line.write(b);
83+
index = 0;
84+
}
85+
}
86+
return result;
87+
}
88+
89+
/**
90+
* Reads a line of text from the given stream
91+
*/
92+
private String getLine(InputStream stream) throws IOException {
93+
StringBuilder result = new StringBuilder();
94+
while (stream.available() != 0) {
95+
int b = stream.read();
96+
if (b == -1 || b == 10 || b == 13) {
97+
break;
98+
} else {
99+
result.append(Character.toChars(b));
100+
}
101+
}
102+
return result.toString();
103+
}
104+
65105
/**
66106
* Parses HTTP GET parameters with the given name
67107
*/
@@ -90,25 +130,8 @@ private Map<String, Collection<String>> getParameters(String url) throws IOExcep
90130
* Parses HTTP POST data from the given input stream
91131
*/
92132
private Map<String, String> getPostData(InputStream inputStream, final String line) throws IOException {
93-
int length = 0;
94-
final String lengthHeader = "content-length: ";
95-
String nextLine = line;
96-
while (nextLine != null && nextLine.length() > 0) {
97-
if (nextLine.toLowerCase(Locale.ENGLISH).startsWith(lengthHeader)) {
98-
length = Integer.parseInt(nextLine.substring(lengthHeader.length()));
99-
}
100-
nextLine = readLine(inputStream);
101-
}
102-
StringBuilder postData = new StringBuilder();
103-
for (int i = 0; i < length; i++) {
104-
int b = inputStream.read();
105-
if (b == -1) {
106-
break;
107-
} else {
108-
postData.append(Character.toChars(b));
109-
}
110-
}
111-
String[] fields = postData.toString().split("&");
133+
String postData = getLine(inputStream);
134+
String[] fields = postData.split("&");
112135
Map<String, String> result = new HashMap<>();
113136
for (String nextField : fields) {
114137
int eq = nextField.indexOf("=");
@@ -181,25 +204,35 @@ private Response handleFileList() throws IOException {
181204
}
182205

183206
/**
184-
* Handler for HTTP GET
207+
* Handler for GET requests
185208
*/
186-
private void handleGet(Socket socket, String url) throws IOException {
187-
Map<String, Collection<String>> parameters = getParameters(url);
209+
private void handleGet(Socket socket, String token, List<String> headers, String url,
210+
Map<String, Collection<String>> parameters) throws IOException {
188211
if (url.startsWith("/api/download?")) {
189-
handleDownload(parameters).send(socket);
212+
if (hasTokenCookie(headers, token)) {
213+
handleDownload(parameters).send(socket, null);
214+
}
190215
} else {
191-
handleWebResponse(url).send(socket);
216+
handleWebResponse(url).send(socket, null);
192217
}
193218
}
194219

195220
/**
196-
* Handler for HTTP POST
221+
* Handler for POST requests
197222
*/
198-
private void handlePost(Socket socket, Map<String, String> postData, String url) throws IOException {
199-
if (url.startsWith("/api/files")) {
200-
handleFileList().send(socket);
223+
private void handlePost(Socket socket, String token, String url,
224+
Map<String, String> parameters) throws IOException {
225+
String userToken = parameters.get(TOKEN);
226+
log("userToken=" + userToken);
227+
if (token.equals(userToken)) {
228+
if (url.startsWith("/api/files")) {
229+
handleFileList().send(socket, token);
230+
}
231+
log("Sent POST response");
232+
} else {
233+
log("Invalid token");
234+
handleError("invalid token").send(socket, null);
201235
}
202-
log("Sent POST response");
203236
}
204237

205238
/**
@@ -218,17 +251,17 @@ private Response handleWebResponse(String asset) throws IOException {
218251
}
219252

220253
/**
221-
* Reads a line of text from the given stream
254+
* Inspects the headers to ensure the token cookie is present
222255
*/
223-
private String readLine(InputStream stream) throws IOException {
224-
ByteArrayOutputStream out = new ByteArrayOutputStream(128);
225-
int b;
226-
for (b = stream.read(); b != -1 && b != '\n'; b = stream.read()) {
227-
if (b != '\r') {
228-
out.write(b);
256+
private boolean hasTokenCookie(List<String> headers, String token) {
257+
boolean result = false;
258+
for (String header : headers) {
259+
if (header.startsWith("Cookie: ") && header.contains(TOKEN) && header.contains(token)) {
260+
result = true;
261+
break;
229262
}
230263
}
231-
return b == -1 ? null : out.size() == 0 ? "" : out.toString();
264+
return result;
232265
}
233266

234267
/**
@@ -251,28 +284,23 @@ private void runServer(final int socketNum, final String token) throws IOExcepti
251284
socket = serverSocket.accept();
252285
log("Accepted connection from " + socket.getRemoteSocketAddress().toString());
253286
inputStream = new DataInputStream(socket.getInputStream());
254-
String line = readLine(inputStream);
255-
if (line != null) {
287+
List<String> headers = getHeaders(inputStream);
288+
if (!headers.isEmpty()) {
289+
String line = headers.get(0);
256290
log(line);
257291
String[] fields = line.split("\\s");
258292
if ("GET".equals(fields[0]) && fields.length > 1) {
259-
handleGet(socket, fields[1]);
260-
} else if ("POST".equals(fields[0])) {
261-
Map<String, String> postData = getPostData(inputStream, line);
262-
String userToken = postData.get("token");
263-
log("userToken=" + userToken);
264-
if (token.equals(userToken)) {
265-
handlePost(socket, postData, fields[1]);
266-
} else {
267-
log("Invalid token");
268-
handleError("invalid token").send(socket);
269-
}
293+
Map<String, Collection<String>> parameters = getParameters(fields[1]);
294+
handleGet(socket, token, headers, fields[1], parameters);
295+
} else if ("POST".equals(fields[0]) && fields.length > 1) {
296+
Map<String, String> parameters = getPostData(inputStream, line);
297+
handlePost(socket, token, fields[1], parameters);
270298
} else {
271299
log("Invalid request");
272300
}
273301
}
274-
} catch (IOException e) {
275-
log("Server failed: ", e);
302+
} catch (Throwable e) {
303+
log("Server failed");
276304
break;
277305
} finally {
278306
log("socket cleanup");
@@ -291,17 +319,14 @@ private void runServer(final int socketNum, final String token) throws IOExcepti
291319
* BASIC file details
292320
*/
293321
public static class FileData {
294-
String fileName;
295-
String date;
296-
long size;
297-
298-
FileData(File file) throws IOException {
299-
BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
300-
FileTime lastModifiedTime = attr.lastModifiedTime();
301-
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.DEFAULT);
302-
this.fileName = file.getName();
303-
this.date = dateFormat.format(lastModifiedTime.toMillis());
304-
this.size = file.length();
322+
private final String fileName;
323+
private final String date;
324+
private final long size;
325+
326+
public FileData(String fileName, String date, long size) {
327+
this.fileName = fileName;
328+
this.date = date;
329+
this.size = size;
305330
}
306331
}
307332

@@ -379,13 +404,16 @@ long getLength() {
379404
/**
380405
* Sends the response to the given socket
381406
*/
382-
void send(Socket socket) throws IOException {
407+
void send(Socket socket, String session) throws IOException {
383408
log("sendResponse() entered");
384409
String contentLength = "Content-length: " + length + "\r\n";
385410
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
386411
out.write("HTTP/1.0 200 OK\r\n".getBytes());
387412
out.write("Content-type: text/html\r\n".getBytes());
388413
out.write(contentLength.getBytes());
414+
if (session != null) {
415+
out.write(("Set-Cookie: " + TOKEN + "=" + session + "\r\n").getBytes());
416+
}
389417
out.write("Server: SmallBASIC for Android\r\n\r\n".getBytes());
390418
socket.setSendBufferSize(SEND_SIZE);
391419
int sent = toStream(out);

src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
import java.io.IOException;
55
import java.io.InputStream;
66
import java.nio.file.Files;
7+
import java.nio.file.attribute.BasicFileAttributes;
8+
import java.nio.file.attribute.FileTime;
9+
import java.text.DateFormat;
710
import java.util.ArrayList;
811
import java.util.Collection;
12+
import java.util.Objects;
913

1014
public class Server {
1115
public static void main( String[] args ) {
@@ -19,9 +23,15 @@ protected void execStream(String line, InputStream inputStream) {
1923
protected Collection<FileData> getFileData() throws IOException {
2024
final File folder = new File("../basic");
2125
Collection<FileData> result = new ArrayList<>();
22-
for (final File fileEntry : folder.listFiles()) {
23-
if (!fileEntry.isDirectory()) {
24-
result.add(new FileData(fileEntry));
26+
for (final File fileEntry : Objects.requireNonNull(folder.listFiles())) {
27+
BasicFileAttributes attr = Files.readAttributes(fileEntry.toPath(), BasicFileAttributes.class);
28+
if (!attr.isDirectory()) {
29+
FileTime lastModifiedTime = attr.lastModifiedTime();
30+
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.DEFAULT);
31+
String fileName = fileEntry.getName();
32+
String date = dateFormat.format(lastModifiedTime.toMillis());
33+
long size = attr.size();
34+
result.add(new FileData(fileName, date, size));
2535
}
2636
}
2737
return result;

0 commit comments

Comments
 (0)