package restx; import com.google.common.base.Optional; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import restx.factory.Factory; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.getFirst; /** * User: xavierhanin * Date: 4/2/13 * Time: 9:16 PM */ public class StdRequest extends AbstractRequest { public static StdRequestBuilder builder() { return new StdRequestBuilder(); } private final String localScheme; private final String baseApiPath; private final String baseUri; private final String restxPath; private final String httpMethod; private final ImmutableMap<String, String> headers; private final ImmutableMap<String, ImmutableList<String>> queryParams; private final ImmutableMap<String, String> cookiesMap; private final Supplier<InputStream> inputStreamSupplier; private InputStream inputStream; private StdRequest(String baseUri, String restxPath, String httpMethod, ImmutableMap<String, String> headers, ImmutableMap<String, ImmutableList<String>> queryParams, ImmutableMap<String, String> cookiesMap, Supplier<InputStream> inputStreamSupplier) { super(Factory.getInstance().getComponent(HttpSettings.class)); this.baseUri = checkNotNull(baseUri, "baseUri is required"); this.restxPath = checkNotNull(restxPath, "restxPath is required"); this.httpMethod = checkNotNull(httpMethod, "httpMethod is required"); this.queryParams = checkNotNull(queryParams, "query params are required"); this.cookiesMap = checkNotNull(cookiesMap, "cookies map is required"); this.headers = checkNotNull(headers, "headers is required"); this.inputStreamSupplier = checkNotNull(inputStreamSupplier, "inputstream supplier is required"); this.localScheme = getSchemeFromBaseUri(baseUri); this.baseApiPath = getBaseApiPathFromBaseUri(baseUri); } private String getBaseApiPathFromBaseUri(String baseUri) { return baseUri.replaceAll("^[^:]+://[^/]+/", "/"); } private String getSchemeFromBaseUri(String baseUri) { try { return new URI(baseUri).getScheme(); } catch (URISyntaxException e) { return "http"; } } @Override protected String getBaseApiPath() { return baseApiPath; } @Override protected String getLocalScheme() { return localScheme; } @Override public String getBaseUri() { return baseUri; } @Override public String getBaseNetworkPath() { return getBaseUri().replaceAll("^https?:", ""); } @Override public String getRestxPath() { return restxPath; } @Override public String getRestxUri() { if (queryParams.isEmpty()) { return restxPath; } StringBuilder sb = new StringBuilder(restxPath).append("?"); for (Map.Entry<String, ImmutableList<String>> entry : queryParams.entrySet()) { for (String val : entry.getValue()) { sb.append(entry.getKey()).append("=").append(val).append("&"); } } sb.setLength(sb.length() - 1); return sb.toString(); } @Override public String getHttpMethod() { return httpMethod; } @Override public Optional<String> getQueryParam(String param) { return Optional.fromNullable(getFirst(getQueryParams(param), null)); } @Override public ImmutableList<String> getQueryParams(String param) { return Optional.fromNullable(queryParams.get(param)).or(ImmutableList.<String>of()); } public ImmutableMap<String, ImmutableList<String>> getQueryParams() { return queryParams; } @Override public Optional<String> getHeader(String headerName) { return Optional.fromNullable(headers.get(headerName)); } @Override public String getContentType() { return headers.get("Content-type"); } @Override public Optional<String> getCookieValue(String cookieName) { return Optional.fromNullable(cookiesMap.get(cookieName)); } @Override public boolean isPersistentCookie(String cookie) { return false; } @Override public ImmutableMap<String, String> getCookiesMap() { return cookiesMap; } @Override public String getLocalClientAddress() { return ""; } @Override public synchronized InputStream getContentStream() throws IOException { if (inputStream != null) { throw new IllegalStateException("can't get content stream multiple times"); } return inputStream = inputStreamSupplier.get(); } @Override public synchronized void closeContentStream() throws IOException { if (inputStream == null) { throw new IllegalStateException("can't close content stream which is not opened"); } inputStream.close(); } @Override public <T> T unwrap(Class<T> clazz) { throw new IllegalArgumentException("no underlying implementation"); } @Override public Locale getLocale() { List<String> languages = getLanguages(); Locale locale; if (languages.isEmpty()) { locale = Locale.getDefault(); } else { locale = Locale.forLanguageTag(languages.get(0)); } return locale; } private List<String> getLanguages() { final String acceptLanguageHeader = headers.get("Accept-Language"); List<String> languages = new ArrayList<>(); if (!Strings.isNullOrEmpty(acceptLanguageHeader)) { final Iterable<String> split = Splitter.on(",").split(acceptLanguageHeader.replace(" ", "").replace(";", ",")); for (String s1 : split) { if (!s1.startsWith("q=")) { languages.add(s1); } } } return languages; } @Override public ImmutableList<Locale> getLocales() { List<Locale> locales = new ArrayList<>(); for (String language : getLanguages()) { Locale locale = Locale.forLanguageTag(language); if (locale != null) { locales.add(locale); } } if (locales.isEmpty()) { locales.add(Locale.getDefault()); } return ImmutableList.copyOf(locales); } public static class StdRequestBuilder { private String baseUri; private String restxPath; private String httpMethod = "GET"; private ImmutableMap<String, String> headers = ImmutableMap.of(); private ImmutableMap<String, ImmutableList<String>> queryParams = ImmutableMap.of(); private ImmutableMap<String, String> cookiesMap = ImmutableMap.of(); private Supplier<InputStream> inputStreamSupplier = new Supplier<InputStream>() { @Override public InputStream get() { throw new UnsupportedOperationException(); } }; /** * Sets a full restx path for the request, properly handling the query string. * * So calling <code>setFullPath("/message?who=xavier")</code> is equivalent to * <code>setRestxPath("/message").setQueryParams(ImmutableMap.of("who", "xavier"))</code>. * * @param fullPath the full path. * @return the current builder. */ public StdRequestBuilder setFullPath(String fullPath) { int queryStringIndex = fullPath.indexOf('?'); if (queryStringIndex == -1) { restxPath = fullPath; return this; } restxPath = fullPath.substring(0, queryStringIndex); Map<String, List<String>> params = Maps.newLinkedHashMap(); for (String queryParam : Splitter.on("&").split(fullPath.substring(queryStringIndex + 1))) { String param = queryParam; String val = ""; int eqIndex = queryParam.indexOf('='); if (eqIndex != -1) { param = queryParam.substring(0, eqIndex); val = queryParam.substring(eqIndex + 1); } List<String> paramValues = params.get(param); if (paramValues == null) { params.put(param, paramValues = Lists.<String>newArrayList()); } paramValues.add(val); } ImmutableMap.Builder<String, ImmutableList<String>> paramsBuilder = ImmutableMap.builder(); for (Map.Entry<String, List<String>> entry : params.entrySet()) { paramsBuilder.put(entry.getKey(), ImmutableList.copyOf(entry.getValue())); } queryParams = paramsBuilder.build(); return this; } public StdRequestBuilder setBaseUri(String baseUri) { this.baseUri = baseUri; return this; } public StdRequestBuilder setRestxPath(String restxPath) { this.restxPath = restxPath; return this; } public StdRequestBuilder setHttpMethod(String httpMethod) { this.httpMethod = httpMethod; return this; } public StdRequestBuilder setHeaders(ImmutableMap<String, String> headers) { this.headers = headers; return this; } public StdRequestBuilder setQueryParams(ImmutableMap<String, ImmutableList<String>> queryParams) { this.queryParams = queryParams; return this; } public StdRequestBuilder setCookiesMap(ImmutableMap<String, String> cookiesMap) { this.cookiesMap = cookiesMap; return this; } public StdRequestBuilder setInputStreamSupplier(Supplier<InputStream> inputStreamSupplier) { this.inputStreamSupplier = inputStreamSupplier; return this; } public StdRequest build() { return new StdRequest(baseUri, restxPath, httpMethod, headers, queryParams, cookiesMap, inputStreamSupplier); } } }