## GskUrlTransfer

GskUrlTransfer — Transfer data with a location by URL.

## Synopsis

                        GskUrlTransferRedirect;
    enum                GskUrlTransferResult;
    #define             GSK_URL_TRANSFER_N_RESULTS
    const char *        gsk_url_transfer_result_name        (GskUrlTransferResult result);
    void                (*GskUrlTransferFunc)               (GskUrlTransfer *info,
                                                             gpointer user_data);
    GskStream *         (*GskUrlUploadFunc)                 (gpointer upload_data,
                                                             gssize *size_out,
                                                             GError **error);
                        GskUrlTransfer;
    gboolean            gsk_url_transfer                    (GskUrl *url,
                                                             GskUrlUploadFunc upload_func,
                                                             gpointer upload_data,
                                                             GDestroyNotify upload_destroy,
                                                             GskUrlTransferFunc handler,
                                                             gpointer data,
                                                             GDestroyNotify destroy,
                                                             GError **error);
    GskUrlTransfer *    gsk_url_transfer_new                (GskUrl *url);
    void                gsk_url_transfer_set_handler        (GskUrlTransfer *transfer,
                                                             GskUrlTransferFunc handler,
                                                             gpointer data,
                                                             GDestroyNotify destroy);
    void                gsk_url_transfer_set_url            (GskUrlTransfer *transfer,
                                                             GskUrl *url);
    void                gsk_url_transfer_set_upload         (GskUrlTransfer *transfer,
                                                             GskUrlUploadFunc func,
                                                             gpointer data,
                                                             GDestroyNotify destroy);
    void                gsk_url_transfer_set_upload_packet  (GskUrlTransfer *transfer,
                                                             GskPacket *packet);
    void                gsk_url_transfer_set_oneshot_upload (GskUrlTransfer *transfer,
                                                             GskStream *stream,
                                                             gssize size);
    void                gsk_url_transfer_set_timeout        (GskUrlTransfer *transfer,
                                                             guint millis);
    void                gsk_url_transfer_clear_timeout      (GskUrlTransfer *transfer);
    void                gsk_url_transfer_set_follow_redirects
                                                            (GskUrlTransfer *transfer,
                                                             gboolean follow_redirs);
    void                gsk_url_transfer_set_address_hint   (GskUrlTransfer *transfer,
                                                             GskSocketAddress *address);
    gboolean            gsk_url_transfer_start              (GskUrlTransfer *transfer,
                                                             GError **error);
    void                gsk_url_transfer_cancel             (GskUrlTransfer *transfer);
    gboolean            gsk_url_transfer_has_upload         (GskUrlTransfer *transfer);
    GskStream *         gsk_url_transfer_create_upload      (GskUrlTransfer *transfer,
                                                             gssize *size_out,
                                                             GError **error);
    gboolean            gsk_url_transfer_peek_expects_download_stream
                                                            (GskUrlTransfer *transfer);
    gboolean            gsk_url_transfer_is_done            (GskUrlTransfer *transfer);
    void                gsk_url_transfer_set_address        (GskUrlTransfer *transfer,
                                                             GskSocketAddress *addr);
    gboolean            gsk_url_transfer_add_redirect       (GskUrlTransfer *transfer,
                                                             GObject *request,
                                                             GObject *response,
                                                             gboolean is_permanent,
                                                             GskUrl *dest_url);
    void                gsk_url_transfer_set_download       (GskUrlTransfer *transfer,
                                                             GskStream *content);
    void                gsk_url_transfer_set_request        (GskUrlTransfer *transfer,
                                                             GObject *request);
    void                gsk_url_transfer_set_response       (GskUrlTransfer *transfer,
                                                             GObject *response);
    void                gsk_url_transfer_set_error          (GskUrlTransfer *transfer,
                                                             const GError *error);
    void                gsk_url_transfer_take_error         (GskUrlTransfer *transfer,
                                                             GError *error);
    void                gsk_url_transfer_notify_done        (GskUrlTransfer *transfer,
                                                             GskUrlTransferResult result);
    void                gsk_url_transfer_class_register     (GskUrlScheme scheme,
                                                             GskUrlTransferClass *transfer_class);
    char *              gsk_url_transfer_get_state_string   (GskUrlTransfer *transfer);
    GskStream *         gsk_url_transfer_stream_new         (GskUrlTransfer *transfer,
                                                             GError **error);

