| package net.fdream.io |
| { |
| /** |
| * @Script: HTTPLoader.as |
| * @Licence: MIT License (http://www.opensource.org/licenses/mit-license.php) |
| * @Author: xushengs@gmail.com |
| * @Website: http://code.google.com/p/fookie/ |
| * @Version: 0.1 |
| * @Creation: Sep 27, 2010 |
| * @Modified: Sep 27, 2010 |
| * @Description: |
| * Use socket to get HTTP headers, status and content |
| * |
| * @Usage: |
| * see it in HTTPLoader.fla |
| * |
| * @Events: |
| * They are the same with URLLoader, just list below: |
| * complete: |
| * Dispatched after all the received data is decoded |
| * and placed in the response property of the HTTPLoader object. |
| * httpStatus: |
| * Dispatched if response headers have received. |
| * ioError: |
| * Dispatched if a call to HTTPLoader.load() |
| * results in a fatal error that terminates the download. |
| * open: |
| * Dispatched when the download operation commences |
| * following a call to the HTTPLoader.load() method. |
| * progress: |
| * Dispatched when data is received as the download |
| * operation progresses. |
| * securityError: |
| * Dispatched if a call to HTTPLoader.load() attempts to |
| * load data from a server outside the security sandbox. |
| * |
| */ |
| |
| import flash.net.Socket; |
| import flash.net.URLRequest; |
| import flash.net.URLRequestHeader; |
| import flash.events.Event; |
| import flash.events.EventDispatcher; |
| import flash.events.IEventDispatcher; |
| import flash.events.IOErrorEvent; |
| import flash.events.SecurityErrorEvent; |
| import flash.events.ProgressEvent; |
| import flash.events.HTTPStatusEvent |
| import flash.utils.ByteArray; |
| |
| public class HTTPLoader extends EventDispatcher |
| { |
| public function get userAgent():String |
| { |
| return _userAgent; |
| } |
| |
| public function set userAgent(value:String):void |
| { |
| this._userAgent = value; |
| } |
| |
| public function get referer():String |
| { |
| return _referer; |
| } |
| |
| public function set referer(value:String):void |
| { |
| this._referer = value; |
| } |
| |
| public function get response():HTTPResponse |
| { |
| return this._response; |
| } |
| |
| // host to connect |
| private var _host:String = null; |
| // port to connect |
| private var _port:int = 80; |
| // path to load |
| private var _path:String = '/'; |
| // user agent of http request |
| private var _userAgent:String = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.10) Gecko/20100914 Firefox/3.6.10'; |
| // referer |
| private var _referer:String = null; |
| |
| private var _socket:Socket = new Socket(); |
| private var _request:URLRequest = null; |
| private var _bytes:Array = new Array(); |
| |
| //private var _dispatcher:EventDispatcher = new EventDispatcher(); |
| |
| private var _encoding:String = 'utf-8'; |
| |
| // progress information |
| private var _bytesLoaded:int = 0; |
| private var _bytesTotal:int = 0; |
| private var _headerLength:int = 0; |
| |
| // response |
| private var _response = {'status': 0, 'headers': {}, 'body': ''}; |
| |
| // url pattern |
| // group[1]: host |
| // group[2]: port |
| // group[3]: path |
| private const URL_PATTERN:RegExp = /http:\/\/([^:\/]+)(?::(\d+))?(\/.*$)/i; |
| |
| /** |
| * constructor |
| * |
| * @param url:String |
| * the request to load |
| * @return: |
| * void |
| */ |
| public function HTTPLoader(request:URLRequest = null) |
| { |
| this._request = request; |
| } |
| |
| /** |
| * load a request |
| * |
| * @param request:URLRequest |
| * the request to load |
| * @return: |
| * void |
| */ |
| public function load(request:URLRequest = null):void |
| { |
| if (request != null) |
| { |
| this._request = request; |
| } |
| |
| if (this._request == null) |
| { |
| throw new Error('the request cannot be null'); |
| } |
| |
| // parse url |
| var match:Object = URL_PATTERN.exec(this._request.url); |
| if (match) |
| { |
| this._host = match[1]; |
| this._port = int(match[2]) || 80; |
| this._path = match[3] || '/'; |
| } |
| else |
| { |
| throw new Error('invalid url'); |
| } |
| |
| this._socket.addEventListener(Event.CLOSE, closeHandler); |
| this._socket.addEventListener(Event.CONNECT, connectHandler, false, 0, true); |
| this._socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); |
| this._socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); |
| this._socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler); |
| |
| this._socket.connect(this._host, this._port); |
| } |
| |
| private function closeHandler(evt:Event) |
| { |
| this._response.body = parseData(); |
| |
| this._response = new HTTPResponse(this._response); |
| dispatchEvent(new Event(flash.events.Event.COMPLETE)); |
| } |
| |
| private function connectHandler(evt:Event) |
| { |
| // headers |
| var headers:String = this._request.method + ' ' + this._path + ' HTTP/1.0\r\nHost: ' + this._host + '\r\nAccept: */*\r\nUser-Agent: ' + this._userAgent + '\r\nAccept-Encoding: deflate\r\nAccept-Language: zh-cn\r\nConnection: Close\r\n'; |
| if (this._referer) |
| { |
| headers += 'referer: ' + this._referer + '\r\n' |
| } |
| |
| if (this._request.requestHeaders.length) |
| { |
| var len:int = this._request.requestHeaders.length, header:URLRequestHeader; |
| for (var i:int = 0; i < len; i++) |
| { |
| header = this._request.requestHeaders[i]; |
| headers += header.name + ': ' + header.value + '\r\n'; |
| } |
| } |
| headers += '\r\n'; |
| |
| // send request |
| this._socket.writeUTFBytes(headers); |
| this._socket.flush(); |
| |
| // dispatch open event |
| dispatchEvent(new Event(flash.events.Event.OPEN)); |
| } |
| |
| private function ioErrorHandler(evt:IOErrorEvent) |
| { |
| dispatchEvent(evt); |
| } |
| |
| private function securityErrorHandler(evt:SecurityErrorEvent) |
| { |
| dispatchEvent(evt); |
| } |
| |
| private function socketDataHandler(evt:ProgressEvent) |
| { |
| var ba:ByteArray = new ByteArray() |
| this._socket.readBytes(ba, 0, this._socket.bytesAvailable); |
| this._bytes.push(ba); |
| if (this._bytes.length == 1) |
| { |
| parseHeaders(ba); |
| _bytesLoaded = -this._headerLength; |
| } |
| |
| _bytesLoaded += ba.length; |
| |
| // dispatch progress event |
| dispatchEvent(new ProgressEvent(flash.events.ProgressEvent.PROGRESS, false, false, _bytesLoaded, _bytesTotal)); |
| } |
| |
| private function parseHeaders(bytes:ByteArray):void |
| { |
| var s:String = bytes.readUTFBytes(1), headers:Array = new Array(), header:String = '', headerEnded:Boolean = false, line:int = 0; |
| while (bytes.bytesAvailable) |
| { |
| switch (s) |
| { |
| case '\r': |
| break; |
| case '\n': |
| line++; |
| if (line == 2) |
| { |
| this._response['status'] = parseInt(headers[0].split(' ')[1]); |
| |
| // dispatch progress event |
| dispatchEvent(new HTTPStatusEvent(flash.events.HTTPStatusEvent.HTTP_STATUS, false, false, this._response['status'])); |
| |
| var h:Array; |
| for (var i:int = 1; i < headers.length; i++) |
| { |
| h = headers[i].split(': '); |
| this._response['headers'][h[0]] = h[1]; |
| |
| switch (h[0]) |
| { |
| case 'Content-Type': |
| var encoding:Array = h[1].split('='); |
| if (encoding.length > 1) |
| { |
| this._encoding = encoding[1]; |
| } |
| break; |
| case 'Content-Length': |
| this._bytesTotal = parseInt(h[1]); |
| break; |
| } |
| } |
| this._headerLength = bytes.length - bytes.bytesAvailable; |
| headerEnded = true; |
| } |
| else |
| { |
| headers.push(header); |
| header = ''; |
| } |
| break; |
| default: |
| line = 0; |
| header += s; |
| break; |
| } |
| if (headerEnded) |
| { |
| break; |
| } |
| else |
| { |
| s = bytes.readUTFBytes(1); |
| } |
| } |
| } |
| |
| private function parseData():String |
| { |
| var data:String = '', ba:ByteArray, i = 0; |
| while (ba = this._bytes[i++]) |
| { |
| data += ba.readMultiByte(ba.bytesAvailable, this._encoding) |
| } |
| return data; |
| } |
| } |
| } |