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

org.rapidoid.http.AbstractHttpServer Maven / Gradle / Ivy

The newest version!
/*-
 * #%L
 * rapidoid-http-fast
 * %%
 * Copyright (C) 2014 - 2018 Nikolche Mihajlovski and contributors
 * %%
 * 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.
 * #L%
 */

package org.rapidoid.http;


import org.rapidoid.RapidoidThing;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.buffer.Buf;
import org.rapidoid.bytes.BytesUtil;
import org.rapidoid.commons.Dates;
import org.rapidoid.config.Conf;
import org.rapidoid.data.BufRange;
import org.rapidoid.data.JSON;
import org.rapidoid.http.impl.HttpParser;
import org.rapidoid.http.impl.MaybeReq;
import org.rapidoid.http.impl.lowlevel.HttpIO;
import org.rapidoid.net.Protocol;
import org.rapidoid.net.Server;
import org.rapidoid.net.TCP;
import org.rapidoid.net.abstracts.Channel;
import org.rapidoid.net.impl.RapidoidHelper;
import org.rapidoid.util.Msc;
import org.rapidoid.writable.ReusableWritable;

@Authors("Nikolche Mihajlovski")
@Since("5.2.1")
public abstract class AbstractHttpServer extends RapidoidThing implements Protocol {

	protected final byte[] STATUS_200 = HttpResponseCodes.get(200);

	protected final byte[] HTTP_404;
	protected final byte[] HTTP_500;

	protected final byte[] CONN_CLOSE_HDR = hdr("Connection: close");
	protected final byte[] SERVER_HDR;

	protected final byte[] DATE_TXT = "Date: ".getBytes();
	protected final byte[] CONTENT_LENGTH_TXT = "Content-Length: ".getBytes();
	protected final byte[] CONTENT_TYPE_TXT = "Content-Type: ".getBytes();

	protected final HttpParser HTTP_PARSER = createParser();

	private final boolean syncBufs;

	public AbstractHttpServer() {
		this("Rapidoid", "Not found!", "Error!", true);
	}

	public AbstractHttpServer(String serverName, String notFoundMsg, String errorMsg, boolean syncBufs) {
		this.SERVER_HDR = hdr("Server: " + serverName);
		this.HTTP_404 = fullResp(404, notFoundMsg.getBytes());
		this.HTTP_500 = fullResp(500, errorMsg.getBytes());
		this.syncBufs = syncBufs;
	}

	private static byte[] hdr(String name) {
		return (name + "\r\n").getBytes();
	}

	protected byte[] fullResp(int code, byte[] content) {
		String status = new String(HttpResponseCodes.get(code));

		String resp = status +
			"Content-Length: " + content.length + "\r\n" +
			"\r\n" + new String(content);

		return resp.getBytes();
	}

	protected HttpParser createParser() {
		return new HttpParser();
	}

	@Override
	public void process(Channel ctx) {
		if (ctx.isInitial()) {
			return;
		}

		Buf buf = ctx.input();
		RapidoidHelper data = ctx.helper();

		HTTP_PARSER.parse(buf, data);

		boolean keepAlive = data.isKeepAlive.value;

		HttpStatus status = handle(ctx, buf, data);

		switch (status) {
			case DONE:
				ctx.closeIf(!keepAlive);
				break;

			case NOT_FOUND:
				ctx.write(HTTP_404);
				ctx.closeIf(!keepAlive);
				break;

			case ERROR:
				ctx.write(HTTP_500);
				ctx.closeIf(!keepAlive);
				break;

			case ASYNC:
				// do nothing
				break;
		}
	}

	protected abstract HttpStatus handle(Channel ctx, Buf buf, RapidoidHelper data);

	protected void startResponse(Channel ctx, boolean isKeepAlive) {
		ctx.write(STATUS_200);
		writeCommonHeaders(ctx, isKeepAlive);
	}

	protected void startResponse(Channel ctx, int code, boolean isKeepAlive) {
		ctx.write(HttpResponseCodes.get(code));
		writeCommonHeaders(ctx, isKeepAlive);
	}

	protected void writeCommonHeaders(Channel ctx, boolean isKeepAlive) {
		if (!isKeepAlive) ctx.write(CONN_CLOSE_HDR);

		ctx.write(SERVER_HDR);

		writeDateHeader(ctx);
	}

	protected void writeDateHeader(Channel ctx) {
		ctx.write(DATE_TXT);
		ctx.write(Dates.getDateTimeBytes());
		ctx.write(CR_LF);
	}

	private void writeContentTypeHeader(Channel ctx, MediaType contentType) {
		ctx.write(CONTENT_TYPE_TXT);
		ctx.write(contentType.getBytes());
		ctx.write(CR_LF);
	}

	protected void writeBody(Channel ctx, byte[] body, MediaType contentType) {
		writeBody(ctx, body, 0, body.length, contentType);
	}

	protected void writeBody(Channel ctx, byte[] body, int offset, int length, MediaType contentType) {
		writeContentTypeHeader(ctx, contentType);
		HttpIO.INSTANCE.writeContentLengthHeader(ctx, length);

		ctx.write(CR_LF);

		ctx.write(body, offset, length);
	}

	protected void writeJsonBody(MaybeReq req, Channel ctx, Object value) {
		writeContentTypeHeader(ctx, MediaType.JSON);

		ReusableWritable out = Msc.locals().jsonRenderingStream();
		JSON.stringify(value, out);

		HttpIO.INSTANCE.writeContentLengthHeader(ctx, out.size());
		HttpIO.INSTANCE.closeHeaders(req, ctx.output());
		ctx.write(out.array(), 0, out.size());
	}

	protected HttpStatus serializeToJson(MaybeReq req, Channel ctx, boolean isKeepAlive, Object value) {
		startResponse(ctx, isKeepAlive);
		writeJsonBody(req, ctx, value);
		return HttpStatus.DONE;
	}

	protected HttpStatus ok(Channel ctx, boolean isKeepAlive, byte[] body, MediaType contentType) {
		return ok(ctx, isKeepAlive, body, 0, body.length, contentType);
	}

	protected HttpStatus ok(Channel ctx, boolean isKeepAlive, byte[] body, int offset, int length, MediaType contentType) {
		startResponse(ctx, isKeepAlive);
		writeBody(ctx, body, offset, length, contentType);
		return HttpStatus.DONE;
	}

	protected HttpStatus plain(Channel ctx, boolean isKeepAlive, byte[] body) {
		return ok(ctx, isKeepAlive, body, MediaType.PLAIN_TEXT_UTF_8);
	}

	protected HttpStatus json(Channel ctx, boolean isKeepAlive, byte[] body) {
		return ok(ctx, isKeepAlive, body, MediaType.JSON);
	}

	protected HttpStatus html(Channel ctx, boolean isKeepAlive, byte[] body) {
		return ok(ctx, isKeepAlive, body, MediaType.HTML_UTF_8);
	}

	protected HttpStatus binary(Channel ctx, boolean isKeepAlive, byte[] body) {
		return ok(ctx, isKeepAlive, body, MediaType.APPLICATION_OCTET_STREAM);
	}

	protected boolean matches(Buf buf, BufRange range, byte[] value) {
		return BytesUtil.matches(buf.bytes(), range, value, true);
	}

	protected boolean matchesIgnoreCase(Buf buf, BufRange range, byte[] value) {
		return BytesUtil.matches(buf.bytes(), range, value, false);
	}

	public Server listen(int port) {
		return listen("0.0.0.0", port);
	}

	public Server listen(String address, int port) {
		return TCP.server(Conf.HTTP)
			.protocol(this)
			.address(address)
			.port(port)
			.syncBufs(syncBufs)
			.build()
			.start();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy