mv com/google/domain/registry google/registry

This change renames directories in preparation for the great package
rename. The repository is now in a broken state because the code
itself hasn't been updated. However this should ensure that git
correctly preserves history for each file.
This commit is contained in:
Justine Tunney 2016-05-13 18:55:08 -04:00
parent a41677aea1
commit 5012893c1d
2396 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,74 @@
package(default_visibility = ["//java/com/google/domain/registry:registry_project"])
java_library(
name = "TestServer",
visibility = ["//visibility:public"],
srcs = [
"HealthzServlet.java",
"Route.java",
"ServletWrapperDelegatorServlet.java",
"StaticResourceServlet.java",
"TestServer.java",
"UrlChecker.java",
],
deps = [
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/net",
"//java/com/google/common/primitives",
"//java/com/google/common/util/concurrent",
"//java/com/google/domain/registry/util",
"//third_party/java/auto:auto_value",
"//third_party/java/jetty/v6_1_22",
"//third_party/java/jsr305_annotations",
"//third_party/java/servlet/servlet_api",
],
)
java_library(
name = "Fixture",
srcs = ["Fixture.java"],
deps = [
"//java/com/google/common/collect",
"//java/com/google/domain/registry/model",
"//javatests/com/google/domain/registry/testing",
"//third_party/java/appengine:appengine-api",
"//third_party/java/joda_time",
],
)
java_library(
name = "RegistryTestServer",
srcs = ["RegistryTestServer.java"],
data = ["//java/com/google/domain/registry/ui:runfiles_debug"],
deps = [
":TestServer",
"//java/com/google/common/collect",
"//java/com/google/common/net",
"//java/com/google/domain/registry/flows",
"//java/com/google/domain/registry/module/backend",
"//java/com/google/domain/registry/module/frontend",
"//java/com/google/domain/registry/ui/server/api",
"//java/com/google/domain/registry/ui/server/registrar",
"//third_party/java/jsr305_annotations",
"//third_party/java/servlet/servlet_api",
],
)
java_binary(
name = "server",
srcs = ["RegistryTestServerMain.java"],
main_class = "com.google.domain.registry.server.RegistryTestServerMain",
deps = [
":Fixture",
":RegistryTestServer",
"//java/com/google/common/collect",
"//java/com/google/common/net",
"//java/com/google/domain/registry/tools/params",
"//java/com/google/domain/registry/ui",
"//javatests/com/google/domain/registry/testing",
"//third_party/java/jcommander",
"//third_party/java/junit",
],
)

View file

