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

io.datakernel.http.HttpRequest Maven / Gradle / Ivy

/*
 * Copyright (C) 2015-2018 SoftIndex LLC.
 *
 * 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 io.datakernel.http;

import io.datakernel.bytebuf.ByteBuf;
import io.datakernel.common.Initializable;
import io.datakernel.common.parse.ParseException;
import io.datakernel.csp.ChannelSupplier;
import io.datakernel.http.HttpHeaderValue.HttpHeaderValueOfSimpleCookies;
import io.datakernel.http.MultipartParser.MultipartDataHandler;
import io.datakernel.promise.Promise;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.net.InetAddress;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static io.datakernel.bytebuf.ByteBufStrings.*;
import static io.datakernel.common.Utils.nullToEmpty;
import static io.datakernel.http.HttpHeaders.*;
import static io.datakernel.http.HttpMethod.*;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;

/**
 * Represents the HTTP request which {@link AsyncHttpClient} sends to
 * {@link AsyncHttpServer}. It must have only one owner in each  part of time.
 * After creating an {@link HttpResponse} in a server, it will be recycled and
 * can not be used later.
 * 

* {@code HttpRequest} class provides methods which can be used intuitively for * creating and configuring an HTTP request. */ public final class HttpRequest extends HttpMessage implements Initializable { private final static int LONGEST_HTTP_METHOD_SIZE = 12; private static final byte[] HTTP_1_1 = encodeAscii(" HTTP/1.1"); private static final int HTTP_1_1_SIZE = HTTP_1_1.length; @NotNull private final HttpMethod method; private UrlParser url; private InetAddress remoteAddress; private Map pathParameters; private Map queryParameters; private Map postParameters; // region creators HttpRequest(@NotNull HttpMethod method, @Nullable UrlParser url) { this.method = method; this.url = url; } @NotNull public static HttpRequest of(@NotNull HttpMethod method, @NotNull String url) { HttpRequest request = new HttpRequest(method, null); request.setUrl(url); return request; } @NotNull public static HttpRequest get(@NotNull String url) { return HttpRequest.of(GET, url); } @NotNull public static HttpRequest post(@NotNull String url) { return HttpRequest.of(POST, url); } @NotNull public static HttpRequest put(@NotNull String url) { return HttpRequest.of(PUT, url); } @NotNull public HttpRequest withHeader(@NotNull HttpHeader header, @NotNull String value) { addHeader(header, value); return this; } @NotNull public HttpRequest withHeader(@NotNull HttpHeader header, @NotNull byte[] value) { addHeader(header, value); return this; } @NotNull public HttpRequest withHeader(@NotNull HttpHeader header, @NotNull HttpHeaderValue value) { addHeader(header, value); return this; } @NotNull public HttpRequest withBody(@NotNull byte[] array) { setBody(array); return this; } @NotNull public HttpRequest withBody(@NotNull ByteBuf body) { setBody(body); return this; } @NotNull public HttpRequest withBodyStream(@NotNull ChannelSupplier stream) { setBodyStream(stream); return this; } @Override public void addCookies(@NotNull List cookies) { assert !isRecycled(); headers.add(COOKIE, new HttpHeaderValueOfSimpleCookies(cookies)); } @Override public void addCookie(@NotNull HttpCookie cookie) { addCookies(singletonList(cookie)); } @NotNull public HttpRequest withCookies(@NotNull List cookies) { addCookies(cookies); return this; } @NotNull public HttpRequest withCookies(@NotNull HttpCookie... cookie) { addCookies(cookie); return this; } @NotNull public HttpRequest withCookie(@NotNull HttpCookie cookie) { addCookie(cookie); return this; } @NotNull public HttpRequest withBodyGzipCompression() { setBodyGzipCompression(); return this; } // endregion @NotNull @Contract(pure = true) public HttpMethod getMethod() { assert !isRecycled(); return method; } @Contract(pure = true) public InetAddress getRemoteAddress() { assert !isRecycled(); return remoteAddress; } void setRemoteAddress(@NotNull InetAddress inetAddress) { remoteAddress = inetAddress; } public boolean isHttps() { return url.isHttps(); } UrlParser getUrl() { assert !isRecycled(); return url; } void setUrl(@NotNull String url) { assert !isRecycled(); this.url = UrlParser.of(url); if (!this.url.isRelativePath()) { assert this.url.getHostAndPort() != null; // sadly no advanced contracts yet addHeader(HOST, this.url.getHostAndPort()); } } @Nullable public String getHostAndPort() { return url.getHostAndPort(); } @NotNull public String getPath() { assert !isRecycled(); return url.getPath(); } @NotNull public String getPathAndQuery() { assert !isRecycled(); return url.getPathAndQuery(); } @Nullable private Map parsedCookies; @NotNull public Map getCookies() { if (parsedCookies != null) { return parsedCookies; } Map cookies = new LinkedHashMap<>(); for (HttpCookie cookie : getHeader(COOKIE, HttpHeaderValue::toSimpleCookies)) { cookies.put(cookie.getName(), cookie.getValue()); } return parsedCookies = cookies; } @Nullable public String getCookie(@NotNull String cookie) { return getCookies().get(cookie); } @NotNull public String getQuery() { return url.getQuery(); } @NotNull public String getFragment() { return url.getFragment(); } @NotNull public Map getQueryParameters() { assert !isRecycled(); if (queryParameters != null) { return queryParameters; } queryParameters = url.getQueryParameters(); return queryParameters; } @Nullable public String getQueryParameter(@NotNull String key) { assert !isRecycled(); return url.getQueryParameter(key); } @NotNull public List getQueryParameters(@NotNull String key) { assert !isRecycled(); return url.getQueryParameters(key); } @NotNull public Iterable getQueryParametersIterable() { assert !isRecycled(); return url.getQueryParametersIterable(); } @Nullable public String getPostParameter(String name) { return getPostParameters().get(name); } @NotNull public Map getPostParameters() { if (postParameters != null) return postParameters; if (body == null) throw new NullPointerException("Body must be loaded to parse post parameters"); return postParameters = containsPostParameters() ? UrlParser.parseQueryIntoMap(decodeAscii(body.array(), body.head(), body.readRemaining())) : emptyMap(); } private boolean containsPostParameters() { ByteBuf buf = getHeaderBuf(CONTENT_TYPE); if (buf == null) { return false; } try { ContentType contentType = HttpHeaderValue.toContentType(buf); if (method != POST || contentType.getMediaType() != MediaTypes.X_WWW_FORM_URLENCODED) { return false; } } catch (ParseException e) { return false; } return true; } @NotNull public Map getPathParameters() { assert !isRecycled(); return pathParameters != null ? pathParameters : emptyMap(); } @NotNull public String getPathParameter(@NotNull String key) { assert !isRecycled(); if (pathParameters != null) { String pathParameter = pathParameters.get(key); if (pathParameter != null) { return pathParameter; } } throw new IllegalArgumentException("No path parameter '" + key + "' found"); } public Promise handleMultipart(MultipartDataHandler multipartDataHandler) { String contentType = getHeader(CONTENT_TYPE); if (contentType == null || !contentType.startsWith("multipart/form-data; boundary=")) { return Promise.ofException(HttpException.ofCode(400, "Content type is not multipart/form-data")); } String boundary = contentType.substring(30); if (boundary.startsWith("\"") && boundary.endsWith("\"")) { boundary = boundary.substring(1, boundary.length() - 1); } return MultipartParser.create(boundary) .split(getBodyStream(), multipartDataHandler); } int getPos() { return url.pos; } void setPos(int pos) { url.pos = (short) pos; } @NotNull public String getRelativePath() { assert !isRecycled(); String partialPath = url.getPartialPath(); return partialPath.startsWith("/") ? partialPath.substring(1) : partialPath; // strip first '/' } String pollUrlPart() { assert !isRecycled(); return url.pollUrlPart(); } void removePathParameter(String key) { pathParameters.remove(key); } void putPathParameter(String key, @NotNull String value) { if (pathParameters == null) { pathParameters = new HashMap<>(); } pathParameters.put(key, UrlParser.urlDecode(value)); } @Override protected int estimateSize() { return estimateSize(LONGEST_HTTP_METHOD_SIZE + 1 // SPACE + url.getPathAndQueryLength()) + HTTP_1_1_SIZE; } @Override protected void writeTo(@NotNull ByteBuf buf) { method.write(buf); buf.put(SP); url.writePathAndQuery(buf); buf.put(HTTP_1_1); writeHeaders(buf); } @Override public String toString() { if (url.isRelativePath()) { String host = getHeader(HOST); return nullToEmpty(host) + url.getPathAndQuery(); } return url.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy