Skip to content

Instantly share code, notes, and snippets.

@darkwave
Last active August 29, 2015 14:05
Show Gist options
  • Save darkwave/ddec4c1bef9013521621 to your computer and use it in GitHub Desktop.
Save darkwave/ddec4c1bef9013521621 to your computer and use it in GitHub Desktop.
Scratch Helper using Processing prototype
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.util.Locale;
import org.apache.http.ConnectionClosedException;
import org.apache.http.HttpConnectionFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpServerConnection;
import org.apache.http.HttpStatus;
import org.apache.http.MethodNotSupportedException;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultBHttpServerConnection;
import org.apache.http.impl.DefaultBHttpServerConnectionFactory;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpProcessorBuilder;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.apache.http.protocol.UriHttpRequestHandlerMapper;
import org.apache.http.util.EntityUtils;
import processing.core.*;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
/**
* Basic, yet fully functional and spec compliant, HTTP/1.1 file server.
*/
import java.lang.reflect.*;
public class ElementalHttpServer {
static PApplet parent;
static Method displayMethod;
ElementalHttpServer(PApplet _parent) {
parent = _parent;
try {
displayMethod = parent.getClass().getMethod("onScratch",
new Class[] {
float.class, float.class, float.class
}
);
}
catch (Exception e) {
// no such method, or an error.. which is fine, just ignore
System.out.println("please implement " + e.getMessage());
}
}
public static void main(String... args) throws Exception {
if (args.length < 1) {
System.err.println("Please specify document root directory");
System.exit(1);
}
// Document root directory
String docRoot = args[0];
int port = 8080;
if (args.length >= 2) {
port = Integer.parseInt(args[1]);
}
// Set up the HTTP protocol processor
HttpProcessor httpproc = HttpProcessorBuilder.create()
.add(new ResponseDate())
.add(new ResponseServer("Test/1.1"))
.add(new ResponseContent())
.add(new ResponseConnControl()).build();
// Set up request handlers
UriHttpRequestHandlerMapper reqistry = new UriHttpRequestHandlerMapper();
reqistry.register("*", new HttpFileHandler(docRoot));
// Set up the HTTP service
HttpService httpService = new HttpService(httpproc, reqistry);
SSLServerSocketFactory sf = null;
if (port == 8443) {
// Initialize SSL context
ClassLoader cl = ElementalHttpServer.class.getClassLoader();
URL url = cl.getResource("my.keystore");
if (url == null) {
System.out.println("Keystore not found");
System.exit(1);
}
KeyStore keystore = KeyStore.getInstance("jks");
keystore.load(url.openStream(), "secret".toCharArray());
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(keystore, "secret".toCharArray());
KeyManager[] keymanagers = kmfactory.getKeyManagers();
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(keymanagers, null, null);
sf = sslcontext.getServerSocketFactory();
}
Thread t = new RequestListenerThread(port, httpService, sf);
t.setDaemon(false);
t.start();
}
static class HttpFileHandler implements HttpRequestHandler {
private final String docRoot;
public HttpFileHandler(final String docRoot) {
super();
this.docRoot = docRoot;
}
float x, y, dir;
public void handle(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) {
throw new MethodNotSupportedException(method + " method not supported");
}
String target = request.getRequestLine().getUri();
if (target.indexOf("setX/")>= 0)
x = Float.parseFloat(target.substring(target.lastIndexOf('/') + 1));
if (target.indexOf("setY/")>= 0)
y = Float.parseFloat(target.substring(target.lastIndexOf('/') + 1));
if (target.indexOf("setDirection/")>= 0)
dir = Float.parseFloat(target.substring(target.lastIndexOf('/') + 1));
try {
displayMethod.invoke(parent, dir, x, y);
}
catch (Exception e) {
System.out.println("Disabling displayMethod() because of an error.");
// e.printStackTrace();
displayMethod = null;
}
/*
if (request instanceof HttpEntityEnclosingRequest) {
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
byte[] entityContent = EntityUtils.toByteArray(entity);
System.out.println("Incoming entity content (bytes): " + entityContent.length);
}
final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8"));
if (!file.exists()) {
response.setStatusCode(HttpStatus.SC_OK);
StringEntity entity = new StringEntity(
"volume " + parent.mouseX,
ContentType.create("text/html", "UTF-8"));
response.setEntity(entity);
System.out.println("File " + file.getPath() + " not found!");
} else if (!file.canRead() || file.isDirectory()) {
response.setStatusCode(HttpStatus.SC_FORBIDDEN);
StringEntity entity = new StringEntity(
"volume " + parent.mouseX,
ContentType.create("text/html", "UTF-8"));
response.setEntity(entity);
//System.out.println("Cannot read file " + file.getPath());
} else {
response.setStatusCode(HttpStatus.SC_OK);
FileEntity body = new FileEntity(file, ContentType.create("text/html", (Charset) null));
response.setEntity(body);
System.out.println("Serving file " + file.getPath());
}
*/
}
}
static class RequestListenerThread extends Thread {
private final HttpConnectionFactory<DefaultBHttpServerConnection> connFactory;
private final ServerSocket serversocket;
private final HttpService httpService;
public RequestListenerThread(
final int port,
final HttpService httpService,
final SSLServerSocketFactory sf) throws IOException {
this.connFactory = DefaultBHttpServerConnectionFactory.INSTANCE;
this.serversocket = sf != null ? sf.createServerSocket(port) : new ServerSocket(port);
this.httpService = httpService;
}
@Override
public void run() {
System.out.println("Listening on port " + this.serversocket.getLocalPort());
while (!Thread.interrupted ()) {
try {
// Set up HTTP connection
Socket socket = this.serversocket.accept();
System.out.println("Incoming connection from " + socket.getInetAddress());
HttpServerConnection conn = this.connFactory.createConnection(socket);
// Start worker thread
Thread t = new WorkerThread(this.httpService, conn);
t.setDaemon(true);
t.start();
}
catch (InterruptedIOException ex) {
break;
}
catch (IOException e) {
System.err.println("I/O error initialising connection thread: "
+ e.getMessage());
break;
}
}
}
}
static class WorkerThread extends Thread {
private final HttpService httpservice;
private final HttpServerConnection conn;
public WorkerThread(
final HttpService httpservice,
final HttpServerConnection conn) {
super();
this.httpservice = httpservice;
this.conn = conn;
}
@Override
public void run() {
System.out.println("New connection thread");
HttpContext context = new BasicHttpContext(null);
try {
while (!Thread.interrupted () && this.conn.isOpen()) {
this.httpservice.handleRequest(this.conn, context);
}
}
catch (ConnectionClosedException ex) {
System.err.println("Client closed connection");
}
catch (IOException ex) {
System.err.println("I/O error: " + ex.getMessage());
}
catch (HttpException ex) {
System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage());
}
finally {
try {
this.conn.shutdown();
}
catch (IOException ignore) {
}
}
}
}
}
{ "extensionName": "Scratch3D",
"extensionPort": 12345,
"blockSpecs": [
[" ", "change background", "randomBackground"],
[" ", "set direction to %n", "setDirection", 5],
[" ", "set x to %n", "setX", 5],
[" ", "set y to %n", "setY", 5],
["r", "mouseX", "mouseX"],
["r", "mouseY", "mouseY"],
]
}
import remixlab.proscene.*;
Scene scene;
ElementalHttpServer server;
void setup() {
size(640, 480, P3D);
noStroke();
scene = new Scene(this);
server = new ElementalHttpServer(this);
try {
server.main(sketchPath, "12345");
}
catch (Exception ex) {
println(ex);
}
}
public float x, y, direction;
void draw() {
background(0);
lights();
pushMatrix();
translate(x, y);
rotateZ(radians(direction));
box(30);
popMatrix();
}
void onScratch(float scratchDirection, float scratchX, float scratchY) {
if (abs(scratchX - x) > 1)
x += (scratchX - x) * 0.1;
if (abs(scratchY - y) > 1)
y += (-scratchY -y) * 0.1;
direction = scratchDirection;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment