Skip to content

Commit

Permalink
Support moving static ode to another server
Browse files Browse the repository at this point in the history
This permits serving the static portions of ode (the Javascript code
and translations) from another web server, including via a third party
CDN such as Cloudflare.

Change-Id: Ic70ca21419c67be66e5c9011c42d53bafe9367bf
  • Loading branch information
jisqyv committed Apr 10, 2024
1 parent 0c18ce5 commit a932fec
Show file tree
Hide file tree
Showing 16 changed files with 117 additions and 16 deletions.
2 changes: 2 additions & 0 deletions appinventor/appengine/build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,8 @@
<arg value="${build.extra.dir}"/>
<arg line="${client.flags} ${client.module}"/>
</java>
<copy todir="${build.war.dir}/ode"
file="${appinventor.dir}/appengine/extra/cdnok.js" />
</target>

<target name="devmode"
Expand Down
4 changes: 4 additions & 0 deletions appinventor/appengine/extra/cdnok.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function cdnok() {
document.getElementById("odeblock").remove()
}
cdnok();
20 changes: 20 additions & 0 deletions appinventor/appengine/src/com/google/appinventor/client/Ode.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.rpc.StatusCodeException;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
Expand Down Expand Up @@ -684,6 +685,14 @@ public void onUncaughtException(Throwable e) {
// This call also stores our sessionId in the backend. This will be checked
// when we go to save a file and if different file saving will be disabled
// Newer sessions invalidate older sessions.

setupOrigin(projectService);
setupOrigin(userInfoService);
setupOrigin(getMotdService);
setupOrigin(componentService);
setupOrigin(adminInfoService);
setupOrigin(tokenAuthService);

Promise.<Config>call(MESSAGES.serverUnavailable(),
c -> userInfoService.getSystemConfig(sessionId, c))
.then(result -> {
Expand Down Expand Up @@ -2344,6 +2353,17 @@ public boolean getDeleteAccountAllowed() {
return config.getDeleteAccountAllowed();
}

public static void setupOrigin(Object service) {
if (service instanceof ServiceDefTarget) {
String host = Window.Location.getProtocol() + "//" + Window.Location.getHost();
String oldUrl = ((ServiceDefTarget)service).getServiceEntryPoint();
if (oldUrl.startsWith(GWT.getModuleBaseURL())) {
String newUrl = host + "/" + GWT.getModuleName() + "/" + oldUrl.substring(GWT.getModuleBaseURL().length());
((ServiceDefTarget)service).setServiceEntryPoint(newUrl);
}
}
}

/**
* setRendezvousServer
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
import com.google.appinventor.client.editor.simple.SimpleEditor;
import com.google.appinventor.client.utils.MessageDialog;
import com.google.appinventor.client.widgets.properties.EditableProperty;
import com.google.appinventor.shared.rpc.ServerLayout;
import com.google.appinventor.shared.rpc.components.FirebaseAuthService;
import com.google.appinventor.shared.rpc.components.FirebaseAuthServiceAsync;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;
import java.util.logging.Logger;
Expand Down Expand Up @@ -54,6 +56,7 @@ public class MockFirebaseDB extends MockNonVisibleComponent {
*/
public MockFirebaseDB(SimpleEditor editor, String type, Image iconImage) {
super(editor, type, iconImage);
Ode.setupOrigin(AUTH_SVC);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public ProjectListItem(Project project) {
initWidget(UI_BINDER.createAndBindUi(this));
this.getElement().setAttribute("data-exporturl",
"application/octet-stream:" + project.getProjectName() + ".aia:"
+ GWT.getModuleBaseURL() + ServerLayout.DOWNLOAD_SERVLET_BASE
+ ServerLayout.getModuleBaseURL() + ServerLayout.DOWNLOAD_SERVLET_BASE
+ ServerLayout.DOWNLOAD_PROJECT_SOURCE + "/" + project.getProjectId());
configureDraggable(this.getElement());
DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat(DATE_TIME_MEDIUM);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
package com.google.appinventor.client.utils;

import com.google.appinventor.client.ErrorReporter;
import com.google.appinventor.client.Ode;
import com.google.appinventor.shared.rpc.ServerLayout;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Frame;
Expand Down Expand Up @@ -45,7 +47,7 @@ private Downloader() {
*/
public final void download(String path) {
ErrorReporter.hide();
setUrl(GWT.getModuleBaseURL() + path);
setUrl(ServerLayout.getModuleBaseURL() + path);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public void execute() {
return;
}

String url = GWT.getModuleBaseURL() +
String url = ServerLayout.getModuleBaseURL() +
ServerLayout.UPLOAD_SERVLET + "/" +
ServerLayout.UPLOAD_COMPONENT + "/" +
trimLeadingPath(fileUpload.getFilename());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.google.appinventor.client.ErrorReporter;
import static com.google.appinventor.client.Ode.MESSAGES;
import com.google.appinventor.client.Ode;
import com.google.appinventor.client.OdeAsyncCallback;
import com.google.appinventor.client.utils.Uploader;
import com.google.appinventor.shared.rpc.ServerLayout;
Expand Down Expand Up @@ -47,7 +48,7 @@ public void execute() {
return;
}

String url = GWT.getModuleBaseURL() +
String url = ServerLayout.getModuleBaseURL() +
ServerLayout.UPLOAD_SERVLET + "/" +
ServerLayout.UPLOAD_COMPONENT + "/" +
trimLeadingPath(uploadWiget.getFilename());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ public void onSuccess(Long date) {

// Use the folderNode's project id and file id in the upload URL so that the file is
// uploaded into that project and that folder in our back-end storage.
String uploadUrl = GWT.getModuleBaseURL() + ServerLayout.UPLOAD_SERVLET + "/" +
ServerLayout.UPLOAD_FILE + "/" + folderNode.getProjectId() + "/" +
folderNode.getFileId() + "/" + filename;
String uploadUrl = ServerLayout.getModuleBaseURL() + ServerLayout.UPLOAD_SERVLET + "/" +
ServerLayout.UPLOAD_FILE + "/" + folderNode.getProjectId() + "/" +
folderNode.getFileId() + "/" + filename;
Uploader.getInstance().upload(upload, uploadUrl,
new OdeAsyncCallback<UploadResponse>(MESSAGES.fileUploadError()) {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public KeystoreUploadWizard(final Command callbackAfterUpload) {
public void execute() {
String filename = upload.getFilename();
if (filename.endsWith(KEYSTORE_EXTENSION)) {
String uploadUrl = GWT.getModuleBaseURL() + ServerLayout.UPLOAD_SERVLET + "/" +
String uploadUrl = ServerLayout.getModuleBaseURL() + ServerLayout.UPLOAD_SERVLET + "/" +
ServerLayout.UPLOAD_USERFILE + "/" + StorageUtil.ANDROID_KEYSTORE_FILENAME;
Uploader.getInstance().upload(upload, uploadUrl,
new OdeAsyncCallback<UploadResponse>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ public void execute() {
// Make sure the project name is legal and unique.
if (TextValidators.checkNewProjectName(filename, true)
!= TextValidators.ProjectNameStatus.SUCCESS) {

// Show Dialog Box and rename the project
new RequestNewProjectNameWizard(new RequestProjectNewNameInterface() {
@Override
public void getNewName(String name) {
upload(upload, name);
}
}, filename, true);

} else {
upload(upload, filename);
}
Expand All @@ -79,9 +79,9 @@ public void getNewName(String name) {
}
});
}

private void upload(FileUpload upload, String filename) {
String uploadUrl = GWT.getModuleBaseURL() + ServerLayout.UPLOAD_SERVLET + "/"
String uploadUrl = ServerLayout.getModuleBaseURL() + ServerLayout.UPLOAD_SERVLET + "/"
+ ServerLayout.UPLOAD_PROJECT + "/" + filename;
Uploader.getInstance().upload(upload, uploadUrl,
new OdeAsyncCallback<UploadResponse>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,42 @@
* servlets.
*
*/
public abstract class OdeRemoteServiceServlet extends RemoteServiceServlet {

import com.google.gwt.user.server.rpc.SerializationPolicy;

import java.net.MalformedURLException;
import java.net.URL;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public abstract class OdeRemoteServiceServlet extends RemoteServiceServlet {
public final static String MODULE_ALIAS = "ode";
protected final UserInfoProvider userInfoProvider = LocalUser.getInstance();

@Override
protected SerializationPolicy doGetSerializationPolicy(final HttpServletRequest request,
final String moduleBaseURL, final String strongName) {

// true client side relative location is the app name
String newModuleBaseURL = moduleBaseURL;
try {
URL url = new URL(moduleBaseURL);

StringBuilder builder = new StringBuilder();
builder.append(url.getProtocol());
builder.append("://");
builder.append(url.getHost());
builder.append("/");
builder.append(MODULE_ALIAS);
builder.append("/");
newModuleBaseURL = builder.toString();

} catch (MalformedURLException ex) {
// we have no affect
}

return super.doGetSerializationPolicy(request, newModuleBaseURL, strongName);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

package com.google.appinventor.shared.rpc;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.ServiceDefTarget;

/**
* Configuration of the URL namespace on the ODE server.
*
Expand Down Expand Up @@ -206,4 +210,10 @@ public static String genRelativeDownloadPath(long projectId, String target) {
public static String genFullDownloadPath(long projectId, String target) {
return ODE_BASEURL + genRelativeDownloadPath(projectId, target);
}

public static String getModuleBaseURL() {
return Window.Location.getProtocol() + "//" + Window.Location.getHost() +
"/" + GWT.getModuleName() + "/";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
package com.google.appinventor.shared.storage;

import com.google.appinventor.shared.rpc.ServerLayout;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;

/**
* Constants and utility methods related to storage.
Expand Down Expand Up @@ -251,7 +251,9 @@ public static boolean isFontFile(String filePath) {
* Returns the URL for the given project file.
*/
public static String getFileUrl(long projectId, String fileId) {
return GWT.getModuleBaseURL() + getFilePath(projectId, fileId);
// Note: Cannot call ode.GetModuleBaseURL() here because it is shared
// and Ode is not in scope on the server side.
return (ServerLayout.getModuleBaseURL() + getFilePath(projectId, fileId));
}

/**
Expand Down
9 changes: 9 additions & 0 deletions appinventor/appengine/war/WEB-INF/appengine-web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

<!-- These are not cached on purpose -->
<include path="/ode/**.nocache.*" expiration="0s" />
<include path="/ode/cdnok.js" expiration="3600s" />
<include path="/index.html" expiration="0s" />

<include path="/ode/**.cache.*" expiration="365d" />
Expand Down Expand Up @@ -178,5 +179,13 @@
<property name="http.keepAlive" value="true" />
<property name="http.maxConnections" value="5" />

<!-- Where to load ode from -->

<!-- Set this to the base URL of where ode is located. Leaving it blank -->
<!-- will load ode from the same host as the server. Note: base URL must be -->
<!-- a top level url. Aka https://example.com/ *not* -->
<!-- https://example.com/something. -->
<property name="ode.base" value="" />

</system-properties>
</appengine-web-app>
15 changes: 14 additions & 1 deletion appinventor/appengine/war/index.jsp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<%@page import="com.google.appinventor.server.Server,com.google.appinventor.common.version.AppInventorFeatures" %>
<%@page import="com.google.appinventor.server.flags.Flag" %>
<%
if (request.getScheme().equals("http") && Server.isProductionServer()
&& AppInventorFeatures.enableHttpRedirect()) {
Expand All @@ -16,6 +17,7 @@
if (AppInventorFeatures.enableHttpRedirect()) {
response.setHeader("Strict-Transport-Security", "max-age=3600");
}
final String odeBase = Flag.createFlag("ode.base", "").get();
%>
<!-- Copyright 2007-2009 Google Inc. All Rights Reserved. -->
<!-- Copyright 2011-2020 Massachusetts Institute of Technology. All Rights Reserved. -->
Expand Down Expand Up @@ -62,8 +64,19 @@
<li> Firefox 52+ </li>
</ul>
</div>
<!-- Message to display if ode doesn't load. When Ode loads,
this message will be hidden. -->
<% if (!odeBase.isEmpty()) { %>
<div id=odeblock>
<h1>If you see this message for an extended period of time, it might be because
your internet service is blocking requests to <%= odeBase %>. Contact your
administrator to check on this and remove the block.
</h1>
</div>
<% } %>
<script type="text/javascript" src="static/closure-library/closure/goog/base.js"></script>
<script type="text/javascript" src="ode/ode.nocache.js"></script>
<script type="text/javascript" src="<%= odeBase %>ode/cdnok.js"></script>
<script type="text/javascript" src="<%= odeBase %>ode/ode.nocache.js"></script>
<script src="static/leaflet/leaflet.js"></script>
<script src="static/leaflet/leaflet.toolbar.js"></script>
<script src="static/leaflet/leaflet-vector-markers.min.js"></script>
Expand Down

0 comments on commit a932fec

Please sign in to comment.