-
Notifications
You must be signed in to change notification settings - Fork 335
PathResourceResolver can't resolve a GoogleStorageResource due to no Google Storage UrlStreamHandler #210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Can you provide more details of your environment, specifically which version of Spring Cloud GCP, which Spring Cloud version, and which Spring Boot version you're using? |
Could you also point me to what documentation you're referring? |
Hi,
I meant the javadoc of GoogleStorageResource /**
* Since the gs: protocol will normally not have a URL stream handler registered,
* this method will always throw a {@link java.net.MalformedURLException}.
* @return the URL for the GCS resource, if a URL stream handler is registered for the gs protocol.
*/
@Override
public URL getURL() throws IOException {
return getURI().toURL();
}
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-dependencies</artifactId>
<version>1.2.6.RELEASE</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.4.RELEASE</version> Thanks |
And to what documentation are you looking at and following? |
None in particular, And as the GoogleStorageResource should be of type Spring Resources (abstraction for a number of low-level resources, such as file system files, classpath files, servlet context-relative files, etc.) I made it work by overriding checkResource/isReourceUnderLocation from the PathResourceResolver as follows to treat the GoogleStorageResource specifically by returning resource.exists() and so avoid calling resource.getURL().getPath() that would trigger the exception : private class SinglePageAppResourceResolver extends PathResourceResolver
{
@Override
protected boolean checkResource(Resource resource, Resource location) throws IOException {
if (isResourceUnderLocation(resource, location)) {
return true;
}
Resource[] allowedLocations = getAllowedLocations();
if (allowedLocations != null) {
for (Resource current : allowedLocations) {
if (isResourceUnderLocation(resource, current)) {
return true;
}
}
}
return false;
}
private boolean isResourceUnderLocation(Resource resource, Resource location) throws IOException {
if (resource.getClass() != location.getClass()) {
return false;
}
String resourcePath;
String locationPath;
if (resource instanceof UrlResource) {
resourcePath = resource.getURL().toExternalForm();
locationPath = StringUtils.cleanPath(location.getURL().toString());
}
else if (resource instanceof ClassPathResource) {
resourcePath = ((ClassPathResource) resource).getPath();
locationPath = StringUtils.cleanPath(((ClassPathResource) location).getPath());
}
else if (resource instanceof ServletContextResource) {
resourcePath = ((ServletContextResource) resource).getPath();
locationPath = StringUtils.cleanPath(((ServletContextResource) location).getPath());
}
else if(resource instanceof GoogleStorageResource){
return resource.exists();
}
else {
resourcePath = resource.getURL().getPath();
locationPath = StringUtils.cleanPath(location.getURL().getPath());
}
if (locationPath.equals(resourcePath)) {
return true;
}
locationPath = (locationPath.endsWith("/") || locationPath.isEmpty() ? locationPath : locationPath + "/");
return (resourcePath.startsWith(locationPath) && !isInvalidEncodedPath(resourcePath));
}
private boolean isInvalidEncodedPath(String resourcePath) {
if (resourcePath.contains("%")) {
// Use URLDecoder (vs UriUtils) to preserve potentially decoded UTF-8 chars...
try {
String decodedPath = URLDecoder.decode(resourcePath, "UTF-8");
if (decodedPath.contains("../") || decodedPath.contains("..\\")) {
logger.warn("Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath);
return true;
}
}
catch (IllegalArgumentException ex) {
// May not be possible to decode...
}
catch (UnsupportedEncodingException ex) {
// Should never happen...
}
}
return false;
}
} The use it like this: @Configuration
public class SinglePageAppWebMvcConfigurer extends WebMvcConfigurerAdapter
{
@Autowired
private ResourceProperties resourceProperties;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/**")
.addResourceLocations(resourceProperties.getStaticLocations())
.resourceChain(true)
.addResolver(new SinglePageAppResourceResolver());
}
} |
Thanks for the fix. |
Add the spring-data r2dbc dialect to the repo.
When trying to use a Google Storage Resource as the spring static resource location the PathResourceResolver calls the method isResourceUnderLocation that calls the GoogleStorageResource.toURL() that throws a MalformedURLException because no URL stream handler is registered for the gs protocol (as stated by the documentation)
Sample
Add the following dependecy to your project:
Set the following property in you project:
spring.resources.static-locations=gs://[YOUR_GCS_BUCKET]/
If this is a public bucket (allUsers with Storage read permission) and no credentials are needed, use the following config class:
Assuming the root of the bucket contains a index.html
Call your server with http://host:port/index.html
Put a breakpoint in PathResourceResolver.isResourceUnderLocation(...)
and see it fail to call resource.getURL() even though resource.getInputStream() would return the index.html
The text was updated successfully, but these errors were encountered: