1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package zeus.actors.intrays;
23 import java.net.*;
24 import java.io.*;
25 import zeus.util.*;
26 import FIPA.*;
27 import zeus.concepts.*;
28 import zeus.actors.outtrays.*;
29 import zeus.actors.*;
30 import zeus.actors.factories.*;
31
32
33 /***
34 * FIPA_2000_HTTP_Connection handles incomming connections on this host/port socket
35 * and decides
36 * whether or not they are meant for it (ie: is the name on the connection the
37 * same as the name in the init of this class)<p>
38 * If the connection is relevant then the data will be read, a response (as per
39 * the spec ) will be sent and the message will be decoded into a FIPA.FipaMessage
40 * and placed on the registered queue for processing by the relevant server <p>
41 * The class constructor takes a host a port and a name : should this agent only listen
42 * for connections for itself at this port and host? That is what is implemented here...
43 * comments on a postcard please<p>
44 * @author Simon Thompson
45 * @since 1.1
46 */
47 public class FIPA_2000_HTTP_Connection implements Runnable {
48 protected Queue queue;
49 private String response_ok = new String("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: no-cache\r\nConnection: close\n");
50 private String response_not_ok = new String("HTTP/1.1 401 NOT_OK \nContent-Type: text/plain\nCache-Control: no-cache\nConnection: close\n");
51 private String host = null;
52 private String name = null;
53 private String port = null;
54 private int MAX_HTTP_QUEUE = 10;
55 /***
56 * MAX_LINES_TO_READ_WITHOUT_YEILDING is used to control how
57 * many lines will be read in from a connection in one lump. Without this I
58 * think that a DOS attack could be mounted via a http connection just
59 * by sending it an infinitely long message: perhaps another agent could
60 * use this to prevent this agent from bidding in a round of contracts, or
61 * from sending some alert information to another agent! <p>
62 * This is protected to prevent it being reset in Agent specific code
63 */
64 protected int MAX_LINES_TO_READ_WITHOUT_YEILDING = 5000;
65 protected int MAX_LINES_TO_READ_WITHOUT_SHUTTING = 5000;
66 /***
67 * if MAX_CONTENT_LENGHT is greater than 200001 it will be ignored and
68 * max_int length content will be accepted
69 */
70 protected int MAX_CONTENT_LENGTH = 100000;
71 private ServerSocket serverSocket = null;
72
73
74 /***
75 * run method that makes sure a thread is trying to pick up inputs from a
76 * connection <p>
77 * The loop blocks on the ServerSocket.accept() method. A count variable
78 * is used to prevent an DOS on this agent by yeilding after a certain number
79 * of lines have been read.
80 */
81 public void run() {
82 while (true) {
83 try {
84 String currentMessage = new String();
85 debug("Listening in http");
86 Socket connection = serverSocket.accept();
87
88
89
90 PrintWriter out = new PrintWriter(connection.getOutputStream(), true );
91 InputStreamReader ins = new InputStreamReader(connection.getInputStream());
92 BufferedReader in = new BufferedReader(ins);
93 int count = 0;
94 String ln;
95 boolean done = false;
96 String contentStr = null;
97 String boundaryVal = null;
98 int contentLength = 0;
99 int bigCount = 0;
100 while(!done) {
101 ln=in.readLine();
102 debug(ln);
103 currentMessage += ln +"\n";
104 if (boundaryVal == null)
105 boundaryVal = testAndSetBoundary(ln);
106 if (contentLength == 0)
107 contentLength = testAndSetContentLength(ln);
108
109 if (contentLength > MAX_CONTENT_LENGTH) {
110 respondNotOK(out);
111 in.close();
112 out.close();
113 connection.close();
114 }
115 debug("boundary=" + boundaryVal);
116
117 if ((boundaryVal != null) && (contentLength > 0)) {
118
119 if (ln.equals("--" + boundaryVal)) {
120 debug("found boundary");
121 contentStr = new String();
122 ln = in.readLine();
123 String ender = "--" + boundaryVal + "--";
124
125 while (!ender.equals(ln.trim())) {
126 debug("content:" + ln);
127 contentStr += ln +" ";
128 ln = in.readLine();
129
130
131 }
132 contentStr += ender;
133
134
135
136
137
138 done = true;
139 }
140 }
141
142 debug("readIn >> " + ln);
143 bigCount++;
144 count++;
145
146 if (count > MAX_LINES_TO_READ_WITHOUT_YEILDING) {
147 Thread.yield();
148 count = 0; }
149
150 if (bigCount > MAX_LINES_TO_READ_WITHOUT_SHUTTING) {
151
152 in.close();
153 out.close();
154 connection.close();
155 }
156
157
158
159
160
161
162
163 }
164
165
166 debug(contentStr);
167 FIPAPerformative fmess = process(contentStr,boundaryVal);
168
169 if (fmess!= null) {
170 respondOK(out);
171 message(fmess); }
172 else {
173 respondNotOK(out);
174 }
175
176
177 connection.close();
178
179 }
180 catch (IOException ioe) {
181 ioe.printStackTrace();
182 System.out.println("IO error opening http connection from another agent: recovering");
183 }
184 catch (Exception e) {
185 e.printStackTrace();}
186 catch (Error er) {
187 System.out.println("Error trapped and handled in FIPA_2000_HTTP_Connection: recovering");
188 er.printStackTrace(); }
189 catch (Throwable tr) {
190 System.out.println("*** Throwable trapped and handled in FIPA_2000_HTTP_Connection: recovering");
191 }
192
193 }
194
195 }
196
197
198 /***
199 * hitch this connection to a message queue
200 */
201 public void register(Queue q) {
202 this.queue = q;
203 }
204
205
206 /***
207 * pull the boundary val from the header
208 */
209 private String testAndSetBoundary(String ln) {
210
211 String tmp = ln.toLowerCase();
212 debug(">> test boundary:" + tmp);
213
214 if (tmp.startsWith("content-type: multipart/mixed; boundary=\"")) {
215
216 debug(">> returning boundary!");
217 return (ln.substring(41,ln.length()-1));
218 }
219 if (tmp.startsWith("content-type: multipart/mixed; boundary=\"")) {
220
221 debug(">> returning boundary!");
222 return (ln.substring(42,ln.length()-1));
223 }
224 if (tmp.startsWith("content-type: multipart/mixed ; boundary=\"")) {
225 return (ln.substring(42,ln.length()-1));
226 }
227 else if (tmp.startsWith("\tboundary=\"")) {
228
229 debug(">> returning boundary!");
230 return (ln.substring(11, ln.length()-1));
231 }
232 else {
233 debug(">> returning null!");
234 return null;
235 }
236 }
237
238
239 /***
240 * pull the content length from the header
241 */
242 private int testAndSetContentLength(String ln) {
243 if (ln.startsWith("Content-length") || ln.startsWith("Content-Length")) {
244 String lengthContent = ln.substring(15,ln.length()).trim();
245 int contentLength = Integer.parseInt(lengthContent);
246
247 return contentLength;
248 }
249 else {
250 return 0;
251 }
252 }
253
254
255
256 /***
257 * send the mandated response to a successful message reception
258 * episode (see XC00084C)
259 */
260 public void respondOK(PrintWriter out) {
261 out.println(response_ok);
262 out.flush();
263 out.close();
264 }
265
266
267 /***
268 * send a not OK response (hopefully we won't have to do this!)<p>
269 * This is not something that I could see mandated in the FIPA-spec,
270 * but I think that it is a good idea - otherwise the connection remains open
271 * until a timeout. <p>
272 * Perhaps sufficient open connections could be used as some sort of attack
273 * on the agent.
274 */
275 public void respondNotOK(PrintWriter out) {
276 out.println(response_not_ok);
277 out.flush();
278 out.close();
279 }
280
281
282 /***
283 * message is used to handle the completely read and parsed message when
284 * it has come off the message queue
285 */
286 public void message(FIPAPerformative aFipaMessage) {
287
288 queue.enqueue(aFipaMessage);
289 }
290
291
292 public FIPA_2000_HTTP_Connection(String host, String port, String name) {
293 this.host = host;
294 this.name = name;
295 this.port = port;
296 try {
297
298 java.io.File file = new java.io.File("http.out");
299 java.io.FileOutputStream outStream = new java.io.FileOutputStream(file);
300 java.io.PrintStream err = new java.io.PrintStream(outStream);
301
302
303 serverSocket = new ServerSocket(Integer.parseInt(port),MAX_HTTP_QUEUE); }
304 catch (IOException e) {
305 e.printStackTrace();
306 System.out.println("cannot get a socket to listen for http connections on: recovering (without HTTP connection)");
307 }
308
309
310 }
311
312
313 /***
314 * used to test the code here!
315 */
316 protected boolean test(String myPort, String testHost, String testPort) {
317 try {
318 TransportFactory tf = new IIOP_Z_HTTP_TransportFactory();
319 InetAddress ip = InetAddress.getLocalHost();
320 String localHost = ip.getHostAddress();
321 String targetAddress = new String("http://"+testHost+":"+testPort+"/test");
322 String sourceAddress = new String("http://"+localHost+":"+myPort+"/test");
323 OutTray transport = tf.getTransport(targetAddress);
324 FIPAPerformative fperf = new FIPAPerformative("inform");
325 javax.agent.Envelope env = fperf.jasEnvelope(new FIPA_AID_Address(sourceAddress),targetAddress);
326 transport.send(env);
327 return true; }
328 catch (Exception e ) {
329 e.printStackTrace();
330 return false;
331 }
332 }
333
334
335 /***
336 * main method for testing this module<p>
337 * <p> parameters are <port number for this> <host to test connection to> <port on test host>
338 */
339 public static void main(String argv[]) {
340 String myPort = argv[0];
341 String testHost = argv[1];
342 String testPort = argv[2];
343 FIPA_2000_HTTP_Connection test = new FIPA_2000_HTTP_Connection("tb-toledo.futures.bt.co.uk",myPort,"test");
344 boolean done = false;
345 while (!done) {
346 try {
347 Thread.sleep(1000);
348 }
349 catch (Exception e) {
350 e.printStackTrace();
351 }
352
353 }
354
355 }
356
357
358 private FIPAPerformative process(String inMess,String bound) {
359 debug("inMess == \n" + inMess + "\n end inMess");
360 String message = stripEnvelope(inMess, bound);
361 debug ("......\n " + message + " -------");
362 FIPAPerformative fperf = ZeusParser.fipaPerformative(message);
363 String content = fperf.getContent();
364 try {
365 fperf.setContent (addEscape(content));}
366 catch (Exception e) {
367 System.out.println("EXCEPTION");
368 e.printStackTrace();
369 }
370 debug("outMess == \n" + fperf.getContent() + "\n end inMess");
371 return (fperf);
372
373 }
374
375 private String addEscape (String in) {
376 try {
377 int counter = 0;
378 int pointer = 0;
379 StringBuffer buf = new StringBuffer (in);
380 StringBuffer newOne = new StringBuffer();
381 while (counter<in.length()-1) {
382 char thisOne = buf.charAt(counter);
383 if (thisOne != '"') {
384 newOne.append(thisOne);
385 }
386 else {
387 System.out.println("replacing");
388 newOne.append('//');
389 newOne.append('//');
390 newOne.append('//');
391 newOne.append(thisOne);
392 }
393 counter++;
394 }
395 String ret = newOne.toString();
396 return ret; }
397 catch (Exception e) {
398 return (new String());
399 }
400 }
401
402 /***
403 * crude for the moment - this needs redoing so it is less fragile.
404 */
405 private String stripEnvelope(String HTTPmessage,String bound) {
406
407 String remLastBoundary = HTTPmessage.substring(0, HTTPmessage.lastIndexOf(bound)-2);
408
409
410 String message = remLastBoundary.substring(HTTPmessage.indexOf(bound)+bound.length(),remLastBoundary.length());
411 message = remLastBoundary.substring(remLastBoundary.indexOf('('),remLastBoundary.length());
412
413
414
415
416
417 return message.trim();
418 }
419
420
421 private void debug(String val) {
422 System.out.println("\nFIPA_2000_HTTP_Connection>>"+val+"\n");
423 }
424 }