@ -0,0 +1,150 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.domain.registry.server;
import static com.google.domain.registry.model.domain.DesignatedContact.Type.ADMIN;
import static com.google.domain.registry.model.domain.DesignatedContact.Type.BILLING;
import static com.google.domain.registry.model.domain.DesignatedContact.Type.TECH;
import static com.google.domain.registry.testing.DatastoreHelper.createTlds;
import static com.google.domain.registry.testing.DatastoreHelper.newContactResource;
import static com.google.domain.registry.testing.DatastoreHelper.newDomainResource;
import static com.google.domain.registry.testing.DatastoreHelper.persistActiveHost;
import static com.google.domain.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.domain.registry.model.contact.ContactAddress;
import com.google.domain.registry.model.contact.ContactResource;
import com.google.domain.registry.model.contact.PostalInfo;
import com.google.domain.registry.model.domain.DesignatedContact;
import com.google.domain.registry.model.domain.ReferenceUnion;
import com.google.domain.registry.model.ofy.Ofy;
import com.google.domain.registry.model.registrar.Registrar;
import com.google.domain.registry.testing.FakeClock;
import com.google.domain.registry.testing.InjectRule;
import org.joda.time.DateTime;
/**
* Datastore fixtures for the development webserver.
*
* <p><b>Warning:</b> These fixtures aren't really intended for unit tests, since they take upwards
* of a second to load.
*/
public enum Fixture {
INJECTED_FAKE_CLOCK {
@Override
public void load() {
new InjectRule()
.setStaticField(Ofy.class, "clock", new FakeClock(DateTime.parse("2000-01-01TZ")));
}
},
/** Fixture of two TLDs, three contacts, two domains, and six hosts. */
BASIC {
@Override
public void load() {
createTlds("xn--q9jyb4c", "example");
ContactResource google = persistResource(newContactResource("google")
.asBuilder()
.setLocalizedPostalInfo(new PostalInfo.Builder()
.setType(PostalInfo.Type.LOCALIZED)
.setName("Mr. Google")
.setOrg("Google Inc.")
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
.setCity("New York")
.setState("NY")
.setZip("10011")
.setCountryCode("US")
.build())
.build())
.build());
ContactResource justine = persistResource(newContactResource("justine")
.asBuilder()
.setLocalizedPostalInfo(new PostalInfo.Builder()
.setType(PostalInfo.Type.LOCALIZED)
.setName("Justine Bean")
.setOrg("(✿◕ ‿◕ ) Incorporated")
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of("123 Fake St."))
.setCity("Stratford")
.setState("CT")
.setZip("06615")
.setCountryCode("US")
.build())
.build())
.build());
ContactResource robert = persistResource(newContactResource("robert")
.asBuilder()
.setLocalizedPostalInfo(new PostalInfo.Builder()
.setType(PostalInfo.Type.LOCALIZED)
.setName("Captain Robert")
.setOrg("Ancient World")
.setAddress(new ContactAddress.Builder()
.setStreet(ImmutableList.of(
"A skeleton crew is what came back",
"And once in port he filled his sack",
"With bribes and cash and fame and coin"))
.setCity("Things to make a new crew join")
.setState("NY")
.setZip("10011")
.setCountryCode("US")
.build())
.build())
.build());
persistResource(
newDomainResource("love.xn--q9jyb4c", justine).asBuilder()
.setContacts(ImmutableSet.of(
DesignatedContact.create(ADMIN, ReferenceUnion.create(robert)),
DesignatedContact.create(BILLING, ReferenceUnion.create(google)),
DesignatedContact.create(TECH, ReferenceUnion.create(justine))))
.setNameservers(ImmutableSet.of(
ReferenceUnion.create(
persistActiveHost("ns1.love.xn--q9jyb4c")),
ReferenceUnion.create(
persistActiveHost("ns2.love.xn--q9jyb4c"))))
.build());
persistResource(
newDomainResource("moogle.example", justine).asBuilder()
.setContacts(ImmutableSet.of(
DesignatedContact.create(ADMIN, ReferenceUnion.create(robert)),
DesignatedContact.create(BILLING, ReferenceUnion.create(google)),
DesignatedContact.create(TECH, ReferenceUnion.create(justine))))
.setNameservers(ImmutableSet.of(
ReferenceUnion.create(persistActiveHost("ns1.linode.com")),
ReferenceUnion.create(persistActiveHost("ns2.linode.com")),
ReferenceUnion.create(persistActiveHost("ns3.linode.com")),
ReferenceUnion.create(persistActiveHost("ns4.linode.com")),
ReferenceUnion.create(persistActiveHost("ns5.linode.com"))))
.build());
persistResource(
Registrar.loadByClientId("TheRegistrar").asBuilder()
.setAllowedTlds(ImmutableSet.of("example", "xn--q9jyb4c"))
.setBillingMethod(Registrar.BillingMethod.BRAINTREE)
.build());
}
};
/** Loads this fixture into the datastore. */
public abstract void load();
}

View file

@ -0,0 +1,36 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.domain.registry.server;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** Servlet for responding to {@code /healthz} requests. */
public final class HealthzServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.setStatus(SC_OK);
rsp.setContentType(PLAIN_TEXT_UTF_8.toString());
rsp.getWriter().write("ok\n");
rsp.getWriter().close();
}
}

View file

