Skip to content

Commit f620a14

Browse files
committed
add: checking for the freshness of a cached response
1 parent d748f18 commit f620a14

File tree

4 files changed

+210
-21
lines changed

4 files changed

+210
-21
lines changed

HttpRequest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import java.io.*;
22
import java.net.*;
33
import java.util.*;
4+
import java.text.SimpleDateFormat;
45

56
public class HttpRequest {
67
/** Help variables */
@@ -65,6 +66,51 @@ public HttpRequest(BufferedReader from) {
6566
System.out.println("Host to contact is: " + host + " at port " + port);
6667
}
6768

69+
// constructor
70+
public HttpRequest(String method, String url, String version, String headers, String host, int port) {
71+
this.method = method;
72+
this.URL = url;
73+
this.version = version;
74+
this.headers = headers;
75+
this.host = host;
76+
this.port = port;
77+
this.error = 0;
78+
}
79+
80+
// copy constructor
81+
public HttpRequest(HttpRequest other) {
82+
this.method = other.method;
83+
this.URL = other.URL;
84+
this.version = other.version;
85+
this.headers = other.headers;
86+
this.host = other.host;
87+
this.port = other.port;
88+
this.error = other.error;
89+
}
90+
91+
public HttpResponse askServerIfvalid(Date lastModified, String etag) {
92+
if (lastModified == null || etag == null || etag.isEmpty()) {
93+
return null;
94+
}
95+
96+
// User-Agent: SSProxyCache/1 (https://github.com/publicarray/SSProxyCache)
97+
// headers = "User-Agent: SSProxyCache/1.0\r\n";
98+
headers = "";
99+
100+
if (!etag.isEmpty()) {
101+
// If-None-Match: "07bb743de42c7918a1a47fd61c6aa9c7:1469533683"\r\n
102+
headers += "If-None-Match: " + etag + CRLF;
103+
} if (lastModified != null) {
104+
// If-Modified-Since: Thu, 13 Oct 2016 00:36:43 GMT\r\n
105+
SimpleDateFormat dateFormat = new SimpleDateFormat ("E, dd MMM yyyy HH:mm:ss z");
106+
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
107+
headers += "If-Modified-Since: " + dateFormat.format(lastModified) + CRLF;
108+
}
109+
110+
headers += "Host: " + host + CRLF;
111+
return send();
112+
}
113+
68114
public int getError() {
69115
return error;
70116
}
@@ -74,11 +120,19 @@ public String getHost() {
74120
return host;
75121
}
76122

123+
public void setHost(String host) {
124+
this.host = host;
125+
}
126+
77127
/** Return port for server */
78128
public int getPort() {
79129
return port;
80130
}
81131

132+
public void setPort(int port) {
133+
this.port = port;
134+
}
135+
82136
/** Return URL to connect to */
83137
public String getURL() {
84138
return URL;

HttpResponse.java

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ public class HttpResponse {
1919
Date lastModified;
2020
Date expires;
2121
String etag;
22-
2322
/* Body of reply */
2423
ByteArrayOutputStream body = new ByteArrayOutputStream();
2524

@@ -105,6 +104,21 @@ else if (line.toLowerCase(Locale.ROOT).startsWith("last-modified")) {
105104
body.write(buf, 0, res);
106105
bytesRead += res;
107106
}
107+
108+
109+
// http://codereview.stackexchange.com/questions/49746/implement-http-server-with-persistent-connection
110+
111+
// http://stackoverflow.com/questions/3870847/how-to-convert-the-datainputstream-to-the-string-in-java
112+
//http://stackoverflow.com/questions/13115857/http-socket-programming-java
113+
//
114+
//http://stackoverflow.com/questions/7696358/server-socket-file-transfer
115+
//http://stackoverflow.com/questions/13555668/java-send-file-using-sockets
116+
// http://stackoverflow.com/questions/10475898/receive-byte-using-bytearrayinputstream-from-a-socket
117+
// http://stackoverflow.com/questions/9520911/java-sending-and-receiving-file-byte-over-sockets
118+
// http://stackoverflow.com/questions/1176135/java-socket-send-receive-byte-array
119+
// http://stackoverflow.com/questions/8274966/reading-a-byte-array-from-socket
120+
// TSL -> http://stackoverflow.com/questions/18787419/ssl-socket-connection#18790838
121+
108122
} catch (IOException e) {
109123
// error = 500;
110124
System.out.println("Error reading response body: " + e);
@@ -155,6 +169,19 @@ public HttpResponse(int statusCode) {
155169
body.write(bodyStr.getBytes(), 0, bodyStr.length());
156170
}
157171

172+
// copy constructor
173+
public HttpResponse (HttpResponse other) {
174+
this.version = other.version;
175+
this.status = other.status;
176+
this.statusMessage = other.statusMessage;
177+
this.statusLine = other.statusLine;
178+
this.headers = other.headers;
179+
this.lastModified = other.lastModified;
180+
this.expires = other.expires;
181+
this.etag = other.etag;
182+
this.body = other.body;
183+
}
184+
158185
public HttpResponse setStaus(int status) {
159186
this.status = status;
160187
return this;
@@ -204,6 +231,7 @@ public Socket send(Socket host) {
204231
private Date toDate(String dateStr, String format) {
205232
try {
206233
SimpleDateFormat inFormat = new SimpleDateFormat(format);
234+
inFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
207235
return inFormat.parse(dateStr);
208236
} catch (ParseException e) {
209237
System.out.println("Parse date error: " + e + " -- at position:" + e.getErrorOffset());
@@ -216,15 +244,12 @@ private Date toDate(String dateStr) {
216244
return toDate(dateStr, format);
217245
}
218246

219-
// check if response is still valid
220-
public boolean isValid() {
221-
Date now = new Date(); // get current date time
247+
// check if response is still valid using the (now old) expires header
248+
public boolean isExpired() {
222249
if (lastModified != null && expires != null) {
250+
Date now = new Date(); // get current date time
251+
return !(now.after(lastModified) && now.before(expires)); // no web request is needed
223252
}
224-
225-
// System.out.println(now.toString() + ", " + lastModified.toString() + ", " + expires.toString());
226-
// TODO: etag
227-
// TODO: last modified
228-
return true; // assume as valid for now.
253+
return true;
229254
}
230255
}

ProxyCache.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public static void handle(Socket client) {
4444
/* Read request */
4545
try {
4646
BufferedReader fromClient = new BufferedReader(new InputStreamReader(client.getInputStream()));
47+
System.out.println("\n**** New Request ****");
4748
request = new HttpRequest(fromClient);
4849
System.out.println("---> Request --->\n" + request.toString()); // Debug
4950
} catch (IOException e) {
@@ -52,19 +53,17 @@ public static void handle(Socket client) {
5253
}
5354

5455
if (request.method.equals("GET")) { // Only GET requests are cached
55-
// create key
56-
String key = request.getURL();
5756
// Check if key is in cache
58-
if ((response = cache.get(key)) != null && response.isValid()) { // get item from cache if the cache is still fresh
57+
if ((response = cache.get(request.getURL())) != null) {// && response.isValid()) { // get item from cache if the cache is still fresh
5958
// response is set and valid
60-
System.out.println("Retrieved " + key);
61-
System.out.println("Is valid? " + response.isValid());
59+
// System.out.println("Retrieved " + request.getURL());
60+
// System.out.println("Is valid? " + cache.isValid(request));
6261
} else { // handle requests
6362
/* Send request to server */
6463
response = request.send();
6564

6665
/* Read response and forward it to client */
67-
cache.put(key, response);// Save response to cache
66+
cache.put(request.getURL(), response);// Save response to cache
6867
}
6968

7069
System.out.println("<--- Response <--- \n" + response.toString()); // Debug
@@ -83,7 +82,7 @@ public static void handle(Socket client) {
8382
// http://stackoverflow.com/questions/16358589/implementing-a-simple-https-proxy-application-with-java
8483
// http://stackoverflow.com/questions/18273703/tunneling-two-socket-client-in-java#18274109
8584
else if (request.method.equals("CONNECT")) {
86-
System.out.println("CONNECT found! Tunnelling connection.");
85+
System.out.println("CONNECT found! Tunnelling HTTPS connection.");
8786
InputStream fromClient;
8887
OutputStream toServer;
8988
try {
@@ -102,8 +101,8 @@ else if (request.method.equals("CONNECT")) {
102101
}
103102

104103
try {
105-
client.setSoTimeout(5000);
106-
server.setSoTimeout(5000);
104+
client.setSoTimeout(3000);
105+
server.setSoTimeout(3000);
107106
byte[] buffer = new byte[2048];
108107
int rc = 0;
109108

SCache.java

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,135 @@
22
import java.net.*;
33
import java.util.*;
44
import java.util.HashMap;
5+
import java.text.SimpleDateFormat;
56

67
public class SCache {
78
private Map<String, HttpResponse> cacheStore;
9+
HttpRequest request;
810

911
public SCache() {
1012
// synchronised HasMap prevents data corruption when reading while writing the Map
1113
cacheStore = Collections.synchronizedMap(new HashMap<String, HttpResponse>());
14+
request = new HttpRequest("GET", "", "HTTP/1.1", "", "", 0);
1215
}
1316

1417
// add value to HashMap
1518
public HttpResponse put(String key, HttpResponse value) {
16-
System.out.println("Saved " + key + " to cache.");
19+
System.out.println("SCACHE: Saved " + key + " to cache.");
1720
return cacheStore.put(key, value);
1821
}
1922

2023
// retrieve value from HashMap
2124
public HttpResponse get(String key) {
22-
System.out.println("looking for " + key + " in cache.");
23-
return cacheStore.get(key);
25+
System.out.println("SCACHE: looking for " + key + " in cache.");
26+
HttpResponse value = cacheStore.get(key);
27+
if (value == null) {
28+
System.out.println("SCACHE: " + key + " not found in cache.");
29+
return null;
30+
}
31+
System.out.println("SCACHE: retrieved " + key + " from cache.");
32+
33+
boolean valid = isValid(key, value);
34+
System.out.println("SCACHE: Is key valid? " + valid);
35+
if (!valid) { // get the new value if there is a new one
36+
value = cacheStore.get(key);
37+
}
38+
return value;
39+
}
40+
41+
// check if response is still valid
42+
public boolean isValid(String requestURL, HttpResponse response) {
43+
if (!response.isExpired()) {
44+
System.out.println("SCACHE: response has not expired");
45+
return true;
46+
}
47+
48+
// HttpRequest check = new HttpRequest(request); // deep copy
49+
request.setHost(getHost(requestURL));
50+
request.setPort(getPort(requestURL));
51+
request.URL = getLocation(requestURL);
52+
53+
HttpResponse newResponse = request.askServerIfvalid(response.lastModified, response.etag); // ask severer
54+
if (newResponse == null) { // if error
55+
return true; // Todo: for debugging
56+
}
57+
// HttpResponse r = new HttpRequest(request, response.lastModified, response.etag).send(); // ask server
58+
if (newResponse.status == 304) { // Not modified, so still valid
59+
System.out.println("SCACHE: 304 Not modified");
60+
return true;
61+
} else { // 200 OK and other responses
62+
System.out.println("SCACHE: response was modified. Updating Cache.");
63+
put(requestURL, newResponse); // save the new response
64+
return false;
65+
}
66+
67+
// return false;
68+
}
69+
70+
public static String getHost(String url) {
71+
if (url == null || url.length() <= 0) {
72+
return "";
73+
}
74+
75+
int start = url.indexOf("//");
76+
if (start == -1) {
77+
return "";
78+
}
79+
start += 2;
80+
81+
int end = url.indexOf('/', start);
82+
if (end == -1) {
83+
return "";
84+
}
85+
86+
return url.substring(start, end);
87+
}
88+
89+
public static int getPort(String url) {
90+
if (url == null || url.length() <= 0) {
91+
return 80;
92+
}
93+
94+
int start = url.indexOf("//");
95+
if (start == -1) {
96+
return 80;
97+
}
98+
99+
start += 2;
100+
101+
int end = url.indexOf('/', start);
102+
if (end == -1) {
103+
return 80;
104+
}
105+
106+
int port = url.indexOf(':', start);
107+
if (port == -1) {
108+
return 80;
109+
}
110+
111+
if (port < end) {
112+
end = port;
113+
}
114+
115+
return Integer.parseInt(url.substring(port, end));
116+
}
117+
118+
public static String getLocation(String url) {
119+
if (url == null || url.length() <= 0) {
120+
return "";
121+
}
122+
123+
int start = url.indexOf("//");
124+
if (start == -1) {
125+
return "";
126+
}
127+
start += 2;
128+
129+
int end = url.indexOf('/', start);
130+
if (end == -1) {
131+
return "";
132+
}
133+
134+
return url.substring(end);
24135
}
25136
}

0 commit comments

Comments
 (0)