package net.fdream.io
|
{
|
/**
|
* @Script: HTTPLoader.as
|
* @Licence: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
* @Author: [email protected]
|
* @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;
|
}
|
}
|
}
|