diff options
Diffstat (limited to 'src/main/java/org/traccar/web')
-rw-r--r-- | src/main/java/org/traccar/web/ConsoleServlet.java | 16 | ||||
-rw-r--r-- | src/main/java/org/traccar/web/ThrottlingFilter.java | 53 | ||||
-rw-r--r-- | src/main/java/org/traccar/web/WebInjectionManagerFactory.java | 50 | ||||
-rw-r--r-- | src/main/java/org/traccar/web/WebModule.java | 30 | ||||
-rw-r--r-- | src/main/java/org/traccar/web/WebServer.java | 105 |
5 files changed, 200 insertions, 54 deletions
diff --git a/src/main/java/org/traccar/web/ConsoleServlet.java b/src/main/java/org/traccar/web/ConsoleServlet.java index 0f3dcd8fd..902a4f7a9 100644 --- a/src/main/java/org/traccar/web/ConsoleServlet.java +++ b/src/main/java/org/traccar/web/ConsoleServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import org.h2.server.web.ConnectionInfo; import org.h2.server.web.WebServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; +import org.traccar.config.Config; import org.traccar.config.Keys; import java.lang.reflect.Field; @@ -30,6 +30,12 @@ public class ConsoleServlet extends WebServlet { private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleServlet.class); + private final Config config; + + public ConsoleServlet(Config config) { + this.config = config; + } + @Override public void init() { super.init(); @@ -40,9 +46,9 @@ public class ConsoleServlet extends WebServlet { org.h2.server.web.WebServer server = (org.h2.server.web.WebServer) field.get(this); ConnectionInfo connectionInfo = new ConnectionInfo("Traccar|" - + Context.getConfig().getString(Keys.DATABASE_DRIVER) + "|" - + Context.getConfig().getString(Keys.DATABASE_URL) + "|" - + Context.getConfig().getString(Keys.DATABASE_USER)); + + config.getString(Keys.DATABASE_DRIVER) + "|" + + config.getString(Keys.DATABASE_URL) + "|" + + config.getString(Keys.DATABASE_USER)); Method method; diff --git a/src/main/java/org/traccar/web/ThrottlingFilter.java b/src/main/java/org/traccar/web/ThrottlingFilter.java new file mode 100644 index 000000000..054af652f --- /dev/null +++ b/src/main/java/org/traccar/web/ThrottlingFilter.java @@ -0,0 +1,53 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * 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 org.traccar.web; + +import org.eclipse.jetty.servlets.DoSFilter; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +@Singleton +public class ThrottlingFilter extends DoSFilter { + + @Inject + private Config config; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + super.init(filterConfig); + if (config.hasKey(Keys.WEB_MAX_REQUESTS_PER_SECOND)) { + setMaxRequestsPerSec(config.getInteger(Keys.WEB_MAX_REQUESTS_PER_SECOND)); + } + } + + @Override + protected String extractUserId(ServletRequest request) { + HttpSession session = ((HttpServletRequest) request).getSession(false); + if (session != null) { + var userId = session.getAttribute("userId"); + return userId != null ? userId.toString() : null; + } + return null; + } +} diff --git a/src/main/java/org/traccar/web/WebInjectionManagerFactory.java b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java new file mode 100644 index 000000000..14d9d3dbc --- /dev/null +++ b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * 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 org.traccar.web; + +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InjectionManagerFactory; +import org.jvnet.hk2.guice.bridge.api.GuiceBridge; +import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge; +import org.traccar.Main; + +import javax.annotation.Priority; + +@Priority(20) +public class WebInjectionManagerFactory implements InjectionManagerFactory { + + private final InjectionManagerFactory originalFactory = new Hk2InjectionManagerFactory(); + + private InjectionManager injectGuiceBridge(InjectionManager injectionManager) { + var serviceLocator = injectionManager.getInstance(ServiceLocator.class); + GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator); + var guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class); + guiceBridge.bridgeGuiceInjector(Main.getInjector()); + return injectionManager; + } + + @Override + public InjectionManager create() { + return injectGuiceBridge(originalFactory.create()); + } + + @Override + public InjectionManager create(Object parent) { + return injectGuiceBridge(originalFactory.create(parent)); + } +} diff --git a/src/main/java/org/traccar/web/WebModule.java b/src/main/java/org/traccar/web/WebModule.java new file mode 100644 index 000000000..0722c5d1e --- /dev/null +++ b/src/main/java/org/traccar/web/WebModule.java @@ -0,0 +1,30 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * 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 org.traccar.web; + +import com.google.inject.servlet.ServletModule; +import org.traccar.api.AsyncSocketServlet; +import org.traccar.api.MediaFilter; + +public class WebModule extends ServletModule { + + @Override + protected void configureServlets() { + filter("/api/*").through(ThrottlingFilter.class); + filter("/api/media/*").through(MediaFilter.class); + serve("/api/socket").with(AsyncSocketServlet.class); + } +} diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java index 604edfedc..79d19cc9b 100644 --- a/src/main/java/org/traccar/web/WebServer.java +++ b/src/main/java/org/traccar/web/WebServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package org.traccar.web; +import com.google.inject.Injector; +import com.google.inject.servlet.GuiceFilter; import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; @@ -40,37 +42,42 @@ import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.Context; -import org.traccar.api.DateParameterConverterProvider; -import org.traccar.config.Config; -import org.traccar.api.AsyncSocketServlet; +import org.traccar.LifecycleObject; import org.traccar.api.CorsResponseFilter; -import org.traccar.api.MediaFilter; -import org.traccar.api.ObjectMapperProvider; +import org.traccar.api.DateParameterConverterProvider; import org.traccar.api.ResourceErrorHandler; -import org.traccar.api.SecurityRequestFilter; import org.traccar.api.resource.ServerResource; +import org.traccar.api.security.SecurityRequestFilter; +import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.helper.ObjectMapperContextResolver; import javax.servlet.DispatcherType; import javax.servlet.ServletException; import javax.servlet.SessionCookieConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.sql.DataSource; import java.io.File; import java.io.IOException; import java.io.Writer; import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.EnumSet; -public class WebServer { +public class WebServer implements LifecycleObject { private static final Logger LOGGER = LoggerFactory.getLogger(WebServer.class); - private Server server; - - private void initServer(Config config) { + private final Injector injector; + private final Config config; + private final Server server; + public WebServer(Injector injector, Config config) { + this.injector = injector; + this.config = config; String address = config.getString(Keys.WEB_ADDRESS); int port = config.getInteger(Keys.WEB_PORT); if (address == null) { @@ -78,39 +85,42 @@ public class WebServer { } else { server = new Server(new InetSocketAddress(address, port)); } - } - - public WebServer(Config config) { - - initServer(config); ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); + JettyWebSocketServletContainerInitializer.configure(servletHandler, null); + servletHandler.addFilter(GuiceFilter.class, "/*", EnumSet.allOf(DispatcherType.class)); - initApi(config, servletHandler); - initSessionConfig(config, servletHandler); + initApi(servletHandler); + initSessionConfig(servletHandler); if (config.getBoolean(Keys.WEB_CONSOLE)) { - servletHandler.addServlet(new ServletHolder(new ConsoleServlet()), "/console/*"); + servletHandler.addServlet(new ServletHolder(new ConsoleServlet(config)), "/console/*"); } - initWebApp(config, servletHandler); + initWebApp(servletHandler); servletHandler.setErrorHandler(new ErrorHandler() { @Override protected void handleErrorPage( HttpServletRequest request, Writer writer, int code, String message) throws IOException { - writer.write("<!DOCTYPE><html><head><title>Error</title></head><html><body>" - + code + " - " + HttpStatus.getMessage(code) + "</body></html>"); + Path index = Paths.get(config.getString(Keys.WEB_PATH), "index.html"); + if (code == HttpStatus.NOT_FOUND_404 + && !request.getPathInfo().startsWith("/api/") && Files.exists(index)) { + writer.write(Files.readString(index)); + } else { + writer.write("<!DOCTYPE><html><head><title>Error</title></head><html><body>" + + code + " - " + HttpStatus.getMessage(code) + "</body></html>"); + } } }); HandlerList handlers = new HandlerList(); - initClientProxy(config, handlers); + initClientProxy(handlers); handlers.addHandler(servletHandler); handlers.addHandler(new GzipHandler()); server.setHandler(handlers); - if (config.getBoolean(Keys.WEB_REQUEST_LOG_ENABLE)) { + if (config.hasKey(Keys.WEB_REQUEST_LOG_PATH)) { RequestLogWriter logWriter = new RequestLogWriter(config.getString(Keys.WEB_REQUEST_LOG_PATH)); logWriter.setAppend(true); logWriter.setRetainDays(config.getInteger(Keys.WEB_REQUEST_LOG_RETAIN_DAYS)); @@ -119,7 +129,7 @@ public class WebServer { } } - private void initClientProxy(Config config, HandlerList handlers) { + private void initClientProxy(HandlerList handlers) { int port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix("osmand")); if (port != 0) { ServletContextHandler servletHandler = new ServletContextHandler() { @@ -132,14 +142,14 @@ public class WebServer { } } }; - ServletHolder servletHolder = new ServletHolder(new AsyncProxyServlet.Transparent()); + ServletHolder servletHolder = new ServletHolder(AsyncProxyServlet.Transparent.class); servletHolder.setInitParameter("proxyTo", "http://localhost:" + port); servletHandler.addServlet(servletHolder, "/"); handlers.addHandler(servletHandler); } } - private void initWebApp(Config config, ServletContextHandler servletHandler) { + private void initWebApp(ServletContextHandler servletHandler) { ServletHolder servletHolder = new ServletHolder(DefaultServlet.class); servletHolder.setInitParameter("resourceBase", new File(config.getString(Keys.WEB_PATH)).getAbsolutePath()); servletHolder.setInitParameter("dirAllowed", "false"); @@ -155,10 +165,7 @@ public class WebServer { servletHandler.addServlet(servletHolder, "/*"); } - private void initApi(Config config, ServletContextHandler servletHandler) { - servletHandler.addServlet(new ServletHolder(new AsyncSocketServlet()), "/api/socket"); - JettyWebSocketServletContainerInitializer.configure(servletHandler, null); - + private void initApi(ServletContextHandler servletHandler) { String mediaPath = config.getString(Keys.MEDIA_PATH); if (mediaPath != null) { ServletHolder servletHolder = new ServletHolder(DefaultServlet.class); @@ -166,21 +173,27 @@ public class WebServer { servletHolder.setInitParameter("dirAllowed", "false"); servletHolder.setInitParameter("pathInfoOnly", "true"); servletHandler.addServlet(servletHolder, "/api/media/*"); - servletHandler.addFilter(MediaFilter.class, "/api/media/*", EnumSet.allOf(DispatcherType.class)); } ResourceConfig resourceConfig = new ResourceConfig(); resourceConfig.registerClasses( - JacksonFeature.class, ObjectMapperProvider.class, ResourceErrorHandler.class, - SecurityRequestFilter.class, CorsResponseFilter.class, DateParameterConverterProvider.class); + JacksonFeature.class, + ObjectMapperContextResolver.class, + DateParameterConverterProvider.class, + SecurityRequestFilter.class, + CorsResponseFilter.class, + ResourceErrorHandler.class); resourceConfig.packages(ServerResource.class.getPackage().getName()); + if (resourceConfig.getClasses().stream().filter(ServerResource.class::equals).findAny().isEmpty()) { + LOGGER.warn("Failed to load API resources"); + } servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/api/*"); } - private void initSessionConfig(Config config, ServletContextHandler servletHandler) { + private void initSessionConfig(ServletContextHandler servletHandler) { if (config.getBoolean(Keys.WEB_PERSIST_SESSION)) { DatabaseAdaptor databaseAdaptor = new DatabaseAdaptor(); - databaseAdaptor.setDatasource(Context.getDataManager().getDataSource()); + databaseAdaptor.setDatasource(injector.getInstance(DataSource.class)); JDBCSessionDataStoreFactory jdbcSessionDataStoreFactory = new JDBCSessionDataStoreFactory(); jdbcSessionDataStoreFactory.setDatabaseAdaptor(databaseAdaptor); SessionHandler sessionHandler = servletHandler.getSessionHandler(); @@ -214,20 +227,14 @@ public class WebServer { } } - public void start() { - try { - server.start(); - } catch (Exception error) { - LOGGER.warn("Web server start failed", error); - } + @Override + public void start() throws Exception { + server.start(); } - public void stop() { - try { - server.stop(); - } catch (Exception error) { - LOGGER.warn("Web server stop failed", error); - } + @Override + public void stop() throws Exception { + server.stop(); } } |