add: hdrs = NULL;
context: static Ns_SockProc HttpCancel;static int HttpDone(SOCKET sock, Http *httpPtr, int state);static Http *HttpOpen(char *method, char *url, Ns_Set *hdrs, char *body);static void HttpClose(Http *httpPtr, int nb);static int HttpAbort(Http *httpPtr);static char *HttpResult(char *response, Ns_Set *hdrs);static Ns_Mutex lock;static Ns_Cond cond;/* *---------------------------------------------------------------------- * * NsTclHttpObjCmd -- * *	Implements ns_http to handle async HTTP requests. * * Results: *	Standard Tcl result. * * Side effects: *	May queue an HTTP request. * *---------------------------------------------------------------------- */intNsTclHttpObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]){    NsInterp *itPtr = arg;    Http *httpPtr;    char buf[20], *result;    int new, status, n;    Ns_Time timeout, incr;    Ns_Set *hdrs;    Tcl_HashEntry *hPtr;    Tcl_HashSearch search;    static CONST char *opts[] = {       "cancel", "cleanup", "queue", "wait", NULL    };    enum {        HCancelIdx, HCleanupIdx, HQueueIdx, HWaitIdx    } opt;    char *method, *url, *body;    if (objc < 2) {        Tcl_WrongNumArgs(interp, 1, objv, "option ?args ...?");        return TCL_ERROR;    }    if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,                            (int *) &opt) != TCL_OK) {        return TCL_ERROR;    }    switch (opt) {    case HQueueIdx:        if (objc < 4 || objc > 6) {            Tcl_WrongNumArgs(interp, 2, objv, "method url ?body? ?headers?");            return TCL_ERROR;        }        method = Tcl_GetString(objv[2]);        url    = Tcl_GetString(objv[3]);        if (objc > 4) {            body = Tcl_GetString(objv[4]);            if (objc > 5) {                if (Ns_TclGetSet2(interp, Tcl_GetString(objv[5]), &hdrs) != TCL_OK) {                    return TCL_ERROR;                }            }            else {                hdrs = NULL;            }        }        else {            body = NULL;        }        if (strcasecmp(method, METHOD_GET) == 0) {            method = METHOD_GET;        }        else if (strcasecmp(method, METHOD_POST) == 0) {            method = METHOD_POST;        }        else {            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),                                   "method must be one of ",                                   METHOD_GET ", " METHOD_POST,                                   NULL);            return TCL_ERROR;        }        httpPtr = HttpOpen(method, url, hdrs, body);        if (httpPtr == NULL) {            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),                     "could not connect to : ",                     url, NULL);            return TCL_ERROR;        }        Ns_SockCallback(httpPtr->sock, HttpSend, httpPtr, NS_SOCK_WRITE);        n = itPtr->https.numEntries;        do {            sprintf(buf, "http%d", n++);            hPtr = Tcl_CreateHashEntry(&itPtr->https, buf, &new);        } while (!new);        Tcl_SetHashValue(hPtr, httpPtr);        Tcl_SetResult(interp, buf, TCL_VOLATILE);        break;    case HCancelIdx:        if (objc != 3) {            Tcl_WrongNumArgs(interp, 2, objv, "id");            return TCL_ERROR;        }        hPtr = Tcl_FindHashEntry(&itPtr->https, Tcl_GetString(objv[2]));        if (hPtr == NULL) {            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "no such request: ",                                    Tcl_GetString(objv[2]), NULL);            return TCL_ERROR;        }        httpPtr = Tcl_GetHashValue(hPtr);        Tcl_DeleteHashEntry(hPtr);        Tcl_SetIntObj(Tcl_GetObjResult(interp), HttpAbort(httpPtr));        break;    case HWaitIdx:        if (objc < 4 || objc > 6) {            Tcl_WrongNumArgs(interp, 2, objv, "id resultsVar ?timeout? ?headers?");            return TCL_ERROR;        }        if (objc < 5) {            incr.sec  = 2;            incr.usec = 0;        } else if (Ns_TclGetTimeFromObj(interp, objv[4], &incr) != TCL_OK) {            return TCL_ERROR;        }        Ns_GetTime(&timeout);        Ns_IncrTime(&timeout, incr.sec, incr.usec);        if (objc < 6) {            hdrs = NULL;        } else if (Ns_TclGetSet2(interp, Tcl_GetString(objv[5]), &hdrs) != TCL_OK) {            return TCL_ERROR;        }        hPtr = Tcl_FindHashEntry(&itPtr->https, Tcl_GetString(objv[2]));        if (hPtr == NULL) {            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "no such request: ",                     Tcl_GetString(objv[2]), NULL);            return TCL_ERROR;        }        httpPtr= Tcl_GetHashValue(hPtr);        status = NS_OK;        Ns_MutexLock(&lock);        while (status == NS_OK && !(httpPtr->state & REQ_DONE)) {            status = Ns_CondTimedWait(&cond, &lock, &timeout);        }        Ns_MutexUnlock(&lock);        if (status != NS_OK) {            httpPtr = NULL;            result = "timeout";        } else {            if (httpPtr->state & REQ_EOF) {                result = HttpResult(httpPtr->ds.string, hdrs);            } else {                status = NS_ERROR;                result = "error";            }        }        result = (char*)Tcl_SetVar(interp, Tcl_GetString(objv[3]), result, TCL_LEAVE_ERR_MSG);        if (httpPtr != NULL) {            Tcl_DeleteHashEntry(hPtr);            HttpClose(httpPtr, 0);        }         if (result == NULL) {            return TCL_ERROR;        }        Tcl_SetBooleanObj(Tcl_GetObjResult(interp), status == NS_OK ? 1 : 0);        break;    case HCleanupIdx:        hPtr = Tcl_FirstHashEntry(&itPtr->https, &search);        while (hPtr != NULL) {            httpPtr = Tcl_GetHashValue(hPtr);            (void) HttpAbort(httpPtr);            hPtr = Tcl_NextHashEntry(&search);        }        Tcl_DeleteHashTable(&itPtr->https);        Tcl_InitHashTable(&itPtr->https, TCL_STRING_KEYS);        break;    }    return TCL_OK;}/* *---------------------------------------------------------------------- * * HttpOpen -- * *        Open a connection to the given URL host and construct *        an Http structure to fetch the file. * * Results: *        Pointer to Http struct or NULL on error. * * Side effects: *        Will open a socket connection. * *---------------------------------------------------------------------- */Http *HttpOpen(char *method, char *url, Ns_Set *hdrs, char *body){    Http *httpPtr = NULL;    SOCKET sock;    char *host, *file, *port;    int i;    int body_len = 0;    char lenbuf[20];    if (strncmp(url, "http://", 7) != 0 || url[7] == '\0') {        return NULL;    }    host = url + 7;    file = strchr(host, '/');    if (file != NULL) {        *file = '\0';    }    port = strchr(host, ':');    if (port == NULL) {        i = 80;    } else {        *port = '\0';        i = atoi(port+1);    }    sock = Ns_SockAsyncConnect(host, i);    if (port != NULL) {        *port = ':';    }    if (sock != INVALID_SOCKET) {        httpPtr = ns_malloc(sizeof(Http));        httpPtr->state = REQ_SEND;        httpPtr->sock = sock;        Tcl_DStringInit(&httpPtr->ds);        if (file != NULL) {            *file = '/';        }        Ns_DStringVarAppend(&httpPtr->ds, method, " ", file ? file : "/", " HTTP/1.0\r\n", NULL);        if (file != NULL) {            *file = '\0';        }        Ns_DStringVarAppend(&httpPtr->ds,            "User-Agent: ", Ns_InfoServerName(), "/", Ns_InfoServerVersion(), "\r\n"            "Connection: close\r\n"            "Host: ", host, "\r\n", NULL);        if (file != NULL) {            *file = '/';        }        if (hdrs != NULL) {            for (i = 0; i < Ns_SetSize(hdrs); i++) {                Ns_DStringVarAppend(&httpPtr->ds,                    Ns_SetKey(hdrs, i), ": ", Ns_SetValue(hdrs, i), "\r\n", NULL);            }        }        if (body != NULL) {            body_len = strlen(body);            if (body_len > 0) {                sprintf(lenbuf, "%d", body_len);                Ns_DStringVarAppend(&httpPtr->ds,                                    "Content-Length: ",                                    lenbuf,                                    "\r\n",                                    NULL);            }        }        Tcl_DStringAppend(&httpPtr->ds, "\r\n", 2);        if (body_len > 0) {            Tcl_DStringAppend(&httpPtr->ds, body, body_len);        }        httpPtr->next = httpPtr->ds.string;        httpPtr->len = httpPtr->ds.length;    }    if (file != NULL) {        *file = '/';    }    return httpPtr;}/* *---------------------------------------------------------------------- * * HttpResult -- * *        Parse an Http response for the result body and headers. * * Results: *        Pointer body within Http buffer. * * Side effects: *        Will append parsed response headers to given hdrs if *        not NULL. * *---------------------------------------------------------------------- */static char *HttpResult(char *response, Ns_Set *hdrs){    int firsthdr, len;    char *eoh, *body, *p;    body = response;    eoh = strstr(response, "\r\n\r\n");    if (eoh != NULL) {        body = eoh + 4;    } else {        eoh = strstr(response, "\n\n");        if (eoh != NULL) {            body = eoh + 2;        }    }    if (eoh != NULL) {        *eoh = '\0';    }    /*     * Parse the headers saved in the dstring if requested.     */    if (hdrs != NULL) {        firsthdr = 1;        p = response;        while ((eoh = strchr(p, '\n')) != NULL) {            *eoh++ = '\0';            len = strlen(p);            if (len > 0 && p[len-1] == '\r') {                p[len-1] = '\0';            }            if (firsthdr) {                if (hdrs->name != NULL) {                    ns_free(hdrs->name);                }                hdrs->name = ns_strdup(p);                firsthdr = 0;            } else if (Ns_ParseHeader(hdrs, p, ToLower) != NS_OK) {                break;            }            p = eoh;        }    }    return body;}static voidHttpClose(Http *httpPtr, int nb){    Tcl_DStringFree(&httpPtr->ds);    if (nb) {        ns_socknbclose(httpPtr->sock);    } else {        ns_sockclose(httpPtr->sock);    }    ns_free(httpPtr);}static intHttpSend(SOCKET sock, void *arg, int why){    Http *httpPtr = arg;    int n;    n = send(sock, httpPtr->next, httpPtr->len, 0);    if (n < 0) {        Tcl_DStringFree(&httpPtr->ds);        return HttpDone(sock, httpPtr, (REQ_DONE|REQ_ERR));    }    httpPtr->next += n;    httpPtr->len -= n;    if (httpPtr->len == 0) {        shutdown(sock, 1);        Tcl_DStringTrunc(&httpPtr->ds, 0);        Ns_MutexLock(&lock);        httpPtr->state = REQ_RECV;        Ns_MutexUnlock(&lock);        Ns_SockCallback(sock, HttpRecv, arg, NS_SOCK_READ);    }    return NS_TRUE;}static intHttpRecv(SOCKET sock, void *arg, int why){    Http *httpPtr = arg;    char buf[1024];    int n, state;    n = recv(sock, buf, sizeof(buf), 0);    if (n > 0) {        Tcl_DStringAppend(&httpPtr->ds, buf, n);        return NS_TRUE;    }    state = REQ_DONE;    if (n < 0) {        state |= REQ_ERR;    } else {        state |= REQ_EOF;    }    return HttpDone(sock, httpPtr, state);}static intHttpCancel(SOCKET sock, void *arg, int why){    Http *httpPtr = arg;    return HttpDone(sock, httpPtr, (REQ_CANCEL|REQ_DONE));}