You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
6.8 KiB
Java
281 lines
6.8 KiB
Java
// Thomas Nagy, 2011
|
|
|
|
// TODO security
|
|
// TODO handle all exceptions properly
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.List;
|
|
import java.util.Date;
|
|
import java.util.ArrayList;
|
|
import java.util.Comparator;
|
|
import java.util.Collections;
|
|
import java.lang.Math;
|
|
import java.lang.StringBuilder;
|
|
import java.io.*;
|
|
import java.net.*;
|
|
import java.security.*;
|
|
|
|
public class Netcache implements Runnable, Comparator<Object[]> {
|
|
private static int PORT_UPLOAD = 11001;
|
|
private static int PORT_DOWNLOAD = 12001;
|
|
private static String CACHEDIR = "/tmp/wafcache/";
|
|
private static long MAX = 10l * 1024l * 1024l * 1024l;
|
|
private static double CLEANRATIO = 0.8;
|
|
private static int BUF = 16 * 8192;
|
|
|
|
private final static HashMap<String, Object[]> flist = new HashMap<String, Object[]>();
|
|
private Socket sock = null;
|
|
private int port = 0;
|
|
|
|
public Netcache(Socket sock, int port) {
|
|
this.sock = sock;
|
|
this.port = port;
|
|
}
|
|
|
|
public void run () {
|
|
try {
|
|
if (sock != null)
|
|
{
|
|
while (true) {
|
|
InputStream in = sock.getInputStream();
|
|
OutputStream out = sock.getOutputStream();
|
|
|
|
byte b[] = new byte[128];
|
|
int off = 0;
|
|
while (off < b.length) {
|
|
off += in.read(b, off, b.length - off);
|
|
}
|
|
|
|
//System.out.println(new String(b));
|
|
String[] args = new String(b).split(",");
|
|
if (args[0].equals("LST")) {
|
|
lst(args, in, out);
|
|
}
|
|
else if (args[0].equals("PUT") && port == PORT_UPLOAD) {
|
|
put(args, in, out);
|
|
}
|
|
else if (args[0].equals("GET") && port == PORT_DOWNLOAD) {
|
|
get(args, in, out);
|
|
}
|
|
else if (args[0].equals("CLEAN") && port == PORT_UPLOAD) {
|
|
clean(args, in, out);
|
|
}
|
|
else if (args[0].equals("BYE")) {
|
|
sock.close();
|
|
break;
|
|
}
|
|
else {
|
|
System.out.println("Invalid command " + new String(b) + " on port " + this.port);
|
|
sock.close();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// magic trick to avoid creating a new inner class
|
|
ServerSocket server = new ServerSocket(port);
|
|
server.setReuseAddress(true);
|
|
while(true) {
|
|
Netcache tmp = new Netcache(server.accept(), port);
|
|
Thread t = new Thread(tmp);
|
|
t.start();
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void lst(String[] args, InputStream in, OutputStream out) throws IOException {
|
|
StringBuilder b = new StringBuilder();
|
|
int k = 0;
|
|
synchronized(flist) {
|
|
for (String name : flist.keySet()) {
|
|
b.append(name);
|
|
if (k <= flist.size()) {
|
|
k++;
|
|
b.append("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
byte[] ret = b.toString().getBytes();
|
|
String header = String.format("%-128s", String.format("%d,", ret.length));
|
|
|
|
out.write(header.getBytes());
|
|
out.write(ret);
|
|
}
|
|
|
|
public void put(String[] args, InputStream in, OutputStream out) throws IOException {
|
|
File cachedir = new File(CACHEDIR);
|
|
File temp = File.createTempFile("foo", ".suffix", cachedir);
|
|
|
|
long size = new Long(args[3].trim());
|
|
|
|
//System.out.println("" + args[1] + " " + args[2] + " " + args[3] + " " + args.length);
|
|
|
|
byte[] buf = new byte[BUF];
|
|
long cnt = 0;
|
|
OutputStream w = new FileOutputStream(temp);
|
|
try {
|
|
while (cnt < size) {
|
|
int c = in.read(buf, 0, (int) Math.min(BUF, size-cnt));
|
|
if (c == 0) {
|
|
throw new RuntimeException("Connection closed too early");
|
|
}
|
|
w.write(buf, 0, c);
|
|
cnt += c;
|
|
}
|
|
} finally {
|
|
w.close();
|
|
}
|
|
|
|
/*if (cnt != size) {
|
|
System.out.println("error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
|
}*/
|
|
|
|
File parent = new File(new File(new File(CACHEDIR), args[1].substring(0, 2)), args[1]);
|
|
File dest = new File(parent, args[2]);
|
|
try {
|
|
dest.getParentFile().mkdirs();
|
|
} catch (Exception e) {
|
|
}
|
|
|
|
if (!temp.renameTo(dest)) {
|
|
throw new RuntimeException("Could not rename the file");
|
|
}
|
|
|
|
long total = 0;
|
|
for (File f : parent.listFiles()) {
|
|
total += f.length();
|
|
}
|
|
|
|
synchronized(flist) {
|
|
if (flist.containsKey(parent.getName())) {
|
|
flist.get(parent.getName())[0] = parent.lastModified();
|
|
}
|
|
else
|
|
{
|
|
flist.put(parent.getName(), new Object[] {parent.lastModified(), total, parent.getName()});
|
|
}
|
|
}
|
|
}
|
|
|
|
public void get(String[] args, InputStream in, OutputStream out) throws IOException {
|
|
File f = new File(new File(new File(new File(CACHEDIR), args[1].substring(0, 2)), args[1]), args[2].trim());
|
|
long fsize = -1;
|
|
try {
|
|
fsize = f.length();
|
|
} catch (Exception e) {
|
|
// return -1 to the client
|
|
}
|
|
|
|
String ret = String.format("%-128s", String.format("%d,", fsize));
|
|
out.write(ret.getBytes());
|
|
|
|
byte[] buf = new byte[BUF];
|
|
|
|
long cnt = 0;
|
|
InputStream s = new FileInputStream(f);
|
|
try {
|
|
while (cnt < fsize) {
|
|
long c = s.read(buf);
|
|
cnt += c;
|
|
out.write(buf, 0, (int) c);
|
|
}
|
|
} finally {
|
|
s.close();
|
|
}
|
|
|
|
File parent = f.getParentFile();
|
|
Date d = new Date();
|
|
parent.setLastModified(d.getTime());
|
|
synchronized(flist) {
|
|
flist.get(parent.getName())[0] = parent.lastModified();
|
|
}
|
|
}
|
|
|
|
public void clean(String[] args, InputStream in, OutputStream out) throws IOException {
|
|
synchronized(flist) {
|
|
long total = 0;
|
|
for (Map.Entry<String, Object[]> entry : flist.entrySet()) {
|
|
total += (Long) entry.getValue()[1];
|
|
}
|
|
|
|
List<Object[]> k = new ArrayList<Object[]>(flist.values());
|
|
Collections.sort(k, this);
|
|
|
|
int cur = 0;
|
|
while (total > MAX * CLEANRATIO) {
|
|
Object[] kk = k.get(cur);
|
|
|
|
String name = (String) kk[2];
|
|
File f = new File(new File(new File(CACHEDIR), name.substring(0, 2)), name);
|
|
//System.out.println("removing " + cur + " " + kk[0] + " " + kk[1] + " " + f.getAbsolutePath());
|
|
rm(f);
|
|
|
|
total -= (Long) kk[1];
|
|
|
|
flist.remove(name);
|
|
cur++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void init_flist() {
|
|
synchronized(flist) {
|
|
flist.clear();
|
|
File dir = new File(CACHEDIR);
|
|
try {
|
|
dir.mkdirs();
|
|
} catch (Exception e) {
|
|
|
|
}
|
|
|
|
for (File d : dir.listFiles()) {
|
|
if (!d.isDirectory()) continue;
|
|
for (File sd : d.listFiles()) {
|
|
if (!sd.isDirectory()) continue;
|
|
long total = 0;
|
|
for (File f : sd.listFiles()) {
|
|
total += f.length();
|
|
}
|
|
//System.out.println(sd.getName());
|
|
flist.put(sd.getName(), new Object[] {sd.lastModified(), total, sd.getName()});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public int compare(Object[] a, Object[] b) {
|
|
return ((Long) a[0]).compareTo((Long) b[0]);
|
|
}
|
|
|
|
public static void rm(File dir) {
|
|
if (dir.isDirectory()) {
|
|
for (File f: dir.listFiles())
|
|
{
|
|
rm(f);
|
|
}
|
|
}
|
|
dir.delete();
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
init_flist();
|
|
System.out.println("ready (" + flist.keySet().size() + " dirs)");
|
|
|
|
// different ports for upload and download, another port could be added for the clean command
|
|
Thread upload = null;
|
|
if (PORT_UPLOAD != PORT_DOWNLOAD) {
|
|
Netcache tmp = new Netcache(null, PORT_UPLOAD);
|
|
upload = new Thread(tmp);
|
|
upload.start();
|
|
}
|
|
|
|
Netcache tmp = new Netcache(null, PORT_DOWNLOAD);
|
|
tmp.run();
|
|
}
|
|
}
|
|
|