## Object Hierarchy

      GObject
       +----GskUrlTransfer
             +----GskUrlTransferHttp
             +----GskUrlTransferFile

## Description

A GskUrlTransfer represents a single upload and/or download with a resource designed by a URL. Although most of the interface is URL-scheme independent, for convenience we allow users to see the URL-specific portions as well.

Some types of URLs are download only, and others support upload, but possibly not at the same time.

The basic plan is to make a GskUrlTransfer object. If you wish to upload, provide a way of constructing an upload content-stream. If you wish to download, provide a function that will be called with the downloading content-stream. Additional configuration may be done: for example, whether to follow redirections.

The transfer which is returned from `gsk_url_transfer_new()` is in an "unstarted" state. After you are done configuring it, you should invoke `gsk_url_transfer_start()` to begin doing actual work.

You should at least connect a handler to it before you start it using `gsk_url_transfer_set_handler()`.

Your handler is guaranteed to be called exactly once, after or during the invocation of `gsk_url_transfer_start()`.

Internally, the URL-transfer code maintains a mapping from url-scheme to a list of GskUrlTransferClasses responsible for that type of URL. When gsk_url_transfer_new(url) is called, each handler for the url's scheme is tried in sequence until one is found that accepts the URL, or NULL will returned if no suitable class is found.

## Details

### GskUrlTransferRedirect

    typedef struct {
      gboolean is_permanent;
      GskUrl *url;
      GObject *request;
      GObject *response;
      GskUrlTransferRedirect *next;
    } GskUrlTransferRedirect;

A node in the linked-list of redirections we have receieved.

gboolean `is_permanent`; | whether the redirect is permanent (versus temporary).
GskUrl *`url`; | the location that we have been redirected to.
GObject *`request`; | the request object.
GObject *`response`; | the response object.
GskUrlTransferRedirect *`next`; | next redirect chronologically.

### enum GskUrlTransferResult

    typedef enum
    {
      GSK_URL_TRANSFER_ERROR_BAD_REQUEST,
      GSK_URL_TRANSFER_ERROR_BAD_NAME,
      GSK_URL_TRANSFER_ERROR_NO_SERVER,
      GSK_URL_TRANSFER_ERROR_NOT_FOUND,
      GSK_URL_TRANSFER_ERROR_SERVER_ERROR,
      GSK_URL_TRANSFER_ERROR_UNSUPPORTED,
      GSK_URL_TRANSFER_ERROR_TIMED_OUT,
      GSK_URL_TRANSFER_ERROR_REDIRECT_LOOP,
      GSK_URL_TRANSFER_REDIRECT,
      GSK_URL_TRANSFER_CANCELLED,
      GSK_URL_TRANSFER_SUCCESS
    } GskUrlTransferResult;

The result element of transfer is prepared before the user's done-handler is invoked and is a general indicator of the result of the transfer.

This enumeration therefore lists all possible ways that a Url-transfer can conclude.

For error values, the "error" member of transfer should be set.

`GSK_URL_TRANSFER_ERROR_BAD_REQUEST` | Some aspect request was invalidly formed: for example, the user provided a bad SSL certificate or upload-function.
`GSK_URL_TRANSFER_ERROR_BAD_NAME` | DNS failed on the hostname.
`GSK_URL_TRANSFER_ERROR_NO_SERVER` | Problem connecting to the server.
`GSK_URL_TRANSFER_ERROR_NOT_FOUND` | Server indicated the entity was not found.
`GSK_URL_TRANSFER_ERROR_SERVER_ERROR` | Server indicated another error.
`GSK_URL_TRANSFER_ERROR_UNSUPPORTED` | Request was not supported (either by the server, the protocol, or the url-transfer-code).
`GSK_URL_TRANSFER_ERROR_TIMED_OUT` | Request timed out.
`GSK_URL_TRANSFER_ERROR_REDIRECT_LOOP` | Request redirection looped circulurly.
`GSK_URL_TRANSFER_REDIRECT` | The URL transfer code was not instructed to follow redirects, but a redirect was encountered. See the transfer's redirect_url member.
`GSK_URL_TRANSFER_CANCELLED` | The transfer was aborted because `gsk_url_transfer_cancel()` was called on it.
`GSK_URL_TRANSFER_SUCCESS` | The transfer initiated successfully (the data may not be transferred yet, though)

### GSK_URL_TRANSFER_N_RESULTS

    #define             GSK_URL_TRANSFER_N_RESULTS

Number of result-codes, seldom needed.

### gsk_url_transfer_result_name ()

    const char *        gsk_url_transfer_result_name        (GskUrlTransferResult result);

`result`: the enumeration value.

Convert a GskUrlTransferResult value into a human-readable string.

Get a mixed-case human-readable name for the Transfer-Result.

`result` : | the result code.
Returns : | the constant string.

### GskUrlTransferFunc ()

    void                (*GskUrlTransferFunc)               (GskUrlTransfer *info,
                                                             gpointer user_data);

A function that is called when the transfer is "done". This function is called if the transfer is successful, or has an error, or times out, or is cancelled.

It will only be called after `gsk_url_transfer_start()` returns TRUE.

`info` : | the object that contains information about the transfer.
`user_data` : | data registered with `gsk_url_transfer()` or `gsk_url_transfer_set_handler()`.

### GskUrlUploadFunc ()

    GskStream *         (*GskUrlUploadFunc)                 (gpointer upload_data,
                                                             gssize *size_out,
                                                             GError **error);

In order to upload content and still follow redirects, we need to be able to make any number of upload streams. Therefore, in order to upload data, you may wish to provide a function like this.

If you don't care about POST-redirects, as you probably don't, you can just use `gsk_url_transfer_set_oneshot_upload()`.

`upload_data` : | data passed into `gsk_url_transfer()` or `gsk_url_transfer_set_upload()`.
`size_out` : | optionally set the length of the stream in bytes. Setting it to -1 is equivalent to not setting it, and indicates that the length is not known in advance.
`error` : | if you cannot make a stream, the error should be set.
Returns : | a newly allocated stream, or NULL if an error occurs.

### GskUrlTransfer

    typedef struct {
      /* --- information prepared for the handler --- */
      GskUrlTransferResult result;
      GskUrl *url;
      GSList *redirect_urls;        // XXX: unused
      GskUrlTransferRedirect *first_redirect, *last_redirect;
      GskSocketAddress *address;

      /* may be available: protocol-specific headers */
      GObject *request;     /* a GskHttpRequest probably */
      GObject *response;    /* a GskHttpResponse probably */

      GskStream *content;   /* the downloading content */

      /* the last redirect (if any) */
      GskUrl *redirect_url; /* [just a peeked version of last_redirect->url, not a ref] */
      gboolean redirect_is_permanent;

      /* ERROR status codes */
      GError *error;
    } GskUrlTransfer;

A GObject that represents a transfer of data with the entity responsible for that type of URL.

GskUrlTransferResult `result`; | code indicating final status of the transfer. (only valid in the "done" state)
GskUrl *`url`; | the url of the initial request.
GSList *`redirect_urls`; | XXX: UNUSED
GskSocketAddress *`address`; | socket-address of the server for this url.
GObject *`request`; | request header.
GObject *`response`; | response header.
GskStream *`content`; | downloading data.
GskUrl *`redirect_url`; | target of first redirect.
gboolean `redirect_is_permanent`; | whether the redirection was permenant (versus temporary)
GError *`error`; | the error, if something went wrong.

### gsk_url_transfer ()

    gboolean            gsk_url_transfer                    (GskUrl *url,
                                                             GskUrlUploadFunc upload_func,
                                                             gpointer upload_data,
                                                             GDestroyNotify upload_destroy,
                                                             GskUrlTransferFunc handler,
                                                             gpointer data,
                                                             GDestroyNotify destroy,
                                                             GError **error);

Begin a upload and/or download with a URL. There is no way to cancel this transfer.

