Skip to content

Commit ac34d4f

Browse files
committed
HDFS-3574. Fix small race and do some cleanup in GetImageServlet. Contributed by Todd Lipcon.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1356939 13f79535-47bb-0310-9956-ffa450edef68
1 parent dbb4a8b commit ac34d4f

File tree

4 files changed

+58
-36
lines changed

4 files changed

+58
-36
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ServletUtil.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ public static String getParameter(ServletRequest request, String name) {
6060
s = s.trim();
6161
return s.length() == 0? null: s;
6262
}
63+
64+
/**
65+
* @return a long value as passed in the given parameter, throwing
66+
* an exception if it is not present or if it is not a valid number.
67+
*/
68+
public static long parseLongParam(ServletRequest request, String param)
69+
throws IOException {
70+
String paramStr = request.getParameter(param);
71+
if (paramStr == null) {
72+
throw new IOException("Invalid request has no " + param + " parameter");
73+
}
74+
75+
return Long.valueOf(paramStr);
76+
}
6377

6478
public static final String HTML_TAIL = "<hr />\n"
6579
+ "<a href='http://hadoop.apache.org/core'>Hadoop</a>, "

hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,8 @@ Branch-2 ( Unreleased changes )
397397

398398
HDFS-3575. HttpFS does not log Exception Stacktraces (brocknoland via tucu)
399399

400+
HDFS-3574. Fix small race and do some cleanup in GetImageServlet (todd)
401+
400402
BREAKDOWN OF HDFS-3042 SUBTASKS
401403

402404
HDFS-2185. HDFS portion of ZK-based FailoverController (todd)

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/GetImageServlet.java

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@
4646
import org.apache.hadoop.hdfs.util.DataTransferThrottler;
4747
import org.apache.hadoop.hdfs.util.MD5FileUtils;
4848
import org.apache.hadoop.http.HttpServer;
49+
import org.apache.hadoop.io.IOUtils;
4950
import org.apache.hadoop.io.MD5Hash;
5051
import org.apache.hadoop.security.UserGroupInformation;
52+
import org.apache.hadoop.util.ServletUtil;
5153
import org.apache.hadoop.util.StringUtils;
5254

5355
import com.google.common.annotations.VisibleForTesting;
@@ -126,23 +128,14 @@ public Void run() throws Exception {
126128
throw new IOException(errorMessage);
127129
}
128130
CheckpointFaultInjector.getInstance().beforeGetImageSetsHeaders();
129-
setFileNameHeaders(response, imageFile);
130-
setVerificationHeaders(response, imageFile);
131-
// send fsImage
132-
TransferFsImage.getFileServer(response.getOutputStream(), imageFile,
133-
getThrottler(conf));
131+
serveFile(imageFile);
134132
} else if (parsedParams.isGetEdit()) {
135133
long startTxId = parsedParams.getStartTxId();
136134
long endTxId = parsedParams.getEndTxId();
137135

138136
File editFile = nnImage.getStorage()
139137
.findFinalizedEditsFile(startTxId, endTxId);
140-
setVerificationHeaders(response, editFile);
141-
142-
setFileNameHeaders(response, editFile);
143-
// send edits
144-
TransferFsImage.getFileServer(response.getOutputStream(), editFile,
145-
getThrottler(conf));
138+
serveFile(editFile);
146139
} else if (parsedParams.isPutImage()) {
147140
final long txid = parsedParams.getTxId();
148141

@@ -182,6 +175,28 @@ public Void run() throws Exception {
182175
}
183176
return null;
184177
}
178+
179+
private void serveFile(File file) throws IOException {
180+
FileInputStream fis = new FileInputStream(file);
181+
try {
182+
setVerificationHeaders(response, file);
183+
setFileNameHeaders(response, file);
184+
if (!file.exists()) {
185+
// Potential race where the file was deleted while we were in the
186+
// process of setting headers!
187+
throw new FileNotFoundException(file.toString());
188+
// It's possible the file could be deleted after this point, but
189+
// we've already opened the 'fis' stream.
190+
// It's also possible length could change, but this would be
191+
// detected by the client side as an inaccurate length header.
192+
}
193+
// send file
194+
TransferFsImage.getFileServer(response, file, fis,
195+
getThrottler(conf));
196+
} finally {
197+
IOUtils.closeStream(fis);
198+
}
199+
}
185200
});
186201

187202
} catch (Throwable t) {
@@ -193,7 +208,7 @@ public Void run() throws Exception {
193208
}
194209
}
195210