@ -0,0 +1,110 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.domain.registry.server;
import static com.google.domain.registry.server.Route.route;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.HostAndPort;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
/** Lightweight HTTP server for testing the Domain Registry Admin and Registrar consoles. */
public final class RegistryTestServer {
public static final ImmutableMap<String, Path> RUNFILES =
new ImmutableMap.Builder<String, Path>()
.put("/index.html", Paths.get("java/com/google/domain/registry/ui/html/index.html"))
.put("/error.html", Paths.get("java/com/google/domain/registry/ui/html/error.html"))
.put("/assets/js/*", Paths.get("java/com/google/domain/registry/ui"))
.put("/assets/css/*", Paths.get("java/com/google/domain/registry/ui/css"))
.put("/assets/sources/deps-runfiles.js",
Paths.get("java/com/google/domain/registry/ui/deps-runfiles.js"))
.put("/assets/sources/*", Paths.get(""))
.put("/assets/*", Paths.get("java/com/google/domain/registry/ui/assets"))
.build();
private static final ImmutableList<Route> ROUTES = ImmutableList.of(
// Frontend Services
route("/whois/*", com.google.domain.registry.module.frontend.FrontendServlet.class),
route("/rdap/*", com.google.domain.registry.module.frontend.FrontendServlet.class),
route("/registrar-xhr", com.google.domain.registry.flows.EppConsoleServlet.class),
route("/check", com.google.domain.registry.ui.server.api.CheckApiServlet.class),
// Proxy Services
route("/_dr/epp", com.google.domain.registry.flows.EppTlsServlet.class),
route("/_dr/whois", com.google.domain.registry.module.frontend.FrontendServlet.class),
// Registry Data Escrow (RDE)
route("/_dr/cron/rdeCreate", com.google.domain.registry.module.backend.BackendServlet.class),
route("/_dr/task/rdeStaging", com.google.domain.registry.module.backend.BackendServlet.class),
route("/_dr/task/rdeUpload", com.google.domain.registry.module.backend.BackendServlet.class),
route("/_dr/task/rdeReport", com.google.domain.registry.module.backend.BackendServlet.class),
route("/_dr/task/brdaCopy", com.google.domain.registry.module.backend.BackendServlet.class),
// Trademark Clearinghouse (TMCH)
route("/_dr/cron/tmchDnl", com.google.domain.registry.module.backend.BackendServlet.class),
route("/_dr/task/tmchSmdrl", com.google.domain.registry.module.backend.BackendServlet.class),
route("/_dr/task/tmchCrl", com.google.domain.registry.module.backend.BackendServlet.class),
// Notification of Registered Domain Names (NORDN)
route("/_dr/task/nordnUpload",
com.google.domain.registry.module.backend.BackendServlet.class),
route("/_dr/task/nordnVerify",
com.google.domain.registry.module.backend.BackendServlet.class),
// Process DNS pull queue
route("/_dr/task/writeDns",
com.google.domain.registry.module.backend.BackendServlet.class),
// Registrar Console
route("/registrar", com.google.domain.registry.module.frontend.FrontendServlet.class),
route("/registrar-settings",
com.google.domain.registry.ui.server.registrar.RegistrarServlet.class),
route("/registrar-payment",
com.google.domain.registry.module.frontend.FrontendServlet.class),
route("/registrar-payment-setup",
com.google.domain.registry.module.frontend.FrontendServlet.class));
private final TestServer server;
/** @see TestServer#TestServer(HostAndPort, java.util.Map, Iterable) */
public RegistryTestServer(HostAndPort address) {
server = new TestServer(address, RUNFILES, ROUTES);
}
/** @see TestServer#start() */
public void start() {
server.start();
}
/** @see TestServer#process() */
public void process() throws InterruptedException {
server.process();
}
/** @see TestServer#stop() */
public void stop() {
server.stop();
}
/** @see TestServer#getUrl(java.lang.String) */
public URL getUrl(String path) {
return server.getUrl(path);
}
}

View file

