A GET followed by a PUT Crashes LIBWWW.

Henrik,

I put together an example program based on the "LoadToFile" and "put" example
programs. This program simply performs the equivalent of the "LoadToFile"
example program and then performs the equivalent of the "put" example program.
Libwww is initialized and finalized before each download or upload.

When I run this on my SGI the program downloads the file correctly but the put
gets stuck for about 15 seconds and then it crashes in the library with a
segmentation fault.

This small program is an good example of the problems I have been having with
the library so I hope it also crashes on your platform and is evident to you
what is going on.

Thanks for looking at it,
Michael
#include <assert.h>

#include "WWWLib.h"
#include "WWWMIME.h"
#include "WWWNews.h"
#include "WWWHTTP.h"
#include "WWWFTP.h"
#include "WWWFile.h"
#include "WWWGophe.h"
#include "WWWInit.h"

/* library specific globals */
static BOOL      LibWWWInterruptTransfer = FALSE;
static HTRequest *LibWWWCurrentRequest = NULL;
static HTTimer   *LibWWWInterruptTimer = NULL;

/* dialog messages */
static const char *LibWWWDialogMsg[HT_MSG_ELEMENTS] =
    {
    HT_MSG_ENGLISH_INITIALIZER
    };


/**
 * Checks to see if an interrupt has been requested.
 */
void Gr_CheckInterrupt(
    const char *InterruptMessage)
    {
    /* normally this function flushes all X events */
    /* and checks for a button press but for this  */
    /* example assume no interruptions             */
    LibWWWInterruptTransfer = FALSE;
    }


/**
 * Checks if the transfer in progress is to be interrupted. If so all
 * requests in the pipeline are killed.
 *
 * param Timer
 *     Timer object that expired and was dispatched.
 * param ClientData
 *     Client data associated with this callback.
 * param EventType
 *     Type of event that occured.
 *
 * return
 *     HT_OK to continue or HT_ERROR to interrupt the transfer.
 */
static int LibWWWCheckForInterrupt(
    HTTimer     *Timer,
    void        *ClientData,
    HTEventType EventType)
    {
    int Result = HT_OK;

    /* determine if the transfer should be canceled. */
    Gr_CheckInterrupt(NULL);
    if (LibWWWInterruptTransfer)
        {
        HTNet  *Net = NULL;
        HTHost *Host = NULL;

        /* request has been interrupted so don't start a new timer */
        LibWWWInterruptTimer = NULL;

        Net = HTRequest_net(LibWWWCurrentRequest);
        Host = HTNet_host(Net);

        HTHost_killPipe(Host);

        /* allows us to escape from event loop */
        HTEventList_stopLoop();

        Result = HT_ERROR;
        }
    else
        {
        /* not interrupted so reinstate the timer */
        LibWWWInterruptTimer = HTTimer_new(NULL, LibWWWCheckForInterrupt,
                                           NULL, 100L, YES, NO);
        Result = HT_OK;
        }

    return Result;
    }


/**
 * Shows the transfer progress in a dialog.
 *
 * param Request
 *     Request in progress.
 * param Op
 *     Message type.
 * param MsgNum
 *     Message index.
 * param Default
 *     Suggested default response to the message.
 * param Input
 *     Call data (varies depends on Op code).
 * param Reply
 *     Reply object encapsulating the reply if applicable.
 *
 * return
 *     YES if successfull, otherwise NO.
 */
