-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathPusher.java
More file actions
264 lines (240 loc) · 7.96 KB
/
Pusher.java
File metadata and controls
264 lines (240 loc) · 7.96 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
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.mortbay.log.Log;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
/**
* Static class to send messages to Pusher's REST API.
*
* Please set pusherApplicationId, pusherApplicationKey, pusherApplicationSecret accordingly
* before sending any request.
*
* @author Stephan Scheuermann
* Copyright 2010. Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
*/
public class Pusher {
/**
* Pusher Host name
*/
private final static String pusherHost = "api.pusherapp.com";
/**
* Pusher Application Identifier
*/
private final static String pusherApplicationId = "";
/**
* Pusher Application Key
*/
private final static String pusherApplicationKey = "";
/**
* Pusher Secret
*/
private final static String pusherApplicationSecret = "";
/**
* Converts a byte array to a string representation
* @param data
* @return
*/
private static String byteArrayToString(byte[] data){
BigInteger bigInteger = new BigInteger(1,data);
String hash = bigInteger.toString(16);
// Zero pad it
while(hash.length() < 32 ){
hash = "0" + hash;
}
return hash;
}
/**
* Returns a md5 representation of the given string
* @param data
* @return
*/
private static String md5Representation(String data) {
try {
//Get MD5 MessageDigest
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] digest = messageDigest.digest(data.getBytes("US-ASCII"));
return byteArrayToString(digest);
} catch (NoSuchAlgorithmException nsae) {
//We should never come here, because GAE has a MD5 algorithm
throw new RuntimeException("No MD5 algorithm");
} catch (UnsupportedEncodingException e) {
//We should never come here, because UTF-8 should be available
throw new RuntimeException("No UTF-8");
}
}
/**
* Returns a HMAC/SHA256 representation of the given string
* @param data
* @return
*/
private static String hmacsha256Representation(String data) {
try {
// Create the HMAC/SHA256 key from application secret
final SecretKeySpec signingKey = new SecretKeySpec( pusherApplicationSecret.getBytes(), "HmacSHA256");
// Create the message authentication code (MAC)
final Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
//Process and return data
byte[] digest = mac.doFinal(data.getBytes("UTF-8"));
digest = mac.doFinal(data.getBytes());
//Convert to string
BigInteger bigInteger = new BigInteger(1,digest);
return String.format("%0" + (digest.length << 1) + "x", bigInteger);
} catch (NoSuchAlgorithmException nsae) {
//We should never come here, because GAE has HMac SHA256
throw new RuntimeException("No HMac SHA256 algorithm");
} catch (UnsupportedEncodingException e) {
//We should never come here, because UTF-8 should be available
throw new RuntimeException("No UTF-8");
} catch (InvalidKeyException e) {
throw new RuntimeException("Invalid key exception while converting to HMac SHA256");
}
}
/**
* Build query string that will be appended to the URI and HMAC/SHA256 encoded
* @param eventName
* @param jsonData
* @return
*/
private static String buildQuery(String eventName, String jsonData, String socketID){
StringBuffer buffer = new StringBuffer();
//Auth_Key
buffer.append("auth_key=");
buffer.append(pusherApplicationKey);
//Timestamp
buffer.append("&auth_timestamp=");
buffer.append(System.currentTimeMillis() / 1000);
//Auth_version
buffer.append("&auth_version=1.0");
//MD5 body
buffer.append("&body_md5=");
buffer.append(md5Representation(jsonData));
//Event Name
buffer.append("&name=");
buffer.append(eventName);
//Append socket id if set
if (!socketID.isEmpty()) {
buffer.append("&socket_id=");
buffer.append(socketID);
}
//Return content of buffer
return buffer.toString();
}
/**
* Build path of the URI that is also required for Authentication
* @return
*/
private static String buildURIPath(String channelName){
StringBuffer buffer = new StringBuffer();
//Application ID
buffer.append("/apps/");
buffer.append(pusherApplicationId);
//Channel name
buffer.append("/channels/");
buffer.append(channelName);
//Event
buffer.append("/events");
//Return content of buffer
return buffer.toString();
}
/**
* Build authentication signature to assure that our event is recognized by Pusher
* @param uriPath
* @param query
* @return
*/
private static String buildAuthenticationSignature(String uriPath, String query){
StringBuffer buffer = new StringBuffer();
//request method
buffer.append("POST\n");
//URI Path
buffer.append(uriPath);
buffer.append("\n");
//Query string
buffer.append(query);
//Encode data
String h = buffer.toString();
return hmacsha256Representation(h);
}
/**
* Build URI where request is send to
* @param uriPath
* @param query
* @param signature
* @return
*/
private static URL buildURI(String uriPath, String query, String signature){
StringBuffer buffer = new StringBuffer();
//Protocol
buffer.append("http://");
//Host
buffer.append(pusherHost);
//URI Path
buffer.append(uriPath);
//Query string
buffer.append("?");
buffer.append(query);
//Authentication signature
buffer.append("&auth_signature=");
buffer.append(signature);
//Build URI
try {
return new URL(buffer.toString());
} catch (MalformedURLException e) {
throw new RuntimeException("Malformed URI");
}
}
/**
* Delivers a message to the Pusher API without providing a socket_id
* @param channel
* @param event
* @param jsonData
* @return
*/
public static HTTPResponse triggerPush(String channel, String event, String jsonData){
return triggerPush(channel, event, jsonData, "");
}
/**
* Delivers a message to the Pusher API
* @param channel
* @param event
* @param jsonData
* @param socketId
* @return
*/
public static HTTPResponse triggerPush(String channel, String event, String jsonData, String socketId){
//Build URI path
String uriPath = buildURIPath(channel);
//Build query
String query = buildQuery(event, jsonData, socketId);
//Generate signature
String signature = buildAuthenticationSignature(uriPath, query);
//Build URI
URL url = buildURI(uriPath, query, signature);
//Create Google APP Engine Fetch URL service and request
URLFetchService urlFetchService = URLFetchServiceFactory.getURLFetchService();
HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST);
request.addHeader(new HTTPHeader("Content-Type", "application/json"));
request.setPayload(jsonData.getBytes());
//Start request
try {
return urlFetchService.fetch(request);
} catch (IOException e) {
//Log warning
Log.warn("Pusher request could not be send to the following URI " + url.toString());
return null;
}
}
}