196-
private static void setFileNameHeaders(HttpServletResponse response,
211+
public static void setFileNameHeaders(HttpServletResponse response,
197212
File file) {
198213
response.setHeader(CONTENT_DISPOSITION, "attachment; filename=" +
199214
file.getName());
@@ -205,7 +220,7 @@ private static void setFileNameHeaders(HttpServletResponse response,
205220
* @param conf configuration
206221
* @return a data transfer throttler
207222
*/
208-
private final DataTransferThrottler getThrottler(Configuration conf) {
223+
public final static DataTransferThrottler getThrottler(Configuration conf) {
209224
long transferBandwidth =
210225
conf.getLong(DFSConfigKeys.DFS_IMAGE_TRANSFER_RATE_KEY,
211226
DFSConfigKeys.DFS_IMAGE_TRANSFER_RATE_DEFAULT);
@@ -263,7 +278,7 @@ static boolean isValidRequestor(ServletContext context, String remoteUser,
263278
* Set headers for content length, and, if available, md5.
264279
* @throws IOException
265280
*/
266-
private void setVerificationHeaders(HttpServletResponse response, File file)
281+
public static void setVerificationHeaders(HttpServletResponse response, File file)
267282
throws IOException {
268283
response.setHeader(TransferFsImage.CONTENT_LENGTH,
269284
String.valueOf(file.length()));
@@ -336,7 +351,7 @@ public GetImageParams(HttpServletRequest request,
336351
if (key.equals("getimage")) {
337352
isGetImage = true;
338353
try {
339-
txId = parseLongParam(request, TXID_PARAM);
354+
txId = ServletUtil.parseLongParam(request, TXID_PARAM);
340355
} catch (NumberFormatException nfe) {
341356
if (request.getParameter(TXID_PARAM).equals(LATEST_FSIMAGE_VALUE)) {
342357
fetchLatest = true;
@@ -346,11 +361,11 @@ public GetImageParams(HttpServletRequest request,
346361
}
347362
} else if (key.equals("getedit")) {
348363
isGetEdit = true;
349-
startTxId = parseLongParam(request, START_TXID_PARAM);
350-
endTxId = parseLongParam(request, END_TXID_PARAM);
364+
startTxId = ServletUtil.parseLongParam(request, START_TXID_PARAM);
365+
endTxId = ServletUtil.parseLongParam(request, END_TXID_PARAM);
351366
} else if (key.equals("putimage")) {
352367
isPutImage = true;
353-
txId = parseLongParam(request, TXID_PARAM);
368+
txId = ServletUtil.parseLongParam(request, TXID_PARAM);
354369
} else if (key.equals("port")) {
355370
remoteport = new Integer(val[0]).intValue();
356371
} else if (key.equals(STORAGEINFO_PARAM)) {
@@ -406,16 +421,5 @@ boolean shouldFetchLatest() {
406421
return fetchLatest;
407422
}
408423

409-
private static long parseLongParam(HttpServletRequest request, String param)
410-
throws IOException {
411-
// Parse the 'txid' parameter which indicates which image is to be
412-
// fetched.
413-
String paramStr = request.getParameter(param);
414-
if (paramStr == null) {
415-
throw new IOException("Invalid request has no " + param + " parameter");
416-
}
417-
418-
return Long.valueOf(paramStr);
419-
}
420424
}
421425
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/TransferFsImage.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import java.util.List;
2626
import java.lang.Math;
2727

28+
import javax.servlet.ServletOutputStream;
29+
import javax.servlet.ServletResponse;
2830
import javax.servlet.http.HttpServletResponse;
2931

3032
import org.apache.commons.logging.Log;
@@ -147,16 +149,16 @@ public static void uploadImageFromStorage(String fsName,
147149
* A server-side method to respond to a getfile http request
148150
* Copies the contents of the local file into the output stream.
149151
*/
150-
static void getFileServer(OutputStream outstream, File localfile,
152+
public static void getFileServer(ServletResponse response, File localfile,
153+
FileInputStream infile,
151154
DataTransferThrottler throttler)
152155
throws IOException {
153156
byte buf[] = new byte[HdfsConstants.IO_FILE_BUFFER_SIZE];
154-
FileInputStream infile = null;
157+
ServletOutputStream out = null;
155158
try {
156-
infile = new FileInputStream(localfile);
157159
CheckpointFaultInjector.getInstance()
158160
.aboutToSendFile(localfile);
159-
161+
out = response.getOutputStream();
160162

161163
if (CheckpointFaultInjector.getInstance().
162164
shouldSendShortFile(localfile)) {
@@ -180,14 +182,14 @@ static void getFileServer(OutputStream outstream, File localfile,
180182
buf[0]++;
181183
}
182184

183-
outstream.write(buf, 0, num);
185+
out.write(buf, 0, num);
184186
if (throttler != null) {
185187
throttler.throttle(num);
186188
}
187189
}
188190
} finally {
189-
if (infile != null) {
190-
infile.close();
191+
if (out != null) {
192+
out.close();
191193
}
192194
}
193195
}

0 commit comments

Comments
 (0)