static BOOL LibWWWShowProgress(
    HTRequest     *Request,
    HTAlertOpcode Op,
    int           MsgNum,
    const char    *Default,
    void          *Input,
    HTAlertPar    *Reply)
    {
#   define           UPDATE_FREQ (0.50) /* 1/2 of a second */
    BOOL             Result = YES;
    char             Message[1023+1];
    double           EllapsedTime = 0.0;
    clock_t          ThisTime;
    static clock_t   LastTime;
    static BOOL      IsLastTimeInitialized = FALSE;

    if (!IsLastTimeInitialized)
        {
        LastTime = clock();
        IsLastTimeInitialized = TRUE;
        }

    switch (Op)
        {
    case HT_PROG_DNS:
        sprintf(Message, "Looking up %s",
        Input != NULL ? (char *)Input : "host");
        fprintf(stdout, "%s\n", Message);
        break;

    case HT_PROG_CONNECT:
        sprintf(Message, "Contacting %s",
        Input != NULL ? (char *)Input : "host");
        fprintf(stdout, "%s\n", Message);
        break;

    case HT_PROG_ACCEPT:
        sprintf(Message, "%s", "Waiting for connection...");
        fprintf(stdout, "%s\n", Message);
        break;

    case HT_PROG_LOGIN:
        sprintf(Message, "%s", "Logging in...");
        fprintf(stdout, "%s\n", Message);
        break;

    case HT_PROG_READ:
        if (Request != NULL)
            {
            long   BytesRead = 0;
            int    *RawBytesRead = Input ? (int *)Input : NULL;
            long   DataLength = HTAnchor_length(HTRequest_anchor(Request));
            double PercentDone = 0.0;

            /* determine if the update time has expired */
            ThisTime = clock();
            EllapsedTime = ((double)ThisTime - LastTime) / CLK_TCK;
            if (EllapsedTime >= UPDATE_FREQ)
                {
                /* record last time */
                LastTime = ThisTime;

                if (DataLength > 0)
                    {
                    /* output a percent done indication */
                    BytesRead = HTRequest_bodyRead(Request);
                    if (BytesRead <= 0 &&
                        RawBytesRead != NULL && *RawBytesRead > 0)
                        {
                        BytesRead = *RawBytesRead;
                        }
                    PercentDone = (double)BytesRead/DataLength*100;

                    sprintf(Message, "%ldK of %ldK read",
                            BytesRead/1024, DataLength/1024);
                    fprintf(stdout, "%s\n", Message);
                    }
                else
                    {
                    /* if possible output a number of bytes read indication */
                    BytesRead = HTRequest_bytesRead(Request);
                    if (BytesRead > 0)
                        {
                        sprintf(Message, "%ldK read", BytesRead/1024);
                        }
                    else if (RawBytesRead != NULL && *RawBytesRead > 0)
                        {
                        sprintf(Message, "%ldK read", *RawBytesRead/1024);
                        }
                    else
                        {
                        strcpy(Message, "Reading...");
                        }

                    fprintf(stdout, "%s\n", Message);
                    }
                }
            }
        break;

    case HT_PROG_WRITE:
        if (Request != NULL && HTMethod_hasEntity(HTRequest_method(Request)))
            {
            long           BytesWritten = 0;
            int            *RawBytesWritten = Input ? (int *)Input : NULL;
            HTParentAnchor *Anchor=HTRequest_anchor(HTRequest_source(Request));
            long           DataLength = HTAnchor_length(Anchor);
            double         PercentDone = 0.0;

            /* determine if the update time has expired */
            ThisTime = clock();
            EllapsedTime = ((double)ThisTime - LastTime) / CLK_TCK;
            if (EllapsedTime >= UPDATE_FREQ)
                {
                /* record last time */
                LastTime = ThisTime;

                if (DataLength > 0)
                    {
                    /* output a percent done indication */
                    BytesWritten = HTRequest_bodyWritten(Request);
                    if (BytesWritten <= 0 &&
                        RawBytesWritten != NULL && *RawBytesWritten > 0)
                        {
                        BytesWritten = *RawBytesWritten;
                        }
                    PercentDone = (double)BytesWritten/DataLength*100;

                    sprintf(Message, "%ldK of %ldK written\n",
                            BytesWritten/1024, DataLength/1024);
                    fprintf(stdout, "%s\n", Message);
                    }
                else
                    {
                    /* if possible output number of bytes written indication */
                    BytesWritten = HTRequest_bytesWritten(Request);
                    if (BytesWritten > 0)
                        {
                        sprintf(Message, "%ldK written", BytesWritten/1024);
                        }
                    else if (RawBytesWritten != NULL && *RawBytesWritten > 0)
                        {
                        sprintf(Message, "%ldK written", *RawBytesWritten/1024);
                        }
                    else
                        {
                        strcpy(Message, "Writing...");
                        }

                    fprintf(stdout, "%s\n", Message);
                    }
                }
            }
        break;

    case HT_PROG_DONE:
        sprintf(Message, "%s", "Complete");
        fprintf(stdout, "%s\n", Message);

        /* all done: indicate that we want to exit the library's event loop */
        HTEventList_stopLoop();
        break;

    case HT_PROG_INTERRUPT:
        sprintf(Message, "%s", "Interrupted");
        fprintf(stdout, "%s\n", Message);
        LibWWWInterruptTransfer = TRUE;
        break;

    case HT_PROG_OTHER:
        sprintf(Message, "%s", "Working - please wait...");
        fprintf(stdout, "%s\n", Message);
        break;

    case HT_PROG_TIMEOUT:
        sprintf(Message, "%s", "Timeout - server did not respond.");
        fprintf(stdout, "%s\n", Message);
        LibWWWInterruptTransfer = TRUE;
        break;

    default:
        sprintf(Message, "%s", "Unknown progress state.");
        fprintf(stdout, "%s\n", Message);
        break;
        }

    return Result;
    }


