Projet_JAVA_P2P_STRI2A/src/serverP2P/ServerManagementTCP.java
flavien 740e25546a
All checks were successful
flavien's git/Projet_JAVA_P2P_STRI2A/pipeline/head This commit looks good
refractor exception -> localexception
2020-03-19 13:48:39 +01:00

313 lines
9.7 KiB
Java

package serverP2P;
import java.util.Vector;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.net.ServerSocket;
import java.net.Socket;
import protocolP2P.ProtocolP2PPacketTCP;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.RequestResponseCode;
import protocolP2P.Payload;
import protocolP2P.LoadRequest;
import protocolP2P.FileList;
import protocolP2P.FilePart;
import localException.InternalError;
import localException.ProtocolError;
import localException.SizeError;
import localException.TransmissionError;
import localException.VersionError;
import localException.SocketClosed;
import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError;
import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import java.util.Arrays;
import tools.Logger;
import tools.LogLevel;
import java.util.HashMap;
import java.util.Map;
import protocolP2P.HashAlgorithm;
import protocolP2P.HashRequest;
import protocolP2P.HashResponse;
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class ServerManagementTCP implements Runnable {
private String[] fileList;
private Map<String, byte[]> sha512 = new HashMap<>();
private String baseDirectory;
private int TCPPort;
private ServerSocket socket;
private Logger logger;
/** Constructor for TCP implementation, with baseDirectory and TCPPort parameters.
* @param baseDirectory the root directory where files are stored
* @param TCPPort the server will listen on this port
*/
public ServerManagementTCP(String baseDirectory, int TCPPort, Logger logger) {
this.logger = logger;
this.baseDirectory = baseDirectory;
this.TCPPort = TCPPort;
initFileList();
initSha512();
try {
socket = new ServerSocket(TCPPort);
} catch (SocketException e) {
logger.writeTCP("Error: cannot listen on port " + TCPPort, LogLevel.Error);
System.exit(-1);
} catch (IOException e) {
logger.writeTCP("Error: cannot openning socket", LogLevel.Error);
System.exit(-2);
}
}
/** Implementation of runnable. This methods allows to run the server.
*/
public void run() {
logger.writeTCP("Server sucessfully started", LogLevel.Info);
do {
try {
Socket s = socket.accept();
ClientHandler c = new ClientHandler(s);
(new Thread(c)).start();
} catch (IOException e) {
logger.writeTCP("Error while accepting new connection", LogLevel.Warning);
}
} while(true);
}
/** Private runnable class allowing to serve one client.
*/
private class ClientHandler implements Runnable {
private Socket s;
private String addr;
/** Constructor with a socket.
* @param s Socket of this client
*/
public ClientHandler(Socket s) {
this.s = s;
this.addr = "[" +s.getInetAddress().getHostAddress() + "]:" + s.getPort() + " ";
}
/** Implementation of runnable. This method allow to serve one client.
*/
public void run() {
boolean end = false;
logger.writeTCP(addr + "New connection", LogLevel.Action);
do {
end = handleRequest();
} while(!end);
logger.writeTCP(addr + "End of connection", LogLevel.Action);
}
/** Respond to next request incomming on socket s.
* @param s Socket used to read request and send response
* @return true if cannot expect another request (ie, socket is closed)
*/
private boolean handleRequest() {
try {
ProtocolP2PPacketTCP pd = new ProtocolP2PPacketTCP((Object)s);
Payload p = pd.getPayload();
switch (p.getRequestResponseCode()) {
case LOAD_REQUEST:
logger.writeTCP(addr + "LOAD_REQUEST", LogLevel.Action);
sendLoadResponse(pd);
break;
case LIST_REQUEST:
logger.writeTCP(addr + "LIST_REQUEST", LogLevel.Action);
sendListResponse(pd);
break;
case HASH_REQUEST:
logger.writeTCP(addr + "HASH_REQUEST", LogLevel.Action);
sendHashResponse(pd);
break;
default:
logger.writeTCP(addr + "Received grabbage", LogLevel.Action);
sendInternalError(pd);
}
} catch (IOException e) {
return true;
} catch (SocketClosed e) {
return true;
}
catch (TransmissionError e) {}
catch (ProtocolError e) {}
catch (VersionError e) {}
catch (InternalError e) {}
catch (SizeError e) {}
return false;
}
}
/** Initialize local list of all files allowed to be shared.
*/
private void initFileList() {
File folder = new File(baseDirectory);
Vector<String> v = new Vector<String>();
File[] files = folder.listFiles();
/* Add non-recursively files's names to fileList */
for (File f : files) {
if (f.isFile()) {
v.add(f.getName());
}
}
fileList = new String[v.size()];
v.toArray(fileList);
Arrays.sort(fileList);
}
/** Init sha512 map.
*/
private void initSha512() {
for(String f: fileList) {
try {
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f))));
md.reset();
} catch (NoSuchAlgorithmException e) {
logger.writeTCP("sha512 not supported", LogLevel.Error);
} catch (IOException e) {
logger.writeTCP("cannot read " + f, LogLevel.Warning);
}
}
}
/** Send an internal error message.
* @param pd ProtocolP2PPacketTCP to respond
*/
private void sendInternalError(ProtocolP2PPacketTCP pd) {
logger.writeTCP("Internal Error", LogLevel.Warning);
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR)));
} catch (Exception e) {
logger.writeTCP(e, LogLevel.Error);
}
}
/** Send response to list request
* @param pd Request received
*/
private void sendListResponse(ProtocolP2PPacketTCP pd) {
try {
if (fileList.length == 0) {
logger.writeTCP("Sending EMPTY_DIRECTORY", LogLevel.Action);
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_DIRECTORY)));
} else {
logger.writeTCP("Sending LIST_RESPONSE", LogLevel.Action);
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FileList(fileList))));
}
} catch (Exception e2) {
logger.writeTCP(e2, LogLevel.Error);
}
}
/** Send hash response to hash request
* @param pd Request received
*/
private void sendHashResponse(ProtocolP2PPacketTCP pd) {
Payload p = pd.getPayload();
assert p instanceof HashRequest : "payload must be an instance of HashRequest";
if (!(p instanceof HashRequest)) {
sendInternalError(pd);
} else {
String filename = ((HashRequest)p).getFilename();
if (Arrays.binarySearch(fileList, filename) >= 0) {
Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) {
switch (h) {
case SHA512:
hashes.put(h, sha512.get(filename));
break;
case MD5:
default:
hashes.put(h, new byte[0]);
break;
}
}
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new HashResponse(filename, hashes))));
} catch (Exception e) {
logger.writeTCP(e, LogLevel.Error);
}
} else {
// file not found
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND)));
} catch (Exception e) {
logger.writeTCP(e, LogLevel.Error);
}
}
}
}
/** Send response to load request
* @param pd Request received
*/
private void sendLoadResponse(ProtocolP2PPacketTCP pd) {
Payload p = pd.getPayload();
assert p instanceof LoadRequest : "payload must be an instance of LoadRequest";
if (!(p instanceof LoadRequest)) {
sendInternalError(pd);
} else {
String filename = ((LoadRequest)p).getFilename();
long offset = ((LoadRequest)p).getOffset();
long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent();
try {
byte[] fullLoad = Files.readAllBytes(Paths.get(baseDirectory + filename));
long sizeToSend = 0;
if (fullLoad.length - offset < maxSizePartialContent) {
logger.writeTCP("Sending last partialContent", LogLevel.Debug);
sizeToSend = fullLoad.length - offset;
} else {
sizeToSend = maxSizePartialContent;
}
logger.writeTCP("maxSizePartialContent: " + maxSizePartialContent, LogLevel.Debug);
logger.writeTCP("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend), LogLevel.Debug);
byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend));
if (Arrays.binarySearch(fileList, filename) >= 0) {
try {
if (load.length == 0) {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_FILE)));
} else {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FilePart(filename, fullLoad.length, offset, load))));
}
} catch (Exception e2) {
logger.writeTCP(e2, LogLevel.Error);
}
} else {
logger.writeTCP("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug);
logger.writeTCP("File list:", LogLevel.Debug);
for (String f: fileList) {
logger.writeTCP("- " + f, LogLevel.Debug);
}
throw new IOException(); // to send a NOT_FOUND in the catch block
}
} catch (IOException e) {
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND)));
} catch (Exception e2) {
logger.writeTCP(e2, LogLevel.Debug);
}
}
}
}
}