did it make it through to the list?
-jj
On Fri, Jan 17, 2003 at 03:45:14PM +1100, fish wrote:
> hi. this is a patchish thingy that adds a stream requst servlet to fred,
> despite GJ's advice to not do that. I'd send a full patch, but it
> seems that cvs.freenet.sourceforge.net is dead, and since I have to go
> home from work,i'm just going to add the required changes to make this
> thing go.
>
> To Main.java, you need to add the following lines at line #1032 (on my
> copy, which is a week old) in Main.java:
>
> setSetValue(params,"mainport.params.defaultServlet.uri", "/default");
> setSetValue(params,"mainport.params.defaultServlet.method", "GET");
> setSetValue(params,"mainport.params.defaultServlet.class",
> "freenet.client.http.RedirectServlet");
> setSetValue(params,"mainport.params.defaultServlet.name", "Web
> Interface Redirect");
> setSetValue(params,"mainport.params.defaultServlet.params.targetURL",
> "/servlet/nodeinfo/");
>
> Also, in order to get the metadata out of
> freenet/client/GetRequestProcess.java, I had to change the definition
> of metadataBucket on line #24 to be public. There is probably a
> better way to achieve this, and I would be appriciated being
> enligtened on this.
>
> anyhow, the extra file is attached. apologised for failure to be able
> to cvs diff :(. i ahte sorceforge :-p
>
> Anyhow, to the functionality: This can stream static files of any
> type, sort of... I've only tested it with ogg, mp3, and asf files. It
> takes two http parameters, ?buffer=number for a buffer time in minutes
> when chunkTime is known, ?htlStep=number for the increment to increase
> HTL on the block requests, and ?htl=number for the htl of the main request,
> you know, the usual shit. I also attached the readme.txt of the insertion
> stuff, which might interst people - i put the programs to insert stuff at
> http://artificial-stupidity.net/~fish/.
>
> - fish
> /*
> * Streaming Audio/Video request servelet
> *
> * @author <a href="mailto:fish at artificial-stupidity.net">Jaymz Julian</a>
> *
> */
>
> package freenet.client.http;
>
> import java.io.*;
> import java.net.*;
> import java.util.*;
> import java.text.DateFormat;
> import javax.servlet.*;
> import javax.servlet.http.*;
>
> import freenet.*;
> import freenet.node.*;
> import freenet.support.*;
> import freenet.support.io.*;
> import freenet.client.*;
> import freenet.client.events.*;
> import freenet.client.listeners.*;
> import freenet.client.metadata.*;
> import freenet.thread.*;
>
> // FEC stuff
> import com.onionnetworks.fec.*;
> import com.onionnetworks.util.*;
>
> public class StreamServlet extends FproxyServlet
> {
> private boolean firstAccess = true;
> private Node node;
> protected static ClientFactory clientFactory;
> private ServletContext context;
> int maxHtl=50;
>
> public void init()
> {
> ServletContext context = getServletContext();
> node = (Node)context.getAttribute("freenet.node.Node");
> context = getServletContext();
> clientFactory = (ClientFactory)
> context.getAttribute("freenet.client.ClientFactory");
> }
>
> protected void doGet(HttpServletRequest req, HttpServletResponse resp)
> throws ServletException, IOException
> {
> if(firstAccess)
> {
> init();
> firstAccess=false;
> }
> int myHtl=15;
> String requestString;
>
> try {
>
> requestString=
> freenet.support.URLDecoder.decode(req.getRequestURI());
> // cut off the '/servlet/stream/' part ;)
> requestString=requestString.substring(16);
> //System.out.println(requestString);
> }
> catch (Exception e)
> {
> PrintWriter pw = resp.getWriter();
> resp.setContentType("text/plain");
> pw.println("freenet.support.URLDecoder.decode threw.
> shite.");
> return;
> }
>
> // okay, get the main bit o metadata
> FreenetURI requestUri;
> try {
> requestUri=new FreenetURI(requestString);
> }
> catch (MalformedURLException e)
> {
> //writeErrorMessage(e, resp, null, key, null, htlUsed,
> null, null, tryNum+1)
> writeErrorMessage(e, resp, null, requestString, null,
> myHtl, null, null, 0);
> return;
> }
>
> // Get the user specified queue length
> int queueLength=3;
> String requestQueueLength=req.getParameter("buffer");
> if(requestQueueLength!=null)
> {
> try {
>
> queueLength=Integer.parseInt(requestQueueLength);
> }
> catch (Exception e) {}
> }
> //System.out.println(queueLength);
>
> int htlStep=5;
> String requestHtlStep=req.getParameter("htlstep");
> if(requestHtlStep!=null)
> {
> try {
> htlStep=Integer.parseInt(requestHtlStep);
> }
> catch (Exception e) {}
> }
> String requestHtl=req.getParameter("htl");
> if(requestHtl!=null)
> {
> try {
> myHtl=Integer.parseInt(requestHtl);
> }
> catch (Exception e) {}
> }
>
> // Welcome to 'I hate you' theatre, with your
> // host, dj fish!
> FileBucket metadata = new FileBucket();
> FileBucket data = new FileBucket();
> GetRequestProcess rp=new GetRequestProcess(requestUri, myHtl,
> data, new FileBucketFactory(), 0, true, null);
> Request r;
> while((r=rp.getNextRequest()) != null)
> {
> try
> {
> Client c = clientFactory.getClient(r);
> c.start();
> }
> catch (IOException e)
> {
> PrintWriter pw = resp.getWriter();
> resp.setContentType("text/plain");
> pw.println("Society has collapsed!");
> pw.println("Lets build villeges, and towns, and
> play each other at cricket!");
> return;
> }
> }
>
> if(rp.failed())
> {
> PrintWriter pw = resp.getWriter();
> resp.setContentType("text/plain");
> pw.println("ERROR: Your key didn't work. try a higher
> htl or something.");
> pw.println("Someone should fix the call to
> writeErrorMessage to display that nice non-threatening freenet error message
> correctly");
> //writeErrorMessage(rp.origThrowable, resp, null,
> requestString, null, myHtl, null, null, 1);
> return;
> }
>
> // This is incredibly nasty - what's the *correct* way to do
> this?
> ReadInputStream myMetadataStream=new
> ReadInputStream(rp.metadataBucket.getInputStream());
> // skip ahead to the main data....
> try
> {
> while(!myMetadataStream.readToEOF('\n',
> '\r').equalsIgnoreCase("Document"));
> }
> catch (Exception e)
> {
> PrintWriter pw = resp.getWriter();
> resp.setContentType("text/plain");
> pw.println("Shouldn't happen...");
> return;
> }
> FieldSet streamMetadata=new FieldSet();
> streamMetadata.parseFields(myMetadataStream);
>
> // Check that this is a correct stream
> FieldSet streamSpecific=streamMetadata.getSet("Stream");
> if(streamSpecific == null)
> {
> PrintWriter pw = resp.getWriter();
> resp.setContentType("text/plain");
> pw.println("Invalid stream format:
> "+streamSpecific.get("Stream.Format"));
> return;
> }
> if( !streamSpecific.get("Format").equalsIgnoreCase("Fish") &&
>
> !streamSpecific.get("Format").equalsIgnoreCase("fproxy") )
> {
> PrintWriter pw = resp.getWriter();
> resp.setContentType("text/plain");
> pw.println("Invalid stream format:
> "+streamSpecific.get("Format"));
> return;
> }
>
> // get the URI
> String myUri=streamSpecific.get("uri");
> if(myUri==null)
> {
> myUri=requestString;
> }
>
> String mimetype=streamSpecific.get("type");
> if(mimetype==null)
> {
> mimetype="audio/ogg";
> }
>
> String headerKey=streamSpecific.get("header");
> if(headerKey==null)
> {
> headerKey="false";
> }
>
> // don't you love these big try/catch waterfalls, it's so much
> nicer
> // than Integer,parseInt behaving sanely, yes.
> int myStartChunk, myEndChunk, chunkSeconds, fecType, fecn, feck;
> try {
>
> myStartChunk=Integer.parseInt(streamSpecific.get("StartChunk"), 16);
> } catch (Exception e) {
> myStartChunk= -1;
> }
>
> try {
>
> myEndChunk=Integer.parseInt(streamSpecific.get("EndChunk"), 16);
> } catch (Exception e) {
> myEndChunk= -1;
> }
>
> try {
>
> chunkSeconds=Integer.parseInt(streamSpecific.get("ChunkSeconds"), 16);
> // if chunk seconds exists. we should adjust the queue
> length to be in minutes!
> int t=queueLength*60;
> queueLength=t/chunkSeconds;
>
> // round up, not down!
> if((queueLength*chunkSeconds)<t)
> queueLength++;
> } catch (Exception e) {
> chunkSeconds=0;
> }
>
> try {
> fecType=Integer.parseInt(streamSpecific.get("fecType"),
> 16);
> } catch (Exception e) {
> fecType= -1;
> }
>
> FieldSet fecSpecific=streamSpecific.getSet("fec");
> try {
> fecn=Integer.parseInt(fecSpecific.get("n"), 16);
> } catch (Exception e) {
> fecn= -1;
> }
>
> try {
> feck=Integer.parseInt(fecSpecific.get("k"), 16);
> } catch (Exception e) {
> feck= -1;
> }
>
> // For some bizzare reason, not setting this *first* will
> // cause my particular JVM to shit itself in a rather unfun
> // way. AngyMunkey!
> //
> // This being said, on blackdown's 1.3 JVM, which co-incidently
> is
> // the ONLY one which will both compile and run fred on my
> system,
> // reching the end of this function will sig11 the JVM anyhow.
> // I believe this to be sun/blackdown's anti-kiddie-porn filter
> :-p.
> //
> // So, waht I need, is a way to signal the JVM that this is
> just audio :-p.
> resp.setStatus(HttpServletResponse.SC_OK);
> resp.setContentType(mimetype);
> OutputStream out = resp.getOutputStream();
>
> // Get the FEC decoder
> FECCode
> fecbitch=FECCodeFactory.getDefault().createFECCode(feck, fecn);
>
> StreamChunkRequestor[] requestQueue=new
> StreamChunkRequestor[queueLength];
> // fill the head of the queue
> int currentChunk=myStartChunk;
> for(int c=0;c<queueLength;c++)
> {
> // get the ASF or other encapsulation format header if
> it exists (as key 0)
> if(c==0 && headerKey.equalsIgnoreCase("true"))
> {
> requestQueue[c]=new StreamChunkRequestor(myUri,
> 0, fecType, fecn, feck, fecbitch, htlStep);
> if(currentChunk==0)
> currentChunk++;
> }
> else
> {
> requestQueue[c]=new StreamChunkRequestor(myUri,
> currentChunk, fecType, fecn, feck, fecbitch, htlStep);
> currentChunk++;
> }
> Thread t=new Thread(requestQueue[c]);
> t.start();
> }
> while(true)
> {
> boolean allWorking=true;
> boolean failedBlock=false;
> // check if the queue is full
> for(int c=0;c<queueLength;c++)
> {
> // this request is finished progress
> if(requestQueue[c].inProgress==true)
> {
> allWorking=false;
> }
> else
> {
> // check if all is kosher, or if there
> // is a block in the queue that failed
> to
> // retrieve
> if(requestQueue[c].success==false)
> failedBlock=true;
> }
> }
>
> // if we have all of the blocks in the queue, head on up
> if(allWorking)
> {
> if(requestQueue[0].success)
> {
> // write the data at the head of the
> // queue.
> out.write(requestQueue[0].outputData);
> // pop the top
> for(int c=1;c<queueLength;c++)
>
> requestQueue[c-1]=requestQueue[c];
>
> // append the next block to the queue
> // if we havn't failed a block
> if(!failedBlock)
> {
> requestQueue[queueLength-1]=new
> StreamChunkRequestor(myUri, currentChunk, fecType, fecn, feck, fecbitch,
> htlStep);
> Thread t=new
> Thread(requestQueue[queueLength-1]);
> t.start();
> currentChunk++;
> }
> }
> else
> {
> // When you walk away, you don't hear
> me say, Please.... oh baby, don't go
> // simple and clean is the way you're
> making me feel tonight!
> //System.out.println("Cleanly
> finished!");
> return;
> }
> }
> try {
> Thread.sleep(1000);
> } catch (InterruptedException e) {}
> }
> }
>
> class StreamPartRequestor implements Runnable
> {
> public boolean success;
> public boolean inProgress;
> public int finalSize;
> public FileBucket data = new FileBucket();
> FreenetURI uri;
> int htl;
> FECCode fecbitch;
> int htlStep;
>
> StreamPartRequestor(String myuri, FECCode ifecbitch, int
> iHtlStep)
> {
> try {
> uri=new FreenetURI(myuri);
> } catch (Exception e) {
> System.out.println("Arg! Bad things happened in
> StreamPartRequestor");
> }
> htl=0;
> success=inProgress=false;
> finalSize=0;
> fecbitch=ifecbitch;
> htlStep=iHtlStep;
> }
>
> public void run()
> {
> inProgress=true;
> htl=htl+htlStep;
> //System.out.println("Requesting "+uri+" at htl="+htl);
>
> GetRequestProcess rp=new GetRequestProcess(uri, htl,
> data, new FileBucketFactory(), 0, true, null);
> Request r;
> while((r=rp.getNextRequest()) != null)
> {
> try
> {
> Client c = clientFactory.getClient(r);
> c.start();
> }
> catch (IOException e)
> {
> System.out.println("You are here. (You
> shouldn't be... bad things in StreamPartRequestor.run())");
> }
> }
> if(!rp.failed())
> {
> // Get the final size for the completed block
> try {
> ReadInputStream myMetadataStream=new
> ReadInputStream(rp.metadataBucket.getInputStream());
> // skip ahead to the main data....
> try
> {
>
> while(!myMetadataStream.readToEOF('\n', '\r').equalsIgnoreCase("EndPart"));
> } catch (Exception e) {}
>
> FieldSet streamMetadata=new FieldSet();
>
> streamMetadata.parseFields(myMetadataStream);
> FieldSet
> streamSpecific=streamMetadata.getSet("Stream");
> FieldSet
> fecSpecific=streamSpecific.getSet("fec");
>
> finalSize=Integer.parseInt(fecSpecific.get("actualSize"), 16);
> } catch (Exception e) {}
>
> success=true;
> //System.out.println("Retrieved block! final
> size is "+finalSize);
> }
> inProgress=false;
> }
> }
>
> class StreamChunkRequestor implements Runnable
> {
> public boolean success;
> public boolean inProgress;
> String myUri;
> int myChunk;
> int fecType;
> int fecn;
> int feck;
> int htlStep;
> byte [] outputData;
> FECCode fecbitch;
>
> // welcome to iFreenet!
> StreamChunkRequestor(String iUri, int iChunk, int ifecType, int
> ifecn, int ifeck, FECCode ifecbitch, int iHtlStep)
> {
> success=false;
> inProgress=false;
>
> myUri=iUri;
> myChunk=iChunk;
> fecType=ifecType;
> fecn=ifecn;
> feck=ifeck;
> fecbitch=ifecbitch;
> htlStep=iHtlStep;
> }
>
> public void run()
> {
> inProgress=true;
> StreamPartRequestor myRequestors[]=new
> StreamPartRequestor[fecn];
>
> // request the chunks
> for(int c=0;c<fecn;c++)
> {
> String realUri=myUri+"/"+myChunk+"/"+c;
> myRequestors[c]=new
> StreamPartRequestor(realUri, fecbitch, htlStep);
> }
>
> int retrievedBlocks;
> int progressBlocks;
> do
> {
> retrievedBlocks=0;
> progressBlocks=0;
> for(int c=0;c<fecn;c++)
> {
> if(myRequestors[c].inProgress==false)
> {
>
> if(myRequestors[c].success==true)
> {
> retrievedBlocks++;
> }
> else
> if(myRequestors[c].htl<maxHtl)
> {
> Thread t=new
> Thread(myRequestors[c]);
> t.start();
> progressBlocks++;
> }
> }
> else
> {
> progressBlocks++;
> }
> }
> //System.out.println("Retrieved Blocks:
> "+retrievedBlocks);
> //System.out.println("Blocks in progress:
> "+progressBlocks);
> // sleep for a second
> try {
> Thread.sleep(1000);
> } catch (InterruptedException e) {}
> } while(retrievedBlocks<feck && progressBlocks>0);
>
> if(retrievedBlocks>=feck)
> {
>
> // create the structures for the decoder
> Buffer[] landingZone= new Buffer[feck];
> int[] index=new int[feck];
> int lastBlock=0;
> int finalSize=0;
>
> for(int c=0;(c<fecn && lastBlock<feck);c++)
> {
> if(myRequestors[c].success==true)
> {
> try {
> // nasty, nasty, nasty,
> nasty dounle hadnling here....
> byte[] b=new
> byte[(int)myRequestors[c].data.size()];
>
> myRequestors[c].data.getInputStream().read(b);
>
> landingZone[lastBlock]=new Buffer(b);
>
> finalSize=myRequestors[c].finalSize;
>
> } catch (IOException e)
> {
> // somebody fucking
> some something!!!!
> }
> index[lastBlock]=c;
> lastBlock++;
> }
> }
>
> // I'd prefer to use System.arraycopy() here,
> but it
> // seems to not quite do what I want, according
> to the
> // code I am reading. Arg.
> outputData=new byte[finalSize];
> int off=0;
> for(int c=0;(c<feck && off<finalSize);c++)
> {
> for(int d=0;(d<landingZone[c].b.length
> && off<finalSize);d++)
> {
>
> outputData[off]=landingZone[c].b[d];
> off++;
> }
> }
>
> // the chills that
> // you spill up my back
> // keep me filled
> // with satistfaction
> // when we're done
> // satisfaction
> // oh what's the harm?
> success=true;
> }
>
> inProgress=false;
> }
> }
> }
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 230 bytes
Desc: not available
URL:
<https://emu.freenetproject.org/pipermail/devl/attachments/20030122/23e45cd8/attachment.pgp>