/**
 * Shows the error message in a dialog.
 *
 * param Request
 *     Request in progress.
 * param Op
 *     Message type.
 * param MsgNum
 *     Message index.
 * param Default
 *     Suggested default response to the message.
 * param Input
 *     Call data (varies depends on Op code).
 * param Reply
 *     Reply object encapsulating the reply if applicable.
 *
 * return
 *     YES if successfull, otherwise NO.
 */
static BOOL LibWWWShowError(
    HTRequest     *Request,
    HTAlertOpcode Op,
    int           MsgNum,
    const char    *Default,
    void          *Input,
    HTAlertPar    *Reply)
    {
    BOOL Result = NO; /* humm.. to what value should this be set? */
    char *Message = HTDialog_errorMessage(Request, Op, MsgNum, Default, Input);
    if (Message != NULL)
        {
        fprintf(stderr, "%s", Message);
        HT_FREE(Message);
        }

    LibWWWInterruptTransfer = TRUE;

    /* allows us to escape from event loop */
    HTEventList_stopLoop();

    return Result;
    }


/**
 * Confirms a condition with the user in a dialog.
 *
 * param Request
 *     Request in progress.
 * param Op
 *     Message type.
 * param MsgNum
 *     Message index.
 * param Default
 *     Suggested default response to the message.
 * param Input
 *     Call data (varies depends on Op code).
 * param Reply
 *     Reply object encapsulating the reply if applicable.
 *
 * return
 *     YES or NO response to the confirmation.
 */
static BOOL LibWWWConfirm(
    HTRequest     *Request,
    HTAlertOpcode Op,
    int           MsgNum,
    const char    *Default,
    void          *Input,
    HTAlertPar    *Reply)
    {
    BOOL Result = NO;

    if (MsgNum >= 0)
        {
        char Message[1023+1];
        char ReplyMessage[1023+1];

        strcpy(Message, LibWWWDialogMsg[MsgNum]);

        if (Input != NULL)
            {
            strcat(Message, " (");
            strcat(Message, (char *)Input);
            strcat(Message, " )");
            }

        fprintf(stdout, "%s\n", Message);
        fgets(ReplyMessage, sizeof(ReplyMessage)-1, stdin);
        if (ReplyMessage[strlen(ReplyMessage)-1] == '\n')
            ReplyMessage[strlen(ReplyMessage)-1] == '\0';

        if (toupper(ReplyMessage[0]) == 'Y')
            Result = YES;
        else
            Result = NO;
        }

    return Result;
    }


