RemoteWebRequest.java
/*
** Module : RemoteWebRequest.java
** Abstract : Expose a remote web request to 4GL APIs.
**
** Copyright (c) 2019-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- -------------------------------Description--------------------------------
** 001 CA 20190924 First version.
** 002 CA 20191024 Added method support levels and updated the class support level.
** 003 CA 20200427 A more improved runtime. cookie/header and other support is still missing.
** 004 ME 20210128 Add errors for read-only, not implemented method/properties.
** 005 CA 20210221 Fixed parameters at the LegacySignature annotation.
** 006 ME 20210423 Annotate class and finish implementation as per OE12.2.
** CA 20210609 Fixed getPathInfo and getPathParameter, when the path info has more than one leading '/' or
** the path parameter doesn't exist.
** CA 20210816 Fixed getEntity() - parameters from 'content-type' must be removed.
** Fixed getHeader() when the header is not found (NULL header must be returned).
** ME 20210916 Default cookie path is unknown.
** VVT 20210917 Javadoc fixed.
** 007 CA 20231113 The 'execute' method must be annotated with LegacySignature Type.Execute, and also can be
** dropped if is a no-op.
** 008 CA 20240219 'getEntity' must fallback to 'raw/memptr' if the body's content type can't be resolved.
** 009 CA 20240523 Cookies from the request may be null.
** 010 AS 20241206 Fixed getUri to return the full uri.
** 011 GBB 20250403 Fixes in comments.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.oo.web;
import static com.goldencode.p2j.report.ReportConstants.*;
import static com.goldencode.p2j.util.BlockManager.*;
import java.io.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
// explicit import
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.server.Request;
import com.goldencode.p2j.directory.Base64;
import com.goldencode.p2j.main.*;
import com.goldencode.p2j.oo.core.*;
import com.goldencode.p2j.oo.lang.*;
import com.goldencode.p2j.oo.net.*;
import com.goldencode.p2j.oo.net.http.*;
import com.goldencode.p2j.oo.net.http.filter.payload.MessageWriter;
import com.goldencode.p2j.oo.net.http.filter.writer.EntityWriterRegistry;
import com.goldencode.p2j.oo.web.WebHandler.WebContext;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.BlockManager.Action;
import com.goldencode.p2j.util.BlockManager.Condition;
import com.goldencode.p2j.util.InternalEntry.Type;
/**
* A wrapper over a {@link HttpServletRequest} instance, to expose it as a legacy
* {@link IwebRequest}.
*/
@LegacyResource(resource = "OpenEdge.Web.WebRequest")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL)
public class RemoteWebRequest
extends BaseObject
implements IwebRequest
{
/** A marker for unsupported {@link #CGI_VARS}. */
private static final Object UNSUPPORTED_CGI_VAR = new Object();
/** All reported CGI variables and their expression to calculate it. */
private static final Map<String, Function<HttpServletRequest, Object>> CGI_VARS = new HashMap<>();
static
{
CGI_VARS.put("AUTH_TYPE" , (r) -> r.getAuthType());
CGI_VARS.put("BASE_PATH" , (r) -> UNSUPPORTED_CGI_VAR); // explicitly handled
CGI_VARS.put("CONTENT_LENGTH" , (r) -> r.getContentLength());
CGI_VARS.put("CONTENT_TYPE" , (r) -> r.getContentType());
CGI_VARS.put("CONTEXT_PATH" , (r) -> r.getContextPath());
CGI_VARS.put("DOCUMENT_ROOT" , (r) -> r.getServletContext().getRealPath("/"));
CGI_VARS.put("GATEWAY_INTERFACE" , (r) -> UNSUPPORTED_CGI_VAR);
CGI_VARS.put("HTTP_ACCEPT_ENCODING" , (r) -> r.getHeader("Accept-Encoding"));
CGI_VARS.put("HTTP_CONNECTION" , (r) -> r.getHeader("Connection"));
CGI_VARS.put("HTTP_HOST" , (r) -> r.getHeader("Host"));
CGI_VARS.put("HTTP_PORT" , (r) ->
{
String host = r.getHeader("Host");
int idx = host.indexOf(':');
return idx < 0 ? "" : host.substring(idx + 1);
});
CGI_VARS.put("HTTPS" , (r) -> r.isSecure());
CGI_VARS.put("HTTP_USER_AGENT" , (r) -> r.getHeader("User-Agent"));
CGI_VARS.put("LOCAL_ADDR" , (r) -> r.getLocalAddr());
CGI_VARS.put("LOCAL_NAME" , (r) -> r.getLocalName());
CGI_VARS.put("LOCAL_PORT" , (r) -> r.getLocalPort());
CGI_VARS.put("PATH_INFO" , (r) -> r.getPathInfo());
CGI_VARS.put("PATH_TRANSLATED" , (r) -> r.getPathTranslated());
CGI_VARS.put("QUERY_STRING" , (r) -> r.getQueryString());
CGI_VARS.put("REMOTE_ADDR" , (r) -> r.getRemoteAddr());
CGI_VARS.put("REMOTE_HOST" , (r) -> r.getRemoteHost());
CGI_VARS.put("REMOTE_PORT" , (r) -> r.getRemotePort());
CGI_VARS.put("REQUEST_METHOD" , (r) -> r.getMethod());
CGI_VARS.put("REQUEST_SCHEME" , (r) -> r.getScheme());
CGI_VARS.put("REQUEST_URI" , (r) -> r.getRequestURI());
CGI_VARS.put("SCRIPT_NAME" , (r) -> r.getServletPath());
CGI_VARS.put("SERVER_NAME" , (r) -> r.getServerName());
CGI_VARS.put("SERVER_PORT" , (r) -> r.getServerPort());
CGI_VARS.put("SERVER_PROTOCOL" , (r) -> r.getProtocol());
CGI_VARS.put("SERVER_SOFTWARE" , (r) -> r.getServletContext().getServerInfo());
CGI_VARS.put("SERVLET_APPLICATION_URL", (r) -> UNSUPPORTED_CGI_VAR); // explicitly handled
CGI_VARS.put("SERVLET_PATH" , (r) -> r.getServletPath());
CGI_VARS.put("SERVLET_SERVER_APP_MODE", (r) -> "production"); // FWD doesn't allow debug mode
CGI_VARS.put("SERVLET_SRVR_DEBUG" , (r) -> "0"); // FWD doesn't allow debug mode
CGI_VARS.put("SERVLET_WSROOT" , (r) -> UNSUPPORTED_CGI_VAR);
CGI_VARS.put("URI_FINAL_MATCH_GROUP" , (r) -> UNSUPPORTED_CGI_VAR); // explicitly handled
CGI_VARS.put("URI_TEMPLATE" , (r) -> UNSUPPORTED_CGI_VAR); // explicitly handled
}
/** The base path for this request. */
private String basepath;
/** The target path for this request. */
private String target;
/** The servlet request. */
private HttpServletRequest request;
/** The paths for this request, as configured at the handler. */
private String[] paths;
/** The MD5 hash of body */
private raw contentMD5 = TypeFactory.raw();
/** The body entity. */
private object<? extends _BaseObject_> entity = TypeFactory.object(_BaseObject_.class);
/**
* The execute method to initialize the FWD-related state for an instance.
*/
@LegacySignature(type = Type.EXECUTE)
public void __web_RemoteWebRequest_execute__()
{
onBlockLevel(Condition.ERROR, Action.THROW);
}
/**
* Default constructor.
*/
@LegacySignature(type = Type.CONSTRUCTOR)
public void __web_RemoteWebRequest_constructor__()
{
internalProcedure(this, "__web_RemoteWebRequest_constructor__", new Block((Body) () ->
{
__lang_BaseObject_constructor__();
}));
}
/**
* Initialize this instance with the specified details.
*
* Web context is fetch from WebHandler's context for now.
*
*/
private boolean _initialize()
{
if (this.request == null)
{
WebContext context = WebHandler.getWebContext();
if (context != null)
{
this.basepath = context.getBasepath();
this.target = context.getTarget();
this.paths = context.getPaths();
this.request = context.getRequest();
}
else
{
ErrorManager.recordOrShowWarning(4088,
"Progress does not support the GET-CGI-VALUE attribute for the PSEUDO-WIDGET in this display environment",
true, false, false, true);
}
}
return this.request != null;
}
/**
* Get the HTTP method.
*
* @return the HTTP method.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Method")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getMethod()
{
return function(RemoteWebRequest.class, this, "Method", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(request.getMethod()));
}));
}
/**
* Set the HTTP method. This is read-only.
*
* @param method
* the HTTP method.
*/
@LegacySignature(type = Type.SETTER, name = "Method", parameters =
{
@LegacyParameter(name = "Method", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setMethod(character method)
{
throwReadOnly("Method");
}
/**
* Get the HTTP URI.
*
* @return the HTTP URI.
*/
@LegacySignature(returns = "OBJECT", type = Type.GETTER, name = "URI", qualified = "OpenEdge.Net.URI" )
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public object<? extends Uri> getUri()
{
return function(RemoteWebRequest.class, this, "URI", object.class, new Block((Body) () ->
{
returnNormal(Uri.parse(_initialize() ? new character(((Request) request).getHttpURI().toString()) : new character()));
}));
}
/**
* Set the HTTP URI. This is read-only.
*
* @param uri
* the HTTP URI.
*/
@LegacySignature(type = Type.SETTER, name = "URI", parameters =
{
@LegacyParameter(name = "uri", type = "OBJECT",
qualified = "OpenEdge.Net.URI", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setUri(object<? extends Uri> uri)
{
throwReadOnly("URI");
}
/**
* Get the character encoding.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "CharacterEncoding")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getCharacterEncoding()
{
return function(RemoteWebRequest.class, this, "CharacterEncoding", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getCharacterEncoding())));
}));
}
/**
* Set the character encoding. This is read-only.
*
* @param _encoding
* The character encoding.
*/
@LegacySignature(type = Type.SETTER, name = "CharacterEncoding", parameters =
{
@LegacyParameter(name = "encoding", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setCharacterEncoding(character _encoding)
{
throwReadOnly("CharacterEncoding");
}
/**
* Get the content length.
*
* @return See above.
*/
@LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "ContentLength")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public integer getContentLength()
{
return function(RemoteWebRequest.class, this, "ContentLength", integer.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new integer());
returnNormal(new integer(request.getContentLength()));
}));
}
/**
* Set the content length. This is read-only.
*
* @param _var
* The content length.
*/
@LegacySignature(type = Type.SETTER, name = "ContentLength", parameters =
{
@LegacyParameter(name = "var", type = "INTEGER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setContentLength(integer _var)
{
throwReadOnly("ContentLength");
}
/**
* Get the content MD5.
*
* @return See above.
*/
@LegacySignature(returns = "RAW", type = Type.GETTER, name = "ContentMD5")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public raw getContentMd5()
{
return function(RemoteWebRequest.class, this, "ContentLength", raw.class, new Block((Body) () ->
{
if (contentMD5.isUnknown() && _initialize())
{
try
{
// 4GL QUIRK - this will throw in 4GL because of the setter method throwing error
contentMD5.assign(Base64.byteArrayToBase64(LegacyServiceHandler.readBody(request)));
}
catch (IOException e)
{
// no-op
}
}
returnNormal(contentMD5);
}));
}
/**
* Set the content MD5. This is not implemented.
*
* @param _var
* The content MD5.
*/
@LegacySignature(type = Type.SETTER, name = "ContentMD5", parameters =
{
@LegacyParameter(name = "var", type = "RAW", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setContentMd5(raw _var)
{
throwNotImplementedProperty("ContentMD5");
}
/**
* Get the content type.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ContentType")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getContentType()
{
return function(RemoteWebRequest.class, this, "ContentType", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getContentType())));
}));
}
/**
* Set the content type. This is read-only.
*
* @param _var
* The content type.
*/
@LegacySignature(type = Type.SETTER, name = "ContentType", parameters =
{
@LegacyParameter(name = "var", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setContentType(character _var)
{
throwReadOnly("ContentType");
}
/**
* Get the body entity.
*
* @return See above.
*/
@LegacySignature(returns = "OBJECT", type = Type.GETTER, name = "Entity", qualified = "Progress.Lang.Object")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public object<? extends _BaseObject_> getEntity()
{
return function(RemoteWebRequest.class, this, "Entity", object.class, new Block((Body) () ->
{
if (!entity._isValid() && _initialize())
{
byte[] body;
try
{
body = LegacyServiceHandler.readBody(request);
if (body.length > 0)
{
// delegate that to entity writer???
String ct = request.getContentType();
if (ct.indexOf(';') > 0)
{
ct = ct.substring(0, ct.indexOf(';')).trim();
}
object<? extends LegacyClass> writerType = EntityWriterRegistry.getRegistry().ref()._get(ct);
if (writerType._isValid()) {
try
{
object<? extends MessageWriter> writer = ObjectOps.cast(writerType.ref().new_(), MessageWriter.class);
writer.ref().open();
writer.ref().write(new memptr(body));
writer.ref().close();
entity.assign(writer.ref().getEntity());
}
catch (Exception e)
{
// fallback to raw body
entity.assign(ObjectOps.newInstance(Memptr.class, "I", new raw(body)));
}
}
else
{
// fallback to raw body
entity.assign(ObjectOps.newInstance(Memptr.class, "I", new raw(body)));
}
}
}
catch (IOException e)
{
// no-op
}
}
if (!entity._isValid())
{
entity.assign(ObjectOps.newInstance(Memptr.class, "I", new integer(0)));
}
returnNormal(entity);
}));
}
/**
* Set the body entity. This is a read-only.
*
* @param _var
* The body entity.
*/
@LegacySignature(type = Type.SETTER, name = "Entity", parameters =
{
@LegacyParameter(name = "Entity", type = "OBJECT",
qualified = "Progress.Lang.Object", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setEntity(object<? extends _BaseObject_> _var)
{
throwReadOnly("Entity");
}
/**
* Get the transfer encoding.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "TransferEncoding")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getTransferEncoding()
{
return function(RemoteWebRequest.class, this, "TransferEncoding", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getHeader("Transfer-Encoding"))));
}));
}
/**
* Set the transfer encoding. This is read-only.
*
* @param transferEncoding
* The transfer encoding.
*/
@LegacySignature(type = Type.SETTER, name = "TransferEncoding", parameters =
{
@LegacyParameter(name = "TransferEncoding", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setTransferEncoding(character transferEncoding)
{
throwReadOnly("TransferEncoding");
}
/**
* Get the HTTP version.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Version")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getVersion()
{
return function(RemoteWebRequest.class, this, "Version", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getProtocol())));
}));
}
/**
* Set the HTTP version. This is read-only.
*
* @param version
* The HTTP version.
*/
@LegacySignature(type = Type.SETTER, name = "Version", parameters =
{
@LegacyParameter(name = "version", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setVersion(character version)
{
throwReadOnly("Version");
}
/**
* Clear the headers. This is not implemented.
*/
@LegacySignature(type = Type.METHOD, name = "ClearHeaders")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void clearHeaders()
{
throwNotImplementedMethod("ClearHeaders");
}
/**
* Get the specified header's value.
*
* @param _pcName
* The header name.
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetHeader", qualified = "OpenEdge.Net.HTTP.HttpHeader", parameters = {
@LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") })
@Override
public object<? extends HttpHeader> getHeader(character _pcName)
{
character pcName = TypeFactory.initInput(_pcName);
return function(this, "GetHeader", object.class, new Block((Body) () ->
{
character val = new character();
if (!pcName.isUnknown() && _initialize())
{
String name = pcName.toStringMessage();
Enumeration<String> hnames = request.getHeaderNames();
while (hnames.hasMoreElements())
{
String header = hnames.nextElement();
if (name.equalsIgnoreCase(header))
val.assign(request.getHeader(header));
}
}
if (!val.isUnknown())
{
returnNormal(HttpHeaderBuilder.build(pcName)
.ref().value(val).ref().getHeader());
}
else
{
returnNormal(HttpHeader.getNullHeader());
}
}));
}
/**
* Populate the specified extent variable with all the headers.
*
* @param _poHeaders
* The header array.
*
* @return The number of read headers.
*/
@LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "GetHeaders", parameters = {
@LegacyParameter(name = "poHeaders", type = "OBJECT", extent = -1, qualified = "OpenEdge.Net.HTTP.HttpHeader", mode = "OUTPUT") })
@Override
public integer getHeaders(OutputExtentParameter<object<? extends HttpHeader>> _poHeaders)
{
object<? extends HttpHeader>[][] poHeaders = new object[][] { TypeFactory.initOutput(_poHeaders) };
return function(this, "GetHeaders", integer.class, new Block((Body) () ->
{
Map<String, String> headers = new LinkedHashMap<>();
if (_initialize())
{
Enumeration<String> hnames = request.getHeaderNames();
while (hnames.hasMoreElements())
{
String header = hnames.nextElement();
headers.put(header, request.getHeader(header));
}
}
poHeaders[0] = ArrayAssigner.resize(poHeaders[0], headers.size());
int i = 1;
for (String header : headers.keySet())
{
String value = headers.get(header);
object<? extends HttpHeader> oheader = HttpHeaderBuilder.build(new character(header))
.ref().value(new character(value)).ref().getHeader();
ArrayAssigner.assignSingle(poHeaders[0], i++, oheader);
}
returnNormal(poHeaders[0].length);
}));
}
/**
* Check if the specified header name exists.
*
* @param _pcName
* The header name.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "HasCookie", parameters = {
@LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") })
@Override
public logical hasHeader(character _pcName)
{
character pcName = TypeFactory.initInput(_pcName);
return function(this, "HasHeader", logical.class, new Block((Body) () ->
{
if (pcName.isUnknown() || !_initialize())
returnNormal(new logical());
String name = _pcName.toStringMessage();
Enumeration<String> hnames = request.getHeaderNames();
while (hnames.hasMoreElements())
{
String header = hnames.nextElement();
if (name.equalsIgnoreCase(header))
returnNormal(new logical(true));
}
returnNormal(new logical(false));
}));
}
/**
* Remove the specified header. This is not implemented.
*
* @param _pcName
* The header name.
*/
@LegacySignature(type = Type.METHOD, name = "RemoveHeader", parameters =
{
@LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void removeHeader(character _pcName)
{
throwNotImplementedMethod("RemoveHeader");
}
/**
* Set the specified header. This is not implemented.
*
* @param _poHeader
* The header instance.
*/
@LegacySignature(type = Type.METHOD, name = "SetHeader", parameters =
{
@LegacyParameter(name = "poHeader", type = "OBJECT",
qualified = "OpenEdge.Net.HTTP.HttpHeader", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setHeader(object<? extends HttpHeader> _poHeader)
{
throwNotImplementedMethod("SetHeader");
}
/**
* Set the specified header. This is not implemented.
*
* @param _pcName
* The header name.
* @param _pcValue
* The header value.
* @return see above
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "SetHeader", parameters =
{
@LegacyParameter(name = "poHeader", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "pcValue", type = "CHARACTER", mode = "INPUT")
}, qualified = "OpenEdge.Net.HTTP.HttpHeader")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends HttpHeader> setHeader(character _pcName, character _pcValue)
{
return function(this, "SetHeader", object.class, new Block((Body) () ->
{
throwNotImplementedMethod("SetHeader");
}));
}
/**
* Resolve the web app path.
*
* @return The servlet path.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ResolvedWebAppPath")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getResolvedWebAppPath()
{
return function(RemoteWebRequest.class, this, "ResolvedWebAppPath", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getServletPath())));
}));
}
/**
* Get the context names. This returns all {@link #CGI_VARS} keys.
*
* @return A comma-separated list of CGI var names.
*/
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetContextNames")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getContextNames()
{
return function(RemoteWebRequest.class, this, "GetContextNames", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
String res = "";
for (String key : CGI_VARS.keySet())
{
res = res + (res.isEmpty() ? "" : ",") + key;
}
returnNormal(new character(res));
}));
}
/**
* Get the default cookie domain.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "DefaultCookieDomain")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getDefaultCookieDomain()
{
UnimplementedFeature.missing("RemoteWebRequest.getDefaultCookiePath");
return function(RemoteWebRequest.class, this, "DefaultCookieDomain", character.class, new Block((Body) () ->
{
// TODO: WEB properties in multi-session agent appservers
if (_initialize())
;
returnNormal(new character());
}));
}
/**
* Get the default cookie path.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "DefaultCookiePath")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getDefaultCookiePath()
{
UnimplementedFeature.missing("RemoteWebRequest.getDefaultCookiePath");
return function(RemoteWebRequest.class, this, "DefaultCookiePath", character.class, new Block((Body) () ->
{
// TODO: WEB properties in multi-session agent appservers
if (_initialize())
;
returnNormal(new character());
}));
}
/**
* Get the local address.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "LocalAddress")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getLocalAddress()
{
return function(RemoteWebRequest.class, this, "LocalAddress", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getLocalAddr())));
}));
}
/**
* Get the local host.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "LocalHost")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getLocalHost()
{
return function(RemoteWebRequest.class, this, "LocalHost", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getLocalName())));
}));
}
/**
* Get the local port.
*
* @return See above.
*/
@LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "LocalPort")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public integer getLocalPort()
{
return function(RemoteWebRequest.class, this, "LocalPort", integer.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new integer());
returnNormal(new integer(request.getLocalPort()));
}));
}
/**
* Get the path information.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "PathInfo")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getPathInfo()
{
return function(RemoteWebRequest.class, this, "PathInfo", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(_getPathInfo()));
}));
}
protected String _getPathInfo()
{
// the URL after the transport path (i.e. basePath)
String res = target;
if (res.indexOf("?") > 0)
{
res = res.substring(0, res.indexOf('?'));
}
if (!res.startsWith("/"))
{
res = "/" + res;
}
while (res.startsWith("//"))
{
res = res.substring(1);
}
return res;
}
/**
* Get the path parameter names.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetPathParameterNames")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getPathParameterNames()
{
return function(this, "GetPathParameterNames", character.class, new Block((Body) () ->
{
String pnames = "";
if (_initialize())
{
pnames = Arrays.stream(paths)
.filter(n -> n.startsWith("{") && n.endsWith("}"))
.map(n -> n.substring(1, n.length() - 1))
.collect(Collectors.joining(","));
}
returnNormal(new character(pnames));
}));
}
/**
* Get the remote address.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "RemoteAddress")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getRemoteAddress()
{
return function(RemoteWebRequest.class, this, "RemoteAddress", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getRemoteAddr())));
}));
}
/**
* Get the remote host.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "RemoteHost")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getRemoteHost()
{
return function(RemoteWebRequest.class, this, "RemoteHost", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getRemoteHost())));
}));
}
/**
* Get the remote port.
*
* @return See above.
*/
@LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "RemotePort")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public integer getRemotePort()
{
return function(RemoteWebRequest.class, this, "RemotePort", integer.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new integer());
returnNormal(new integer(request.getRemotePort()));
}));
}
/**
* Get the remote user.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "RemoteUser")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getRemoteUser()
{
return function(RemoteWebRequest.class, this, "RemoteUser", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getRemoteUser())));
}));
}
/**
* Get the resolved transport path.
*
* @return Always empty string.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ResolvedTransportPath")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getResolvedTransportPath()
{
// always empty
return function(RemoteWebRequest.class, this, "ResolvedTransportPath", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(""));
}));
}
/**
* Get the server software info.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ServerSoftware")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getServerSoftware()
{
return function(RemoteWebRequest.class, this, "ServerSoftware", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getServletContext().getServerInfo())));
}));
}
/**
* Get the transport path.
*
* @return The {@link #basepath}.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "TransportPath")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getTransportPath()
{
return function(RemoteWebRequest.class, this, "TransportPath", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(basepath)));
}));
}
/**
* Get the URI template.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "UriTemplate")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getUriTemplate()
{
return function(RemoteWebRequest.class, this, "UriTemplate", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
String pnames = Arrays.stream(paths)
.collect(Collectors.joining("/", "/", ""));
returnNormal(new character(pnames));
}));
}
/**
* Get the servlet path.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "WebAppPath")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getWebAppPath()
{
return function(RemoteWebRequest.class, this, "WebAppPath", character.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new character());
returnNormal(new character(emptyIfNull(request.getServletPath())));
}));
}
/**
* Get the context value with the specified name.
*
* @param _pcName
* The context name.
*
* @return The context value.
*/
@LegacySignature(returns = "LONGCHAR", type = Type.METHOD, name = "GetContextValue", parameters = {
@LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public longchar getContextValue(character _pcName)
{
return function(RemoteWebRequest.class, this, "GetContextValue", longchar.class, new Block((Body) () ->
{
if (!_initialize())
returnNormal(new longchar());
String val = _pcName.toStringMessage();
String key = val.toUpperCase();
if (CGI_VARS.containsKey(key))
{
Function<HttpServletRequest, Object> f = CGI_VARS.get(key);
Object res = f.apply(request);
if (res == UNSUPPORTED_CGI_VAR)
{
// check if is an explicit one
switch (key)
{
case "BASE_PATH":
res = basepath;
break;
case "SERVLET_APPLICATION_URL":
res = request.getServletPath();
break;
case "URI_FINAL_MATCH_GROUP":
res = "/";
break;
case "URI_TEMPLATE":
res = "";
for (String p : paths)
{
res = res + "/" + p;
}
break;
default:
res = "UNSUPPORTED";
UnimplementedFeature.missing("RemoteWebRequest.getContextValue(" + key + ")");
break;
}
}
returnNormal(new longchar(res == null ? "" : res.toString()));
}
if (val.startsWith("HTTP_"))
{
// this is a header
String headerName = val.substring("HTTP_".length());
returnNormal(new longchar(emptyIfNull(request.getHeader(headerName))));
}
returnNormal(new longchar("")); // empty string always
}));
}
/**
* Get the path parameter value, with the specified name.
*
* @param _pcName
* The path parameter name.
*
* @return See above.
*/
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetPathParameter", parameters = {
@LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public character getPathParameter(character _pcName)
{
character pcName = TypeFactory.initInput(_pcName);
return function(this, "GetPathParameter", character.class, new Block((Body) () ->
{
if (!pcName.isUnknown() && _initialize())
{
String param = pcName.toStringMessage();
for (int i = 0; i < paths.length; i++)
{
if (paths[i].equalsIgnoreCase("{" + param + "}"))
{
// skip leading /, entry is 1-base
returnNormal(TextOps.entry(i + 1, _getPathInfo().substring(1), "/"));
}
}
}
returnNormal(new character(""));
}));
}
/**
* Get the request cookies.
*
* @param _poCookies
* The extent instance to populate with cookies.
*
* @return The number of found cookies.
*/
@LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "GetCookies", parameters = {
@LegacyParameter(name = "poCookies", type = "OBJECT", extent = -1, qualified = "OpenEdge.Net.HTTP.Cookie", mode = "OUTPUT") })
@Override
public integer getCookies(OutputExtentParameter<object<? extends Cookie>> _poCookies)
{
object<? extends HttpHeader>[][] poCookies = new object[][] { TypeFactory.initOutput(_poCookies) };
return function(this, "GetCookies", integer.class, new Block((Body) () ->
{
javax.servlet.http.Cookie[] cookies = request.getCookies();
if (_initialize() && cookies != null)
{
poCookies[0] = ArrayAssigner.resize(poCookies[0], cookies.length);
int i = 1;
for (javax.servlet.http.Cookie cookie : cookies)
{
object<? extends Cookie> oCookie = _getCookie(cookie);
ArrayAssigner.assignSingle(poCookies[0], i++, oCookie);
}
}
returnNormal(poCookies[0].length);
}));
}
/**
* Get the cookie with the specified name.
*
* @param _pcName
* The cookie name.
*
* @return The cookie instance.
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetCookie", qualified = "OpenEdge.Net.HTTP.Cookie", parameters = {
@LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
@Override
public object<? extends Cookie> getCookie(character _pcName)
{
return function(this, "GetCookie", object.class, new Block((Body) () -> {
String name = _pcName.isUnknown() ? "" : _pcName.toStringMessage().trim();
javax.servlet.http.Cookie[] cookies = request.getCookies();
if ( _initialize() && !name.isEmpty() && cookies != null)
{
for (javax.servlet.http.Cookie cookie : cookies)
{
if (cookie.getName().equalsIgnoreCase(name))
returnNormal(_getCookie(cookie));
}
}
returnNormal(new object());
}));
}
private object<? extends Cookie> _getCookie(javax.servlet.http.Cookie cookie)
{
return ObjectOps.newInstance(Cookie.class, "IIIIIIIII",
new character(cookie.getName()),
new character(cookie.getDomain() != null ? cookie.getDomain() : request.getLocalName()),
new character(cookie.getPath() != null ? cookie.getPath() : "/"),
new character(cookie.getValue()),
cookie.getMaxAge() > 0 ? new integer(cookie.getMaxAge()) : new integer(),
new datetimetz(),
new logical(cookie.getSecure()),
new logical(cookie.isHttpOnly()),
cookie.getVersion() > 0 ? new decimal(cookie.getVersion()) : new decimal());
}
/**
* Set or add the specified cookie. This is a no-op.
*
* @param _poCookie
* The cookie to set.
*/
@LegacySignature(type = Type.SETTER, name = "SetCookie", parameters =
{
@LegacyParameter(name = "poHeader", type = "OBJECT",
qualified = "OpenEdge.Net.HTTP.Cookie", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setCookie(object<? extends Cookie> _poCookie)
{
throwNotImplementedMethod("SetCookie");
}
/**
* Check if the specified cookie exists in the request.
*
* @param _poCookie
* The cookie instance.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "HasCookie", parameters = {
@LegacyParameter(name = "poCookie", type = "OBJECT", qualified = "OpenEdge.Net.HTTP.Cookie", mode = "INPUT") })
@Override
public logical hasCookie(object<? extends Cookie> _poCookie)
{
object<? extends Cookie> poCookie = TypeFactory.initInput(_poCookie);
return function(this, "HasCookie", logical.class, new Block((Body) () ->
{
Assert.notNull(poCookie, new character("Cookie"));
returnNormal(hasCookie(poCookie.ref().getName()));
}));
}
/**
* Check if the specified cookie exists in the request.
*
* @param _pcName
* The cookie name.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "HasCookie", parameters = {
@LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") })
@Override
public logical hasCookie(character _pcName)
{
character pcName = TypeFactory.initInput(_pcName);
return function(this, "HasCookie", logical.class, new Block((Body) () ->
{
Assert.notNullOrEmpty(pcName, new character("Cookie name"));
javax.servlet.http.Cookie[] cookies = request.getCookies();
if (_initialize() && cookies != null)
{
String name = _pcName.toStringMessage();
for (javax.servlet.http.Cookie cookie : cookies)
{
if (cookie.getName().equalsIgnoreCase(name))
{
returnNormal(new logical(true));
}
}
}
returnNormal(new logical(false));
}));
}
/**
* Remove the specified cookie. This is not implemented.
*
* @param _pcName
* The cookie name.
*/
@LegacySignature(type = Type.METHOD, name = "RemoveCookie", parameters =
{
@LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void removeCookie(character _pcName)
{
throwNotImplementedMethod("RemoveCookie");
}
/**
* Remove the specified cookie. This is a no-op.
*
* @param _poCookie
* The cookie instance.
*/
@LegacySignature(type = Type.METHOD, name = "RemoveCookie", parameters =
{
@LegacyParameter(name = "poHeader", type = "OBJECT",
qualified = "OpenEdge.Net.HTTP.Cookie", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void removeCookie(object<? extends Cookie> _poCookie)
{
throwNotImplementedMethod("RemoveCookie");
}
/**
* Clear all cookies. This is a no-op.
*/
@LegacySignature(type = Type.METHOD, name = "ClearCookies")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void clearCookies()
{
throwNotImplementedMethod("ClearCookies");
}
/**
* Return the empty value for a null string.
*
* @param val
* The string.
*
* @return See above.
*/
private String emptyIfNull(String val)
{
return val == null ? "" : val;
}
@LegacySignature(type = Type.METHOD, name = "SetCookies", parameters =
{
@LegacyParameter(name = "poHeader", type = "OBJECT",
qualified = "OpenEdge.Net.HTTP.Cookie", extent = -1, mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setCookies(object<? extends Cookie>[] _poCookies)
{
throwNotImplementedMethod("SetCookies");
}
@LegacySignature(type = Type.METHOD, name = "SetHeaders", parameters =
{
@LegacyParameter(name = "poHeader", type = "OBJECT",
qualified = "OpenEdge.Net.HTTP.HttpHeader", extent = -1, mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
@Override
public void setHeaders(object<? extends HttpHeader>[] _poHeader)
{
throwNotImplementedMethod("SetHeaders");
}
private void throwReadOnly (String name)
{
undoThrow(AppError.newInstance(TextOps.substitute("OpenEdge.Web.WebRequest.&1 property is read-only", name), new integer()));
}
private void throwNotImplementedMethod (String name)
{
undoThrow(AppError.newInstance(TextOps.substitute("OpenEdge.Web.WebRequest.&1 method is not implemented", name), new integer()));
}
private void throwNotImplementedProperty (String name)
{
undoThrow(AppError.newInstance(TextOps.substitute("OpenEdge.Web.WebRequest.&1 property is not implemented", name), new integer()));
}
}