All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.milton.http.HttpManager Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 io.milton.http;

import io.milton.common.Utils;
import io.milton.resource.Resource;
import io.milton.http.entity.EntityTransport;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.ConflictException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.http11.CustomPostHandler;
import io.milton.http.http11.Http11ResponseHandler;
import io.milton.http.webdav.WebDavResponseHandler;
import io.milton.common.Stoppable;
import io.milton.event.EventManager;
import io.milton.event.RequestEvent;
import io.milton.event.ResponseEvent;
import io.milton.http.Request.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Milton's main class. All the servlets and stuff is just fluff, this is where
 * stuff really starts to happen
 *
 *
 * @author brad
 */
public class HttpManager {

	private static final Logger log = LoggerFactory.getLogger(HttpManager.class);

	private static final ThreadLocal tlRequest = new ThreadLocal<>();
	private static final ThreadLocal tlResponse = new ThreadLocal<>();
	private static final Map mapOfRequestsByThread = new ConcurrentHashMap<>();

	public static RequestInfo getRequestDataForThread(Thread th) {
		return mapOfRequestsByThread.get(th);
	}

	public static String decodeUrl(String s) {
		return Utils.decodePath(s);
	}

	public static Request request() {
		return tlRequest.get();
	}

	public static Response response() {
		return tlResponse.get();
	}
	private final ProtocolHandlers handlers;
	private final List filters;
	private final List eventListeners = new ArrayList<>();
	private final ResourceFactory resourceFactory;
	private final Http11ResponseHandler responseHandler;
	private final EventManager eventManager;
	private final List shutdownHandlers;
	private final EntityTransport entityTransport;
	private Map methodHandlers;

	/**
	 * Instead of using this constructor directly, consider using the
	 * HttpManagerConfig builder class
	 *
	 * @param resourceFactory
	 * @param responseHandler
	 * @param handlers
	 * @param entityTransport
	 * @param filters
	 * @param eventManager
	 * @param shutdownHandlers
	 */
	public HttpManager(ResourceFactory resourceFactory, WebDavResponseHandler responseHandler, ProtocolHandlers handlers, EntityTransport entityTransport, List filters, EventManager eventManager, List shutdownHandlers) {
		this.responseHandler = responseHandler;
		this.handlers = handlers;
		this.resourceFactory = resourceFactory;
		this.entityTransport = entityTransport;
		this.filters = filters;
		this.eventManager = eventManager;
		this.shutdownHandlers = shutdownHandlers;
		initHandlers();
	}

	private void initHandlers() {
		this.methodHandlers = new ConcurrentHashMap<>();
		for (HttpExtension ext : handlers) {
			for (Handler h : ext.getHandlers()) {
				for (String m : h.getMethods()) {
					this.methodHandlers.put(m, h);
				}
			}
		}
	}

	public void sendResponseEntity(Response response) throws Exception {
		entityTransport.sendResponseEntity(response);
	}

	public void closeResponse(Response response) {
		entityTransport.closeResponse(response);

	}

	public Handler getMethodHandler(Request.Method m) {
		return methodHandlers.get(m.code);
	}

	public ResourceFactory getResourceFactory() {
		return resourceFactory;
	}

	public void process(Request request, Response response) {
		if (request == null) {
			throw new RuntimeException("request is null");
		}
		String host = request.getHostHeader();
		if (host == null) {
			host = "";
		}

		if (log.isInfoEnabled()) {
			log.info(request.getMethod() + " :: " + host + "//" + request.getAbsolutePath() + " start");
		}

		try {
			setThreadAffinityData(request, response);
			try {
				fireRequestEvent(request);
			} catch (ConflictException ex) {
				responseHandler.respondConflict(null, response, request, null);
			} catch (BadRequestException ex) {
				responseHandler.respondBadRequest(null, response, request);
			} catch (NotAuthorizedException ex) {
				responseHandler.respondUnauthorised(null, response, request);
			}

			FilterChain chain = new FilterChain(this);
			long tm = System.currentTimeMillis();
			chain.process(request, response);
			try {
				tm = System.currentTimeMillis() - tm;
				if (log.isInfoEnabled()) {
					log.info(request.getMethod() + " :: " + host + "//" + request.getAbsolutePath() + " finished " + tm + "ms, Status:" + response.getStatus() + ", Length:" + response.getContentLength());
				}
				fireResponseEvent(request, response, tm);
			} catch (ConflictException | NotAuthorizedException | BadRequestException ex) {
				log.warn("exception thrown from event handler after response is complete", ex);
			}
		} finally {
			clearThreadAffinity();
		}
	}