/**
 * Prompts the user for a result in a dialog.
 *
 * param Request
 *     Request in progress.
 * param Op
 *     Message type.
 * param MsgNum
 *     Message index.
 * param Default
 *     Suggested default response to the message.
 * param Input
 *     Call data (varies depends on Op code).
 * param Reply
 *     Reply object encapsulating the reply if applicable.
 *
 * return
 *     YES if OK, otherwise NO
 */
static BOOL LibWWWPrompt(
    HTRequest     *Request,
    HTAlertOpcode Op,
    int           MsgNum,
    const char    *Default,
    void          *Input,
    HTAlertPar    *Reply)
    {
    BOOL Result = NO;

    if (Reply != NULL && MsgNum >= 0)
        {
        char Message[1023+1];
        char ReplyMessage[1023+1];

        strcpy(Message, LibWWWDialogMsg[MsgNum]);
        if (Input != NULL)
            {
            strcat(Message, " (");
            strcat(Message, (char *)Input);
            strcat(Message, ")");
            }

        fprintf(stdout, "%s\n", Message);
        fgets(ReplyMessage, sizeof(ReplyMessage)-1, stdin);
        if (ReplyMessage[strlen(ReplyMessage)-1] == '\n')
            ReplyMessage[strlen(ReplyMessage)-1] == '\0';

        if (strlen(ReplyMessage) != 0)
            {
            HTAlert_setReplyMessage(Reply, ReplyMessage);
            Result = YES;
            }
        else
            {
            Result = NO;
            }
        }

    return Result;
    }


/**
 * Prompts the user for a password in a dialog.
 *
 * param Request
 *     Request in progress.
 * param Op
 *     Message type.
 * param MsgNum
 *     Message index.
 * param Default
 *     Suggested default response to the message.
 * param Input
 *     Call data (varies depends on Op code).
 * param Reply
 *     Reply object encapsulating the reply if applicable.
 *
 * return
 *     YES if OK, otherwise NO
 */
static BOOL LibWWWPromptPassword(
    HTRequest     *Request,
    HTAlertOpcode Op,
    int           MsgNum,
    const char    *Default,
    void          *Input,
    HTAlertPar    *Reply)
    {
    BOOL Result = YES;
    char Password[1023+1];

    fprintf(stdout, "%s", "Enter password: ");
    fgets(Password, sizeof(Password)-1, stdin);
    if (Password[strlen(Password)-1] == '\n')
        Password[strlen(Password)-1] == '\0';

    if (strlen(Password) != 0)
        HTAlert_setReplySecret(Reply, Password);

    Result = (strlen(Password) != 0);
    return Result;
    }


/**
 * Prompts the user for a user ID and a password in a dialog.
 *
 * param Request
 *     Request in progress.
 * param Op
 *     Message type.
 * param MsgNum
 *     Message index.
 * param Default
 *     Suggested default response to the message.
 * param Input
 *     Call data (varies depends on Op code).
 * param Reply
 *     Reply object encapsulating the reply if applicable.
 *
 * return
 *     YES if OK, otherwise NO
 */
static BOOL LibWWWPromptUserNameAndPassword(
    HTRequest     *Request,
    HTAlertOpcode Op,
    int           MsgNum,
    const char    *Default,
    void          *Input,
    HTAlertPar    *Reply)
    {
    BOOL Result = YES;
    char UserName[1023+1];
    char Password[1023+1];

    fprintf(stdout, "%s", "Enter user name: ");
    fgets(UserName, sizeof(Password)-1, stdin);
    if (UserName[strlen(UserName)-1] == '\n')
        UserName[strlen(UserName)-1] == '\0';

    if (strlen(UserName) != 0)
        HTAlert_setReplyMessage(Reply, UserName);

    fprintf(stdout, "%s", "Enter password: ");
    fgets(Password, sizeof(Password)-1, stdin);
    if (Password[strlen(Password)-1] == '\n')
        Password[strlen(Password)-1] == '\0';

    if (strlen(Password) != 0)
        HTAlert_setReplySecret(Reply, Password);

    Result = (strlen(UserName) != 0 && strlen(Password) != 0);
    return Result;
    }