If you wish to perform an upload, provide a function that can create the stream of content to upload on demand. Note that the `upload_destroy()` method is called only once the transfer is done and all the upload streams are finalized. Therefore, you can assume that the upload_data will be available for all your upload-streams.

The handler/data/destroy triple is used for result notification. `handler()` is always invoked exactly once. To find out how things went, the `handler()` should almost always start by examining transfer->result.

`url` : | the URL to which to upload or from which to download data.
`upload_func` : | optional function that can create the upload's content as a GskStream.
`upload_data` : | data which can be used by the upload function.
`upload_destroy` : | optional function that will be notified when `upload()` will no longer be called (note that the streams it created may still be extant though).
`handler` : | function to be called with the transfer request is done. (The transfer content itself is just provided as a stream though-- only after reading the stream is the transfer truly done) This function may also be called in a number of error cases.
`data` : | data to pass to the handler function.
`destroy` : | function to call when you are done with data.
`error` : | place to put the error if anything goes wrong.
Returns : | whether the transfer began. Unsupported URL schemes and malformed URLs are the most common ways for this function to fail.

### gsk_url_transfer_new ()

    GskUrlTransfer *    gsk_url_transfer_new                (GskUrl *url);

Create a URL transfer of the appropriate type for the given URL. We try the registered classes, in order.

`url` : | the URL to create a transfer object for.
Returns : | a newly allocated Transfer object, or NULL if no transfer-class could handle the URL.

### gsk_url_transfer_set_handler ()

    void                gsk_url_transfer_set_handler        (GskUrlTransfer *transfer,
                                                             GskUrlTransferFunc handler,
                                                             gpointer data,
                                                             GDestroyNotify destroy);

The handler/data/destroy triple is used for result notification. `handler()` is always invoked exactly once. To find out how things went, the `handler()` should almost always start by examining transfer->result.

`transfer` : | the Transfer to affect.
`handler` : | function to be called with the transfer request is done. (The transfer content itself is just provided as a stream though-- only after reading the stream is the transfer truly done) This function may also be called in a number of error cases.
`data` : | data to pass to the handler function.
`destroy` : | function to call when you are done with data.

### gsk_url_transfer_set_url ()

    void                gsk_url_transfer_set_url            (GskUrlTransfer *transfer,
                                                             GskUrl *url);

Set the URL that is the target of this transfer. This can only be done once, before the transfer is started.

You seldom need to use this function, as it is called by `gsk_url_transfer_new()`.

`transfer` : | the Transfer to affect.
`url` : | the URL to which to upload or from which to download data.

### gsk_url_transfer_set_upload ()

    void                gsk_url_transfer_set_upload         (GskUrlTransfer *transfer,
                                                             GskUrlUploadFunc func,
                                                             gpointer data,
                                                             GDestroyNotify destroy);

Set the upload stream as generally as possible. Actually you must provide a function that can make an upload stream on demand-- this is necessary to get redirects right.

The `destroy()` function will be called after no more upload-streams need to be created-- it is quite possible that not all upload-streams have been finalized by the time the `destroy()` is invoked.

If you don't care about redirects, you can use `gsk_url_transfer_set_oneshot_upload()`.

If you have a slab of memory that you want to use as the upload stream, consider using `gsk_url_transfer_set_upload_packet()`.

`transfer` : | the Transfer to affect.
`func` : | function that can create the upload's content as a GskStream.
`data` : | data which can be used by the upload function.
`destroy` : | optional function that will be notified when `upload()` will no longer be called (note that the streams it created may still be extant though).

### gsk_url_transfer_set_upload_packet ()

    void                gsk_url_transfer_set_upload_packet  (GskUrlTransfer *transfer,
                                                             GskPacket *packet);

Set the upload stream in an easy, reliable way using a GskPacket.

`transfer` : | the Transfer to affect.
`packet` : | the GskPacket containing the upload content as data.

### gsk_url_transfer_set_oneshot_upload ()

    void                gsk_url_transfer_set_oneshot_upload (GskUrlTransfer *transfer,
                                                             GskStream *stream,
                                                             gssize size);

Set the content to upload to the remote URL, as a GskStream.

Since streams can only be read once, this method only works on URLs that do not require redirection.

`transfer` : | the Transfer to affect.
`stream` : | the upload content stream.
`size` : | the length of the stream in bytes, or -1 if you don't know.

### gsk_url_transfer_set_timeout ()

    void                gsk_url_transfer_set_timeout        (GskUrlTransfer *transfer,
                                                             guint millis);

Set the timeout on the download.

This can be used to avoid hanging on slow servers.

This must be called before the transfer is started (with gsk_url_transfer_start).

`transfer` : | the Transfer to affect.
`millis` : | milliseconds to wait before aborting the transfer.

### gsk_url_transfer_clear_timeout ()

    void                gsk_url_transfer_clear_timeout      (GskUrlTransfer *transfer);

Clear the timeout on the download.

This must be called before the transfer is started (with gsk_url_transfer_start).

`transfer` : | the Transfer to affect.

### gsk_url_transfer_set_follow_redirects ()

    void                gsk_url_transfer_set_follow_redirects
                                                            (GskUrlTransfer *transfer,
                                                             gboolean follow_redirs);

Configure how the transfer will behave when it encounters redirection responses.

The default behavior is to follow redirects, adding them to the list of redirects, but not notifying the user until we reach a real page (or error).

If follow_redirects is FALSE, then we are done even if the download led to a redirect.

`transfer` : | the Transfer to affect.
`follow_redirs` : | whether to follow redirect responses.

### gsk_url_transfer_set_address_hint ()

    void                gsk_url_transfer_set_address_hint   (GskUrlTransfer *transfer,
                                                             GskSocketAddress *address);

To avoid DNS lookups in very bulky transfer situations, DNS may be bypassed and replaced with this address.

Chances are, you want to suppress redirects to: otherwise, DNS may be used on the redirected URLs.

`transfer` : | the Transfer to affect.
`address` : | the socket-address to use for connecting, possibly with the wrong port (the port will be overridden by the URL's port).

### gsk_url_transfer_start ()

    gboolean            gsk_url_transfer_start              (GskUrlTransfer *transfer,
                                                             GError **error);

Begin the upload and/or download. (Maybe start with name-lookup).

`transfer` : | the Transfer to affect.
`error` : | place to put the error if anything goes wrong.
Returns : | whether the transfer started successfully. If it returns TRUE, you are guaranteed to receive your done-notification. If is returns FALSE, you will definitely not receive done-notification.

### gsk_url_transfer_cancel ()

    void                gsk_url_transfer_cancel             (GskUrlTransfer *transfer);

Abort a running transfer.

If you registered a handler, it will be called with result GSK_URL_TRANSFER_CANCELLED.

`transfer` : | the Transfer to affect.

### gsk_url_transfer_has_upload ()

    gboolean            gsk_url_transfer_has_upload         (GskUrlTransfer *transfer);

Figure out whether this transfer has upload data.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to query.
Returns : | whether the transfer has upload data.

### gsk_url_transfer_create_upload ()

    GskStream *         gsk_url_transfer_create_upload      (GskUrlTransfer *transfer,
                                                             gssize *size_out,
                                                             GError **error);

Create a upload stream for this transfer based on the user's creator function.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to use.
`size_out` : | the size of the stream in bytes, or -1 if the size is unknown.
`error` : | optional location to store the GError if there is a problem.
Returns : | a newly allocated GskStream, or NULL if an error occurs.

### gsk_url_transfer_peek_expects_download_stream ()

    gboolean            gsk_url_transfer_peek_expects_download_stream
                                                            (GskUrlTransfer *transfer);

This function can be used to see if download-content is expected.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to use. returns: whether this transfer has a download handler.
Returns : |

### gsk_url_transfer_is_done ()

    gboolean            gsk_url_transfer_is_done            (GskUrlTransfer *transfer);

Find out whether the transfer is done. The transfer is done iff the callback has been invoked.

`transfer` : | the Transfer to query.
Returns : | whether the function is done.

### gsk_url_transfer_set_address ()

    void                gsk_url_transfer_set_address        (GskUrlTransfer *transfer,
                                                             GskSocketAddress *addr);

Set the socket-address for informational purposes. This is occasionally interesting to the user of the Transfer.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to affect.
`addr` : | the address of the host whose lookup was completed.

### gsk_url_transfer_add_redirect ()

    gboolean            gsk_url_transfer_add_redirect       (GskUrlTransfer *transfer,
                                                             GObject *request,
                                                             GObject *response,
                                                             gboolean is_permanent,
                                                             GskUrl *dest_url);

Add an entry to the list of redirects that we have encountered while trying to service this request.

Most users of GskUrlTransfer won't care about these redirects-- they are provided to the rare client that cares about the redirect-path. More commonly, users merely wish to suppress redirect handling: that can be done more easily by `gsk_url_transfer_set_follow_redirects()`.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to affect.
`request` : | request object for this segment of the transfer.
`response` : | response object for this segment of the transfer.
`is_permanent` : | whether the content is permanently relocated to this address.
`dest_url` : | the URL to which we have been redirected.
Returns : | whether the redirect was allowed (it is disallowed if it is a circular redirect. In that case, we will set 'transfer->error', and call `gsk_url_transfer_notify_done()`.

### gsk_url_transfer_set_download ()

    void                gsk_url_transfer_set_download       (GskUrlTransfer *transfer,
                                                             GskStream *content);

Set the incoming-content that is associated with this transfer. This will used by the user of the Transfer.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to affect.
`content` : | the content-stream for the downloaded data.

### gsk_url_transfer_set_request ()

    void                gsk_url_transfer_set_request        (GskUrlTransfer *transfer,
                                                             GObject *request);

Set the outgoing-request header data for this transaction.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to affect.
`request` : | the request object to store in the transfer information.

### gsk_url_transfer_set_response ()

    void                gsk_url_transfer_set_response       (GskUrlTransfer *transfer,
                                                             GObject *response);

Set the incoming-response header data for this transaction.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to affect.
`response` : | the response object to store in the transfer information.

### gsk_url_transfer_set_error ()

    void                gsk_url_transfer_set_error          (GskUrlTransfer *transfer,
                                                             const GError *error);

Set the error field for this transaction. A copy of the error parameter is made.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to affect.
`error` : | the error to associate with the transfer.

### gsk_url_transfer_take_error ()

    void                gsk_url_transfer_take_error         (GskUrlTransfer *transfer,
                                                             GError *error);

Set the error field for this transaction. The error parameter will be freed eventually by the GskUrlTransfer.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to affect.
`error` : | the error to associate with the transfer.

### gsk_url_transfer_notify_done ()

    void                gsk_url_transfer_notify_done        (GskUrlTransfer *transfer,
                                                             GskUrlTransferResult result);

Transition the transfer to the DONE state, and invoke the user's callback (if any). This function may only be invoked once per transfer.

This function should only be needed by implementors of types of GskUrlTransfer.

`transfer` : | the Transfer to affect.
`result` : | the transfer's result status code.

### gsk_url_transfer_class_register ()

    void                gsk_url_transfer_class_register     (GskUrlScheme scheme,
                                                             GskUrlTransferClass *transfer_class);

Register a class of URL transfer that can handle a given scheme. It will only be instantiated if the class' test method

`scheme` : | the URL scheme that this class of transfer can handle.
`transfer_class` : | the class that can handle the URL type.

### gsk_url_transfer_get_state_string ()

    char *              gsk_url_transfer_get_state_string   (GskUrlTransfer *transfer);

Get a newly allocated, human-readable description of the state of the transfer.

`transfer` : | the transfer to describe.
Returns : | the newly-allocated string.

### gsk_url_transfer_stream_new ()

    GskStream *         gsk_url_transfer_stream_new         (GskUrlTransfer *transfer,
                                                             GError **error);

This code will start the transfer, and return a stream that you can trap immediately.

`transfer` : | the transfer. must not be started.
`error` : | optional location to store the GError if there is a problem.
Returns : | the new stream, or NULL if an error occurred.

## See Also

GskUrlTransferHttp.