rem: HttpOpen(char *url, Ns_Set *hdrs)
add: HttpOpen(char *method, char *url, Ns_Set *hdrs, char *body)
context: static Ns_SockProc HttpRecv;static Ns_SockProc HttpCancel;static int HttpDone(SOCKET sock, Http *httpPtr, int state);static Http *HttpOpen(char *url, Ns_Set *hdrs);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;    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 != 3 && objc != 4) {	    Tcl_WrongNumArgs(interp, 2, objv, "url ?headers ...?");	    return TCL_ERROR;	}	if (objc == 3) {	    hdrs = NULL;	} else if (Ns_TclGetSet2(interp, Tcl_GetString(objv[3]), &hdrs) != TCL_OK) {	    return TCL_ERROR;	}	httpPtr = HttpOpen(Tcl_GetString(objv[2]), hdrs);	if (httpPtr == NULL) {	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "could not connect to : ", 		    Tcl_GetString(objv[2]), 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 *url, Ns_Set *hdrs){    Http *httpPtr = NULL;    SOCKET sock;    char *host, *file, *port;    int i;    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, "GET ", 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);	    }	}	Tcl_DStringAppend(&httpPtr->ds, "\r\n", 2);	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);}