use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.
the class LogviewerLogPageHandler method pageFile.
private String pageFile(String path, boolean isZipFile, long fileLength, Integer start, Integer readLength) throws IOException, InvalidRequestException {
try (InputStream input = isZipFile ? new GZIPInputStream(new FileInputStream(path)) : new FileInputStream(path);
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
if (start >= fileLength) {
throw new InvalidRequestException("Cannot start past the end of the file");
}
if (start > 0) {
StreamUtil.skipBytes(input, start);
}
byte[] buffer = new byte[1024];
while (output.size() < readLength) {
int size = input.read(buffer, 0, Math.min(1024, readLength - output.size()));
if (size > 0) {
output.write(buffer, 0, size);
} else {
break;
}
}
numPageRead.mark();
return output.toString();
} catch (FileNotFoundException e) {
numFileOpenExceptions.mark();
throw e;
} catch (IOException e) {
numFileReadExceptions.mark();
throw e;
}
}
use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.
the class LogviewerLogPageHandler method logPage.
/**
* Provides a worker log file to view, starting from the specified position
* or default starting position of the most recent page.
*
* @param fileName file to view
* @param start start offset, or null if the most recent page is desired
* @param length length to read in this page, or null if default page length is desired
* @param grep search string if request is a result of the search, can be null
* @param user username
* @return HTML view page of worker log
*/
public Response logPage(String fileName, Integer start, Integer length, String grep, String user) throws IOException, InvalidRequestException {
Path rawFile = logRoot.resolve(fileName);
Path absFile = rawFile.toAbsolutePath().normalize();
if (!absFile.startsWith(logRoot) || !rawFile.normalize().toString().equals(rawFile.toString())) {
// Ensure filename doesn't contain ../ parts
return LogviewerResponseBuilder.buildResponsePageNotFound();
}
if (resourceAuthorizer.isUserAllowedToAccessFile(user, fileName)) {
workerLogs.setLogFilePermission(fileName);
Path topoDir = absFile.getParent().getParent();
if (absFile.toFile().exists()) {
SortedSet<Path> logFiles;
try {
logFiles = Arrays.stream(topoDir.toFile().listFiles()).flatMap(Unchecked.function(portDir -> directoryCleaner.getFilesForDir(portDir.toPath()).stream())).filter(Files::isRegularFile).collect(toCollection(TreeSet::new));
} catch (UncheckedIOException e) {
throw e.getCause();
}
List<String> reorderedFilesStr = logFiles.stream().map(WorkerLogs::getTopologyPortWorkerLog).filter(fileStr -> !StringUtils.equals(fileName, fileStr)).collect(toList());
reorderedFilesStr.add(fileName);
length = length != null ? Math.min(10485760, length) : LogviewerConstant.DEFAULT_BYTES_PER_PAGE;
final boolean isZipFile = absFile.getFileName().toString().endsWith(".gz");
long fileLength = getFileLength(absFile.toFile(), isZipFile);
if (start == null) {
start = Long.valueOf(fileLength - length).intValue();
}
String logString = isTxtFile(fileName) ? escapeHtml(pageFile(absFile.toString(), isZipFile, fileLength, start, length)) : escapeHtml("This is a binary file and cannot display! You may download the full file.");
List<DomContent> bodyContents = new ArrayList<>();
if (StringUtils.isNotEmpty(grep)) {
String matchedString = String.join("\n", Arrays.stream(logString.split("\n")).filter(str -> str.contains(grep)).collect(toList()));
bodyContents.add(pre(matchedString).withId("logContent"));
} else {
DomContent pagerData = null;
if (isTxtFile(fileName)) {
pagerData = pagerLinks(fileName, start, length, Long.valueOf(fileLength).intValue(), "log");
}
bodyContents.add(searchFileForm(fileName, "no"));
// list all files for this topology
bodyContents.add(logFileSelectionForm(reorderedFilesStr, fileName, "log"));
if (pagerData != null) {
bodyContents.add(pagerData);
}
bodyContents.add(downloadLink(fileName));
bodyContents.add(pre(logString).withClass("logContent"));
if (pagerData != null) {
bodyContents.add(pagerData);
}
}
String content = logTemplate(bodyContents, fileName, user).render();
return LogviewerResponseBuilder.buildSuccessHtmlResponse(content);
} else {
return LogviewerResponseBuilder.buildResponsePageNotFound();
}
} else {
if (resourceAuthorizer.getLogUserGroupWhitelist(fileName) == null) {
return LogviewerResponseBuilder.buildResponsePageNotFound();
} else {
return LogviewerResponseBuilder.buildResponseUnauthorizedUser(user);
}
}
}
use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.
the class LogviewerLogPageHandler method daemonLogPage.
/**
* Provides a daemon log file to view.
*
* @param fileName file to view
* @param start start offset, or null if the most recent page is desired
* @param length length to read in this page, or null if default page length is desired
* @param grep search string if request is a result of the search, can be null
* @param user username
* @return HTML view page of daemon log
*/
public Response daemonLogPage(String fileName, Integer start, Integer length, String grep, String user) throws IOException, InvalidRequestException {
Path file = daemonLogRoot.resolve(fileName).toAbsolutePath().normalize();
if (!file.startsWith(daemonLogRoot) || Paths.get(fileName).getNameCount() != 1) {
// Prevent fileName from pathing into worker logs, or outside daemon log root
return LogviewerResponseBuilder.buildResponsePageNotFound();
}
if (file.toFile().exists()) {
// all types of files included
List<File> logFiles = Arrays.stream(daemonLogRoot.toFile().listFiles()).filter(File::isFile).collect(toList());
List<String> reorderedFilesStr = logFiles.stream().map(File::getName).filter(fName -> !StringUtils.equals(fileName, fName)).collect(toList());
reorderedFilesStr.add(fileName);
length = length != null ? Math.min(10485760, length) : LogviewerConstant.DEFAULT_BYTES_PER_PAGE;
final boolean isZipFile = file.getFileName().toString().endsWith(".gz");
long fileLength = getFileLength(file.toFile(), isZipFile);
if (start == null) {
start = Long.valueOf(fileLength - length).intValue();
}
String logString = isTxtFile(fileName) ? escapeHtml(pageFile(file.toString(), isZipFile, fileLength, start, length)) : escapeHtml("This is a binary file and cannot display! You may download the full file.");
List<DomContent> bodyContents = new ArrayList<>();
if (StringUtils.isNotEmpty(grep)) {
String matchedString = String.join("\n", Arrays.stream(logString.split("\n")).filter(str -> str.contains(grep)).collect(toList()));
bodyContents.add(pre(matchedString).withId("logContent"));
} else {
DomContent pagerData = null;
if (isTxtFile(fileName)) {
pagerData = pagerLinks(fileName, start, length, Long.valueOf(fileLength).intValue(), "daemonlog");
}
bodyContents.add(searchFileForm(fileName, "yes"));
// list all daemon logs
bodyContents.add(logFileSelectionForm(reorderedFilesStr, fileName, "daemonlog"));
if (pagerData != null) {
bodyContents.add(pagerData);
}
bodyContents.add(daemonDownloadLink(fileName));
bodyContents.add(pre(logString).withClass("logContent"));
if (pagerData != null) {
bodyContents.add(pagerData);
}
}
String content = logTemplate(bodyContents, fileName, user).render();
return LogviewerResponseBuilder.buildSuccessHtmlResponse(content);
} else {
return LogviewerResponseBuilder.buildResponsePageNotFound();
}
}
use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.
the class LogviewerLogSearchHandler method searchLogFile.
/**
* Search from a worker log file.
*
* @param fileName log file
* @param user username
* @param isDaemon whether the log file is regarding worker or daemon
* @param search search string
* @param numMatchesStr the count of maximum matches
* @param offsetStr start offset for log file
* @param callback callbackParameterName for JSONP
* @param origin origin
* @return Response containing JSON content representing search result
*/
public Response searchLogFile(String fileName, String user, boolean isDaemon, String search, String numMatchesStr, String offsetStr, String callback, String origin) throws IOException, InvalidRequestException {
boolean noResult = true;
Path rootDir = isDaemon ? daemonLogRoot : logRoot;
Path rawFile = rootDir.resolve(fileName);
Path absFile = rawFile.toAbsolutePath().normalize();
if (!absFile.startsWith(rootDir) || !rawFile.normalize().toString().equals(rawFile.toString())) {
// Ensure filename doesn't contain ../ parts
return searchLogFileNotFound(callback);
}
if (isDaemon && Paths.get(fileName).getNameCount() != 1) {
// Don't permit path traversal for calls intended to read from the daemon logs
return searchLogFileNotFound(callback);
}
Response response;
if (absFile.toFile().exists()) {
if (isDaemon || resourceAuthorizer.isUserAllowedToAccessFile(user, fileName)) {
Integer numMatchesInt = numMatchesStr != null ? tryParseIntParam("num-matches", numMatchesStr) : null;
Integer offsetInt = offsetStr != null ? tryParseIntParam("start-byte-offset", offsetStr) : null;
try {
if (StringUtils.isNotEmpty(search) && search.getBytes("UTF-8").length <= GREP_MAX_SEARCH_SIZE) {
Map<String, Object> entity = new HashMap<>();
entity.put("isDaemon", isDaemon ? "yes" : "no");
Map<String, Object> res = substringSearch(absFile, search, isDaemon, numMatchesInt, offsetInt);
entity.putAll(res);
noResult = ((List) res.get("matches")).isEmpty();
response = LogviewerResponseBuilder.buildSuccessJsonResponse(entity, callback, origin);
} else {
throw new InvalidRequestException("Search substring must be between 1 and 1024 " + "UTF-8 bytes in size (inclusive)");
}
} catch (Exception ex) {
response = LogviewerResponseBuilder.buildExceptionJsonResponse(ex, callback);
}
} else {
// unauthorized
response = LogviewerResponseBuilder.buildUnauthorizedUserJsonResponse(user, callback);
}
} else {
response = searchLogFileNotFound(callback);
}
if (noResult) {
numSearchRequestNoResult.mark();
}
return response;
}
use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.
the class LogviewerLogSearchHandler method substringSearch.
private Map<String, Object> substringSearch(Path file, String searchString, boolean isDaemon, Integer numMatches, Integer startByteOffset) throws InvalidRequestException {
if (StringUtils.isEmpty(searchString)) {
throw new IllegalArgumentException("Precondition fails: search string should not be empty.");
}
if (searchString.getBytes(StandardCharsets.UTF_8).length > GREP_MAX_SEARCH_SIZE) {
throw new IllegalArgumentException("Precondition fails: the length of search string should be less than " + GREP_MAX_SEARCH_SIZE);
}
boolean isZipFile = file.toString().endsWith(".gz");
try (InputStream fis = Files.newInputStream(file)) {
try (InputStream gzippedInputStream = isZipFile ? new GZIPInputStream(fis) : fis;
BufferedInputStream stream = new BufferedInputStream(gzippedInputStream)) {
// It's more likely to be a file read exception here, so we don't differentiate
int fileLength = isZipFile ? (int) ServerUtils.zipFileSize(file.toFile()) : (int) Files.size(file);
ByteBuffer buf = ByteBuffer.allocate(GREP_BUF_SIZE);
final byte[] bufArray = buf.array();
final byte[] searchBytes = searchString.getBytes(StandardCharsets.UTF_8);
numMatches = numMatches != null ? numMatches : 10;
startByteOffset = startByteOffset != null ? startByteOffset : 0;
// Allow searching when start-byte-offset == file-len so it doesn't blow up on 0-length files
if (startByteOffset > fileLength) {
throw new InvalidRequestException("Cannot search past the end of the file");
}
if (startByteOffset > 0) {
StreamUtil.skipBytes(stream, startByteOffset);
}
Arrays.fill(bufArray, (byte) 0);
int totalBytesRead = 0;
int bytesRead = stream.read(bufArray, 0, Math.min(fileLength, GREP_BUF_SIZE));
buf.limit(bytesRead);
totalBytesRead += bytesRead;
List<Map<String, Object>> initialMatches = new ArrayList<>();
int initBufOffset = 0;
int byteOffset = startByteOffset;
byte[] beforeBytes = null;
Map<String, Object> ret = new HashMap<>();
while (true) {
SubstringSearchResult searchRet = bufferSubstringSearch(isDaemon, file, fileLength, byteOffset, initBufOffset, stream, startByteOffset, totalBytesRead, buf, searchBytes, initialMatches, numMatches, beforeBytes);
List<Map<String, Object>> matches = searchRet.getMatches();
Integer newByteOffset = searchRet.getNewByteOffset();
byte[] newBeforeBytes = searchRet.getNewBeforeBytes();
if (matches.size() < numMatches && totalBytesRead + startByteOffset < fileLength) {
// The start index is positioned to find any possible
// occurrence search string that did not quite fit in the
// buffer on the previous read.
final int newBufOffset = Math.min(buf.limit(), GREP_MAX_SEARCH_SIZE) - searchBytes.length;
totalBytesRead = rotateGrepBuffer(buf, stream, totalBytesRead, fileLength);
if (totalBytesRead < 0) {
throw new InvalidRequestException("Cannot search past the end of the file");
}
initialMatches = matches;
initBufOffset = newBufOffset;
byteOffset = newByteOffset;
beforeBytes = newBeforeBytes;
} else {
ret.put("isDaemon", isDaemon ? "yes" : "no");
Integer nextByteOffset = null;
if (matches.size() >= numMatches || totalBytesRead < fileLength) {
nextByteOffset = (Integer) last(matches).get("byteOffset") + searchBytes.length;
if (fileLength <= nextByteOffset) {
nextByteOffset = null;
}
}
ret.putAll(mkGrepResponse(searchBytes, startByteOffset, matches, nextByteOffset));
break;
}
}
return ret;
} catch (UnknownHostException | UnsupportedEncodingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
numFileReadExceptions.mark();
throw new RuntimeException(e);
}
} catch (IOException e) {
numFileOpenExceptions.mark();
throw new RuntimeException(e);
}
}
Aggregations