org.rapidoid.http.HttpUtils 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.bytes.Bytes;
import org.rapidoid.bytes.BytesUtil;
import org.rapidoid.commons.Str;
import org.rapidoid.config.BasicConfig;
import org.rapidoid.config.Conf;
import org.rapidoid.ctx.Ctxs;
import org.rapidoid.ctx.UserInfo;
import org.rapidoid.data.BufRanges;
import org.rapidoid.gui.reqinfo.ReqInfo;
import org.rapidoid.http.customize.Customization;
import org.rapidoid.http.customize.HttpResponseRenderer;
import org.rapidoid.http.handler.HandlerResultProcessor;
import org.rapidoid.http.impl.MaybeReq;
import org.rapidoid.io.Res;
import org.rapidoid.lambda.Mapper;
import org.rapidoid.u.U;
import org.rapidoid.util.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.Serializable;
import java.util.Map;
import java.util.regex.Pattern;
@Authors("Nikolche Mihajlovski")
@Since("5.0.0")
public class HttpUtils extends RapidoidThing implements HttpMetadata {
private static final MediaType DEFAULT_CONTENT_TYPE = MscOpts.hasRapidoidGUI() ? MediaType.HTML_UTF_8 : MediaType.JSON;
private static final String PAGE_RELOAD = " Reloading...
";
private static final byte[] EMPTY_RESPONSE = {};
private static final MaybeReq NO_REQ = new MaybeReq() {
@Override
public Req getReqOrNull() {
return null;
}
};
private static volatile Pattern REGEX_VALID_HTTP_RESOURCE = Pattern.compile("(?U)(?:/[ \\w\\-\\.]+)*/?");
private static final Mapper PATH_PARAM_EXTRACTOR = new Mapper() {
@Override
public String map(String[] src) throws Exception {
return src[1].split(":", 2)[0];
}
};
public static Map initAndDeserializeToken(Req req) {
String token = req.cookie(TOKEN, null);
if (U.isEmpty(token)) {
token = req.data(TOKEN, null);
}
return Tokens.deserialize(token);
}
public static void saveTokenBeforeRenderingHeaders(Req req, Map tokenData) {
String token = Tokens.serialize(tokenData);
setResponseTokenCookie(req.response(), token);
}
public static boolean isGetReq(Req req) {
return req.verb().equals(GET);
}
public static boolean isPostReq(Req req) {
return req.verb().equals(POST);
}
public static String resName(Req req) {
String resName = resName(req.path());
U.notNull(resName, "resource name");
return resName;
}
public static String resName(String path) {
if (U.notEmpty(path) && REGEX_VALID_HTTP_RESOURCE.matcher(path).matches() && !path.contains("..")) {
String res = Str.triml(path, "/");
return res.isEmpty() ? "index" : Str.trimr(res, ".html");
} else {
throw U.rte("Invalid path!");
}
}
public static String viewName(Req req) {
//return req.route() != null ? inferViewNameFromRoutePath(req.route().path()) : resName(req.path());
return resName(req.path());
}
public static boolean hasExtension(String name) {
int pos = name.lastIndexOf('.');
return pos > 0 && pos < name.length() - 1;
}
public static void setContentTypeForFile(Resp resp, File file) {
U.must(file.exists());
setContentType(resp, MediaType.getByFileName(file.getAbsolutePath()));
}
public static void setContentTypeForResource(Resp resp, Res resource) {
U.must(resource.exists());
setContentType(resp, MediaType.getByFileName(resource.getName()));
}
public static void setContentType(Resp resp, MediaType mediaType) {
resp.contentType(mediaType);
}
public static Res staticResource(Req req, String... possibleLocations) {
String resName = resName(req);
if (resName == null) return null;
if (hasExtension(resName)) {
return Res.from(resName, possibleLocations);
} else {
Res res = Res.from(resName, possibleLocations);
if (!res.exists()) {
res = Res.from(resName + ".html", possibleLocations);
}
return res;
}
}
public static String getErrorMessageAndSetCode(Resp resp, Throwable err) {
ErrCodeAndMsg codeAndMsg = Msc.getErrorCodeAndMsg(err);
resp.code(codeAndMsg.code());
return codeAndMsg.msg();
}
public static Map getErrorInfo(Resp resp, Throwable error) {
String errorMessage = getErrorMessageAndSetCode(resp, error);
return U.map("error", errorMessage, "code", resp.code(), "status", HttpResponseCodes.status(resp.code()));
}
public static void postProcessResponse(Resp resp) {
postProcessRedirect(resp);
postProcessFile(resp);
postProcessFilename(resp);
}
private static void postProcessFile(Resp resp) {
File file = resp.file();
if (file != null) {
U.must(file.exists());
if (resp.filename() == null) {
resp.filename();
}
setContentTypeForFile(resp, file);
resp.result(Res.from(file).getBytes());
}
}
private static void postProcessFilename(Resp resp) {
String filename = resp.filename();
if (filename != null) {
resp.headers().put(HttpHeaders.CONTENT_DISPOSITION.name(), "attachment; filename=\"" + filename + "\"");
resp.headers().put(HttpHeaders.CACHE_CONTROL.name(), "private");
}
}
private static void postProcessRedirect(Resp resp) {
String redirect = resp.redirect();
if (redirect != null) {
if (resp.code() < 300 || resp.code() >= 400) {
resp.code(303);
}
resp.headers().put(HttpHeaders.LOCATION.name(), redirect);
if (resp.result() == null && resp.body() == null) {
resp.body(EMPTY_RESPONSE);
}
}
}
public static void reload(Req x) {
Map sel = U.map("body", PAGE_RELOAD);
x.response().json(U.map("_sel_", sel));
}
public static String constructUrl(Req x, String path) {
return (Conf.ROOT.is("https") ? "https://" : "http://") + x.host() + path;
}
public static byte[] responseToBytes(Req req, Object result, MediaType contentType, HttpResponseRenderer responseRenderer) {
if (U.eq(contentType, MediaType.JSON) || U.eq(contentType, MediaType.XML_UTF_8)) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
responseRenderer.render(req, result, out);
} catch (Exception e) {
throw U.rte(e);
}
return out.toByteArray();
} else {
return Msc.toBytes(result);
}
}
public static Object resultOf(Req req, Object result) {
result = HandlerResultProcessor.INSTANCE.postProcessResult(req, result);
HttpUtils.resultToResponse(req, result);
return req.response().result();
}
public static void resultToResponse(Req req, Object result) {
if (result instanceof Req) {
if (req != result) {
req.response().result("Unknown request instance was received as result!");
}
} else if (result instanceof Resp) {
if (req.response() != result) {
req.response().result("Unknown response instance was received as result!");
}
} else if (!(result instanceof HttpStatus)) {
req.response().result(result);
}
}
public static String getContextPath(Req req) {
return zone(req).entry("contextPath").or("");
}
public static BasicConfig zone(Customization custom, String zone) {
BasicConfig defaultConfig = custom.config().sub("gui").or(custom.config().sub("app"));
if (zone != null) {
String zoneKey = zone + "-zone";
return custom.config().sub(zoneKey).or(defaultConfig);
} else {
return defaultConfig;
}
}
public static BasicConfig zone(Req req) {
Customization custom = Customization.of(req);
return zone(custom, req.zone());
}
public static void setResponseTokenCookie(Resp resp, String token) {
resp.cookie(TOKEN, token, "HttpOnly");
}
public static String cookiePath() {
String ctxPath = ReqInfo.get().contextPath();
return U.notEmpty(ctxPath) ? ctxPath : "/";
}
public static void clearUserData(Req req) {
if (Ctxs.hasContext()) {
Ctxs.required().setUser(UserInfo.ANONYMOUS);
}
if (req.hasToken()) {
Map token = req.token();
token.remove(Tokens._USER);
token.remove(Tokens._SCOPE);
}
}
public static TokenAuthData getAuth(Req req) {
TokenAuthData auth = req.hasToken() ? Tokens.getAuth(req.token()) : null;
// check if the route is outside of scope
if (auth != null && U.notEmpty(auth.scope) && !auth.scope.contains(req.verb() + " " + req.path())) auth = null;
return auth;
}
public static String inferRealIpAddress(Req req) {
// TODO if CloudFlare is detected, use req.header("cf-connecting-ip")?
return req.clientIpAddress();
}
public static MaybeReq noReq() {
return NO_REQ;
}
public static MaybeReq maybe(Req req) {
return req != null ? (MaybeReq) req : noReq();
}
public static MaybeReq req(Req req) {
U.notNull(req, "HTTP request");
return (MaybeReq) req;
}
public static int findBodyStart(byte[] response) {
Bytes bytes = BytesUtil.from(response);
BufRanges lines = new BufRanges(100);
int pos = BytesUtil.parseLines(bytes, lines, 0, bytes.limit());
U.must(pos > 0, "Invalid HTTP response!");
return pos;
}
private static boolean ignoreResponseHeaderInProxy(String name) {
return name.equalsIgnoreCase("Transfer-encoding")
|| name.equalsIgnoreCase("Content-length")
|| name.equalsIgnoreCase("Connection")
|| name.equalsIgnoreCase("Date")
|| name.equalsIgnoreCase("Server");
}
public static void proxyResponseHeaders(Map respHeaders, SimpleHttpResp resp) {
for (Map.Entry hdr : respHeaders.entrySet()) {
String name = hdr.getKey();
String value = hdr.getValue();
if (name.equalsIgnoreCase("Content-type")) {
resp.contentType = MediaType.of(value);
} else if (name.equalsIgnoreCase("Set-Cookie")) {
String[] parts = value.split("=", 2);
U.must(parts.length == 2, "Invalid value of the Set-Cookie header!");
if (resp.cookies == null) {
resp.cookies = U.map();
}
resp.cookies.put(parts[0], parts[1]);
} else if (!ignoreResponseHeaderInProxy(name)) {
if (resp.headers == null) {
resp.headers = U.map();
}
resp.headers.put(name, value);
}
}
}
public static MediaType getDefaultContentType() {
return DEFAULT_CONTENT_TYPE;
}
public static void validateViewName(String view) {
U.must(!view.startsWith("/"), "Invalid view name: '%s'", view);
}
public static Map webParams(Req req) {
Map webParams = U.map();
for (Map.Entry e : req.params().entrySet()) {
webParams.put(e.getKey(), new WebData(e.getValue()));
}
return webParams;
}
public static boolean isSpecialResult(Object result) {
return result == null
|| result instanceof HttpStatus
|| result instanceof Req
|| result instanceof Resp;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy