mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-11-04 17:16:20 +00:00
Allow multiple HTTP request methods
This implements an argument format similar to LuaReqeust, as described in dan200/ComputerCraft#515. The Lua argument checking code is a little verbose and repetitive, but I'm not sure how to avoid that - we should look into improving it in the future. Closes #21
This commit is contained in:
parent
e4164ee9a1
commit
5bf9f9e3c5
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
package dan200.computercraft.core.apis;
|
package dan200.computercraft.core.apis;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
import dan200.computercraft.api.lua.ILuaContext;
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
@ -21,9 +22,14 @@ import java.util.*;
|
|||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
||||||
|
import static dan200.computercraft.core.apis.TableHelper.*;
|
||||||
|
|
||||||
public class HTTPAPI implements ILuaAPI
|
public class HTTPAPI implements ILuaAPI
|
||||||
{
|
{
|
||||||
|
private static final Set<String> HTTP_METHODS = ImmutableSet.of(
|
||||||
|
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE"
|
||||||
|
);
|
||||||
|
|
||||||
private final IAPIEnvironment m_apiEnvironment;
|
private final IAPIEnvironment m_apiEnvironment;
|
||||||
private final List<Future<?>> m_httpTasks;
|
private final List<Future<?>> m_httpTasks;
|
||||||
private final Set<Closeable> m_closeables;
|
private final Set<Closeable> m_closeables;
|
||||||
@ -101,24 +107,41 @@ public class HTTPAPI implements ILuaAPI
|
|||||||
{
|
{
|
||||||
switch( method )
|
switch( method )
|
||||||
{
|
{
|
||||||
case 0:
|
case 0: // request
|
||||||
{
|
{
|
||||||
// request
|
String urlString, postString, requestMethod;
|
||||||
// Get URL
|
Map<Object, Object> headerTable;
|
||||||
String urlString = getString( args, 0 );
|
boolean binary, redirect;
|
||||||
|
|
||||||
// Get POST
|
if( args.length >= 1 && args[0] instanceof Map )
|
||||||
String postString = optString( args, 1, null );
|
{
|
||||||
|
Map<?, ?> options = (Map) args[0];
|
||||||
|
urlString = getStringField( options, "url" );
|
||||||
|
postString = optStringField( options, "body", null );
|
||||||
|
headerTable = optTableField( options, "headers", null );
|
||||||
|
binary = optBooleanField( options, "binary", false );
|
||||||
|
requestMethod = optStringField( options, "method", null );
|
||||||
|
redirect = optBooleanField( options, "redirect", true );
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Get URL and post information
|
||||||
|
urlString = getString( args, 0 );
|
||||||
|
postString = optString( args, 1, null );
|
||||||
|
headerTable = optTable( args, 2, null );
|
||||||
|
binary = optBoolean( args, 3, false );
|
||||||
|
requestMethod = null;
|
||||||
|
redirect = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Get Headers
|
|
||||||
Map<String, String> headers = null;
|
Map<String, String> headers = null;
|
||||||
Map<Object, Object> table = optTable( args, 2, null );
|
if( headerTable != null )
|
||||||
if( table != null )
|
|
||||||
{
|
{
|
||||||
headers = new HashMap<>( table.size() );
|
headers = new HashMap<>( headerTable.size() );
|
||||||
for( Object key : table.keySet() )
|
for( Object key : headerTable.keySet() )
|
||||||
{
|
{
|
||||||
Object value = table.get( key );
|
Object value = headerTable.get( key );
|
||||||
if( key instanceof String && value instanceof String )
|
if( key instanceof String && value instanceof String )
|
||||||
{
|
{
|
||||||
headers.put( (String) key, (String) value );
|
headers.put( (String) key, (String) value );
|
||||||
@ -126,18 +149,17 @@ public class HTTPAPI implements ILuaAPI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get binary
|
|
||||||
boolean binary = false;
|
if( requestMethod != null && !HTTP_METHODS.contains( requestMethod ) )
|
||||||
if( args.length >= 4 )
|
|
||||||
{
|
{
|
||||||
binary = args[ 3 ] != null && !args[ 3 ].equals( Boolean.FALSE );
|
throw new LuaException( "Unsupported HTTP method" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the request
|
// Make the request
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
URL url = HTTPRequest.checkURL( urlString );
|
URL url = HTTPRequest.checkURL( urlString );
|
||||||
HTTPRequest request = new HTTPRequest( m_apiEnvironment, urlString, url, postString, headers, binary );
|
HTTPRequest request = new HTTPRequest( m_apiEnvironment, urlString, url, postString, headers, binary, requestMethod, redirect );
|
||||||
synchronized( m_httpTasks )
|
synchronized( m_httpTasks )
|
||||||
{
|
{
|
||||||
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( request ) );
|
m_httpTasks.add( HTTPExecutor.EXECUTOR.submit( request ) );
|
||||||
|
214
src/main/java/dan200/computercraft/core/apis/TableHelper.java
Normal file
214
src/main/java/dan200/computercraft/core/apis/TableHelper.java
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package dan200.computercraft.core.apis;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Various helpers for tables
|
||||||
|
*/
|
||||||
|
public final class TableHelper
|
||||||
|
{
|
||||||
|
private TableHelper()
|
||||||
|
{
|
||||||
|
throw new IllegalStateException( "Cannot instantiate singleton " + getClass().getName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static LuaException badKey( @Nonnull String key, @Nonnull String expected, @Nullable Object actual )
|
||||||
|
{
|
||||||
|
return badKey( key, expected, ArgumentHelper.getType( actual ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static LuaException badKey( @Nonnull String key, @Nonnull String expected, @Nonnull String actual )
|
||||||
|
{
|
||||||
|
return new LuaException( "bad field '" + key + "' (" + expected + " expected, got " + actual + ")" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getNumberField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value instanceof Number )
|
||||||
|
{
|
||||||
|
return ((Number) value).doubleValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "number", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getIntField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value instanceof Number )
|
||||||
|
{
|
||||||
|
return (int) ((Number) value).longValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "number", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getRealField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||||
|
{
|
||||||
|
return checkReal( key, getNumberField( table, key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean getBooleanField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value instanceof Boolean )
|
||||||
|
{
|
||||||
|
return (Boolean) value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "boolean", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static String getStringField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value instanceof String )
|
||||||
|
{
|
||||||
|
return (String) value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "string", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
@Nonnull
|
||||||
|
public static Map<Object, Object> getTableField( @Nonnull Map<?, ?> table, @Nonnull String key ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value instanceof Map )
|
||||||
|
{
|
||||||
|
return (Map<Object, Object>) value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "table", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double optNumberField( @Nonnull Map<?, ?> table, @Nonnull String key, double def ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value == null )
|
||||||
|
{
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
else if( value instanceof Number )
|
||||||
|
{
|
||||||
|
return ((Number) value).doubleValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "number", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int optIntField( @Nonnull Map<?, ?> table, @Nonnull String key, int def ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value == null )
|
||||||
|
{
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
else if( value instanceof Number )
|
||||||
|
{
|
||||||
|
return (int) ((Number) value).longValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "number", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double optRealField( @Nonnull Map<?, ?> table, @Nonnull String key, double def ) throws LuaException
|
||||||
|
{
|
||||||
|
return checkReal( key, optNumberField( table, key, def ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean optBooleanField( @Nonnull Map<?, ?> table, @Nonnull String key, boolean def ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value == null )
|
||||||
|
{
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
else if( value instanceof Boolean )
|
||||||
|
{
|
||||||
|
return (Boolean) value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "boolean", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String optStringField( @Nonnull Map<?, ?> table, @Nonnull String key, String def ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value == null )
|
||||||
|
{
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
else if( value instanceof String )
|
||||||
|
{
|
||||||
|
return (String) value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "string", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public static Map<Object, Object> optTableField( @Nonnull Map<?, ?> table, @Nonnull String key, Map<Object, Object> def ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( key );
|
||||||
|
if( value == null )
|
||||||
|
{
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
else if( value instanceof Map )
|
||||||
|
{
|
||||||
|
return (Map<Object, Object>) value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw badKey( key, "table", value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double checkReal( @Nonnull String key, double value ) throws LuaException
|
||||||
|
{
|
||||||
|
if( Double.isNaN( value ) )
|
||||||
|
{
|
||||||
|
throw badKey( key, "number", "nan" );
|
||||||
|
}
|
||||||
|
else if( value == Double.POSITIVE_INFINITY )
|
||||||
|
{
|
||||||
|
throw badKey( key, "number", "inf" );
|
||||||
|
}
|
||||||
|
else if( value == Double.NEGATIVE_INFINITY )
|
||||||
|
{
|
||||||
|
throw badKey( key, "number", "-inf" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -79,8 +79,10 @@ public class HTTPRequest implements Runnable
|
|||||||
private final String m_postText;
|
private final String m_postText;
|
||||||
private final Map<String, String> m_headers;
|
private final Map<String, String> m_headers;
|
||||||
private boolean m_binary;
|
private boolean m_binary;
|
||||||
|
private final String m_method;
|
||||||
|
private final boolean m_followRedirects;
|
||||||
|
|
||||||
public HTTPRequest( IAPIEnvironment environment, 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, final String method, final boolean followRedirects ) throws HTTPRequestException
|
||||||
{
|
{
|
||||||
m_environment = environment;
|
m_environment = environment;
|
||||||
m_urlString = urlString;
|
m_urlString = urlString;
|
||||||
@ -88,6 +90,8 @@ public class HTTPRequest implements Runnable
|
|||||||
m_binary = binary;
|
m_binary = binary;
|
||||||
m_postText = postText;
|
m_postText = postText;
|
||||||
m_headers = headers;
|
m_headers = headers;
|
||||||
|
m_method = method;
|
||||||
|
m_followRedirects = followRedirects;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -120,6 +124,8 @@ public class HTTPRequest implements Runnable
|
|||||||
{
|
{
|
||||||
connection.setRequestMethod( "GET" );
|
connection.setRequestMethod( "GET" );
|
||||||
}
|
}
|
||||||
|
if( m_method != null ) connection.setRequestMethod( m_method );
|
||||||
|
connection.setInstanceFollowRedirects( m_followRedirects );
|
||||||
|
|
||||||
// Set headers
|
// Set headers
|
||||||
connection.setRequestProperty( "accept-charset", "UTF-8" );
|
connection.setRequestProperty( "accept-charset", "UTF-8" );
|
||||||
|
@ -662,8 +662,36 @@ end
|
|||||||
if http then
|
if http then
|
||||||
local nativeHTTPRequest = http.request
|
local nativeHTTPRequest = http.request
|
||||||
|
|
||||||
local function wrapRequest( _url, _post, _headers, _binary )
|
local methods = {
|
||||||
local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary )
|
GET = true, POST = true, HEAD = true,
|
||||||
|
OPTIONS = true, PUT = true, DELETE = true
|
||||||
|
}
|
||||||
|
|
||||||
|
local function checkKey( options, key, ty, opt )
|
||||||
|
local value = options[key]
|
||||||
|
local valueTy = type(value)
|
||||||
|
|
||||||
|
if (value ~= nil or not opt) and valueTy ~= ty then
|
||||||
|
error(("bad field '%s' (expected %s, got %s"):format(key, ty, valueTy), 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkOptions( options, body )
|
||||||
|
checkKey( options, "url", "string")
|
||||||
|
if body == false
|
||||||
|
then checkKey( options, "body", "nil" )
|
||||||
|
else checkKey( options, "body", "string", not body ) end
|
||||||
|
checkKey( options, "headers", "table", true )
|
||||||
|
checkKey( options, "method", "string", true )
|
||||||
|
checkKey( options, "redirect", "boolean", true )
|
||||||
|
|
||||||
|
if options.method and not methods[options.method] then
|
||||||
|
error( "Unsupported HTTP method", 3 )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function wrapRequest( _url, ... )
|
||||||
|
local ok, err = nativeHTTPRequest( ... )
|
||||||
if ok then
|
if ok then
|
||||||
while true do
|
while true do
|
||||||
local event, param1, param2, param3 = os.pullEvent()
|
local event, param1, param2, param3 = os.pullEvent()
|
||||||
@ -678,6 +706,11 @@ if http then
|
|||||||
end
|
end
|
||||||
|
|
||||||
http.get = function( _url, _headers, _binary)
|
http.get = function( _url, _headers, _binary)
|
||||||
|
if type( _url ) == "table" then
|
||||||
|
checkOptions( _url, false )
|
||||||
|
return wrapRequest( _url.url, _url )
|
||||||
|
end
|
||||||
|
|
||||||
if type( _url ) ~= "string" then
|
if type( _url ) ~= "string" then
|
||||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
||||||
end
|
end
|
||||||
@ -687,10 +720,15 @@ if http then
|
|||||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
||||||
error( "bad argument #3 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
error( "bad argument #3 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
||||||
end
|
end
|
||||||
return wrapRequest( _url, nil, _headers, _binary)
|
return wrapRequest( _url, _url, nil, _headers, _binary )
|
||||||
end
|
end
|
||||||
|
|
||||||
http.post = function( _url, _post, _headers, _binary)
|
http.post = function( _url, _post, _headers, _binary)
|
||||||
|
if type( _url ) == "table" then
|
||||||
|
checkOptions( _url, true )
|
||||||
|
return wrapRequest( _url.url, _url )
|
||||||
|
end
|
||||||
|
|
||||||
if type( _url ) ~= "string" then
|
if type( _url ) ~= "string" then
|
||||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
||||||
end
|
end
|
||||||
@ -703,10 +741,15 @@ if http then
|
|||||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
||||||
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
||||||
end
|
end
|
||||||
return wrapRequest( _url, _post or "", _headers, _binary)
|
return wrapRequest( _url, _url, _post, _headers, _binary )
|
||||||
end
|
end
|
||||||
|
|
||||||
http.request = function( _url, _post, _headers, _binary )
|
http.request = function( _url, _post, _headers, _binary )
|
||||||
|
local url
|
||||||
|
if type( _url ) == "table" then
|
||||||
|
checkOptions( _url )
|
||||||
|
url = _url.url
|
||||||
|
else
|
||||||
if type( _url ) ~= "string" then
|
if type( _url ) ~= "string" then
|
||||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
||||||
end
|
end
|
||||||
@ -719,9 +762,13 @@ if http then
|
|||||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
||||||
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
url = _url.url
|
||||||
|
end
|
||||||
|
|
||||||
local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary )
|
local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary )
|
||||||
if not ok then
|
if not ok then
|
||||||
os.queueEvent( "http_failure", _url, err )
|
os.queueEvent( "http_failure", url, err )
|
||||||
end
|
end
|
||||||
return ok, err
|
return ok, err
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user