1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-13 03:30:29 +00:00

A couple of minor changes to HTTP limiting

- We now error if there are too many websockets, instead of queuing
   them up. As these have a more explicit "lifetime", it could be
   confusing if http.websocket just blocks indefinitely.
 - Fix a CCME when cleaning up resources.
This commit is contained in:
SquidDev 2019-01-11 12:07:56 +00:00
parent 932f8a44fc
commit 8dd084ac5c
8 changed files with 109 additions and 64 deletions

View File

@ -10,10 +10,7 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.http.CheckUrl;
import dan200.computercraft.core.apis.http.HTTPRequestException;
import dan200.computercraft.core.apis.http.Resource;
import dan200.computercraft.core.apis.http.ResourceQueue;
import dan200.computercraft.core.apis.http.*;
import dan200.computercraft.core.apis.http.request.HttpRequest;
import dan200.computercraft.core.apis.http.websocket.Websocket;
import io.netty.handler.codec.http.DefaultHttpHeaders;
@ -33,9 +30,9 @@ public class HTTPAPI implements ILuaAPI
{
private final IAPIEnvironment m_apiEnvironment;
private final ResourceQueue<CheckUrl> checkUrls = new ResourceQueue<>();
private final ResourceQueue<HttpRequest> requests = new ResourceQueue<>( () -> ComputerCraft.httpMaxRequests );
private final ResourceQueue<Websocket> websockets = new ResourceQueue<>( () -> ComputerCraft.httpMaxWebsockets );
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>();
private final ResourceGroup<HttpRequest> requests = new ResourceQueue<>( () -> ComputerCraft.httpMaxRequests );
private final ResourceGroup<Websocket> websockets = new ResourceGroup<>( () -> ComputerCraft.httpMaxWebsockets );
public HTTPAPI( IAPIEnvironment environment )
{
@ -189,7 +186,10 @@ public class HTTPAPI implements ILuaAPI
try
{
URI uri = Websocket.checkUri( address );
new Websocket( websockets, m_apiEnvironment, uri, address, headers ).queue( Websocket::connect );
if( !new Websocket( websockets, m_apiEnvironment, uri, address, headers ).queue( Websocket::connect ) )
{
throw new LuaException( "Too many websockets already open" );
}
return new Object[] { true };
}

View File

@ -26,7 +26,7 @@ public class CheckUrl extends Resource<CheckUrl>
private final String address;
private final String host;
public CheckUrl( ResourceQueue<CheckUrl> limiter, IAPIEnvironment environment, String address, URI uri )
public CheckUrl( ResourceGroup<CheckUrl> limiter, IAPIEnvironment environment, String address, URI uri )
{
super( limiter );
this.environment = environment;

View File

@ -24,9 +24,9 @@ import java.util.function.Consumer;
public abstract class Resource<T extends Resource<T>> implements Closeable
{
private final AtomicBoolean closed = new AtomicBoolean( false );
private final ResourceQueue<T> limiter;
private final ResourceGroup<T> limiter;
protected Resource( ResourceQueue<T> limiter )
protected Resource( ResourceGroup<T> limiter )
{
this.limiter = limiter;
}
@ -93,10 +93,10 @@ public abstract class Resource<T extends Resource<T>> implements Closeable
tryClose();
}
public void queue( Consumer<T> task )
public boolean queue( Consumer<T> task )
{
@SuppressWarnings( "unchecked" ) T thisT = (T) this;
limiter.queue( thisT, () -> task.accept( thisT ) );
return limiter.queue( thisT, () -> task.accept( thisT ) );
}
protected static <T extends Closeable> T closeCloseable( T closeable )

View File

@ -0,0 +1,81 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
/**
* A collection of {@link Resource}s, with an upper bound on capacity.
*/
public class ResourceGroup<T extends Resource<T>>
{
private static final IntSupplier ZERO = () -> 0;
final IntSupplier limit;
boolean active = false;
final Set<T> resources = Collections.newSetFromMap( new ConcurrentHashMap<>() );
public ResourceGroup( IntSupplier limit )
{
this.limit = limit;
}
public ResourceGroup()
{
this.limit = ZERO;
}
public void startup()
{
active = true;
}
public synchronized void shutdown()
{
active = false;
for( T resource : resources ) resource.close();
resources.clear();
Resource.cleanup();
}
public final boolean queue( T resource, Runnable setup )
{
return queue( () -> {
setup.run();
return resource;
} );
}
public synchronized boolean queue( Supplier<T> resource )
{
Resource.cleanup();
if( !active ) return false;
int limit = this.limit.getAsInt();
if( limit <= 0 || resources.size() < limit )
{
resources.add( resource.get() );
return true;
}
return false;
}
public synchronized void release( T resource )
{
resources.remove( resource );
}
}

View File

@ -7,80 +7,44 @@
package dan200.computercraft.core.apis.http;
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Set;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
/**
* A queue for {@link Resource}s, with built-in rate-limiting.
* A {@link ResourceGroup} which will queue items when the group at capacity.
*/
public class ResourceQueue<T extends Resource<T>>
public class ResourceQueue<T extends Resource<T>> extends ResourceGroup<T>
{
private static final IntSupplier ZERO = () -> 0;
private final IntSupplier limit;
private boolean active = false;
private final Set<T> resources = new HashSet<>();
private final ArrayDeque<Supplier<T>> pending = new ArrayDeque<>();
public ResourceQueue( IntSupplier limit )
{
this.limit = limit;
super( limit );
}
public ResourceQueue()
{
this.limit = ZERO;
}
public void startup()
{
active = true;
}
public synchronized void shutdown()
{
active = false;
super.shutdown();
pending.clear();
for( T resource : resources ) resource.close();
resources.clear();
Resource.cleanup();
}
public void queue( T resource, Runnable setup )
public synchronized boolean queue( Supplier<T> resource )
{
queue( () -> {
setup.run();
return resource;
} );
}
if( !active ) return false;
public synchronized void queue( Supplier<T> resource )
{
Resource.cleanup();
if( !active ) return;
int limit = this.limit.getAsInt();
if( limit <= 0 || resources.size() < limit )
{
resources.add( resource.get() );
}
else
{
pending.add( resource );
}
if( !super.queue( resource ) ) pending.add( resource );
return true;
}
public synchronized void release( T resource )
{
if( !active ) return;
super.release( resource );
resources.remove( resource );
if( !active ) return;
int limit = this.limit.getAsInt();
if( limit <= 0 || resources.size() < limit )

View File

@ -12,7 +12,7 @@ import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.http.HTTPRequestException;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.Resource;
import dan200.computercraft.core.apis.http.ResourceQueue;
import dan200.computercraft.core.apis.http.ResourceGroup;
import dan200.computercraft.core.tracking.TrackingField;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
@ -64,7 +64,7 @@ public class HttpRequest extends Resource<HttpRequest>
final AtomicInteger redirects;
public HttpRequest( ResourceQueue<HttpRequest> limiter, IAPIEnvironment environment, String address, String postText, HttpHeaders headers, boolean binary, boolean followRedirects )
public HttpRequest( ResourceGroup<HttpRequest> limiter, IAPIEnvironment environment, String address, String postText, HttpHeaders headers, boolean binary, boolean followRedirects )
{
super( limiter );
this.environment = environment;

View File

@ -27,7 +27,7 @@ public class HttpResponseHandle implements ILuaObject
private final String responseStatus;
private final Map<String, String> responseHeaders;
public HttpResponseHandle(@Nonnull ILuaObject reader, int responseCode, String responseStatus, @Nonnull Map<String, String> responseHeaders)
public HttpResponseHandle( @Nonnull ILuaObject reader, int responseCode, String responseStatus, @Nonnull Map<String, String> responseHeaders )
{
this.reader = reader;
this.responseCode = responseCode;

View File

@ -12,7 +12,7 @@ import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.http.HTTPRequestException;
import dan200.computercraft.core.apis.http.NetworkUtils;
import dan200.computercraft.core.apis.http.Resource;
import dan200.computercraft.core.apis.http.ResourceQueue;
import dan200.computercraft.core.apis.http.ResourceGroup;
import dan200.computercraft.shared.util.IoUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
@ -57,7 +57,7 @@ public class Websocket extends Resource<Websocket>
private final String address;
private final HttpHeaders headers;
public Websocket( ResourceQueue<Websocket> limiter, IAPIEnvironment environment, URI uri, String address, HttpHeaders headers )
public Websocket( ResourceGroup<Websocket> limiter, IAPIEnvironment environment, URI uri, String address, HttpHeaders headers )
{
super( limiter );
this.environment = environment;