com.github.zhengframework.swagger.SwaggerUIServlet Maven / Gradle / Ivy
package com.github.zhengframework.swagger;
/*-
* #%L
* zheng-swagger
* %%
* Copyright (C) 2020 Zheng MingHai
* %%
* 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%
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import javax.inject.Inject;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.webjars.NotFoundException;
import org.webjars.WebJarAssetLocator;
@Slf4j
public class SwaggerUIServlet extends HttpServlet {
private static final long DEFAULT_EXPIRE_TIME_MS = 86400000L; // 1 day
private static final long DEFAULT_EXPIRE_TIME_S = 86400L; // 1 day
/** The default buffer size ({@value}) to use */
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private static final int EOF = -1;
private static final long serialVersionUID = 6245564254901248946L;
private transient SwaggerConfig swaggerConfig;
private final String indexContent;
private boolean disableCache = false;
private transient WebJarAssetLocator locator = new WebJarAssetLocator();
@Inject
public SwaggerUIServlet(SwaggerConfig swaggerConfig) {
this.swaggerConfig = swaggerConfig;
URL resource = getClass().getResource("/swagger-ui/index.html");
try (InputStream inputStream = resource.openStream()) {
String string = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
indexContent =
string.replace("http://127.0.0.1:8080/openapi.json", swaggerConfig.getApiUrl());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Copy bytes from an InputStream
to an OutputStream
.
*
* This method buffers the input internally, so there is no need to use a
* BufferedInputStream
.
*
*
Large streams (over 2GB) will return a bytes copied value of -1
after the copy
* has completed since the correct number of bytes cannot be returned as an int. For large streams
* use the copyLarge(InputStream, OutputStream)
method.
*
* @param input the InputStream
to read from
* @param output the OutputStream
to write to
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 1.1
*/
private static void copy(InputStream input, OutputStream output) throws IOException {
int n;
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
}
@Override
public void init() {
log.info("WebjarsServlet initialization completed");
}
private String getFullPath(String webjar, String uri) throws NotFoundException {
log.info("uri={}", uri);
if (uri.startsWith("/")) {
uri = uri.substring(1);
}
try {
return locator.getFullPath(webjar, uri);
} catch (NullPointerException e) {
log.error("get full path error, uri={}", uri, e);
throw new NotFoundException(uri);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
String eTagName;
String uri =
request
.getRequestURI()
.replaceFirst(request.getContextPath(), "")
.replaceFirst(swaggerConfig.getUiPath(), "");
if (uri.endsWith("/")) {
uri = uri + "index.html";
}
if (uri.endsWith("/index.html")) {
eTagName = swaggerConfig.getUiPath() + "/index.html";
if (!disableCache) {
prepareCacheHeaders(response, eTagName);
}
String filename = getFileName(uri);
String mimeType = this.getServletContext().getMimeType(filename);
response.setContentType(mimeType != null ? mimeType : "application/octet-stream");
IOUtils.write(indexContent, response.getOutputStream(), StandardCharsets.UTF_8);
return;
}
String webjarsResourceURI;
try {
webjarsResourceURI = getFullPath("swagger-ui", uri);
} catch (NotFoundException e) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
log.debug("Webjars resource requested: {}", webjarsResourceURI);
try {
eTagName = this.getETagName(webjarsResourceURI);
} catch (IllegalArgumentException e) {
log.info("etag");
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
if (!disableCache) {
if (checkETagMatch(request, eTagName) || checkLastModify(request)) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
}
InputStream inputStream = this.getClass().getResourceAsStream("/" + webjarsResourceURI);
if (inputStream != null) {
try {
if (!disableCache) {
prepareCacheHeaders(response, eTagName);
}
String filename = getFileName(webjarsResourceURI);
String mimeType = this.getServletContext().getMimeType(filename);
response.setContentType(mimeType != null ? mimeType : "application/octet-stream");
copy(inputStream, response.getOutputStream());
} finally {
inputStream.close();
}
} else {
// return HTTP error
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
private String getFileName(String webjarsResourceURI) {
String[] tokens = webjarsResourceURI.split("/");
return tokens[tokens.length - 1];
}
/**
* @param webjarsResourceURI webjarsResourceURI
* @return ETag name
* @throws IllegalArgumentException when insufficient URI has given
*/
private String getETagName(String webjarsResourceURI) {
return webjarsResourceURI;
}
/* Important!!*/
/* The code bellow has been copied from apache Commons IO. More specifically from its IOUtils class. */
/* The reason is becasue I don't want to include any more dependencies */
private boolean checkETagMatch(HttpServletRequest request, String eTagName) {
String token = request.getHeader("If-None-Match");
return (token != null && token.equals(eTagName));
}
private boolean checkLastModify(HttpServletRequest request) {
long last = request.getDateHeader("If-Modified-Since");
return (last != -1L && (last - System.currentTimeMillis() > 0L));
}
// copy from InputStream
// -----------------------------------------------------------------------
private void prepareCacheHeaders(HttpServletResponse response, String eTag) {
response.setHeader("ETag", eTag);
response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME_MS);
response.addDateHeader("Last-Modified", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME_MS);
response.addHeader("Cache-Control", "private, max-age=" + DEFAULT_EXPIRE_TIME_S);
}
}