/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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 com.android.statementservice.retriever;
import org.json.JSONObject;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
/**
* Immutable value type that names a web asset.
*
* <p>A web asset can be named by its protocol, domain, and port using this JSON string:
* { "namespace": "web",
* "site": "[protocol]://[fully-qualified domain]{:[optional port]}" }
*
* <p>For example, a website hosted on a https server at www.test.com can be named using
* { "namespace": "web",
* "site": "https://www.test.com" }
*
* <p>The only protocol supported now are https and http. If the optional port is not specified,
* the default for each protocol will be used (i.e. 80 for http and 443 for https).
*/
/* package private */ final class WebAsset extends AbstractAsset {
private static final String MISSING_FIELD_FORMAT_STRING = "Expected %s to be set.";
private static final String SCHEME_HTTP = "http";
private final URL mUrl;
private WebAsset(URL url) {
int port = url.getPort() != -1 ? url.getPort() : url.getDefaultPort();
try {
mUrl = new URL(url.getProtocol().toLowerCase(), url.getHost().toLowerCase(), port, "");
} catch (MalformedURLException e) {
throw new AssertionError(
"Url should always be validated before calling the constructor.");
}
}
public String getDomain() {
return mUrl.getHost();
}
public String getPath() {
return mUrl.getPath();
}
public String getScheme() {
return mUrl.getProtocol();
}
public int getPort() {
return mUrl.getPort();
}
@Override
public String toJson() {
AssetJsonWriter writer = new AssetJsonWriter();
writer.writeFieldLower(Utils.NAMESPACE_FIELD, Utils.NAMESPACE_WEB);
writer.writeFieldLower(Utils.WEB_ASSET_FIELD_SITE, mUrl.toExternalForm());
return writer.closeAndGetString();
}
@Override
public String toString() {
StringBuilder asset = new StringBuilder();
asset.append("WebAsset: ");
asset.append(toJson());
return asset.toString();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof WebAsset)) {
return false;
}
return ((WebAsset) o).toJson().equals(toJson());
}
@Override
public int hashCode() {
return toJson().hashCode();
}
@Override
public int lookupKey() {
return toJson().hashCode();
}
@Override
public boolean followInsecureInclude() {
// Only allow insecure include file if the asset scheme is http.
return SCHEME_HTTP.equals(getScheme());
}
/**
* Checks that the input is a valid web asset.
*
* @throws AssociationServiceException if the asset is not well formatted.
*/
protected static WebAsset create(JSONObject asset)
throws AssociationServiceException {
if (asset.optString(Utils.WEB_ASSET_FIELD_SITE).equals("")) {
throw new AssociationServiceException(String.format(MISSING_FIELD_FORMAT_STRING,
Utils.WEB_ASSET_FIELD_SITE));
}
URL url;
try {
url = new URL(asset.optString(Utils.WEB_ASSET_FIELD_SITE));
} catch (MalformedURLException e) {
throw new AssociationServiceException("Url is not well formatted.", e);
}
String scheme = url.getProtocol().toLowerCase(Locale.US);
if (!scheme.equals("https") && !scheme.equals("http")) {
throw new AssociationServiceException("Expected scheme to be http or https.");
}
if (url.getUserInfo() != null) {
throw new AssociationServiceException("The url should not contain user info.");
}
String path = url.getFile(); // This is url.getPath() + url.getQuery().
if (!path.equals("/") && !path.equals("")) {
throw new AssociationServiceException(
"Site should only have scheme, domain, and port.");
}
return new WebAsset(url);
}
}