@ -0,0 +1,172 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.domain.registry.server;
import com.google.common.collect.ImmutableList;
import com.google.common.net.HostAndPort;
import com.google.domain.registry.testing.AppEngineRule;
import com.google.domain.registry.testing.UserInfo;
import com.google.domain.registry.tools.params.HostAndPortParameter;
import com.google.domain.registry.ui.ConsoleDebug;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.util.List;
/** Command-line interface for {@link RegistryTestServer}. */
public final class RegistryTestServerMain {
private static final String RESET = "\u001b[0m";
private static final String BLUE = "\u001b[1;34m";
private static final String PURPLE = "\u001b[1;35m";
private static final String PINK = "\u001b[1;38;5;205m";
private static final String LIGHT_PURPLE = "\u001b[38;5;139m";
private static final String ORANGE = "\u001b[1;38;5;172m";
@Parameter(
names = "--mode",
description = "UI console debug mode. RAW allows live editing; DEBUG allows rename testing.")
private ConsoleDebug mode = ConsoleDebug.RAW;
@Parameter(
names = "--address",
description = "Listening address.",
validateWith = HostAndPortParameter.class)
private HostAndPort address = HostAndPort.fromString("[::]:8080");
@Parameter(
names = "--fixtures",
description = "Fixtures to load into the datastore.")
private List<Fixture> fixtures = ImmutableList.of(Fixture.BASIC);
@Parameter(
names = "--login_email",
description = "Login email address for App Engine Local User Service.")
private String loginEmail = "Marla.Singer@crr.com";
@Parameter(
names = "--login_user_id",
description = "GAE User ID for App Engine Local User Service.")
private String loginUserId = AppEngineRule.THE_REGISTRAR_GAE_USER_ID;
@Parameter(
names = "--login_is_admin",
description = "Should logged in user be an admin for App Engine Local User Service.",
arity = 1)
private boolean loginIsAdmin = true;
@Parameter(
names = "--jetty_debug",
description = "Enables Jetty debug logging.")
private boolean jettyDebug;
@Parameter(
names = "--jetty_verbose",
description = "Enables Jetty verbose logging.")
private boolean jettyVerbose;
@Parameter(
names = {"-h", "--help"},
description = "Display help and list flags for this command.",
help = true)
private boolean help;
public static void main(String[] args) throws Throwable {
RegistryTestServerMain serverMain = new RegistryTestServerMain();
JCommander jCommander = new JCommander(serverMain);
jCommander.setProgramName("dr-run server");
jCommander.parse(args);
if (serverMain.help) {
jCommander.usage();
return;
}
serverMain.run();
}
private void run() throws Throwable {
ConsoleDebug.set(mode);
if (jettyDebug) {
System.setProperty("DEBUG", "true");
}
if (jettyVerbose) {
System.setProperty("VERBOSE", "true");
}
System.out.printf("\n"
+ " CHARLESTON ROAD REGISTRY SHARED REGISTRATION SYSTEM\n"
+ " ICANN-GTLD-AGB-20120604\n\n%s"
+ " ▓█████▄ ▒█████ ███▄ ▄███▓ ▄▄▄ ██▓ ███▄ █\n"
+ " ▒██▀ ██▌▒██▒ ██▒▓██▒▀█▀ ██▒▒████▄ ▓██▒ ██ ▀█ █\n"
+ " ░██ █▌▒██░ ██▒▓██ ▓██░▒██ ▀█▄ ▒██▒▓██ ▀█ ██▒\n"
+ " ░▓█▄ ▌▒██ ██░▒██ ▒██ ░██▄▄▄▄██ ░██░▓██▒ ▐▌██▒\n"
+ " ░▒████▓ ░ ████▓▒░▒██▒ ░██▒ ▓█ ▓██▒░██░▒██░ ▓██░\n"
+ " ▒▒▓ ▒ ░ ▒░▒░▒░ ░ ▒░ ░ ░ ▒▒ ▓▒█░░▓ ░ ▒░ ▒ ▒\n"
+ " ░ ▒ ▒ ░ ▒ ▒░ ░ ░ ░ ▒ ▒▒ ░ ▒ ░░ ░░ ░ ▒░\n"
+ " ░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ▒ ▒ ░ ░ ░ ░\n"
+ " ░ ░ ░ ░ ░ ░ ░ ░\n"
+ "\n%s"
+ " ██▀███ ▓█████ ▄████ ██▓ ██████ ▄▄▄█████▓ ██▀███ ▓██ ██▓\n"
+ " ▓██ ▒ ██▒▓█ ▀ ██▒ ▀█▒▓██▒▒██ ▒ ▓ ██▒ ▓▒▓██ ▒ ██▒▒██ ██▒\n"
+ " ▓██ ░▄█ ▒▒███ ▒██░▄▄▄░▒██▒░ ▓██▄ ▒ ▓██░ ▒░▓██ ░▄█ ▒ ▒██ ██░\n"
+ " ▒██▀▀█▄ ▒▓█ ▄ ░▓█ ██▓░██░ ▒ ██▒░ ▓██▓ ░ ▒██▀▀█▄ ░ ▐██▓░\n"
+ " ░██▓ ▒██▒░▒████▒░▒▓███▀▒░██░▒██████▒▒ ▒██▒ ░ ░██▓ ▒██▒ ░ ██▒▓░\n"
+ " ░ ▒▓ ░▒▓░░░ ▒░ ░ ░▒ ▒ ░▓ ▒ ▒▓▒ ▒ ░ ▒ ░░ ░ ▒▓ ░▒▓░ ██▒▒▒\n"
+ " ░▒ ░ ▒░ ░ ░ ░ ░ ░ ▒ ░░ ░▒ ░ ░ ░ ░▒ ░ ▒░▓██ ░▒░\n"
+ " ░░ ░ ░ ░ ░ ░ ▒ ░░ ░ ░ ░ ░░ ░ ▒ ▒ ░░\n"
+ " ░ ░ ░ ░ ░ ░ ░ ░ ░\n"
+ " ░ ░\n%s"
+ "(✿◕ ‿◕ )%s\n",
LIGHT_PURPLE, ORANGE, PINK, RESET);
final RegistryTestServer server = new RegistryTestServer(address);
Statement runner = new Statement() {
@Override
public void evaluate() throws InterruptedException {
System.out.printf("%sLoading datastore fixtures...%s\n", BLUE, RESET);
for (Fixture fixture : fixtures) {
fixture.load();
}
System.out.printf("%sStarting Jetty6 HTTP Server...%s\n", BLUE, RESET);
server.start();
System.out.printf("%sListening on: %s%s\n", PURPLE, server.getUrl("/"), RESET);
try {
while (true) {
server.process();
}
} finally {
server.stop();
}
}};
System.out.printf("%sLoading AppEngineRule...%s\n", BLUE, RESET);
AppEngineRule.builder()
.withDatastore()
.withUrlFetch()
.withTaskQueue()
.withLocalModules()
.withUserService(loginIsAdmin
? UserInfo.createAdmin(loginEmail, loginUserId)
: UserInfo.create(loginEmail, loginUserId))
.build()
.apply(runner, Description.EMPTY)
.evaluate();
}
private RegistryTestServerMain() {}
}

View file

@ -0,0 +1,34 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.domain.registry.server;
import com.google.auto.value.AutoValue;
import javax.servlet.http.HttpServlet;
/** Pair of servlet path and servlet instance object. */
@AutoValue
public abstract class Route {
abstract String path();
abstract Class<? extends HttpServlet> servletClass();
/** Creates a new route mapping between a path (may have wildcards) and a servlet. */
public static Route route(String path, Class<? extends HttpServlet> servletClass) {
return new AutoValue_Route(path, servletClass);
}
Route() {}
}

View file

@ -0,0 +1,84 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.domain.registry.server;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Suppliers.memoize;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.Uninterruptibles;
import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import javax.annotation.Nullable;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet that wraps a servlet and delegates request execution to a queue.
*
* @see TestServer
*/
public final class ServletWrapperDelegatorServlet extends HttpServlet {
private final Queue<FutureTask<Void>> requestQueue;
private final Supplier<HttpServlet> servlet;
ServletWrapperDelegatorServlet(
Class<? extends HttpServlet> servletClass,
Queue<FutureTask<Void>> requestQueue) {
this.servlet = lazilyInstantiate(checkNotNull(servletClass, "servletClass"));
this.requestQueue = checkNotNull(requestQueue, "requestQueue");
}
@Override
public void service(final HttpServletRequest req, final HttpServletResponse rsp)
throws ServletException, IOException {
FutureTask<Void> task = new FutureTask<>(new Callable<Void>() {
@Nullable
@Override
public Void call() throws ServletException, IOException {
servlet.get().service(req, rsp);
return null;
}});
requestQueue.add(task);
try {
Uninterruptibles.getUninterruptibly(task);
} catch (ExecutionException e) {
Throwables.propagateIfInstanceOf(e.getCause(), ServletException.class);
Throwables.propagateIfInstanceOf(e.getCause(), IOException.class);
throw Throwables.propagate(e.getCause());
}
}
private static <T> Supplier<T> lazilyInstantiate(final Class<? extends T> clazz) {
return memoize(new Supplier<T>() {
@Override
public T get() {
try {
return clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}});
}
}

View file

@ -0,0 +1,164 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.domain.registry.server;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Verify.verify;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.MediaType;
import com.google.common.primitives.Ints;
import com.google.domain.registry.util.FormattingLogger;
import org.mortbay.jetty.servlet.ServletHolder;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.annotation.PostConstruct;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet for serving static resources on a Jetty development server path prefix.
*
* <p>This servlet can serve either a single file or the contents of a directory.
*
* <p><b>Note:</b> This code should never be used in production. It's for testing purposes only.
*/
public final class StaticResourceServlet extends HttpServlet {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
private static final MediaType DEFAULT_MIME_TYPE = MediaType.OCTET_STREAM;
private static final ImmutableMap<String, MediaType> MIMES_BY_EXTENSION =
new ImmutableMap.Builder<String, MediaType>()
.put("css", MediaType.CSS_UTF_8)
.put("csv", MediaType.CSV_UTF_8)
.put("gif", MediaType.GIF)
.put("html", MediaType.HTML_UTF_8)
.put("jpeg", MediaType.JPEG)
.put("jpg", MediaType.JPEG)
.put("js", MediaType.JAVASCRIPT_UTF_8)
.put("json", MediaType.JSON_UTF_8)
.put("png", MediaType.PNG)
.put("txt", MediaType.PLAIN_TEXT_UTF_8)
.put("xml", MediaType.XML_UTF_8)
.build();
/**
* Creates a servlet holder for this servlet so it can be used with Jetty.
*
* @param prefix servlet path starting with a slash and ending with {@code "/*"} if {@code root}
* is a directory
* @param root file or root directory to serve
*/
public static ServletHolder create(String prefix, Path root) {
root = root.toAbsolutePath();
checkArgument(Files.exists(root), "Root must exist: %s", root);
checkArgument(prefix.startsWith("/"), "Prefix must start with a slash: %s", prefix);
ServletHolder holder = new ServletHolder(StaticResourceServlet.class);
holder.setInitParameter("root", root.toString());
if (Files.isDirectory(root)) {
checkArgument(prefix.endsWith("/*"),
"Prefix (%s) must end with /* since root (%s) is a directory", prefix, root);
holder.setInitParameter("prefix", prefix.substring(0, prefix.length() - 1));
} else {
holder.setInitParameter("prefix", prefix);
}
return holder;
}
private Optional<FileServer> fileServer = Optional.absent();
@Override
@PostConstruct
public void init(ServletConfig config) {
Path root = Paths.get(config.getInitParameter("root"));
String prefix = config.getInitParameter("prefix");
verify(prefix.startsWith("/"));
boolean isDirectory = Files.isDirectory(root);
verify(!isDirectory || isDirectory && prefix.endsWith("/"));
fileServer = Optional.of(new FileServer(root, prefix));
}
@Override
public void doHead(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
fileServer.get().doHead(req, rsp);
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
fileServer.get().doGet(req, rsp);
}
private static final class FileServer {
private final Path root;
private final String prefix;
FileServer(Path root, String prefix) {
this.root = root;
this.prefix = prefix;
}
Optional<Path> doHead(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
verify(req.getRequestURI().startsWith(prefix));
Path file = root.resolve(req.getRequestURI().substring(prefix.length())).normalize();
if (!file.startsWith(root)) {
logger.infofmt("Evil request: %s (%s) (%s + %s)", req.getRequestURI(), file, root, prefix);
rsp.sendError(SC_BAD_REQUEST, "Naughty naughty!");
return Optional.absent();
}
if (!Files.exists(file)) {
logger.infofmt("Not found: %s (%s)", req.getRequestURI(), file);
rsp.sendError(SC_NOT_FOUND, "Not found");
return Optional.absent();
}
if (Files.isDirectory(file)) {
logger.infofmt("Directory listing forbidden: %s (%s)", req.getRequestURI(), file);
rsp.sendError(SC_FORBIDDEN, "No directory listing");
return Optional.absent();
}
rsp.setContentType(
firstNonNull(
MIMES_BY_EXTENSION.get(getExtension(file.getFileName().toString())),
DEFAULT_MIME_TYPE)
.toString());
rsp.setContentLength(Ints.checkedCast(Files.size(file)));
return Optional.of(file);
}
void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
for (Path file : doHead(req, rsp).asSet()) {
rsp.getOutputStream().write(Files.readAllBytes(file));
}
}
}
private static String getExtension(String filename) {
int dot = filename.lastIndexOf('.');
return dot == -1 ? "" : filename.substring(dot + 1);
}
}