	private void clearThreadAffinity() {
		tlRequest.remove();
		tlResponse.remove();
		try {
			mapOfRequestsByThread.remove(Thread.currentThread());
		} catch (Throwable e) {
			log.info("Couldnt clear thread affinity request data");
		}
	}

	private void setThreadAffinityData(Request request, Response response) {
		tlRequest.set(request);
		tlResponse.set(response);
		try {
			RequestInfo info = new RequestInfo(request.getMethod(), request.getAbsoluteUrl(), new Date());
			mapOfRequestsByThread.put(Thread.currentThread(), info);
		} catch (Throwable e) {
			log.info("Couldnt set thread affinity request data");
		}
	}

	public void addEventListener(EventListener l) {
		eventListeners.add(l);
	}

	public void removeEventListener(EventListener l) {
		eventListeners.remove(l);
	}

	public void onProcessResourceFinish(Request request, Response response, Resource resource, long duration) {
		for (EventListener l : eventListeners) {
			l.onProcessResourceFinish(request, response, resource, duration);
		}
	}

	public void onProcessResourceStart(Request request, Response response, Resource resource) {
		for (EventListener l : eventListeners) {
			l.onProcessResourceStart(request, response, resource);
		}
	}

	public void onPost(Request request, Response response, Resource resource, Map params, Map files) {
		for (EventListener l : eventListeners) {
			l.onPost(request, response, resource, params, files);
		}
	}

	public void onGet(Request request, Response response, Resource resource, Map params) {
		for (EventListener l : eventListeners) {
			l.onGet(request, response, resource, params);
		}
	}

	public List getFilters() {
		return new ArrayList(filters);
	}

	public Collection getAllHandlers() {
		return this.methodHandlers.values();
	}

	public Http11ResponseHandler getResponseHandler() {
		return responseHandler;
	}

	public ProtocolHandlers getHandlers() {
		return handlers;
	}

	public boolean isEnableExpectContinue() {
		return handlers.isEnableExpectContinue();
	}

	public void setEnableExpectContinue(boolean enableExpectContinue) {
		handlers.setEnableExpectContinue(enableExpectContinue);
	}

	public EventManager getEventManager() {
		return eventManager;
	}

	private void fireRequestEvent(Request request) throws ConflictException, BadRequestException, NotAuthorizedException {
		if (eventManager == null) {
			return;
		}
		eventManager.fireEvent(new RequestEvent(request));
	}

	private void fireResponseEvent(Request request, Response response, long duration) throws ConflictException, BadRequestException, NotAuthorizedException {
		if (eventManager == null) {
			return;
		}
		eventManager.fireEvent(new ResponseEvent(request, response, duration));

	}

	public List getCustomPostHandlers() {
		List list = new ArrayList<>();
		for (HttpExtension p : this.handlers) {
			if (p.getCustomPostHandlers() != null) {
				list.addAll(p.getCustomPostHandlers());
			}
		}
		return list;
	}

	public final void shutdown() {
		for (Stoppable stoppable : shutdownHandlers) {
			try {
				stoppable.stop();
			} catch (Throwable e) {
				log.warn("Exception stopping: " + stoppable.getClass(), e);
			}
		}
	}

	public EntityTransport getEntityTransport() {
		return entityTransport;
	}

	public static class RequestInfo {

		private final Method method;
		private final String url;
		private final Date started;

		public RequestInfo(Method method, String url, Date started) {
			this.method = method;
			this.url = url;
			this.started = started;
		}

		public Method getMethod() {
			return method;
		}

		public Date getStarted() {
			return started;
		}

		public String getUrl() {
			return url;
		}

		public long getDurationMillis() {
			return System.currentTimeMillis() - started.getTime();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy