NetHopper 3.0 Plug-In Common API

Plug-In Common API Introduction

There are some data structures and interfaces which are used repeatedly in the various NetHopper Plug-In APIs. To keep the documentation compact, I'll describe those elements here and refer to them from the other API chapters.

Specifying MIME Types

We've endeavored to handle MIME types in a complete and consistent manner. A MIME type frame has the following structure:

[pathExpr: '|application|, '|x-newton-compatible-pkg| ]

A MIME type is a path expression array: that is, just an array with the class 'pathExpr. The length of the array is fixed at two. The first item in the array is a symbol which represents the main MIME type (text, image, audio, video, etc.). The second item is a symbol which represents the MIME subtytpe (html, jpeg, wav, mpeg, etc.) This is a fairly compact way to represent MIME types since there is a limited set of main types and all subtypes can be expressed in 7-bit ASCII strings.

Typically the MIME types are pulled from MIME header strings such as "application/x-newton-compatible-pkg". Typically the MIME subtype is more meaningful than the main type: for instance, various HTTP servers will provide HTML as "application/html" or "text/html" depending on their configuration.

One can build a MIME type object as follows:

SetClass( ['|application|, '|x-newton-compatible-pkg|], 'pathExpr);

or:

SetClass([Intern("application"), Intern("x-newton-compatible-pkg"), 'pathExpr);

The NetHopper 3.0 core code will also provide appropriate utility methods for converting MIME type strings such as "application/x-newton-compatible-pkg" into the appropriate form.

Typically you can specify more than one MIME type at a time when an API asks for a MIME type. To do this, simply provide an array of MIME types.

Input Streams

Input streams are at the heart of the various NetHopper data-passing interfaces. Typically NetHopper will hand your plug-in an input stream from which you can read data.

InputStream Read Method

This is the core method for receiving data from an InputStream: the stream Read method.

stream:Read(readRequestFrame, cbContext, cbMethodSym);

The various parameters are described in detail in the following sections.

InputStream Read Request Frame

This is the frame passed to a stream Read operation. It has the following structure:

{
async: TRUE, /* Is this request async? currently should always be TRUE */
buffer: <myVBO, Length 1024>, /* a binary object or VBO where you want data stuffed */
offset: 42, /* where to start stuffing data into the buffer: defaults to 0 */
pleaseRead: 512, /* max number of bytes to read: defaults to len(buffer) - offset */
bytesRead: 0, /* returned: how many bytes actually read */
error: NIL, /* returned: any errors encountered while reading from the stream */
}

The frame slots are explained below.

async. This boolean value indicates whether this is an asynchronous Read request. Currently, this slot value should always be set to TRUE, as synchronous operations are not yet supported.

buffer. This is a reference to a binary object or VBO where the results of the read should be stored. This object must not be read-only. Your code should provide this object and cache/reuse it as appropriate in your application. Note: the stream interface may repeatedly flush the VBO cache as it stuffs data into a VBO-based buffer.

offset. This integer value is the offset into the buffer where data should be stored by the Read operation. This defaults to 0 or NIL, which indicates that data should be stuffed into the buffer starting at zero offset.

pleaseRead. This integer value specifies the number of bytes that your code is requesting. Weird things might happen if you specify 0, so don't. If you pass NIL for this value, the stream interface code will assume you want to stuff data into the whole length of the buffer. This value defaults to the length of the buffer minus the offset.

bytesRead. This integer value is returned after the read has completed. It details how many bytes were actually read. Note that bytesRead might not equal pleaseRead.

error. This integer value specifies any error encountered during the Read. Currently, both 0 and NIL mean no error. If this slot contains the value -1, it means that there is no more data to be read on this stream (EOF).

InputStream GetDefaultReadRequest Method

This method, in the inputStream interface frame, will generate a default read request frame.

readReqFrame := inputStream:GetDefaultReadRequest();

The client can then turn around and use this read request in the future to read data from the input stream.

Note that this method allocates a heap-based buffer for the read request. If you do not wish to have a buffer allocated, consider calling inputStream:getSizedReadRequest(0). You will then be responsible for setting readReqFrame.buffer and readReqFrame.pleaseRead to appropriate values.

InputStream GetSizedReadRequest Method

This method allocates a heap-based buffer of the size you specify and sets readReqFrame.pleaseRead to the same value.

readReqFrame := inputStream:GetSizedReadRequest(1024);

InputStream Read Request Callback

Once the stream has finished reading data into your buffer, it hands the buffer back to your code by calling your callback method in your callback context with the readRequestFrame you originally passed to Read. This means cbContext and cbMethod should be defined as follows:

cbContext: {
cbMethod: func(readRequestFrame)
begin
 
end,
 
}

The cbContext frame can be read-only. The stream will call your cbMethod in a manner similar to the following:

AddDeferredSend(cbContext,cbMethodSym,[readRequestFrame]);

This guarantees that the current context will be cbContext when cbMethod is executed.

 

InputStream totalBytesAvailable Method

This method returns the total number of bytes which will be available from the stream, if known. For instance, if we were reading from a local file, this method would return the total number of bytes in the file. If we're reading from an HTTP server connection, we may or may not know the size of the content being served.

Int totalAvail := stream:totalBytesAvailable();

Note that if the total number of bytes that will be received on the stream is unknown, this method returns -1.

InputStream Available Method

This method returns the number of bytes currently waiting to be read from the stream. This basically indicates how much data is currently buffered by the stream.

Int currentAvail := stream:Available();

Typically it's a bad idea to continuously poll this method to determine how much data is available. A better technique is to pass off a reasonably-sized Read request (such as that returned by the default read request constructor) and receive data as it arrives.

InputStream GetMIMEDataType Method

This method allows the inputStream client to find out what kind of data is being provided on the inputStream.

InputStream:GetMIMEDataType();

This method returns NIL if the type of data is not yet known. This could be the case, for instance, on a inputStream provided by a Protocol before the Protocol has formed a connection to the remote host and started pulling in data.

InputStream Close Method

You should call the stream Close method as soon as you're finished with the stream, so that it can free up any resources allocated for maintaining the stream.

stream:Close();

Note that after calling the Close method, the stream cannot be used to obtain additional data. You should NIL-out your reference to the stream frame at this point so that the memory used by the stream can be garbage-collected.

Output Streams

Output streams provide your plug-in with a way to send data immediately to NetHopper. The various output stream methods are described in the sections that follow.

OutputStream Write Method

This method takes a buffer of data and writes it out on the output stream starting at the offset given, for the length given.

outputStream:Write(writeRequestFrame, cbContext, cbMethodSym);

The parameters passed to Write are described in the sections that follow.

OutputStream Write Request Frame

This is the frame passed to a stream Write operation. It has the following structure:

{
async: TRUE, /* Is this request async? currently should always be TRUE */
buffer: <myVBO, Length 1024>, /* a binary object or VBO source of data */
offset: 42, /* where to start reading data from the buffer: defaults to 0 */
pleaseWrite: 512, /* max number of bytes to write defaults to len(buffer) - offset */
bytesWritten: 0, /* returned: how many bytes actually written */
error: NIL, /* returned: any errors encountered while writing out the data */
}

The frame slots are explained below.

async. This boolean value indicates whether this is an asynchronous Read request. Currently, this slot value should always be set to TRUE, as synchronous operations are not yet supported.

buffer. This is a reference to a binary object or VBO where the output stream should pull bytes from. This object may be read-only. Your code should provide this object and cache/reuse it as appropriate in your application.

offset. This integer value is the offset into the buffer where the output stream should start pulling data from during the Write operation. This defaults to 0 or NIL, which indicates that data should be pulled from the buffer starting at zero offset.

pleaseWrite. This integer value specifies the number of bytes that your code is providing to be written out on the output stream. Weird things might happen if you specify 0, so don't. If you pass NIL for this value, the stream interface code will assume you want to write out the whole length of the buffer. This value defaults to the length of the buffer minus the offset.

bytesWritten. This integer value is returned after the read has completed. It details how many bytes were actually read. Note that bytesWritten might not equal pleaseWrite.

error. This integer value specifies any error encountered during the Read. Currently, both 0 and NIL mean no error. If this value is returned as -1, it means the outputStream has been closed and no further data can be written out on this outputStream .

OutputStream GetDefaultWriteRequest Method

This method, in the outputStream interface frame, will generate a default write request frame.

writeReqFrame := outputStream:GetDefaultWriteRequest();

The client can then turn around and use this write request in the future to write data to the outputStream.

OutputStream Write Request Callback

Once the stream has finished writing data from your buffer (for whatever reason), it hands the buffer back to your code by calling your callback method in your callback context with the writeRequestFrame you originally passed to Read. This means cbContext and cbMethod should be defined as follows:

cbContext: {
cbMethod: func(writeRequestFrame)
begin
 
end,
 
}

The cbContext frame can be read-only. The stream will call your cbMethod in a manner similar to the following:

AddDeferredSend(cbContext,cbMethodSym,[writeRequestFrame]);

This guarantees that the current context will be cbContext when cbMethod is executed.

Note that after the Write callback is called, the written bytes are guaranteed to be "flushed" on the output stream.

OutputStream SetTotalAvailable Method

This method allows you to give the output stream an estimate of how many bytes you will eventually be providing, in total. It is strongly recommended that you call this method if you have an idea of how much data will be available. Because of the nature of this streams implementation, this estimate doesn't need to be exact. The information you provide here will be passed along to any client which eventually reads data from the output stream to which you're writing. Having a reasonable estimate of the total size of the data you're going to provide helps optimize the speed of data transfer between your plugin and other plugins.

outputStream:SetTotalAvailable(Int numBytes);

By default, the outputStream assumes that the number of bytes is unknown.

OutputStream SetMIMEDataType Method

This method allows you to set the type of data you're providing on an outputStream, so that other plugins reading from the same stream will know what kind of data they're dealing with.

OutputStream:SetMIMEDataType(mimeTypeFrame);

This method sets the type of data that the client will be writing out on the outputStream.

OutputStream Close Method

This method allows you to tell the output stream that you will be providing no additional data, and that the output stream should notify any of its clients. This shuts down the output stream and disposes of allocated resources.

outputStream:Close();

Note that after calling this method you must not try to write any additional data. After calling this method you should not attempt any further operations on the output stream, and you should nil out any references you might have to the output stream.

Registration Methods

All of the NetHopper registration methods contained within the Registration Manager unnit have a similar format:

RegistrationManager:RegFoo(yourUniqueSym, infoFrame);

Here, yourUniqueSym is a symbol that uniquely identifies your Data Handler or Data Viewer or whatever. This symbol must contain your developer signature to guarantee uniqueness (i.e. '|FooDataHandler:ALLPEN|).

infoFrame is a frame which contains info specific to the kind of registration. See the specific API document for details on what is contained within this frame. Certain plugins may need a user prefs user interface. For these plugins, we provide a way to register your preferences template to be displayed in the main NetHopper application prefs. Simply add a slot to your infoFrame called preferencesForm. This is a view template which must use the protoPrefsRollItem as its _proto. This view template should have an overview slot which is the name of the prefs item as it should appear in the NetHopper prefs.

Similarly, the methods for Un-Registering have the following format:

RegistrationManager:UnRegFoo(yourUniqueSym);

Here you just need to make certain that yourUniqueSym is the same for RegFoo and UnRegFoo.