View file

@ -0,0 +1,189 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.domain.registry.server;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.domain.registry.util.NetworkUtils.getCanonicalHostName;
import com.google.common.base.Throwables;
import com.google.common.net.HostAndPort;
import com.google.common.util.concurrent.Callables;
import com.google.common.util.concurrent.SimpleTimeLimiter;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.DefaultServlet;
import org.mortbay.jetty.servlet.ServletHolder;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServlet;
/**
* HTTP server that serves static content and handles servlet requests in the calling thread.
*
* <p>Using this server is similar to to other server classes, in that it has {@link #start()} and
* {@link #stop()} methods. However a {@link #process()} method was added, which is used to process
* requests made to servlets (not static files) in the calling thread.
*
* <p><b>Note:</b> This server is intended for development purposes. For the love all that is good,
* do not make this public facing.
*
* <h3>Implementation Details</h3>
*
* <p>Jetty6 is multithreaded and provides no mechanism for controlling which threads execute your
* requests. HttpServer solves this problem by wrapping all the servlets provided to the constructor
* inside {@link ServletWrapperDelegatorServlet}. When requests come in, a {@link FutureTask} will
* be sent back to this class using a {@link LinkedBlockingDeque} message queue. Those messages are
* then consumed by the {@code process()} method.
*
* <p>The reason why this is necessary is because the App Engine local testing services (created by
* {@code LocalServiceTestHelper}) only apply to a single thread (probably to allow multi-threaded
* tests). So when Jetty creates random threads to handle requests, they won't have access to the
* datastore and other stuff.
*/
public final class TestServer {
private static final int DEFAULT_PORT = 80;
private static final String CONTEXT_PATH = "/";
private static final int STARTUP_TIMEOUT_MS = 5000;
private static final int SHUTDOWN_TIMEOUT_MS = 5000;
private final HostAndPort urlAddress;
private final Server server = new Server();
private final BlockingQueue<FutureTask<Void>> requestQueue = new LinkedBlockingDeque<>();
/**
* Creates a new instance, but does not begin serving.
*
* @param address socket bind address
* @param runfiles map of server paths to local directories or files, to be served statically
* @param routes list of servlet endpoints
*/
public TestServer(HostAndPort address, Map<String, Path> runfiles, Iterable<Route> routes) {
urlAddress = createUrlAddress(address);
server.addConnector(createConnector(address));
server.addHandler(createHandler(runfiles, routes));
}
/** Starts the HTTP server in a new thread and returns once it's online. */
public void start() {
try {
server.start();
} catch (Exception e) {
throw Throwables.propagate(e);
}
UrlChecker.waitUntilAvailable(getUrl("/healthz"), STARTUP_TIMEOUT_MS);
}
/**
* Processes a single servlet request.
*
* <p>This method should be called from within a loop.
*
* @throws InterruptedException if this thread was interrupted while waiting for a request.
*/
public void process() throws InterruptedException {
requestQueue.take().run();
}
/**
* Adds a fake entry to this server's event loop.
*
* <p>This is useful in situations when a random thread wants {@link #process()} to return in the
* main event loop, for post-request processing.
*/
public void ping() {
requestQueue.add(new FutureTask<>(Callables.<Void>returning(null)));
}
/** Stops the HTTP server. */
public void stop() {
try {
new SimpleTimeLimiter().callWithTimeout(new Callable<Void>() {
@Nullable
@Override
public Void call() throws Exception {
server.stop();
return null;
}
}, SHUTDOWN_TIMEOUT_MS, TimeUnit.MILLISECONDS, true);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
/** Returns a URL that can be used to communicate with this server. */
public URL getUrl(String path) {
checkArgument(path.startsWith("/"), "Path must start with a slash: %s", path);
try {
return new URL(String.format("http://%s%s", urlAddress, path));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
private Context createHandler(Map<String, Path> runfiles, Iterable<Route> routes) {
Context context = new Context(server, CONTEXT_PATH, Context.SESSIONS);
context.addServlet(new ServletHolder(HealthzServlet.class), "/healthz");
for (Map.Entry<String, Path> runfile : runfiles.entrySet()) {
context.addServlet(
StaticResourceServlet.create(runfile.getKey(), runfile.getValue()),
runfile.getKey());
}
for (Route route : routes) {
context.addServlet(new ServletHolder(wrapServlet(route.servletClass())), route.path());
}
ServletHolder holder = new ServletHolder(DefaultServlet.class);
holder.setInitParameter("aliases", "1");
context.addServlet(holder, "/*");
return context;
}
private HttpServlet wrapServlet(Class<? extends HttpServlet> servletClass) {
return new ServletWrapperDelegatorServlet(servletClass, requestQueue);
}
private static Connector createConnector(HostAndPort address) {
SocketConnector connector = new SocketConnector();
connector.setHost(address.getHostText());
connector.setPort(address.getPortOrDefault(DEFAULT_PORT));
return connector;
}
/** Converts a bind address into an address that other machines can use to connect here. */
private static HostAndPort createUrlAddress(HostAndPort address) {
if (address.getHostText().equals("::") || address.getHostText().equals("0.0.0.0")) {
return address.getPortOrDefault(DEFAULT_PORT) == DEFAULT_PORT
? HostAndPort.fromHost(getCanonicalHostName())
: HostAndPort.fromParts(getCanonicalHostName(), address.getPort());
} else {
return address.getPortOrDefault(DEFAULT_PORT) == DEFAULT_PORT
? HostAndPort.fromHost(address.getHostText())
: address;
}
}
}

View file

@ -0,0 +1,70 @@
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.domain.registry.server;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.SimpleTimeLimiter;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
final class UrlChecker {
private static final int READ_TIMEOUT_MS = 1000;
private static final int CONNECT_TIMEOUT_MS = 500;
/** Probes {@code url} until it becomes available. */
static void waitUntilAvailable(final URL url, int timeoutMs) {
try {
new SimpleTimeLimiter().callWithTimeout(new Callable<Void>() {
@Nullable
@Override
public Void call() throws InterruptedException, IOException {
int exponentialBackoffMs = 1;
while (true) {
if (isAvailable(url)) {
return null;
}
Thread.sleep(exponentialBackoffMs *= 2);
}
}
}, timeoutMs, TimeUnit.MILLISECONDS, true);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
/** Returns {@code true} if page is available and returns {@code 200 OK}. */
static boolean isAvailable(URL url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
connection.setReadTimeout(READ_TIMEOUT_MS);
try {
connection.connect();
return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
} catch (IOException e) {
return false;
} finally {
connection.disconnect();
}
}
private UrlChecker() {}
}