/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.debug.core.util;
import com.google.common.io.CharStreams;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.internal.model.DartProjectNature;
import com.google.dart.tools.core.utilities.net.NetUtils;
import com.google.dart.tools.debug.core.DartDebugCorePlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* A web server that serves up workspace resources.
*/
public class ResourceServer implements IResourceResolver {
private ServerSocket serverSocket;
private ExecutorService threadPool;
private Set<String> previousAgents = new HashSet<String>();
/**
* Create a ResourceServer; serve its resources from any free port.
*
* @throws IOException
*/
public ResourceServer() throws IOException {
this(0);
}
/**
* Create a ResourceServer; serve its resources from the given port.
*
* @throws IOException
*/
public ResourceServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
threadPool = Executors.newCachedThreadPool();
new Thread(new Runnable() {
@Override
public void run() {
startServer();
}
}, "Web Server Dispatch").start();
}
/**
* @return the user's IP address, if available (null otherwise)
*/
public String getLocalAddress() {
return NetUtils.getIpAddress();
}
public int getPort() {
return serverSocket.getLocalPort();
}
@Override
public String getUrlForFile(File file) {
IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(file.toURI());
if (files.length > 0) {
return getUrlForResource(files[0]);
} else {
return null;
}
}
@Override
public String getUrlForResource(IResource resource) {
return getUrlForFilePath(resource.getFullPath().toPortableString());
}
@Override
public String getUrlRegexForResource(IResource resource) {
IProject project = resource.getProject();
File projectDir = project.getLocation().toFile();
return projectDir.getName() + "/" + resource.getProjectRelativePath().toPortableString();
}
@Override
public IResource resolveUrl(String url) {
try {
URI uri = new URI(url);
String filePath = uri.getPath();
IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(filePath);
if (resource.exists()) {
return resource;
}
return null;
} catch (Throwable t) {
return null;
}
}
/**
* Close the resource server.
*/
public void shutdown() {
try {
serverSocket.close();
} catch (IOException exception) {
DartDebugCorePlugin.logError(exception);
}
}
protected String getAvailableAppsContent() throws IOException {
InputStream in = ResourceServer.class.getResourceAsStream("template.html");
String template = CharStreams.toString(new InputStreamReader(in));
List<IFile> files = getAllExecutableFiles();
// Sort by project name, then html file name
Collections.sort(files, new Comparator<IFile>() {
@Override
public int compare(IFile o1, IFile o2) {
String str1 = o1.getFullPath().toString();
String str2 = o2.getFullPath().toString();
return str1.compareToIgnoreCase(str2);
}
});
if (files.size() == 0) {
template = replaceTemplate(template, "count", "No");
template = replaceTemplate(template, "apps", "");
} else {
template = replaceTemplate(template, "count", Integer.toString(files.size()));
StringBuilder builder = new StringBuilder();
for (IFile file : files) {
String hrefStart = "<a href=\"" + getPathFor(file) + "\">";
builder.append("<div class=\"app\"><table><tr>");
builder.append("<td rowspan=2>" + hrefStart
+ "<img src=\"dart_16_16.gif\" width=16 height=16></a></td>");
builder.append("<td class=\"title\">" + hrefStart + webSafe(file.getFullPath().toString())
+ "</a></td</tr>");
builder.append("</table></div>");
}
template = replaceTemplate(template, "apps", builder.toString());
}
return template;
}
protected void loadingContentFrom(String hostAddress, String userAgent) {
if (!previousAgents.contains(userAgent)) {
previousAgents.add(userAgent);
DartCore.getConsole().println(
"Remote connection from " + hostAddress + " [" + userAgent + "]");
}
}
private List<IFile> getAllExecutableFiles() {
final List<IFile> files = new ArrayList<IFile>();
for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
if (DartProjectNature.hasDartNature(project)) {
try {
project.accept(new IResourceVisitor() {
@Override
public boolean visit(IResource resource) throws CoreException {
if (resource instanceof IFile) {
IFile file = (IFile) resource;
if (DartCore.isHtmlLikeFileName(file.getName())) {
files.add(file);
} else if ("crx".equals(file.getFileExtension())) {
files.add(file);
}
}
return true;
}
});
} catch (CoreException e) {
}
}
}
return files;
}
private String getPathFor(IFile file) throws IOException {
String url = getUrlForResource(file);
return URI.create(url).getPath();
}
private String getUrlForFilePath(String path) {
try {
URI uri = new URI(
"http",
null,
NetUtils.getLoopbackAddress(),
serverSocket.getLocalPort(),
path,
null,
null);
return uri.toString();
} catch (URISyntaxException e) {
DartDebugCorePlugin.logError(e);
return null;
}
}
private String replaceTemplate(String template, String target, String replace) {
target = "${" + target + "}";
int index = template.indexOf(target);
String template1 = template.substring(0, index);
String template2 = template.substring(index + target.length());
return template1 + replace + template2;
}
private void startServer() {
try {
while (true) {
Socket socket = serverSocket.accept();
threadPool.execute(new ResourceServerHandler(this, socket));
}
} catch (IOException e) {
// The server socket was closed by the shutdown() call.
}
}
private String webSafe(String s) {
StringBuffer out = new StringBuffer();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c > 127 || c == '"' || c == '<' || c == '>') {
out.append("" + (int) c + ";");
} else {
out.append(c);
}
}
return out.toString();
}
}