<!---
	Calculate the URL to which we are posting. For this demo, it
	will be another page on this ColdFusion server.
--->
<cfset postUrl = (
	"http://" &
	cgi.server_name &
	getDirectoryFromPath( cgi.script_name ) &
	"target.cfm"
	) />

<!---
	Create an instance of our Java URL - This is the object that we
	will use to open the connection to the above location.
--->
<cfset targetUrl = createObject( "java", "java.net.URL" ).init(
	javaCast( "string", postUrl )
	) />

<!---
	Now that we have our URL, let's open a connection to it. This
	will give us access to the input (download) and output (upload)
	streams for the target end point.

	NOTE: This gives us an instance of java.net.URLConnection (or
	one of its sub-classes).
--->
<cfset connection = targetUrl.openConnection() />

<!---
	Be default, the connection is only set to gather target content,
	not to POST it. As such, we have to make sure that we turn on
	output (upload) before we access the data streams.
--->
<cfset connection.setDoOutput( javaCast( "boolean", true ) ) />

<!--- Since we are uploading, we have to set the method to POST. --->
<cfset connection.setRequestMethod( javaCast( "string", "POST" ) ) />

<!---
	By default, the connection will locally buffer the data until it
	is ready to be posted in its entirety. We don't want to hold it
	all in memory, however; as such, we need to explicitly turn data
	Chunking on. This will allow the connection to flush data to the
	target url without having to load it all in memory (this is
	perfect for when the size of the data is not known ahead of time).
--->
<cfset connection.setChunkedStreamingMode( javaCast( "int", 50 ) ) />

<!---
	When posting data, the content-type will determine how the
	target server parses the incoming request. If the target server
	is ColdFusion, this is especially crtical as it will throw an
	error if it tries to parse this POST as a collection of
	name-value pairs.

	In this case, we WANT it to see the form as multi-part, which
	will be a collection of name-value pairs. In order to delimit
	the part of the form post, we need to create a bondary identifier.
	This is how the server will know where one value ends and the
	next one starts.

	This needs to be a random string so as not to show up in the
	form data itself (as a false boundary).
--->
<cfset fieldBoundary = ("POST------------------" & getTickCount()) />

<!---
	Set the content type and include the boundary information so the
	server knowns how to parse the data.
--->
<cfset connection.setRequestProperty(
	javaCast( "string", "Content-Type" ),
	javaCast( "string", ("multipart/form-data; boundary=" & fieldBoundary) )
	) />


<!---
	Now that we have prepared the connection to the target URL, let's
	get the output stream - this is the UPLOAD stream to which we can
	write data to be posted to the target server.
--->
<cfset uploadStream = connection.getOutputStream() />

<!---
	Before we send the file data, we'll send some simple
	name-value pairs in plain-text format. In order to make it easier
	to write strings to the upload stream, let's wrap it in a Writer.
	This will allow us to write string data rather than just bytes.
--->
<cfset uploadWriter = createObject( "java", "java.io.OutputStreamWriter" ).init(
	uploadStream
	) />

<!---
	Form data makes heavy use of the Carriage Return and New Line
	characters to delimite values.
--->
<cfset crnl = (chr( 13 ) & chr( 10 )) />

<!--- A double break is also used. --->
<cfset crnl2 = (crnl & crnl) />


<!--- Delimit the field. --->
<cfset uploadWriter.write(
	javaCast( "string", ("--" & fieldBoundary & crnl) )
	) />

<!--- Send the title. --->
<cfset uploadWriter.write(
	javaCast(
		"string",
		(
			"Content-Disposition: form-data; name=""title""" &
			crnl2 &
			"The Bride" &
			crnl
		))
	) />


<!--- Delimit the field. --->
<cfset uploadWriter.write(
	javaCast( "string", ("--" & fieldBoundary & crnl) )
	) />

<!--- Send the author. --->
<cfset uploadWriter.write(
	javaCast(
		"string",
		(
			"Content-Disposition: form-data; name=""author""" &
			crnl2 &
			"Julie Garwood" &
			crnl
		))
	) />


