package co.uk.mikestead.net
{
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import flash.net.URLRequest;
import flash.net.URLVariables;
import flash.utils.ByteArray;
import co.uk.mikestead.net.URLFileVariable;
/**
* The <code>URLRequestBuilder</code> class takes an instance of <code>URLVariables</code>
* and wraps these variables in a <code>URLRequest</code> with the appropriate HTTP encoding.
*
* This class is needed to build URLRequest objects with encodings not automatically applied
* when the <code>URLRequest.data</code> property is set.
*
* <p>To determine the correct encoding to apply, each variable in the <code>URLVariables</code>
* instance is examined. If a variable of type <code>URLFileVariable</code> is found
* then the encoding is set to <code>multipart/form-data</code>, and the
* <code>URLRequest.method</code> set to POST. If no <code>URLFileVariable</code> is found
* then the <code>URLRequest.data</code> property is set with the <code>URLVariables</code>
* instance directly. To see the encoding used in this case refer to the documentation for the
* <code>URLRequest.data</code> property</p>.
*
* @example
* <pre>
* // Construct variables (name-value pairs) to be sent to sever
* var variables:URLVariable = new URLVariables();
* variables.userImage = new URLFileVariable(jpegEncodedData, "user_image.jpg");
* variables.userPDF = new URLFileVariable(pdfEncodedData, "user_doc.pdf");
* variables.userName = "Mike";
* // Build the request which houses these variables
* var request:URLRequest = new URLRequestBuilder(variables).build();
* request.url = "some.web.address.php";
* // Create the loader and use it to send the request off to the server
* var loader:URLLoader = new URLLoader();
* loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
* loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onError);
* loader.addEventListener(IOErrorEvent.IO_ERROR, onError);
* loader.addEventListener(Event.COMPLETE, onServerResponse);
* loader.load(request);
* function onServerResponse(event:Event):void
* {
* trace("Variables uploaded successfully");
* }
* function onError(event:Event):void
* {
* trace("An error occurred while trying to upload data to the server: \n" + event);
* }
* </pre>
*
* @author Mike Stead
* @see URLFileVariable
*/
public class URLRequestBuilder
{
private static const MULTIPART_BOUNDARY:String = "----------196f00b77b968397849367c61a2080";
private static const MULTIPART_MARK:String = "--";
private static const LF:String = "\r\n";
/** The variables to encode within a URLRequest */
private var variables:URLVariables;
/**
* Constructor.
*
* @param variables The URLVariables to encode within a URLRequest.
*/
public function URLRequestBuilder(variables:URLVariables)
{
this.variables = variables;
}
/**
* Build a URLRequest instance with the correct encoding given the URLVariables
* provided to the constructor.
*
* @return URLRequest instance primed and ready for submission
*/
public function build():URLRequest
{
var request:URLRequest = new URLRequest();
if (isMultipartData)
{
request.data = buildMultipartBody();
addMultipartHeadersTo(request);
}
else
{
request.data = variables;
}
return request;
}
/**
* Determines whether, given the URLVariables instance provided to the constructor, the
* URLRequest should be encoded using <code>multipart/form-data</code>.
*/
private function get isMultipartData():Boolean
{
for each (var variable:* in variables)
{
if (variable is URLFileVariable)
return true;
}
return false;
}
/**
* Build a ByteArray instance containing the <code>multipart/form-data</code> encoded URLVariables.
*
* @return ByteArray containing the encoded variables
*/
private function buildMultipartBody():ByteArray
{
var body:ByteArray = new ByteArray();
for (var id:String in variables)
body.writeBytes(encodeMultipartVariable(id, variables[id]));
body.writeUTFBytes(MULTIPART_MARK + MULTIPART_BOUNDARY + MULTIPART_MARK + LF);
return body;
}
/**
* Encode a variable using <code>multipart/form-data</code>.
*
* @param id The unique id of the variable
* @param value The value of the variable
*/
private function encodeMultipartVariable(id:String, variable:Object):ByteArray
{
if (variable is URLFileVariable)
return encodeMultipartFile(id, URLFileVariable(variable));
else
return encodeMultipartString(id, variable.toString());
}
/**
* Encode a file using <code>multipart/form-data</code>.
*
* @param id The unique id of the file variable
* @param file The URLFileVariable containing the file name and file data
*
* @return The encoded variable
*/
private function encodeMultipartFile(id:String, file:URLFileVariable):ByteArray
{
var field:ByteArray = new ByteArray();
field.writeUTFBytes(MULTIPART_MARK + MULTIPART_BOUNDARY + LF +
"Content-Disposition: form-data; name=\"" + id + "\"; " +
"filename=\"" + file.name + "\"" + LF +
"Content-Type: application/octet-stream" + LF + LF);
field.writeBytes(file.data);
field.writeUTFBytes(LF);
return field;
}
/**
* Encode a string using <code>multipart/form-data</code>.
*
* @param id The unique id of the string
* @param text The value of the string
*
* @return The encoded variable
*/
private function encodeMultipartString(id:String, text:String):ByteArray
{
var field:ByteArray = new ByteArray();
field.writeUTFBytes(MULTIPART_MARK + MULTIPART_BOUNDARY + LF +
"Content-Disposition: form-data; name=\"" + id + "\"" + LF + LF +
text + LF);
return field;
}
/**
* Add the relevant <code>multipart/form-data</code> headers to a URLRequest.
*/
private function addMultipartHeadersTo(request:URLRequest):void
{
request.method = URLRequestMethod.POST;
request.contentType = "multipart/form-data; boundary=" + MULTIPART_BOUNDARY;
request.requestHeaders =
[
new URLRequestHeader("Accept", "*/*"), new URLRequestHeader("Cache-Control", "no-cache")
];
}
}
}