/**
 * "Before" filter called by LibWWW immediately before processing a request.
 *
 * param Request
 *     Request to process.
 * param Param
 *     Client data.
 * param Mode
 *     Extra client data.
 *
 * return
 *     HT_OK if OK, otherwise some other value.
 */
static int LibWWWBeforeDownload(
    HTRequest  *Request,
    void       *Param,
    int        Mode)
    {
    int Result = HT_OK;

    LibWWWInterruptTransfer = FALSE;

    LibWWWCurrentRequest = NULL;

    /* timer for checking if user interrupt requests */
    LibWWWInterruptTimer = HTTimer_new(NULL, LibWWWCheckForInterrupt,
                                       NULL, 100L, YES, NO);

    return Result;
    }


/**
 * "After" filter called by LibWWW immediately after processing a request.
 *
 * param Request
 *     Request processed.
 * param Response
 *     Response to processed request.
 * param Param
 *     Client data.
 * param StatusCode
 *     Status of request.
 *
 * return
 *     HT_OK if OK, otherwise some other value.
 */
static int LibWWWAfterDownload(
    HTRequest  *Request,
    HTResponse *Response,
    void       *Param,
    int        StatusCode)
    {
    int Result = HT_OK;

    LibWWWCurrentRequest = NULL;

    /* cleanup timer and request memory */
    if (LibWWWInterruptTimer != NULL)
        HTTimer_delete(LibWWWInterruptTimer);
    LibWWWInterruptTimer = NULL;

    HTRequest_delete(Request);

    /* allows us to escape from event loop */
    HTEventList_stopLoop();

    return Result;
    }


/**
 * Initializes the LibWWW library and gets things set up to transfer a file.
 */
static void LibWWWInitialize(void)
    {
    LibWWWInterruptTransfer = FALSE;
    LibWWWInterruptTimer = NULL;

    # if defined DEBUG_LIBWWW
        /* used for debug trace */
        WWWTRACE = SHOW_ALL_TRACE & (SHOW_ALL_TRACE ^ SHOW_MEM_TRACE);
    # endif /* DEBUG_LIBWWW */

    /* 15 second timeout for hosts not responding */
    HTHost_setEventTimeout(15000);

    /* initialize the library and create a new cacheless client profile */
    HTProfile_newNoCacheClient("MYAPP", "1.0");

    /* replace the default alert callbacks with our own */
    HTAlert_deleteAll();
    HTAlert_add(LibWWWShowProgress, HT_A_PROGRESS);
    HTAlert_add(LibWWWShowError, HT_A_MESSAGE);
    HTAlert_add(LibWWWConfirm, HT_A_CONFIRM);
    HTAlert_add(LibWWWPrompt, HT_A_PROMPT);
    HTAlert_add(LibWWWPromptPassword, HT_A_SECRET);
    HTAlert_add(LibWWWPromptUserNameAndPassword, HT_A_USER_PW);

    /* add before and after filters for setup and cleanup of requests */
    HTNet_addBefore(LibWWWBeforeDownload, NULL, NULL, HT_FILTER_FIRST);
    HTNet_addAfter(LibWWWAfterDownload, NULL, NULL, HT_ALL, HT_FILTER_LAST);
    }


/**
 * Finalizes the LibWWW library by cleaning up everything.
 */
static void LibWWWFinalize(void)
    {
    /* just in case it didn't get cleaned up */
    if (LibWWWInterruptTimer != NULL)
        HTTimer_delete(LibWWWInterruptTimer);
    LibWWWInterruptTimer = NULL;

    /* make sure no requests are left hanging */
    HTNet_killAll();

    /* delete before and after filters for setup and cleanup of requests */
    HTNet_deleteBefore(LibWWWBeforeDownload);
    HTNet_deleteAfter(LibWWWAfterDownload);

    /* clean up the profile (this also cleans up the core library) */
    HTProfile_delete();

    LibWWWInterruptTransfer = FALSE;
    }


