net.officefloor.server.http.AbstractHttpServicerFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of officeserver_default Show documentation
Show all versions of officeserver_default Show documentation
Default OfficeFloor HTTP Server implementation
/*
* OfficeFloor - http://www.officefloor.net
* Copyright (C) 2005-2018 Daniel Sagenschneider
*
* 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 should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package net.officefloor.server.http;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.function.Supplier;
import net.officefloor.frame.api.escalate.Escalation;
import net.officefloor.frame.api.manage.ProcessManager;
import net.officefloor.server.RequestHandler;
import net.officefloor.server.RequestServicer;
import net.officefloor.server.RequestServicerFactory;
import net.officefloor.server.ResponseWriter;
import net.officefloor.server.SocketServicer;
import net.officefloor.server.SocketServicerFactory;
import net.officefloor.server.http.impl.NonMaterialisedHttpHeaders;
import net.officefloor.server.http.impl.ProcessAwareServerHttpConnectionManagedObject;
import net.officefloor.server.http.parse.HttpRequestParser;
import net.officefloor.server.http.parse.HttpRequestParser.HttpRequestParserMetaData;
import net.officefloor.server.stream.StreamBuffer;
import net.officefloor.server.stream.StreamBufferPool;
import net.officefloor.server.stream.impl.ByteSequence;
/**
* {@link SocketServicerFactory} and {@link RequestServicerFactory} to use the
* {@link HttpRequestParser} to produce {@link ServerHttpConnection} instances
* for servicing.
*
* @author Daniel Sagenschneider
*/
public abstract class AbstractHttpServicerFactory
implements SocketServicerFactory, RequestServicerFactory {
private static final byte[] SPACE = " ".getBytes(ServerHttpConnection.HTTP_CHARSET);
private static byte[] HEADER_EOLN = "\r\n".getBytes(ServerHttpConnection.HTTP_CHARSET);
private static byte[] COLON_SPACE = ": ".getBytes(ServerHttpConnection.HTTP_CHARSET);
private static final HttpHeaderName CONTENT_LENGTH_NAME = new HttpHeaderName("Content-Length");
private static final HttpHeaderName CONTENT_TYPE_NAME = new HttpHeaderName("Content-Type");
private static final ProcessManager FAIL_PROCESSING = () -> {
// nothing to cancel, as already failed
};
/**
* {@link HttpServerLocation}.
*/
private final HttpServerLocation serverLocation;
/**
* Indicates if over secure {@link Socket}.
*/
private final boolean isSecure;
/**
* {@link HttpRequestParserMetaData}.
*/
private final HttpRequestParserMetaData metaData;
/**
* {@link StreamBufferPool} for servicing requests to capture the response
* entity.
*/
private final StreamBufferPool serviceBufferPool;
/**
* Server
{@link HttpHeaderValue}.
*/
private final HttpHeaderValue serverName;
/**
* {@link DateHttpHeaderClock}.
*/
private final DateHttpHeaderClock dateHttpHeaderClock;
/**
* Indicates if include the {@link Escalation} stack trace in the
* {@link HttpResponse}.
*/
private boolean isIncludeEscalationStackTrace;
/**
* Services the {@link ProcessAwareServerHttpConnectionManagedObject}.
*
* @param connection {@link ProcessAwareServerHttpConnectionManagedObject}.
* @return {@link ProcessManager} to servicing the
* {@link ProcessAwareServerHttpConnectionManagedObject}.
* @throws IOException If IO failure.
* @throws HttpException If HTTP failure.
*/
protected abstract ProcessManager service(ProcessAwareServerHttpConnectionManagedObject connection)
throws IOException, HttpException;
/*
* =============== SocketServicerFactory =================
*/
@Override
public SocketServicer createSocketServicer(RequestHandler requestHandler) {
return new HttpServicer(requestHandler);
}
/*
* =============== RequestServicerFactory ================
*/
@Override
public RequestServicer createRequestServicer(SocketServicer socketServicer) {
return (HttpServicer) socketServicer;
}
/**
* Instantiate.
*
* @param serverLocation {@link HttpServerLocation}.
* @param isSecure Indicates if over secure {@link Socket}.
* @param serviceBufferPool {@link StreamBufferPool} used to service
* requests.
* @param metaData {@link HttpRequestParserMetaData}.
* @param serverName Server
* {@link HttpHeaderValue}.
* @param dateHttpHeaderClock {@link DateHttpHeaderClock}.
* @param isIncludeEscalationStackTrace Indicates whether to include the
* {@link Escalation} stack trace in
* {@link HttpResponse}.
*/
public AbstractHttpServicerFactory(HttpServerLocation serverLocation, boolean isSecure,
HttpRequestParserMetaData metaData, StreamBufferPool serviceBufferPool,
HttpHeaderValue serverName, DateHttpHeaderClock dateHttpHeaderClock,
boolean isIncludeEscalationStackTrace) {
this.serverLocation = serverLocation;
this.isSecure = isSecure;
this.metaData = metaData;
this.serviceBufferPool = serviceBufferPool;
this.serverName = serverName;
this.dateHttpHeaderClock = dateHttpHeaderClock;
this.isIncludeEscalationStackTrace = isIncludeEscalationStackTrace;
}
/**
* HTTP servicer.
*/
private class HttpServicer extends HttpRequestParser
implements SocketServicer, RequestServicer {
/**
* {@link RequestHandler}.
*/
private final RequestHandler requestHandler;
/**
* {@link HttpException} in attempting to parse {@link HttpRequest}.
*/
private HttpException parseFailure = null;
/**
* Instantiate.
*
* @param requestHandler {@link RequestHandler}.
*/
private HttpServicer(RequestHandler requestHandler) {
super(AbstractHttpServicerFactory.this.metaData);
this.requestHandler = requestHandler;
}
/*
* ===================== SocketServicer ======================
*/
@Override
public void service(StreamBuffer readBuffer, long bytesRead, boolean isNewBuffer) {
// Add the buffer
this.appendStreamBuffer(readBuffer);
// Parse out the requests
try {
while (this.parse()) {
// Create request from parser
this.requestHandler.handleRequest(this);
}
} catch (HttpException ex) {
// Failed to parse request
this.parseFailure = ex;
this.requestHandler.handleRequest(this);
}
}
/*
* ===================== RequestServicer ====================
*/
@Override
public ProcessManager service(HttpRequestParser request, ResponseWriter responseWriter) {
// Determine if parse failure
if (this.parseFailure != null) {
// Write parse failure
responseWriter.write((responseHead, socketBufferPool) -> {
this.parseFailure.writeHttpResponse(HttpVersion.HTTP_1_1,
AbstractHttpServicerFactory.this.isIncludeEscalationStackTrace, responseHead,
socketBufferPool);
}, null);
return FAIL_PROCESSING;
}
// Request ready, so obtain details
Supplier methodSupplier = this.getMethod();
Supplier requestUriSupplier = this.getRequestURI();
HttpVersion version = this.getVersion();
NonMaterialisedHttpHeaders requestHeaders = this.getHeaders();
ByteSequence requestEntity = this.getEntity();
// Create the HTTP response writer
HttpResponseWriter writer = (responseVersion, status, httpHeader, httpCookie, contentLength,
contentType, content) -> {
// Write the response
responseWriter.write((responseHead, socketBufferPool) -> {
// Write the status line
responseVersion.write(responseHead, socketBufferPool);
StreamBuffer.write(SPACE, 0, SPACE.length, responseHead, socketBufferPool);
status.write(responseHead, socketBufferPool);
StreamBuffer.write(HEADER_EOLN, 0, HEADER_EOLN.length, responseHead, socketBufferPool);
// Write the headers
if (contentType != null) {
CONTENT_TYPE_NAME.write(responseHead, socketBufferPool);
StreamBuffer.write(COLON_SPACE, 0, COLON_SPACE.length, responseHead, socketBufferPool);
contentType.write(responseHead, socketBufferPool);
StreamBuffer.write(HEADER_EOLN, 0, HEADER_EOLN.length, responseHead, socketBufferPool);
}
if (contentLength >= 0) {
CONTENT_LENGTH_NAME.write(responseHead, socketBufferPool);
StreamBuffer.write(COLON_SPACE, 0, COLON_SPACE.length, responseHead, socketBufferPool);
StreamBuffer.write(contentLength, responseHead, socketBufferPool);
StreamBuffer.write(HEADER_EOLN, 0, HEADER_EOLN.length, responseHead, socketBufferPool);
}
WritableHttpHeader header = httpHeader;
while (header != null) {
header.write(responseHead, socketBufferPool);
header = header.next;
}
WritableHttpCookie cookie = httpCookie;
while (cookie != null) {
cookie.write(responseHead, socketBufferPool);
cookie = cookie.next;
}
StreamBuffer.write(HEADER_EOLN, 0, HEADER_EOLN.length, responseHead, socketBufferPool);
}, content);
};
// Create the connection
ProcessAwareServerHttpConnectionManagedObject connection = new ProcessAwareServerHttpConnectionManagedObject(
AbstractHttpServicerFactory.this.serverLocation, AbstractHttpServicerFactory.this.isSecure,
methodSupplier, requestUriSupplier, version, requestHeaders, requestEntity,
AbstractHttpServicerFactory.this.serverName, AbstractHttpServicerFactory.this.dateHttpHeaderClock,
AbstractHttpServicerFactory.this.isIncludeEscalationStackTrace, writer,
AbstractHttpServicerFactory.this.serviceBufferPool);
try {
try {
// Service the connection
return AbstractHttpServicerFactory.this.service(connection);
} catch (IOException ex) {
// Propagate as HTTP exception
throw new HttpException(
new HttpStatus(HttpStatus.INTERNAL_SERVER_ERROR.getStatusCode(), ex.getMessage()));
}
} catch (HttpException ex) {
// Send HTTP exception
responseWriter.write((responseHead, socketBufferPool) -> {
ex.writeHttpResponse(version, AbstractHttpServicerFactory.this.isIncludeEscalationStackTrace,
responseHead, socketBufferPool);
}, null);
return FAIL_PROCESSING;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy