-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWebServer.java
More file actions
325 lines (284 loc) · 12.1 KB
/
WebServer.java
File metadata and controls
325 lines (284 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
import java.io.*;
import java.net.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
public class WebServer implements Runnable {
static int PORT;
// root location
static File ROOT_LOC = new File(".");
// default file if no file is given
static final String INDEX_FILE = "index.html";
// variable defined in WebServer object
Socket mySocket;
// constructor for WebServer object
public WebServer(Socket connect) {
this.mySocket = connect;
}
// function to handle GET request
private void getRequest(File file, int fileLength, PrintWriter clientOut, BufferedOutputStream binaryOut) throws IOException {
FileInputStream fileInput = null;
// stores file data
byte[] fileData = new byte[fileLength];
try {
// open input stream
fileInput = new FileInputStream(file);
// read file data
fileInput.read(fileData);
}
finally {
// close file input stream
close(fileInput);
}
// headers when GET command is called
clientOut.println();
clientOut.println();
clientOut.println("HTTP/1.1 200 OK");
clientOut.println("Server: HTTP/1.1 server");
clientOut.println("Date: " + new Date());
clientOut.println("Last-Modified: " + new Date(file.lastModified()));
clientOut.println("Content-length: " + file.length());
clientOut.println();
clientOut.flush();
// write file
binaryOut.write(fileData,0,fileLength);
binaryOut.flush();
}
// function to return 404 error if file is not found
private void NotFound(PrintWriter out, String file) {
// headers when 404 Error is returned
out.println("HTTP/1.1 404 Not Found");
out.println("Server: HTTP/1.1 server");
out.println("Date: " + new Date());
out.println();
// show html in browser
out.println("<HTML>");
out.println("<HEAD><TITLE>File Not Found</TITLE>" + "</HEAD>");
out.println("<BODY>");
out.println("<H2>404 Not Found: " + file + "</H2>");
out.println("</BODY>");
out.println("</HTML>");
out.flush();
}
// close function for object stream s
public void close(Object s) {
if (s == null)
return;
// close depending on the type of object
try {
if (s instanceof Socket) {
((Socket)s).close();
}
if (s instanceof Reader) {
((Reader)s).close();
}
else if (s instanceof Writer) {
((Writer)s).close();
}
else if (s instanceof InputStream) {
((InputStream)s).close();
}
else if (s instanceof OutputStream) {
((OutputStream)s).close();
}
}
catch (Exception e) {
System.err.println("Error closing stream: " + e);
}
}
public void run() {
// variable to get name of file
String fileRequest = null;
// variable that reads data from client
BufferedReader input = null;
// variable to get output stream in characters from client
PrintWriter clientOut = null;
// variable to get binary output stream
BufferedOutputStream binaryOut = null;
// variable to hold list of headers
List<String> request_Header = new ArrayList<>();
// request variable
String request = null;
// variables to handle the ifModifiedSince
Boolean ifModifiedSince = false;
Date convertedDate = null;
long millis = 0;
try {
// retrieve character input stream from client
input = new BufferedReader(new InputStreamReader(mySocket.getInputStream()));
// retrieve character output stream to client
clientOut = new PrintWriter(mySocket.getOutputStream());
// retrieve binary output stream to client
binaryOut = new BufferedOutputStream(mySocket.getOutputStream());
// while loop to put all the headers in a list
while ((request = input.readLine()) != null && request.length() >0){
request_Header.add(request);
}
// get the command
String command = request_Header.get(0);
// check to see if if-modified-since is a header in the request
for(String listItem : request_Header){
if (listItem.contains("If-Modified-Since:")) {
ifModifiedSince = true;
String parseDate = listItem.replace("If-Modified-Since: ","");
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
try {
convertedDate = dateFormat.parse(parseDate);
} catch (ParseException e) {
// headers when error 400 is returned
clientOut.println("HTTP/1.1 400 Bad Request");
clientOut.println("Server: HTTP/1.1 server");
clientOut.println("Date: " + new Date());
clientOut.println();
// html to show up in browser
clientOut.println("<HTML>");
clientOut.println("<HEAD><TITLE>Bad Request</TITLE>" + "</HEAD>");
clientOut.println("<BODY>");
clientOut.println("<H2>Bad Request");
clientOut.println("</BODY></HTML>");
clientOut.flush();
return;
}
// convert date to long so we can compare it
millis = convertedDate.getTime();
break;
}
}
// print variables and requests for testing
String[] commandGet = command.split(" ");
System.out.println("Test (requests): "+request_Header);
System.out.println("Test (variables): "+"Command:"+commandGet[0]+", Path:"+commandGet[1]+", If-Modified-Since:"+convertedDate);
System.out.println("\n");
// parse request
StringTokenizer parse = new StringTokenizer(command);
// parse out method
String method = parse.nextToken().toUpperCase();
// parse out file requested
fileRequest = parse.nextToken();
// parse out ifModifiedSince date
// condition to see if commands are not GET or HEAD
if (!method.equals("HEAD") && !method.equals("GET")) {
// headers when error 501 is returned
clientOut.println("HTTP/1.1 501 Not Implemented");
clientOut.println("Server: HTTP/1.1 server");
clientOut.println("Date: " + new Date());
clientOut.println();
// html to show up in the browser
clientOut.println("<HTML>");
clientOut.println("<HEAD><TITLE>Not Implemented</TITLE>" + "</HEAD>");
clientOut.println("<BODY>");
clientOut.println("<H2>501 Not Implemented: " + method + " method.</H2>");
clientOut.println("</BODY></HTML>");
clientOut.flush();
return;
}
// conditions to determine if second command argument is a file or directory
if (ROOT_LOC.isFile()){
String[] separated = ROOT_LOC.toString().split("/");
fileRequest = separated[separated.length-1];
ROOT_LOC = new File(ROOT_LOC.toString().replace(fileRequest,""));
}
// if a directory add index.html automatically
if (fileRequest.endsWith("/")) {
fileRequest += INDEX_FILE;
}
// create file object from request
File file = new File(ROOT_LOC, fileRequest);
int fileLength = (int)file.length();
// If request is a HEAD only send the headers
if (method.equals("HEAD")){
// send HTTP headers
clientOut.println();
clientOut.println();
clientOut.println("HTTP/1.1 200 OK");
clientOut.println("Server: HTTP/1.1 server");
clientOut.println("Content-length: " + file.length());
clientOut.println("Date: " + new Date());
clientOut.println("Last-Modified: " + new Date(file.lastModified()));
clientOut.println(); //blank line between headers and content
clientOut.flush(); //flush character output stream buffer
}
// if request is a GET send the file content
if (method.equals("GET")) {
// add ifModifiedSince condition
if (ifModifiedSince){
// if last modified is later than ifModifiedSince then send get file
if (file.lastModified()>millis){
getRequest(file,fileLength,clientOut,binaryOut);
}
else {
// headers when 304 Error is returned
clientOut.println("HTTP/1.1 304 Not Modified");
clientOut.println("Server: HTTP/1.1 server");
clientOut.println("Date: " + new Date());
clientOut.println();
// html error to show up in the browser
clientOut.println("<HTML>");
clientOut.println("<HEAD><TITLE>Not Modified</TITLE>" + "</HEAD>");
clientOut.println("<BODY>");
clientOut.println("<H2>304 Not Modified");
clientOut.println("</BODY></HTML>");
clientOut.flush();
}
}
else{
// if there is no ifModifiedSince header than get file normally
getRequest(file,fileLength,clientOut,binaryOut);
}
}
}
catch (FileNotFoundException not_found) {
// calls the not found function to return error 404
NotFound(clientOut, fileRequest);
}
catch (Exception bad_request){
// headers when error 400 is returned
clientOut.println("HTTP/1.1 400 Bad Request");
clientOut.println("Server: HTTP/1.1 server");
clientOut.println("Date: " + new Date());
clientOut.println();
// html to show up in browser
clientOut.println("<HTML>");
clientOut.println("<HEAD><TITLE>Bad Request</TITLE>" + "</HEAD>");
clientOut.println("<BODY>");
clientOut.println("<H2>Bad Request");
clientOut.println("</BODY></HTML>");
clientOut.flush();
}
finally {
close(input);
close(clientOut);
close(binaryOut);
close(mySocket);
}
}
// main function
public static void main(String[] args) {
// command argument for port number
if (args.length > 0) {
PORT = Integer.parseInt(args[0]);
}
// command argument for root path
if (args.length > 1) {
ROOT_LOC = new File(args[1]);
}
try {
ServerSocket serverConnect = new ServerSocket(PORT);
System.out.println("\nServer is listening on " + PORT + "...\n");
while (true) {
// start the server
WebServer server = new WebServer(serverConnect.accept());
// make a new thread
Thread threadRunner = new Thread(server);
threadRunner.start();
}
}
catch (IOException e) {
System.err.println("Server error: " + e);
System.exit(-1);
}
}
}