/**
 * Uploads the source file to the target URL.
 *
 * param SourceFileName
 *     Full path name of the source file to upload.
 * param TargetURL
 *     Target URL to which to transfer the source file.
 *
 * return
 *     YES if successful, otherwise NO.
 */
static BOOL LibWWWUploadFile(
    const char      *SourceFileName,
    const char      *TargetURLName)
    {
    BOOL     Result = NO;
    char     *SourceFileURL = NULL;
    HTAnchor *SourceAnchor = NULL;
    HTAnchor *TargetAnchor = NULL;

    assert(SourceFileName != NULL &&
           strlen(SourceFileName) != 0 &&
           "SourceFileName is an absolute path name");
    assert(TargetURLName != NULL &&
           strlen(TargetURLName) != 0 &&
           "TargetURLName conforms to the URL syntax");

    LibWWWInitialize();

    SourceFileURL = (char *)malloc(strlen("file:") +
                                   strlen(SourceFileName) + 1);
    /* give the source file name a file URL syntax */
    sprintf(SourceFileURL, "file:%s", SourceFileName);

    /* create a request and upload the file */
    LibWWWCurrentRequest = HTRequest_new();
    SourceAnchor = HTAnchor_findAddress(SourceFileURL);
    TargetAnchor = HTAnchor_findAddress(TargetURLName);

    Result = (HTPutDocumentAnchor(HTAnchor_parent(SourceAnchor),
                                  TargetAnchor, LibWWWCurrentRequest) == YES);
    if (Result == TRUE)
        Result = (HTEventList_newLoop() == HT_OK &&
                  !LibWWWInterruptTransfer);

    free(SourceFileURL);

    LibWWWFinalize();

    assert(Result == YES || Result == NO);
    return Result;
    }


/**
 * Downloads the source URL to the target file.
 *
 * param SourceURL
 *     Source URL of the file containing data to transfer.
 * param TargetFileName
 *     Full path name of the target file to receive the data.
 *
 * return
 *     YES if successful, otherwise NO.
 */
static BOOL LibWWWDownloadFile(
    const char      *SourceURLName,
    const char      *TargetFileName)
    {
    BOOL Result = NO;

    assert(SourceURLName != NULL &&
           strlen(SourceURLName) != 0 &&
           "SourceURLName conforms to the URL syntax");
    assert(TargetFileName != NULL &&
           strlen(TargetFileName) != 0 &&
           "TargetFileName is an absolute path name");

    LibWWWInitialize();

    /* create a request and download the file */
    LibWWWCurrentRequest = HTRequest_new();
    Result = HTLoadToFile(SourceURLName, LibWWWCurrentRequest, TargetFileName);
    if (Result == YES)
        Result = (HTEventList_newLoop() == HT_OK &&
                  !LibWWWInterruptTransfer);

    LibWWWFinalize();

    assert(Result == YES || Result == NO);
    return Result;
    }


/**
 * Test driver.
 */
int main(
    int  argc,
    char **argv)
    {
    /* download something from the web */
    fprintf(stdout, "Downloading file from the web...\n");
    LibWWWDownloadFile("http://www.w3.org/INSTALL.html",
                       "/tmp/INSTALL.html");

    /* obviously the w3c server won't allow an */
    /* upload but it demonstrates the problem  */
    fprintf(stdout,
            "Attempting to upload file to the web. Please be patient.\n");
    fprintf(stdout,
            "On my SGI it takes about 15 seconds until it crashes...\n");
    LibWWWUploadFile("/tmp/INSTALL.html",
                     "http://www.w3.org/INSTALL.html");

    return 0;
    }

Received on Friday, 25 June 1999 19:42:25 UTC