001 package com.croftsoft.core.net.http;
002
003 import java.io.*;
004 import java.net.*;
005 import java.util.*;
006
007 import com.croftsoft.core.io.Parser;
008
009 /*********************************************************************
010 * Somewhat of a replacement for HttpURLConnection.
011 *
012 * <p />
013 *
014 * @version
015 * 2003-05-19
016 * @since
017 * 2000-04-27
018 * @author
019 * <a href="http://www.CroftSoft.com/">David W. Croft</a>
020 *********************************************************************/
021
022 public final class HttpConnection
023 //////////////////////////////////////////////////////////////////////
024 //////////////////////////////////////////////////////////////////////
025 {
026
027 public static final int HTTP_ACCEPTED = 202;
028
029 public static final int HTTP_NO_CONTENT = 204;
030
031 private final String host;
032
033 private final int port;
034
035 private final String path;
036
037 private final String userAgent;
038
039 private final String contentType;
040
041 private byte [ ] requestBytes;
042
043 private final Parser contentParser;
044
045 // need to modify so that Headers used during outgoing posts;
046 // don't forget to clear!
047
048 private final Hashtable headerHashtable;
049
050 private int responseCode;
051
052 private Object parsedContent;
053
054 private Socket socket;
055
056 //////////////////////////////////////////////////////////////////////
057 //////////////////////////////////////////////////////////////////////
058
059 public HttpConnection (
060 String host,
061 int port,
062 String path,
063 String userAgent,
064 String contentType,
065 byte [ ] requestBytes,
066 Parser contentParser )
067 //////////////////////////////////////////////////////////////////////
068 {
069 if ( host == null )
070 {
071 throw new IllegalArgumentException ( "null host" );
072 }
073
074 if ( port == -1 )
075 {
076 port = 80;
077 }
078
079 if ( path == null )
080 {
081 throw new IllegalArgumentException ( "null path" );
082 }
083
084 if ( userAgent == null )
085 {
086 throw new IllegalArgumentException ( "null userAgent" );
087 }
088
089 if ( contentType == null )
090 {
091 throw new IllegalArgumentException ( "null contentType" );
092 }
093
094 this.host = host;
095
096 this.port = port;
097
098 this.path = path;
099
100 this.userAgent = userAgent;
101
102 this.contentType = contentType;
103
104 this.requestBytes = requestBytes;
105
106 this.contentParser = contentParser;
107
108 headerHashtable = new Hashtable ( );
109 }
110
111 //////////////////////////////////////////////////////////////////////
112 //////////////////////////////////////////////////////////////////////
113
114 public void post ( )
115 throws IOException
116 //////////////////////////////////////////////////////////////////////
117 {
118 // Save a reference to requestBytes in case it gets changed.
119
120 byte [ ] requestBytes = this.requestBytes;
121
122 OutputStream out = null;
123
124 InputStream in = null;
125
126 try
127 {
128 socket = new Socket ( host, port );
129
130 out = socket.getOutputStream ( );
131
132 StringBuffer stringBuffer = new StringBuffer ( );
133
134 stringBuffer.append ( "POST " + path + " HTTP/1.0\r\n" );
135
136 stringBuffer.append ( "Connection: Keep-Alive\r\n" );
137
138 stringBuffer.append ( "User-Agent: " + userAgent + "\r\n" );
139
140 // What was that other one? Webmaster?
141
142 stringBuffer.append ( "Content-type: " + contentType + "\r\n" );
143
144 if ( requestBytes != null )
145 {
146 stringBuffer.append (
147 "Content-length: " + requestBytes.length + "\r\n" );
148 }
149
150 stringBuffer.append ( "\r\n" );
151
152 // US-ASCII was here
153 out.write ( stringBuffer.toString ( ).getBytes ( ) );
154
155 if ( requestBytes != null )
156 {
157 out.write ( requestBytes );
158 }
159
160 out.flush ( );
161
162 in = socket.getInputStream ( );
163
164 parseResponse ( in );
165 }
166 finally
167 {
168 try
169 {
170 if ( in != null )
171 {
172 in.close ( );
173 }
174 }
175 catch ( Exception ex )
176 {
177 ex.printStackTrace ( );
178 }
179
180 try
181 {
182 if ( out != null )
183 {
184 out.close ( );
185 }
186 }
187 catch ( Exception ex )
188 {
189 ex.printStackTrace ( );
190 }
191
192 // keep-alive?
193
194 try
195 {
196 if ( socket != null )
197 {
198 socket.close ( );
199 }
200 }
201 catch ( Exception ex )
202 {
203 ex.printStackTrace ( );
204 }
205 }
206 }
207
208 public void abort ( )
209 //////////////////////////////////////////////////////////////////////
210 {
211 //System.out.println ( "HttpConnection.abort()" );
212
213 try
214 {
215 if ( socket != null )
216 {
217 socket.close ( );
218 }
219 }
220 catch ( Exception ex )
221 {
222 }
223 }
224
225 //////////////////////////////////////////////////////////////////////
226 //////////////////////////////////////////////////////////////////////
227
228 public int getResponseCode ( ) { return responseCode; }
229
230 public Object getParsedContent ( ) { return parsedContent; }
231
232 public Hashtable getHeaderHashtable ( ) { return headerHashtable; }
233
234 public void setRequestBytes ( byte [ ] requestBytes )
235 //////////////////////////////////////////////////////////////////////
236 {
237 this.requestBytes = requestBytes;
238 }
239
240 //////////////////////////////////////////////////////////////////////
241 //////////////////////////////////////////////////////////////////////
242
243 private void parseResponse ( InputStream inputStream )
244 throws IOException
245 //////////////////////////////////////////////////////////////////////
246 {
247 headerHashtable.clear ( );
248
249 if ( inputStream == null )
250 {
251 responseCode = -1;
252
253 return;
254 }
255
256 byte [ ] statusBytes = new byte [ 12 ];
257
258 int bytesRead = inputStream.read ( statusBytes );
259
260 if ( bytesRead < 12 )
261 {
262 responseCode = -1;
263
264 return;
265 }
266
267 // US-ASCII was here
268 String statusString = new String ( statusBytes );
269
270 // weak
271 if ( !statusString.startsWith ( "HTTP/1." ) )
272 {
273 responseCode = -1;
274
275 return;
276 }
277
278 try
279 {
280 responseCode = Integer.parseInt ( statusString.substring ( 9 ) );
281 }
282 catch ( NumberFormatException ex )
283 {
284 responseCode = -1;
285
286 return;
287 }
288
289 readLine ( inputStream );
290
291 String line;
292
293 while ( ( line = readLine ( inputStream ) ) != null )
294 {
295 if ( line.equals ( "" ) )
296 {
297 break;
298 }
299
300 int index = line.indexOf ( ":" );
301
302 if ( index < 1 )
303 {
304 responseCode = -1;
305
306 return;
307 }
308
309 String headerName = line.substring ( 0, index );
310
311 if ( line.length ( ) > index + 2 )
312 {
313 String headerValue = line.substring ( index + 2 ).trim ( );
314
315 //System.out.println ( "HttpConnection: (\"" + headerName + "\",\""
316 // + headerValue + "\")" );
317
318 headerHashtable.put ( headerName, headerValue );
319 }
320 else
321 {
322 responseCode = -1;
323
324 return;
325 }
326 }
327
328 if ( line == null )
329 {
330 return;
331 }
332
333 if ( contentParser == null )
334 {
335 return;
336 }
337
338 if ( ( responseCode == HTTP_ACCEPTED )
339 || ( responseCode == HTTP_NO_CONTENT ) )
340 {
341 return;
342 }
343
344 String contentLengthStr
345 = ( String ) headerHashtable.get ( "Content-Length" );
346
347 int contentLength;
348
349 if ( contentLengthStr == null )
350 {
351 contentLength = -1;
352 }
353 else
354 {
355 try
356 {
357 contentLength = Integer.parseInt ( contentLengthStr );
358 }
359 catch ( NumberFormatException ex )
360 {
361 responseCode = -1;
362
363 return;
364 }
365 }
366
367 if ( contentLength == 0 )
368 {
369 return;
370 }
371
372 parsedContent = contentParser.parse ( inputStream, contentLength );
373 }
374
375 // don't want to buffer but do want to have readLine capability;
376 // DataInputStream.readLine deprecated, BufferedReader buffers
377
378 // only works with "\r\n"; note: no pushback
379
380 private static String readLine ( InputStream inputStream )
381 throws IOException
382 //////////////////////////////////////////////////////////////////////
383 {
384 // looking for "\r\n"
385
386 ByteArrayOutputStream byteArrayOutputStream
387 = new ByteArrayOutputStream ( );
388
389 int state = 0;
390
391 while ( true )
392 {
393 int c = inputStream.read ( );
394
395 if ( c < 0 )
396 {
397 // encoding?
398
399 String s = byteArrayOutputStream.toString ( );
400
401 if ( s.length ( ) < 1 )
402 {
403 return null;
404 }
405
406 return s;
407 }
408
409 if ( c == '\r' )
410 {
411 if ( state == 0 )
412 {
413 state = 1;
414 }
415 else
416 {
417 state = 0;
418 }
419 }
420 else if ( c == '\n' )
421 {
422 if ( state == 1 )
423 {
424 // encoding?
425
426 String s = byteArrayOutputStream.toString ( );
427
428 // trim off the "\r"
429
430 return s.substring ( 0, s.length ( ) - 1 );
431 }
432 else
433 {
434 state = 0;
435 }
436 }
437 else
438 {
439 state = 0;
440 }
441
442 byteArrayOutputStream.write ( c );
443 }
444 }
445
446 //////////////////////////////////////////////////////////////////////
447 //////////////////////////////////////////////////////////////////////
448 }