1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-29 00:23:23 +00:00

Remove HTTPTask, queueing the event when it has finished executing

This means we don't have to have lots of shared state between the run
and whenFinished method, and allows for easier chaining of futures later
on.
This commit is contained in:
SquidDev 2017-07-29 19:06:36 +01:00
parent 1fdfcdb5f2
commit 4bd5b0d236
5 changed files with 86 additions and 161 deletions

View File

@ -10,18 +10,19 @@
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.http.HTTPCheck;
import dan200.computercraft.core.apis.http.HTTPRequest;
import dan200.computercraft.core.apis.http.HTTPTask;
import dan200.computercraft.core.apis.http.HTTPExecutor;
import javax.annotation.Nonnull;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Future;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class HTTPAPI implements ILuaAPI
{
private final IAPIEnvironment m_apiEnvironment;
private final List<HTTPTask> m_httpTasks;
private final List<Future<?>> m_httpTasks;
public HTTPAPI( IAPIEnvironment environment )
{
@ -48,15 +49,11 @@ public void advance( double _dt )
// Wait for all of our http requests
synchronized( m_httpTasks )
{
Iterator<HTTPTask> it = m_httpTasks.iterator();
Iterator<Future<?>> it = m_httpTasks.iterator();
while( it.hasNext() )
{
final HTTPTask h = it.next();
if( h.isFinished() )
{
h.whenFinished( m_apiEnvironment );
it.remove();
}
final Future<?> h = it.next();
if( h.isDone() ) it.remove();
}
}
}
@ -66,9 +63,9 @@ public void shutdown( )
{
synchronized( m_httpTasks )
{
for( HTTPTask r : m_httpTasks )
for( Future<?> r : m_httpTasks )
{
r.cancel();
r.cancel( false );
}
m_httpTasks.clear();
}
@ -125,10 +122,10 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
try
{
URL url = HTTPRequest.checkURL( urlString );
HTTPRequest request = new HTTPRequest( urlString, url, postString, headers, binary );
HTTPRequest request = new HTTPRequest( m_apiEnvironment, urlString, url, postString, headers, binary );
synchronized( m_httpTasks )
{
m_httpTasks.add( HTTPTask.submit( request ) );
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( request ) );
}
return new Object[] { true };
}
@ -147,9 +144,10 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
try
{
URL url = HTTPRequest.checkURL( urlString );
HTTPCheck check = new HTTPCheck( urlString, url );
synchronized( m_httpTasks ) {
m_httpTasks.add( HTTPTask.submit( check ) );
HTTPCheck check = new HTTPCheck( m_apiEnvironment, urlString, url );
synchronized( m_httpTasks )
{
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( check ) );
}
return new Object[] { true };
}

View File

@ -5,14 +5,15 @@
import java.net.URL;
public class HTTPCheck implements HTTPTask.IHTTPTask
public class HTTPCheck implements Runnable
{
private final IAPIEnvironment environment;
private final String urlString;
private final URL url;
private String error;
public HTTPCheck( String urlString, URL url )
public HTTPCheck( IAPIEnvironment environment, String urlString, URL url )
{
this.environment = environment;
this.urlString = urlString;
this.url = url;
}
@ -22,24 +23,12 @@ public void run()
{
try
{
HTTPRequest.checkHost( url );
HTTPRequest.checkHost( url.getHost() );
environment.queueEvent( "http_check", new Object[] { urlString, true } );
}
catch( HTTPRequestException e )
{
error = e.getMessage();
}
}
@Override
public void whenFinished( IAPIEnvironment environment )
{
if( error == null )
{
environment.queueEvent( "http_check", new Object[] { urlString, true } );
}
else
{
environment.queueEvent( "http_check", new Object[] { urlString, false, error } );
environment.queueEvent( "http_check", new Object[] { urlString, false, e.getMessage() } );
}
}
}

View File

@ -0,0 +1,36 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Just a shared object for executing simple HTTP related tasks.
*/
public final class HTTPExecutor
{
public static final ListeningExecutorService EXECUTOR = MoreExecutors.listeningDecorator( new ThreadPoolExecutor(
4, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setDaemon( true )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.setNameFormat( "ComputerCraft-HTTP-%d" )
.build()
) );
private HTTPExecutor()
{
}
}

View File

@ -25,7 +25,7 @@
import java.util.List;
import java.util.Map;
public class HTTPRequest implements HTTPTask.IHTTPTask
public class HTTPRequest implements Runnable
{
public static URL checkURL( String urlString ) throws HTTPRequestException
{
@ -55,11 +55,11 @@ public static URL checkURL( String urlString ) throws HTTPRequestException
return url;
}
public static InetAddress checkHost( URL url ) throws HTTPRequestException
public static InetAddress checkHost( String host ) throws HTTPRequestException
{
try
{
InetAddress resolved = InetAddress.getByName( url.getHost() );
InetAddress resolved = InetAddress.getByName( host );
if( !ComputerCraft.http_whitelist.matches( resolved ) || ComputerCraft.http_blacklist.matches( resolved ) )
{
throw new HTTPRequestException( "Domain not permitted" );
@ -73,22 +73,16 @@ public static InetAddress checkHost( URL url ) throws HTTPRequestException
}
}
private final IAPIEnvironment m_environment;
private final URL m_url;
private final String m_urlString;
private final String m_postText;
private final Map<String, String> m_headers;
private boolean m_success = false;
private String m_encoding;
private byte[] m_result;
private boolean m_binary;
private int m_responseCode = -1;
private Map<String, String> m_responseHeaders;
private String m_errorMessage;
public HTTPRequest( String urlString, URL url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
public HTTPRequest( IAPIEnvironment environment, String urlString, URL url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
{
// Parse the URL
m_environment = environment;
m_urlString = urlString;
m_url = url;
m_binary = binary;
@ -96,28 +90,19 @@ public HTTPRequest( String urlString, URL url, final String postText, final Map<
m_headers = headers;
}
public InputStream getContents()
{
byte[] result = m_result;
if( result != null )
{
return new ByteArrayInputStream( result );
}
return null;
}
@Override
public void run()
{
// First verify the address is allowed.
try
{
checkHost( m_url );
checkHost( m_url.getHost() );
}
catch( HTTPRequestException e )
{
m_success = false;
m_errorMessage = e.getMessage();
// Queue the failure event if not.
String error = e.getMessage();
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, error == null ? "Could not connect" : error, null } );
return;
}
@ -186,58 +171,36 @@ public void run()
byte[] result = ByteStreams.toByteArray( is );
is.close();
// We completed
m_success = responseSuccess;
m_result = result;
m_responseCode = connection.getResponseCode();
m_encoding = connection.getContentEncoding();
// We've got some sort of response, so let's build a resulting object.
Joiner joiner = Joiner.on( ',' );
Map<String, String> headers = m_responseHeaders = new HashMap<String, String>();
Map<String, String> headers = new HashMap<String, String>();
for( Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet() )
{
headers.put( header.getKey(), joiner.join( header.getValue() ) );
}
InputStream contents = new ByteArrayInputStream( result );
ILuaObject stream = wrapStream(
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, connection.getContentEncoding() ),
connection.getResponseCode(), headers
);
connection.disconnect(); // disconnect
// Queue the appropriate event.
if( responseSuccess )
{
m_environment.queueEvent( "http_success", new Object[] { m_urlString, stream } );
}
else
{
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", stream } );
}
}
catch( IOException e )
{
// There was an error
m_success = false;
}
}
@Override
public void whenFinished( IAPIEnvironment environment )
{
final String url = m_urlString;
if( m_success )
{
// Queue the "http_success" event
InputStream contents = getContents();
Object result = wrapStream(
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, m_encoding ),
m_responseCode, m_responseHeaders
);
environment.queueEvent( "http_success", new Object[] { url, result } );
}
else
{
// Queue the "http_failure" event
String error = "Could not connect";
if( m_errorMessage != null ) error = m_errorMessage;
InputStream contents = getContents();
Object result = null;
if( contents != null )
{
result = wrapStream(
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, m_encoding ),
m_responseCode, m_responseHeaders
);
}
environment.queueEvent( "http_failure", new Object[] { url, error, result } );
m_environment.queueEvent( "http_failure", new Object[] { m_urlString, "Could not connect", null } );
}
}

View File

@ -1,61 +0,0 @@
package dan200.computercraft.core.apis.http;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dan200.computercraft.core.apis.IAPIEnvironment;
import java.util.concurrent.*;
/**
* A task which executes asynchronously on a new thread.
*
* This functions very similarly to a {@link Future}, but with an additional
* method which is called on the main thread when the task is completed.
*/
public class HTTPTask
{
public interface IHTTPTask extends Runnable
{
void whenFinished( IAPIEnvironment environment );
}
private static final ExecutorService httpThreads = new ThreadPoolExecutor(
4, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setDaemon( true )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.setNameFormat( "ComputerCraft-HTTP-%d" )
.build()
);
private final Future<?> future;
private final IHTTPTask task;
private HTTPTask( Future<?> future, IHTTPTask task )
{
this.future = future;
this.task = task;
}
public static HTTPTask submit( IHTTPTask task )
{
Future<?> future = httpThreads.submit( task );
return new HTTPTask( future, task );
}
public void cancel()
{
future.cancel( false );
}
public boolean isFinished()
{
return future.isDone();
}
public void whenFinished( IAPIEnvironment environment )
{
task.whenFinished( environment );
}
}