<!--- Delimit the field. --->
<cfset uploadWriter.write(
	javaCast( "string", ("--" & fieldBoundary & crnl) )
	) />

<!--- Send the publisher. --->
<cfset uploadWriter.write(
	javaCast(
		"string",
		(
			"Content-Disposition: form-data; name=""publisher""" &
			crnl2 &
			"Pocket Star" &
			crnl
		))
	) />



<!---
	Now that we've written the simple name/value pairs, let's post
	the actual file data as part of the incoming request. This works
	very much in the same way, although we are going to stream the
	local file into the post data.

	Let's open a connection to a local file that we will stream to
	the output a byte at a time.

	NOTE: There are more effficient, buffered ways to read a file
	into memory; however, this is just trying to keep it simple.
--->
<cfset fileInputStream = createObject( "java", "java.io.FileInputStream" ).init(
	javaCast( "string", expandPath( "./data2.txt" ) )
	) />

<!--- Delimit the field. --->
<cfset uploadWriter.write(
	javaCast( "string", ("--" & fieldBoundary & crnl) )
	) />

<!--- Send the file along. --->
<cfset uploadWriter.write(
	javaCast(
		"string",
		(
			"Content-Disposition: form-data; name=""text""; filename=""the_bride.txt""" &
			crnl &
			"Content-Type: ""text/plain""" &
			crnl2
		))
	) />

<!--- Read the first byte from the file. --->
<cfset nextByte = fileInputStream.read() />

<!---
	Keep reading from the file, one byte at a time, until we hit
	(-1) - the End of File marker for the input stream.
--->
<cfloop condition="(nextByte neq -1)">

	<!--- Write this byte to the output (UPLOAD) stream. --->
	<cfset uploadWriter.write( javaCast( "int", nextByte ) ) />

	<!--- Read the next byte from the file. --->
	<cfset nextByte = fileInputStream.read() />

</cfloop>

<!--- Add the new line to the field value. --->
<cfset uploadWriter.write( javaCast( "string", crnl ) ) />



<!---
	Delimit the end of the post. Notice that the last delimiter has
	a trailing double-slash after it.
--->
<cfset uploadWriter.write(
	javaCast( "string", (crnl & "--" & fieldBoundary & "--" & crnl) )
	) />

<!--- Now that we're done streaming the file, close the stream. --->
<cfset uploadWriter.close() />


<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->


<!---
	At this point, we have completed the UPLOAD portion of the
	request. We could be done; or we could look at the input
	(download) portion of the request in order to view the response
	or the error.
--->
<cfoutput>

	Response:
	#connection.getResponseCode()# -
	#connection.getResponseMessage()#<br />
	<br />

</cfoutput>

<!---
	The input stream is mutually exclusive with the error stream,
	although both can return data. As such, let's try to access
	the input stream... and then use the error stream if there is
	a problem.
--->
<cftry>

	<!--- Try for the input stream. --->
	<cfset downloadStream = connection.getInputStream() />

	<!---
		If the input stream is not available (ie. the server returned
		an error response), then we'll have to use the error output
		as the response stream.
	--->
	<cfcatch>

		<!--- Use the error stream as the download. --->
		<cfset downloadStream = connection.getErrorStream() />

	</cfcatch>

</cftry>


<!---
	At this point, we have either the natural download or the error
	download. In either case, we can start reading the output in
	the same mannor.
--->
<cfset responseBuffer = [] />

<!--- Get the first byte. --->
<cfset nextByte = downloadStream.read() />

<!---
	Keep reading from the response stream until we run out of bytes
	(-1). We'll be building up the response buffer a byte at a time
	and then outputting it as a single value.
--->
<cfloop condition="(nextByte neq -1)">

	<!--- Add the byte AS CHAR to the response buffer. --->
	<cfset arrayAppend( responseBuffer, chr( nextByte ) ) />

	<!--- Get the next byte. --->
	<cfset nextByte = downloadStream.read() />

</cfloop>

<!--- Close the response stream. --->
<cfset downloadStream.close() />

<!--- Output the response. --->
<cfoutput>

	Response: #arrayToList( responseBuffer, "" )#

</cfoutput>