/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*
* This particular file is subject to the "Classpath" exception as provided in the
* LICENSE file that accompanied this code.
*
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License,
* along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package com.redhat.ceylon.compiler.java.test.cmr;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import com.redhat.ceylon.compiler.java.test.cmr.CMRHTTPTests.ExpectedError;
import com.redhat.ceylon.compiler.java.test.cmr.CMRHTTPTests.HttpError;
import com.redhat.ceylon.compiler.java.test.cmr.CMRHTTPTests.RequestCounter;
import com.redhat.ceylon.compiler.java.test.cmr.CMRHTTPTests.TimeoutIn;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
public class RepoFileHandler implements HttpHandler {
enum Method {
Get, Put;
}
private static final String DAV_LOCK_RESPONSE = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>"
+"<prop xmlns='DAV:'>"
+"<lockdiscovery>"
+"<activelock>"
+"<locktype><write/></locktype>"
+"<lockscope><exclusive/></lockscope>"
+"<depth>Infinity</depth>"
+"<timeout>Second-604800</timeout>"
+"<locktoken>"
+"<href>"
+"opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6be4"
+"</href>"
+"</locktoken>"
+"</activelock>"
+"</lockdiscovery>"
+"</prop>";
private String folder;
private RequestCounter rq;
private boolean herd;
private TimeoutIn timeoutIn;
private HttpError httpError;
public RepoFileHandler(String destdir, boolean herd, RequestCounter rq, ExpectedError error) {
this.folder = destdir;
this.rq = rq;
this.herd = herd;
if(error instanceof TimeoutIn)
this.timeoutIn = (TimeoutIn) error;
else if(error instanceof HttpError)
this.httpError = (HttpError) error;
}
@Override
public void handle(HttpExchange t) throws IOException {
if(rq != null)
rq.add();
String path = t.getRequestURI().getPath();
String method = t.getRequestMethod();
if(httpError != null){
switch(httpError){
case FORBIDDEN:
t.sendResponseHeaders(HttpURLConnection.HTTP_FORBIDDEN, -1);
t.close();
return;
}
}
log("Serving URI "+method+" "+path);
// filter on our prefix
if(path.equals("/repo") || path.startsWith("/repo/")){
// ignore this if not Herd, to simulate a non-responsive server
if(herd && "OPTIONS".equals(method)){
t.getResponseHeaders().add("X-Herd-Version", "1");
t.sendResponseHeaders(HttpURLConnection.HTTP_OK, -1);
t.close();
return;
}
if("LOCK".equals(method)){
// we need to send a lock response: http://www.webdav.org/specs/rfc2518.html#rfc.figure.u.26
byte[] bytes = DAV_LOCK_RESPONSE.getBytes();
t.sendResponseHeaders(HttpURLConnection.HTTP_OK, bytes.length);
OutputStream response = t.getResponseBody();
response.write(bytes);
t.close();
return;
}
File file;
if(path.equals("/repo")){
file = new File(folder);
}else{
path = path.substring(6);
file = new File(folder, path);
}
if("UNLOCK".equals(method)){
t.sendResponseHeaders(HttpURLConnection.HTTP_OK, -1);
t.close();
return;
}
if("MKCOL".equals(method)){
file.mkdirs();
// OK
t.sendResponseHeaders(HttpURLConnection.HTTP_CREATED, -1);
t.close();
return;
}
if("PUT".equals(method)){
if(timeoutIn == TimeoutIn.PutInitial)
timeout();
// make sure parents exist
file.getParentFile().mkdirs();
// save the file
FileOutputStream os = new FileOutputStream(file);
InputStream body = t.getRequestBody();
copy(body, os, Method.Put);
body.close();
os.close();
// OKGet
t.sendResponseHeaders(HttpURLConnection.HTTP_CREATED, -1);
t.close();
return;
}
if(file.exists()){
if("GET".equals(method)){
log("Serving file "+file.getPath());
if(timeoutIn == TimeoutIn.GetInitial)
timeout();
t.sendResponseHeaders(HttpURLConnection.HTTP_OK, file.length());
OutputStream os = t.getResponseBody();
// only write the contents if it's not a directory, otherwise the CMR expects an empty 200 response
if(!file.isDirectory()){
InputStream is = new FileInputStream(file);
copy(is, os, Method.Get);
}
t.close();
return;
}
if("HEAD".equals(method)){
if(timeoutIn == TimeoutIn.Head)
timeout();
t.sendResponseHeaders(HttpURLConnection.HTTP_OK, -1);
t.close();
return;
}
if("PROPFIND".equals(method)){
String ret = propfind(file);
t.sendResponseHeaders(207, ret.length()); // MULTI STATUS
t.getResponseBody().write(ret.getBytes());
t.close();
return;
}
}else
log("File does not exist: "+file.getAbsolutePath());
}
log("Returning 404");
t.sendResponseHeaders(HttpURLConnection.HTTP_NOT_FOUND, -1);
t.close();
}
private String propfind(File file) throws IOException {
StringBuilder ret = new StringBuilder("<?xml version='1.0' encoding='utf-8' ?>\n");
ret.append("<multistatus xmlns='DAV:'>\n");
if(file.isFile())
propfindFile(file, ret);
else{
// first one is the folder itself
propfindFile(file, ret);
for(File child : file.listFiles())
propfindFile(child, ret);
}
ret.append("</multistatus>\n");
return ret.toString();
}
private void propfindFile(File file, StringBuilder xml) throws IOException {
String path = file.getCanonicalPath();
path = path.substring(folder.length());
xml.append("<response>\n");
xml.append(" <href>/").append(path).append("</href>\n");
xml.append(" <propstat>\n");
xml.append(" <prop>\n");
if(file.isDirectory())
xml.append(" <resourcetype><collection/></resourcetype>\n");
else
xml.append(" <resourcetype/>\n");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
xml.append(" <getlastmodified>").append(format.format(new Date(file.lastModified()))).append("</getlastmodified>\n");
xml.append(" </prop>\n");
xml.append(" <status>HTTP/1.1 200 OK</status>\n");
xml.append(" </propstat>\n");
xml.append("</response>\n");
}
private void copy(InputStream in, OutputStream out, Method method) throws IOException {
byte[] buf = new byte[1024];
int read;
while((read = in.read(buf)) != -1){
out.write(buf, 0, read);
if((timeoutIn == TimeoutIn.GetMiddle && method == Method.Get)
|| (timeoutIn == TimeoutIn.PutMiddle && method == Method.Put))
timeout();
}
out.flush();
}
private void timeout() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void log(String string) {
System.err.println(string);
}
}