2525
2626package sun .net .www .protocol .file ;
2727
28+ import java .io .BufferedInputStream ;
29+ import java .io .ByteArrayInputStream ;
30+ import java .io .File ;
31+ import java .io .FileInputStream ;
32+ import java .io .FileNotFoundException ;
33+ import java .io .FilePermission ;
34+ import java .io .IOException ;
35+ import java .io .InputStream ;
36+ import java .net .FileNameMap ;
2837import java .net .MalformedURLException ;
2938import java .net .URL ;
30- import java .net .FileNameMap ;
31- import java .io .*;
32- import java .text .Collator ;
3339import java .security .Permission ;
34- import sun .net .www .*;
35- import java .util .*;
40+ import java .text .Collator ;
3641import java .text .SimpleDateFormat ;
42+ import java .util .Arrays ;
43+ import java .util .Date ;
44+ import java .util .List ;
45+ import java .util .Locale ;
46+ import java .util .Map ;
47+ import java .util .TimeZone ;
48+
49+ import sun .net .www .MessageHeader ;
50+ import sun .net .www .ParseUtil ;
51+ import sun .net .www .URLConnection ;
3752
3853/**
3954 * Open a file input stream given a URL.
@@ -67,25 +82,49 @@ protected FileURLConnection(URL u, File file) {
6782 this .file = file ;
6883 }
6984
70- /*
85+ /**
86+ * If already connected, then this method is a no-op.
87+ * If not already connected, then this method does
88+ * readability checks for the File.
89+ * <p>
90+ * If the File is a directory then the readability check
91+ * is done by verifying that File.list() does not return
92+ * null. On the other hand, if the File is not a directory,
93+ * then this method constructs a temporary FileInputStream
94+ * for the File and lets the FileInputStream's constructor
95+ * implementation do the necessary readability checks.
96+ * That temporary FileInputStream is closed before returning
97+ * from this method.
98+ * <p>
99+ * In either case, if the readability checks fail, then
100+ * an IOException is thrown from this method and the
101+ * FileURLConnection stays unconnected.
102+ * <p>
103+ * A normal return from this method implies that the
104+ * FileURLConnection is connected and the readability
105+ * checks have passed for the File.
106+ * <p>
71107 * Note: the semantics of FileURLConnection object is that the
72108 * results of the various URLConnection calls, such as
73109 * getContentType, getInputStream or getContentLength reflect
74110 * whatever was true when connect was called.
75111 */
112+ @ Override
76113 public void connect () throws IOException {
77114 if (!connected ) {
78-
79115 isDirectory = file .isDirectory ();
116+ // verify readability of the directory or the regular file
80117 if (isDirectory ) {
81118 String [] fileList = file .list ();
82- if (fileList == null )
119+ if (fileList == null ) {
83120 throw new FileNotFoundException (file .getPath () + " exists, but is not accessible" );
121+ }
84122 directoryListing = Arrays .asList (fileList );
85123 } else {
86- is = new BufferedInputStream (new FileInputStream (file .getPath ()));
124+ // let FileInputStream constructor do the necessary readability checks
125+ // and propagate any failures
126+ new FileInputStream (file .getPath ()).close ();
87127 }
88-
89128 connected = true ;
90129 }
91130 }
@@ -112,9 +151,9 @@ private void initializeHeaders() {
112151 FileNameMap map = java .net .URLConnection .getFileNameMap ();
113152 String contentType = map .getContentTypeFor (file .getPath ());
114153 if (contentType != null ) {
115- properties .add (CONTENT_TYPE , contentType );
154+ properties .set (CONTENT_TYPE , contentType );
116155 }
117- properties .add (CONTENT_LENGTH , Long .toString (length ));
156+ properties .set (CONTENT_LENGTH , Long .toString (length ));
118157
119158 /*
120159 * Format the last-modified field into the preferred
@@ -126,85 +165,109 @@ private void initializeHeaders() {
126165 SimpleDateFormat fo =
127166 new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'" , Locale .US );
128167 fo .setTimeZone (TimeZone .getTimeZone ("GMT" ));
129- properties .add (LAST_MODIFIED , fo .format (date ));
168+ properties .set (LAST_MODIFIED , fo .format (date ));
130169 }
131170 } else {
132- properties .add (CONTENT_TYPE , TEXT_PLAIN );
171+ properties .set (CONTENT_TYPE , TEXT_PLAIN );
133172 }
134173 initializedHeaders = true ;
135174 }
136175 }
137176
138- public Map <String ,List <String >> getHeaderFields () {
177+ @ Override
178+ public Map <String , List <String >> getHeaderFields () {
139179 initializeHeaders ();
140180 return super .getHeaderFields ();
141181 }
142182
183+ @ Override
143184 public String getHeaderField (String name ) {
144185 initializeHeaders ();
145186 return super .getHeaderField (name );
146187 }
147188
189+ @ Override
148190 public String getHeaderField (int n ) {
149191 initializeHeaders ();
150192 return super .getHeaderField (n );
151193 }
152194
195+ @ Override
153196 public int getContentLength () {
154197 initializeHeaders ();
155198 if (length > Integer .MAX_VALUE )
156199 return -1 ;
157200 return (int ) length ;
158201 }
159202
203+ @ Override
160204 public long getContentLengthLong () {
161205 initializeHeaders ();
162206 return length ;
163207 }
164208
209+ @ Override
165210 public String getHeaderFieldKey (int n ) {
166211 initializeHeaders ();
167212 return super .getHeaderFieldKey (n );
168213 }
169214
215+ @ Override
170216 public MessageHeader getProperties () {
171217 initializeHeaders ();
172218 return super .getProperties ();
173219 }
174220
221+ @ Override
175222 public long getLastModified () {
176223 initializeHeaders ();
177224 return lastModified ;
178225 }
179226
227+ @ Override
180228 public synchronized InputStream getInputStream ()
181229 throws IOException {
182230
183231 connect ();
232+ // connect() does the necessary readability checks and is expected to
233+ // throw IOException if any of those checks fail. A normal completion of connect()
234+ // must mean that connect succeeded.
235+ assert connected : "not connected" ;
184236
185- if (is == null ) {
186- if (isDirectory ) {
237+ // a FileURLConnection only ever creates and provides a single InputStream
238+ if (is != null ) {
239+ return is ;
240+ }
187241
188- if (directoryListing == null ) {
189- throw new FileNotFoundException (file .getPath ());
190- }
242+ if (isDirectory ) {
243+ // a successful connect() implies the directoryListing is non-null
244+ // if the file is a directory
245+ assert directoryListing != null : "missing directory listing" ;
191246
192- directoryListing .sort (Collator .getInstance ());
247+ directoryListing .sort (Collator .getInstance ());
193248
194- StringBuilder sb = new StringBuilder ();
195- for (String fileName : directoryListing ) {
196- sb .append (fileName );
197- sb .append ("\n " );
198- }
199- // Put it into a (default) locale-specific byte-stream.
200- is = new ByteArrayInputStream (sb .toString ().getBytes ());
201- } else {
202- throw new FileNotFoundException (file .getPath ());
249+ StringBuilder sb = new StringBuilder ();
250+ for (String fileName : directoryListing ) {
251+ sb .append (fileName );
252+ sb .append ("\n " );
203253 }
254+ // Put it into a (default) locale-specific byte-stream.
255+ is = new ByteArrayInputStream (sb .toString ().getBytes ());
256+ } else {
257+ is = new BufferedInputStream (new FileInputStream (file .getPath ()));
204258 }
205259 return is ;
206260 }
207261
262+ @ Override
263+ protected synchronized void ensureCanServeHeaders () throws IOException {
264+ // connect() (if not already connected) does the readability checks
265+ // and throws an IOException if those checks fail. A successful
266+ // completion from connect() implies the File is readable.
267+ connect ();
268+ }
269+
270+
208271 Permission permission ;
209272
210273 /* since getOutputStream isn't supported, only read permission is
0 commit comments