Skip to content

Commit

Permalink
Merge branch 'development' of github.com:ortus-boxlang/BoxLang into d…
Browse files Browse the repository at this point in the history
…evelopment
  • Loading branch information
michaelborn committed Jan 21, 2025
2 parents 4b65eea + 7b72b48 commit da1b845
Show file tree
Hide file tree
Showing 27 changed files with 348 additions and 265 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ plugins {
id "application"
id 'antlr'
// For source code formatting
id "com.diffplug.spotless" version "7.0.1"
id "com.diffplug.spotless" version "7.0.2"
// For building shadow jars with jdk 17 ONLY
//id 'com.github.johnrengelman.shadow' version '8.1.1'
// For building shadow jars using JDK 21 +, they had to fork
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public List<AbstractInsnNode> transformEquals( BoxExpression left, List<Abstract
"getDefaultAssignmentScope",
Type.getMethodDescriptor( Type.getType( IScope.class ) ),
true ) );
nodes.add( new LdcInsnNode( true ) );
nodes.add( new LdcInsnNode( 1 ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEINTERFACE,
Type.getInternalName( IBoxContext.class ),
"scopeFindNearby",
Expand Down Expand Up @@ -373,7 +373,7 @@ private List<AbstractInsnNode> transformCompoundEquals( BoxAssignment assigment
"getDefaultAssignmentScope",
Type.getMethodDescriptor( Type.getType( IScope.class ) ),
true ) );
nodes.add( new LdcInsnNode( true ) );
nodes.add( new LdcInsnNode( 1 ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEINTERFACE,
Type.getInternalName( IBoxContext.class ),
"scopeFindNearby",
Expand Down Expand Up @@ -463,7 +463,7 @@ private List<AbstractInsnNode> assignNullValue( BoxIdentifier name ) {
"name",
Type.getDescriptor( Key.class ) ) );
nodes.add( new InsnNode( Opcodes.ACONST_NULL ) );
nodes.add( new LdcInsnNode( true ) );
nodes.add( new LdcInsnNode( 1 ) );

nodes.add( new MethodInsnNode( Opcodes.INVOKEINTERFACE,
Type.getInternalName( IBoxContext.class ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public List<AbstractInsnNode> transform( BoxNode node, TransformerContext contex
} else {
nodes.add( new InsnNode( Opcodes.ACONST_NULL ) );
}
nodes.add( new LdcInsnNode( false ) );
nodes.add( new LdcInsnNode( 0 ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEINTERFACE,
Type.getInternalName( IBoxContext.class ),
"scopeFindNearby",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public List<AbstractInsnNode> transform( BoxNode node, TransformerContext contex
nodes.addAll( transpiler.getCurrentMethodContextTracker().get().loadCurrentContext() );
nodes.addAll( transpiler.createKey( id.getName() ) );
nodes.add( new InsnNode( Opcodes.ACONST_NULL ) );
nodes.add( new LdcInsnNode( true ) );
nodes.add( new LdcInsnNode( 1 ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEINTERFACE,
Type.getInternalName( IBoxContext.class ),
"scopeFindNearby",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import ortus.boxlang.compiler.asmboxpiler.transformer.ReturnValueContext;
import ortus.boxlang.compiler.asmboxpiler.transformer.TransformerContext;
import ortus.boxlang.compiler.ast.BoxNode;
import ortus.boxlang.compiler.ast.BoxStatement;
import ortus.boxlang.compiler.ast.statement.BoxScriptIsland;

public class BoxScriptIslandTransformer extends AbstractTransformer {
Expand All @@ -42,9 +41,8 @@ public List<AbstractInsnNode> transform( BoxNode node, TransformerContext contex
BoxScriptIsland scriptIsland = ( BoxScriptIsland ) node;

List<AbstractInsnNode> nodes = new ArrayList<>();
for ( BoxStatement statement : scriptIsland.getStatements() ) {
nodes.addAll( transpiler.transform( statement, context, returnContext ) );
}

nodes.addAll( AsmHelper.transformBodyExpressions( transpiler, scriptIsland.getStatements(), context, returnContext ) );

return AsmHelper.addLineNumberLabels( nodes, node );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import ortus.boxlang.compiler.asmboxpiler.transformer.ReturnValueContext;
import ortus.boxlang.compiler.asmboxpiler.transformer.TransformerContext;
import ortus.boxlang.compiler.ast.BoxNode;
import ortus.boxlang.compiler.ast.BoxStatement;
import ortus.boxlang.compiler.ast.statement.component.BoxTemplateIsland;

public class BoxTemplateIslandTransformer extends AbstractTransformer {
Expand All @@ -42,9 +41,8 @@ public List<AbstractInsnNode> transform( BoxNode node, TransformerContext contex
BoxTemplateIsland templateIsland = ( BoxTemplateIsland ) node;

List<AbstractInsnNode> nodes = new ArrayList<>();
for ( BoxStatement statement : templateIsland.getStatements() ) {
nodes.addAll( transpiler.transform( statement, context, returnContext ) );
}

nodes.addAll( AsmHelper.transformBodyExpressions( transpiler, templateIsland.getStatements(), context, returnContext ) );

return AsmHelper.addLineNumberLabels( nodes, node );
}
Expand Down
67 changes: 49 additions & 18 deletions src/main/java/ortus/boxlang/runtime/components/net/HTTP.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import ortus.boxlang.runtime.dynamic.casters.DoubleCaster;
import ortus.boxlang.runtime.dynamic.casters.StringCaster;
import ortus.boxlang.runtime.dynamic.casters.StructCaster;
import ortus.boxlang.runtime.events.BoxEvent;
import ortus.boxlang.runtime.net.HTTPStatusReasons;
import ortus.boxlang.runtime.net.HttpManager;
import ortus.boxlang.runtime.net.HttpRequestMultipartBody;
Expand Down Expand Up @@ -148,28 +149,33 @@ public HTTP() {
*
*/
public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBody body, IStruct executionState ) {
// Keeps track of the HTTPParams
executionState.put( Key.HTTPParams, new Array() );

// Process the component for HTTPParams
BodyResult bodyResult = processBody( context, body );

// IF there was a return statement inside our body, we early exit now
if ( bodyResult.isEarlyExit() ) {
return bodyResult;
}

String variableName = StringCaster.cast( attributes.getOrDefault( Key.result, "bxhttp" ) );
// Prepare invocation of the HTTP request
String variableName = attributes.getAsString( Key.result );
String theURL = attributes.getAsString( Key.URL );
String method = StringCaster.cast( attributes.getOrDefault( Key.method, "GET" ) ).toUpperCase();
String method = attributes.getAsString( Key.method ).toUpperCase();
Array params = executionState.getAsArray( Key.HTTPParams );
Struct HTTPResult = new Struct();
URI targetURI = null;

URI uri = null;
try {
HttpRequest.Builder builder = HttpRequest.newBuilder();
URIBuilder uriBuilder = new URIBuilder( theURL );
HttpRequest.BodyPublisher bodyPublisher = null;
List<IStruct> formFields = new ArrayList<>();
List<IStruct> files = new ArrayList<>();
builder.header( "User-Agent", "BoxLang" );

for ( Object p : params ) {
IStruct param = StructCaster.cast( p );
String type = param.getAsString( Key.type );
Expand Down Expand Up @@ -247,24 +253,43 @@ public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBod
}

builder.method( method, bodyPublisher );
uri = uriBuilder.build();
builder.uri( uri );
HttpRequest request = builder.build();
HttpClient client = HttpManager.getClient();
CompletableFuture<HttpResponse<String>> inflightRequest = client.sendAsync( request, HttpResponse.BodyHandlers.ofString() );
targetURI = uriBuilder.build();
builder.uri( targetURI );
HttpRequest targetHTTPRequest = builder.build();
HttpClient client = HttpManager.getClient();

// Announce the HTTP request
interceptorService.announce( BoxEvent.ON_HTTP_REQUEST, Struct.of(
"httpClient", client,
"httpRequest", targetHTTPRequest,
"targetURI", targetURI,
"attributes", attributes
) );

// Announce the HTTP request
CompletableFuture<HttpResponse<String>> inflightRequest = client.sendAsync( targetHTTPRequest, HttpResponse.BodyHandlers.ofString() );
CompletableFuture<HttpResponse<String>> winner = inflightRequest;
if ( attributes.containsKey( Key.timeout ) ) {
winner = inflightRequest.applyToEither( HttpManager.getTimeoutRequestAsync( attributes.getAsInteger( Key.timeout ) ), result -> result );
}
HttpResponse<String> response = winner.get();
HttpResponse<String> response = winner.get();

HttpHeaders httpHeaders = Optional.ofNullable( response.headers() )
// Announce the HTTP RAW response
// Useful for debugging and pre-processing and timing, since the other events are after the response is processed
interceptorService.announce( BoxEvent.ON_HTTP_RAW_RESPONSE, Struct.of(
"response", response
) );

// Start Processing Results
HttpHeaders httpHeaders = Optional
.ofNullable( response.headers() )
.orElse( HttpHeaders.of( Map.of(), ( a, b ) -> true ) );
IStruct headers = transformToResponseHeaderStruct(
httpHeaders.map() );
String httpVersionString = response.version() == HttpClient.Version.HTTP_1_1 ? "HTTP/1.1" : "HTTP/2";
String statusCodeString = String.valueOf( response.statusCode() );
String statusText = HTTPStatusReasons.getReasonForStatus( response.statusCode() );
IStruct headers = transformToResponseHeaderStruct(
httpHeaders.map()
);
String httpVersionString = response.version() == HttpClient.Version.HTTP_1_1 ? "HTTP/1.1" : "HTTP/2";
String statusCodeString = String.valueOf( response.statusCode() );
String statusText = HTTPStatusReasons.getReasonForStatus( response.statusCode() );

headers.put( Key.HTTP_Version, httpVersionString );
headers.put( Key.status_code, statusCodeString );
Expand Down Expand Up @@ -292,9 +317,14 @@ public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBod
} );
HTTPResult.put( Key.cookies, generateCookiesQuery( headers ) );

// Set the result back into the page
// Set the result back into the page using the variable name
ExpressionInterpreter.setVariable( context, variableName, HTTPResult );

// Announce the HTTP response
interceptorService.announce( BoxEvent.ON_HTTP_RESPONSE, Struct.of(
"result", HTTPResult
) );

return DEFAULT_RETURN;
} catch ( ExecutionException e ) {
Throwable innerException = e.getCause();
Expand All @@ -306,8 +336,8 @@ public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBod
HTTPResult.put( Key.statusText, "Bad Gateway" );
HTTPResult.put( Key.status_text, "Bad Gateway" );
HTTPResult.put( Key.fileContent, "Connection Failure" );
if ( uri != null ) {
HTTPResult.put( Key.errorDetail, String.format( "Unknown host: %s: Name or service not known.", uri.getHost() ) );
if ( targetURI != null ) {
HTTPResult.put( Key.errorDetail, String.format( "Unknown host: %s: Name or service not known.", targetURI.getHost() ) );
} else {
HTTPResult.put( Key.errorDetail, String.format( "Unknown host: %s: Name or service not known.", theURL ) );
}
Expand All @@ -317,6 +347,7 @@ public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBod
}
return DEFAULT_RETURN;
} catch ( InterruptedException e ) {
Thread.currentThread().interrupt();
throw new BoxRuntimeException( "The request was interrupted", "InterruptedException", e );
} catch ( URISyntaxException | IOException e ) {
throw new BoxRuntimeException( e.getMessage(), e );
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/ortus/boxlang/runtime/events/BoxEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ public enum BoxEvent {
PRE_MODULE_UNLOAD( "preModuleUnload" ),
POST_MODULE_UNLOAD( "postModuleUnload" ),

/**
* HTTP Events
*/
ON_HTTP_REQUEST( "onHTTPRequest" ),
ON_HTTP_RAW_RESPONSE( "onHTTPRawResponse" ),
ON_HTTP_RESPONSE( "onHTTPResponse" ),

/**
* Module Service Events
*/
Expand Down
13 changes: 10 additions & 3 deletions src/main/java/ortus/boxlang/runtime/net/HTTPStatusReasons.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
*/
package ortus.boxlang.runtime.net;

import java.util.Map;

import static java.util.Map.entry;

import java.util.Map;

public class HTTPStatusReasons {

private static final String UNKNOWN_STATUS = "Unknown Status";
Expand Down Expand Up @@ -99,7 +99,14 @@ public class HTTPStatusReasons {
entry( 511, "Network Authentication Required" )
);

/**
* Returns the reason for the given status code.
*
* @param status The status code.
*
* @return The reason for the status code.
*/
public static String getReasonForStatus( int status ) {
return REASONS.getOrDefault( status, UNKNOWN_STATUS );
}
}
}
6 changes: 5 additions & 1 deletion src/main/java/ortus/boxlang/runtime/net/HttpManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ private HttpManager() {
*/
public static HttpClient getClient() {
if ( instance == null ) {
instance = HttpClient.newHttpClient();
synchronized ( HttpManager.class ) {
if ( instance == null ) {
instance = HttpClient.newHttpClient();
}
}
}
return instance;
}
Expand Down
7 changes: 3 additions & 4 deletions src/main/java/ortus/boxlang/runtime/net/URIBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@
*/
package ortus.boxlang.runtime.net;

import ortus.boxlang.runtime.net.NameValuePair;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class URIBuilder {

private @Nullable String scheme;
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/ortus/boxlang/runtime/types/util/StructUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -464,11 +464,25 @@ public static Stream<IStruct> findKey( IStruct struct, String key ) {
flatMap.entrySet()
.stream()
.filter( entry -> {
Array splitParts = Array.of( entry.getKey().getName().toLowerCase().split( "\\." ) );
String[] splitParts = entry.getKey().getName().toLowerCase().split( "\\." );
String stringKey = entry.getKey().getName().toLowerCase();
return splitParts.size() > 1
? splitParts.get( splitParts.size() - 1 ).equals( key.toLowerCase() ) || stringKey.equals( key.toLowerCase() )
: stringKey.equals( key.toLowerCase() );
return splitParts.length > 1
? splitParts[ splitParts.length - 1 ].equals( key.toLowerCase() ) || stringKey.equals( key.toLowerCase() )
// For single keys make sure we check that it wasn't added above
: results.stream()
.filter(
result -> {
Object resultObj = result.get( Key.value );
Object entryObj = entry.getValue();
if ( resultObj == null ) {
return entry.getValue() == null;
} else {
return entryObj != null ? resultObj.equals( entryObj ) : false;
}
}
)
.count() == 0
&& stringKey.equals( key.toLowerCase() );
} )
.map( entry -> {
Struct returnStruct = new Struct( Struct.TYPES.LINKED );
Expand Down
Loading

0 comments on commit da1b845

Please sign in to comment.