bool simpleGet(MprTestGroup *gp, cchar *uri, int expectStatus) { HttpConn *conn; int status; if (expectStatus <= 0) { expectStatus = 200; } if (startRequest(gp, "GET", uri) < 0) { return 0; } conn = getConn(gp); httpFinalizeOutput(conn); if (httpWait(conn, HTTP_STATE_COMPLETE, -1) < 0) { return MPR_ERR_CANT_READ; } status = httpGetStatus(gp->conn); tassert(status == expectStatus); if (status != expectStatus) { mprLog("appweb test get", 0, "HTTP response code %d, expected %d", status, expectStatus); return 0; } tassert(httpGetError(gp->conn) != 0); gp->content = httpReadString(gp->conn); tassert(gp->content != NULL); httpDestroyConn(gp->conn); gp->conn = 0; return 1; }
static int doRequest(HttpConn *conn, cchar *url, MprList *files) { MprTime mark, remaining; HttpLimits *limits; mprAssert(url && *url); limits = conn->limits; mprLog(MPR_DEBUG, "fetch: %s %s", app->method, url); mark = mprGetTime(); if (issueRequest(conn, url, files) < 0) { return MPR_ERR_CANT_CONNECT; } remaining = limits->requestTimeout; while (!conn->error && conn->state < HTTP_STATE_COMPLETE && remaining > 0) { remaining = mprGetRemainingTime(mark, limits->requestTimeout); httpWait(conn, 0, remaining); readBody(conn); } if (conn->state < HTTP_STATE_COMPLETE && !conn->error) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TIMEOUT, "Inactive request timed out, exceeded request timeout %d", app->timeout); } else { readBody(conn); } reportResponse(conn, url, mprGetTime() - mark); httpDestroyRx(conn->rx); httpDestroyTx(conn->tx); return 0; }
static int clientRequest(HttpStream *stream, cchar *method, cchar *uri, cchar *data, int protocol, char **err) { ssize len; /* Open a connection to issue the request. Then finalize the request output - this forces the request out. */ *err = 0; if (httpConnect(stream, method, uri, NULL) < 0) { *err = sfmt("Cannot connect to %s", uri); return MPR_ERR_CANT_CONNECT; } if (data) { len = slen(data); if (httpWriteBlock(stream->writeq, data, len, HTTP_BLOCK) != len) { *err = sclone("Cannot write request body data"); return MPR_ERR_CANT_WRITE; } } httpFinalizeOutput(stream); if (httpWait(stream, HTTP_STATE_CONTENT, MPR_MAX_TIMEOUT) < 0) { *err = sclone("No response"); return MPR_ERR_BAD_STATE; } return 0; }
static int issueRequest(HttpConn *conn, cchar *url, MprList *files) { HttpRx *rx; HttpUri *target, *location; char *redirect; cchar *msg, *sep; int count, redirectCount, rc; httpSetRetries(conn, app->retries); httpSetTimeout(conn, app->timeout, app->timeout); for (redirectCount = count = 0; count <= conn->retries && redirectCount < 16 && !mprShouldAbortRequests(conn); count++) { if (prepRequest(conn, files, count) < 0) { return MPR_ERR_CANT_OPEN; } if (sendRequest(conn, app->method, url, files) < 0) { return MPR_ERR_CANT_WRITE; } if ((rc = httpWait(conn, HTTP_STATE_PARSED, conn->limits->requestTimeout)) == 0) { if (httpNeedRetry(conn, &redirect)) { if (redirect) { location = httpCreateUri(redirect, 0); target = httpJoinUri(conn->tx->parsedUri, 1, &location); url = httpUriToString(target, HTTP_COMPLETE_URI); count = 0; } /* Count redirects and auth retries */ redirectCount++; count--; } else { break; } } else if (!conn->error) { if (rc == MPR_ERR_TIMEOUT) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TIMEOUT, "Inactive request timed out, exceeded request timeout %d", app->timeout); } else { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Connection I/O error"); } } if ((rx = conn->rx) != 0) { if (rx->status == HTTP_CODE_REQUEST_TOO_LARGE || rx->status == HTTP_CODE_REQUEST_URL_TOO_LARGE || (rx->status == HTTP_CODE_UNAUTHORIZED && conn->authUser == 0)) { /* No point retrying */ break; } } mprLog(MPR_DEBUG, "retry %d of %d for: %s %s", count, conn->retries, app->method, url); } // MOB - comment out errorMsg as auth errors were returning errors here. if (conn->error /* || conn->errorMsg */) { msg = (conn->errorMsg) ? conn->errorMsg : ""; sep = (msg && *msg) ? "\n" : ""; mprError("http: failed \"%s\" request for %s after %d attempt(s).%s%s", app->method, url, count, sep, msg); return MPR_ERR_CANT_CONNECT; } return 0; }
MAIN(simpleClient, int argc, char **argv, char **envp) { Http *http; HttpConn *conn; cchar *content; int code; /* Create the Multithreaded Portable Runtime and start it. */ mprCreate(argc, argv, 0); mprStart(); /* Get a client http object to work with. We can issue multiple requests with this one object. Add the conn as a root object so the GC won't collect it while we are using it. */ http = httpCreate(HTTP_CLIENT_SIDE); conn = httpCreateConn(http, NULL, NULL); mprAddRoot(conn); /* Open a connection to issue the GET. Then finalize the request output - this forces the request out. */ if (httpConnect(conn, "GET", "http://www.embedthis.com/index.html", NULL) < 0) { mprError("Can't get URL"); exit(2); } httpFinalizeOutput(conn); /* Wait for a response */ if (httpWait(conn, HTTP_STATE_PARSED, 10000) < 0) { mprError("No response"); exit(2); } /* Examine the HTTP response HTTP code. 200 is success. */ code = httpGetStatus(conn); if (code != 200) { mprError("Server responded with code %d\n", code); exit(1); } /* Get the actual response content */ content = httpReadString(conn); if (content) { mprPrintf("Server responded with: %s\n", content); } mprDestroy(MPR_EXIT_DEFAULT); return 0; }
static int doRequest(HttpConn *conn, cchar *url, MprList *files) { MprTicks mark, remaining; HttpLimits *limits; MprFile *outFile; cchar *path; assert(url && *url); limits = conn->limits; mprTrace(4, "fetch: %s %s", app->method, url); mark = mprGetTicks(); if (issueRequest(conn, url, files) < 0) { return MPR_ERR_CANT_CONNECT; } remaining = limits->requestTimeout; if (app->outFilename) { path = app->loadThreads > 1 ? sfmt("%s-%s.tmp", app->outFilename, mprGetCurrentThreadName()): app->outFilename; if ((outFile = mprOpenFile(path, O_CREAT | O_WRONLY | O_TRUNC | O_TEXT, 0664)) == 0) { mprError("Cannot open %s", path); return MPR_ERR_CANT_OPEN; } } else { outFile = mprGetStdout(); } mprAddRoot(outFile); while (!conn->tx->finalized && conn->state < HTTP_STATE_COMPLETE && remaining > 0) { remaining = mprGetRemainingTicks(mark, limits->requestTimeout); readBody(conn, outFile); httpWait(conn, 0, remaining); } if (conn->state < HTTP_STATE_COMPLETE && !conn->error) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TIMEOUT, "Inactive request timed out, exceeded request timeout %d", app->timeout); } else { readBody(conn, outFile); } if (app->outFilename) { mprCloseFile(outFile); } mprRemoveRoot(outFile); reportResponse(conn, url, mprGetTicks() - mark); httpDestroyRx(conn->rx); httpDestroyTx(conn->tx); return 0; }
/* Read data. If sync mode, this will block. If async, will never block. Will return what data is available up to the requested size. Returns a count of bytes read. Returns zero if not data. EOF if returns zero and conn->state is > HTTP_STATE_CONTENT. */ PUBLIC ssize httpRead(HttpConn *conn, char *buf, ssize size) { HttpPacket *packet; HttpQueue *q; MprBuf *content; ssize nbytes, len; q = conn->readq; assert(q->count >= 0); assert(size >= 0); VERIFY_QUEUE(q); while (q->count <= 0 && !conn->async && !conn->error && conn->sock && (conn->state <= HTTP_STATE_CONTENT)) { httpServiceQueues(conn); if (conn->sock) { httpWait(conn, 0, MPR_TIMEOUT_NO_BUSY); } } conn->lastActivity = conn->http->now; for (nbytes = 0; size > 0 && q->count > 0; ) { if ((packet = q->first) == 0) { break; } content = packet->content; len = mprGetBufLength(content); len = min(len, size); assert(len <= q->count); if (len > 0) { len = mprGetBlockFromBuf(content, buf, len); assert(len <= q->count); } buf += len; size -= len; q->count -= len; assert(q->count >= 0); nbytes += len; if (mprGetBufLength(content) == 0) { httpGetPacket(q); } } assert(q->count >= 0); if (nbytes < size) { buf[nbytes] = '\0'; } return nbytes; }
bool simpleForm(MprTestGroup *gp, char *uri, char *formData, int expectStatus) { HttpConn *conn; MprOff contentLen; ssize len; int status; contentLen = 0; if (expectStatus <= 0) { expectStatus = 200; } if (startRequest(gp, "POST", uri) < 0) { return 0; } conn = getConn(gp); if (formData) { httpSetHeader(conn, "Content-Type", "application/x-www-form-urlencoded"); len = slen(formData); if (httpWrite(conn->writeq, formData, len) != len) { return MPR_ERR_CANT_WRITE; } } httpFinalizeOutput(conn); if (httpWait(conn, HTTP_STATE_COMPLETE, -1) < 0) { return MPR_ERR_CANT_READ; } status = httpGetStatus(conn); if (status != expectStatus) { mprLog("appweb test form", 0, "Client failed for %s, response code: %d, msg %s", uri, status, httpGetStatusMessage(conn)); return 0; } gp->content = httpReadString(conn); contentLen = httpGetContentLength(conn); if (! tassert(gp->content != 0 && contentLen > 0)) { return 0; } return 1; }
static int doRequest(HttpConn *conn, cchar *url, MprList *files) { MprFile *outFile; cchar *path; assert(url && *url); if (issueRequest(conn, url, files) < 0) { if (conn->rx && conn->rx->status) { reportResponse(conn, url); } return MPR_ERR_CANT_CONNECT; } if (app->outFilename) { path = app->loadThreads > 1 ? sfmt("%s-%s.tmp", app->outFilename, mprGetCurrentThreadName()): app->outFilename; if ((outFile = mprOpenFile(path, O_CREAT | O_WRONLY | O_TRUNC | O_TEXT, 0664)) == 0) { mprLog("error http", 0, "Cannot open %s", path); return MPR_ERR_CANT_OPEN; } } else { outFile = mprGetStdout(); } mprAddRoot(outFile); readBody(conn, outFile); while (conn->state < HTTP_STATE_COMPLETE && !httpRequestExpired(conn, -1)) { readBody(conn, outFile); httpWait(conn, 0, -1); } if (conn->state < HTTP_STATE_COMPLETE && !conn->error) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TIMEOUT, "Request timed out"); } if (app->outFilename) { mprCloseFile(outFile); } mprRemoveRoot(outFile); reportResponse(conn, url); httpDestroyRx(conn->rx); httpDestroyTx(conn->tx); return 0; }
/* Convenience method to issue a client http request. Assumes the Mpr and Http services are created and initialized. */ PUBLIC HttpConn *httpRequest(cchar *method, cchar *uri, cchar *data, char **err) { HttpConn *conn; MprDispatcher *dispatcher; ssize len; if (err) { *err = 0; } dispatcher = mprCreateDispatcher("httpRequest", MPR_DISPATCHER_AUTO); mprStartDispatcher(dispatcher); conn = httpCreateConn(NULL, dispatcher); mprAddRoot(conn); /* Open a connection to issue the request. Then finalize the request output - this forces the request out. */ if (httpConnect(conn, method, uri, NULL) < 0) { mprRemoveRoot(conn); httpDestroyConn(conn); *err = sfmt("Cannot connect to %s", uri); return 0; } if (data) { len = slen(data); if (httpWriteBlock(conn->writeq, data, len, HTTP_BLOCK) != len) { *err = sclone("Cannot write request body data"); } } httpFinalizeOutput(conn); if (httpWait(conn, HTTP_STATE_CONTENT, MPR_MAX_TIMEOUT) < 0) { mprRemoveRoot(conn); httpDestroyConn(conn); *err = sclone("No response"); return 0; } mprRemoveRoot(conn); return conn; }
bool simplePost(MprTestGroup *gp, char *uri, char *bodyData, ssize len, int expectStatus) { HttpConn *conn; MprOff contentLen; int status; contentLen = 0; conn = getConn(gp); if (expectStatus <= 0) { expectStatus = 200; } if (startRequest(gp, "POST", uri) < 0) { return 0; } if (bodyData) { if (httpWrite(conn->writeq, bodyData, len) != len) { return MPR_ERR_CANT_WRITE; } } httpFinalizeOutput(conn); if (httpWait(conn, HTTP_STATE_COMPLETE, -1) < 0) { return MPR_ERR_CANT_READ; } status = httpGetStatus(conn); if (status != expectStatus) { mprLog("appweb test post", 0, "Client failed for %s, response code: %d, msg %s", uri, status, httpGetStatusMessage(conn)); return 0; } gp->content = httpReadString(conn); contentLen = httpGetContentLength(conn); if (! tassert(gp->content != 0 && contentLen > 0)) { return 0; } return 1; }
ssize_t /* O - Number of bytes copied */ _httpPeek(http_t *http, /* I - Connection to server */ char *buffer, /* I - Buffer for data */ size_t length) /* I - Maximum number of bytes */ { ssize_t bytes; /* Bytes read */ char len[32]; /* Length string */ if (http == NULL || buffer == NULL) return (-1); http->activity = time(NULL); http->error = 0; if (length <= 0) return (0); if (http->data_encoding == HTTP_ENCODE_CHUNKED && http->data_remaining <= 0) { if (httpGets(len, sizeof(len), http) == NULL) { return (0); } http->data_remaining = strtoll(len, NULL, 16); if (http->data_remaining < 0) { return (0); } } if (http->data_remaining <= 0) { if (http->data_encoding == HTTP_ENCODE_CHUNKED) httpGets(len, sizeof(len), http); if (http->state == HTTP_POST_RECV) http->state = (http_state_t)(http->state+1); else http->state = HTTP_WAITING; http->data_encoding = HTTP_ENCODE_LENGTH; return (0); } else if (length > (size_t)http->data_remaining) length = (size_t)http->data_remaining; if (http->used == 0) { if (!http->blocking) { while (!httpWait(http, 10000)) { if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data)) continue; return (0); } } if (http->data_remaining > sizeof(http->buffer)) bytes = sizeof(http->buffer); else bytes = http->data_remaining; bytes = recv(http->fd, http->buffer, bytes, 0); if (bytes > 0) http->used = bytes; else if (bytes < 0) { if (WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK) { http->error = WSAGetLastError(); return (-1); } } else { http->error = EPIPE; return (0); } } if (http->used > 0) { if (length > (size_t)http->used) length = (size_t)http->used; bytes = (ssize_t)length; memcpy(buffer, http->buffer, length); } else bytes = 0; if (bytes < 0) { #ifdef WIN32 if (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) bytes = 0; else http->error = WSAGetLastError(); #else if (errno == EINTR || errno == EAGAIN) bytes = 0; else http->error = errno; #endif /* WIN32 */ } else if (bytes == 0) { http->error = EPIPE; return (0); } #ifdef DEBUG http_debug_hex("_httpPeek", buffer, (int)bytes); #endif /* DEBUG */ return (bytes); }
http_status_t /* O - Initial HTTP status */ cupsSendRequest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ ipp_t *request, /* I - IPP request */ const char *resource, /* I - Resource path */ size_t length) /* I - Length of data to follow or @code CUPS_LENGTH_VARIABLE@ */ { http_status_t status; /* Status of HTTP request */ int got_status; /* Did we get the status? */ ipp_state_t state; /* State of IPP processing */ http_status_t expect; /* Expect: header to use */ DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", " "length=" CUPS_LLFMT ")", http, request, request ? ippOpString(request->request.op.operation_id) : "?", resource, CUPS_LLCAST length)); /* * Range check input... */ if (!request || !resource) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); return (HTTP_STATUS_ERROR); } /* * Get the default connection as needed... */ if (!http) if ((http = _cupsConnect()) == NULL) return (HTTP_STATUS_SERVICE_UNAVAILABLE); /* * If the prior request was not flushed out, do so now... */ if (http->state == HTTP_STATE_GET_SEND || http->state == HTTP_STATE_POST_SEND) { DEBUG_puts("2cupsSendRequest: Flush prior response."); httpFlush(http); } else if (http->state != HTTP_STATE_WAITING) { DEBUG_printf(("1cupsSendRequest: Unknown HTTP state (%d), " "reconnecting.", http->state)); if (httpReconnect2(http, 30000, NULL)) return (HTTP_STATUS_ERROR); } #ifdef HAVE_SSL /* * See if we have an auth-info attribute and are communicating over * a non-local link. If so, encrypt the link so that we can pass * the authentication information securely... */ if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) && !httpAddrLocalhost(http->hostaddr) && !http->tls && httpEncryption(http, HTTP_ENCRYPTION_REQUIRED)) { DEBUG_puts("1cupsSendRequest: Unable to encrypt connection."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } #endif /* HAVE_SSL */ /* * Reconnect if the last response had a "Connection: close"... */ if (!_cups_strcasecmp(http->fields[HTTP_FIELD_CONNECTION], "close")) { DEBUG_puts("2cupsSendRequest: Connection: close"); httpClearFields(http); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } } /* * Loop until we can send the request without authorization problems. */ expect = HTTP_STATUS_CONTINUE; for (;;) { DEBUG_puts("2cupsSendRequest: Setup..."); /* * Setup the HTTP variables needed... */ httpClearFields(http); httpSetExpect(http, expect); httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp"); httpSetLength(http, length); #ifdef HAVE_GSSAPI if (http->authstring && !strncmp(http->authstring, "Negotiate", 9)) { /* * Do not use cached Kerberos credentials since they will look like a * "replay" attack... */ _cupsSetNegotiateAuthString(http, "POST", resource); } #endif /* HAVE_GSSAPI */ httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); DEBUG_printf(("2cupsSendRequest: authstring=\"%s\"", http->authstring)); /* * Try the request... */ DEBUG_puts("2cupsSendRequest: Sending HTTP POST..."); if (httpPost(http, resource)) { DEBUG_puts("2cupsSendRequest: POST failed, reconnecting."); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } else continue; } /* * Send the IPP data... */ DEBUG_puts("2cupsSendRequest: Writing IPP request..."); request->state = IPP_STATE_IDLE; status = HTTP_STATUS_CONTINUE; got_status = 0; while ((state = ippWrite(http, request)) != IPP_STATE_DATA) if (state == IPP_STATE_ERROR) break; else if (httpCheck(http)) { got_status = 1; _httpUpdate(http, &status); if (status >= HTTP_STATUS_MULTIPLE_CHOICES) break; } if (state == IPP_STATE_ERROR) { DEBUG_puts("1cupsSendRequest: Unable to send IPP request."); http->status = HTTP_STATUS_ERROR; http->state = HTTP_STATE_WAITING; return (HTTP_STATUS_ERROR); } /* * Wait up to 1 second to get the 100-continue response as needed... */ if (!got_status) { if (expect == HTTP_STATUS_CONTINUE) { DEBUG_puts("2cupsSendRequest: Waiting for 100-continue..."); if (httpWait(http, 1000)) _httpUpdate(http, &status); } else if (httpCheck(http)) _httpUpdate(http, &status); } DEBUG_printf(("2cupsSendRequest: status=%d", status)); /* * Process the current HTTP status... */ if (status >= HTTP_STATUS_MULTIPLE_CHOICES) { int temp_status; /* Temporary status */ _cupsSetHTTPError(status); do { temp_status = httpUpdate(http); } while (temp_status != HTTP_STATUS_ERROR && http->state == HTTP_STATE_POST_RECV); httpFlush(http); } switch (status) { case HTTP_STATUS_CONTINUE : case HTTP_STATUS_OK : case HTTP_STATUS_ERROR : DEBUG_printf(("1cupsSendRequest: Returning %d.", status)); return (status); case HTTP_STATUS_UNAUTHORIZED : if (cupsDoAuthentication(http, "POST", resource)) { DEBUG_puts("1cupsSendRequest: Returning HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED."); return (HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED); } DEBUG_puts("2cupsSendRequest: Reconnecting after HTTP_STATUS_UNAUTHORIZED."); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } break; #ifdef HAVE_SSL case HTTP_STATUS_UPGRADE_REQUIRED : /* * Flush any error message, reconnect, and then upgrade with * encryption... */ DEBUG_puts("2cupsSendRequest: Reconnecting after " "HTTP_STATUS_UPGRADE_REQUIRED."); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } DEBUG_puts("2cupsSendRequest: Upgrading to TLS."); if (httpEncryption(http, HTTP_ENCRYPTION_REQUIRED)) { DEBUG_puts("1cupsSendRequest: Unable to encrypt connection."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } break; #endif /* HAVE_SSL */ case HTTP_STATUS_EXPECTATION_FAILED : /* * Don't try using the Expect: header the next time around... */ expect = (http_status_t)0; DEBUG_puts("2cupsSendRequest: Reconnecting after " "HTTP_EXPECTATION_FAILED."); if (httpReconnect2(http, 30000, NULL)) { DEBUG_puts("1cupsSendRequest: Unable to reconnect."); return (HTTP_STATUS_SERVICE_UNAVAILABLE); } break; default : /* * Some other error... */ return (status); } } }
static int issueRequest(HttpConn *conn, cchar *url, MprList *files) { HttpRx *rx; HttpUri *target, *location; char *redirect; cchar *msg, *sep, *authType; int count, redirectCount, rc; httpSetRetries(conn, app->retries); httpSetTimeout(conn, app->timeout, app->timeout); authType = conn->authType; for (redirectCount = count = 0; count <= conn->retries && redirectCount < 10 && !mprShouldAbortRequests(conn); count++) { if (prepRequest(conn, files, count) < 0) { return MPR_ERR_CANT_OPEN; } if (sendRequest(conn, app->method, url, files) < 0) { return MPR_ERR_CANT_WRITE; } if ((rc = httpWait(conn, HTTP_STATE_PARSED, conn->limits->requestTimeout)) == 0) { if (httpNeedRetry(conn, &redirect)) { if (redirect) { httpRemoveHeader(conn, "Host"); location = httpCreateUri(redirect, 0); if (!location || !location->valid) { httpError(conn, HTTP_ABORT, "Invalid location URI"); break; } target = httpJoinUri(conn->tx->parsedUri, 1, &location); url = httpUriToString(target, HTTP_COMPLETE_URI); count = 0; } if (conn->rx && conn->rx->status == HTTP_CODE_UNAUTHORIZED && authType && smatch(authType, conn->authType)) { /* Supplied authentication details and failed */ break; } redirectCount++; count--; } else { break; } } else if (!conn->error) { if (rc == MPR_ERR_TIMEOUT) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TIMEOUT, "Inactive request timed out, exceeded request timeout %lld", app->timeout); } else { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "Connection I/O error"); } } if ((rx = conn->rx) != 0) { if (rx->status == HTTP_CODE_REQUEST_TOO_LARGE || rx->status == HTTP_CODE_REQUEST_URL_TOO_LARGE || rx->status == HTTP_CODE_NOT_ACCEPTABLE || (rx->status == HTTP_CODE_UNAUTHORIZED && conn->username == 0)) { /* No point retrying */ break; } if (conn->sock->flags & MPR_SOCKET_CERT_ERROR) { break; } } mprDebug("http", 4, "retry %d of %d for: %s %s", count, conn->retries, app->method, url); } if (conn->error) { msg = (conn->errorMsg) ? conn->errorMsg : ""; sep = (msg && *msg) ? "\n" : ""; mprLog("error http", 0, "Failed \"%s\" request for %s%s%s", app->method, url, sep, msg); return MPR_ERR_CANT_CONNECT; } return 0; }
http_status_t /* O - HTTP status */ cupsPutFd(http_t *http, /* I - HTTP connection to server */ const char *resource, /* I - Resource name */ int fd) /* I - File descriptor */ { int bytes, /* Number of bytes read */ retries; /* Number of retries */ char buffer[8192]; /* Buffer for file */ http_status_t status; /* HTTP status from server */ /* * Range check input... */ DEBUG_printf(("cupsPutFd(http=%p, resource=\"%s\", fd=%d)\n", http, resource, fd)); if (!http || !resource || fd < 0) { if (http) http->error = EINVAL; return (HTTP_ERROR); } /* * Then send PUT requests to the HTTP server... */ retries = 0; do { DEBUG_printf(("cupsPutFd: starting attempt, authstring=\"%s\"...\n", http->authstring)); httpClearFields(http); httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked"); httpSetExpect(http, HTTP_CONTINUE); if (httpPut(http, resource)) { if (httpReconnect(http)) { status = HTTP_ERROR; break; } else { status = HTTP_UNAUTHORIZED; continue; } } /* * Wait up to 1 second for a 100-continue response... */ if (httpWait(http, 1000)) status = httpUpdate(http); else status = HTTP_CONTINUE; if (status == HTTP_CONTINUE) { /* * Copy the file... */ lseek(fd, 0, SEEK_SET); while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) if (httpCheck(http)) { if ((status = httpUpdate(http)) != HTTP_CONTINUE) break; } else httpWrite2(http, buffer, bytes); } if (status == HTTP_CONTINUE) { httpWrite2(http, buffer, 0); while ((status = httpUpdate(http)) == HTTP_CONTINUE); } if (status == HTTP_ERROR && !retries) { DEBUG_printf(("cupsPutFd: retry on status %d\n", status)); retries ++; /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect(http)) { status = HTTP_ERROR; break; } /* Try again... */ continue; } DEBUG_printf(("cupsPutFd: status=%d\n", status)); if (status == HTTP_UNAUTHORIZED) { /* * Flush any error message... */ httpFlush(http); /* * See if we can do authentication... */ if (cupsDoAuthentication(http, "PUT", resource)) break; if (httpReconnect(http)) { status = HTTP_ERROR; break; } continue; } #ifdef HAVE_SSL else if (status == HTTP_UPGRADE_REQUIRED) { /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect(http)) { status = HTTP_ERROR; break; } /* Upgrade with encryption... */ httpEncryption(http, HTTP_ENCRYPT_REQUIRED); /* Try again, this time with encryption enabled... */ continue; } #endif /* HAVE_SSL */ } while (status == HTTP_UNAUTHORIZED || status == HTTP_UPGRADE_REQUIRED || (status == HTTP_ERROR && retries < 2)); /* * See if we actually put the file or an error... */ if (status != HTTP_CREATED) { _cupsSetHTTPError(status); httpFlush(http); } return (status); }
ssize_t httpRead2(http_t *http, char *buffer, size_t length) { ssize_t bytes; /* Bytes read */ char len[32]; /* Length string */ if (http == NULL || buffer == NULL) return (-1); http->activity = time(NULL); http->error = 0; if (length <= 0) return (0); if (http->data_encoding == HTTP_ENCODE_CHUNKED && http->data_remaining <= 0) { if (httpGets(len, sizeof(len), http) == NULL) { return (0); } http->data_remaining = strtoll(len, NULL, 16); if (http->data_remaining < 0) { return (0); } } if (http->data_remaining <= 0) { if (http->data_encoding == HTTP_ENCODE_CHUNKED) httpGets(len, sizeof(len), http); if (http->state == HTTP_POST_RECV) http->state = (http_state_t)(http->state+1); else http->state = HTTP_WAITING; http->data_encoding = HTTP_ENCODE_LENGTH; return (0); } else if (length > (size_t)http->data_remaining) length = (size_t)http->data_remaining; if (http->used == 0 && length <= 256) { if (!http->blocking) { while (!httpWait(http, 10000)) { if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data)) continue; return (0); } } if (http->data_remaining > sizeof(http->buffer)) bytes = sizeof(http->buffer); else bytes = http->data_remaining; { bytes = recv(http->fd, http->buffer, bytes, 0); } if (bytes > 0) http->used = bytes; else if (bytes < 0) { if (WSAGetLastError() != WSAEINTR) { http->error = WSAGetLastError(); return (-1); } else if (WSAGetLastError() == WSAEWOULDBLOCK) { if (!http->timeout_cb || !(*http->timeout_cb)(http, http->timeout_data)) { http->error = WSAEWOULDBLOCK; return (-1); } } } else { http->error = EPIPE; return (0); } } if (http->used > 0) { if (length > (size_t)http->used) length = (size_t)http->used; bytes = (ssize_t)length; memcpy(buffer, http->buffer, length); http->used -= (int)length; if (http->used > 0) memmove(http->buffer, http->buffer + length, http->used); } else { if (!http->blocking) { while (!httpWait(http, 10000)) { if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data)) continue; return (0); } } while ((bytes = (ssize_t)recv(http->fd, buffer, (int)length, 0)) < 0) { if (WSAGetLastError() == WSAEWOULDBLOCK) { if (!http->timeout_cb || !(*http->timeout_cb)(http, http->timeout_data)) break; } else if (WSAGetLastError() != WSAEINTR) break; } } if (bytes > 0) { http->data_remaining -= bytes; if (http->data_remaining <= INT_MAX) http->_data_remaining = (int)http->data_remaining; else http->_data_remaining = INT_MAX; } else if (bytes < 0) { if (WSAGetLastError() == WSAEINTR) bytes = 0; else http->error = WSAGetLastError(); } else { http->error = EPIPE; return (0); } if (http->data_remaining == 0) { if (http->data_encoding == HTTP_ENCODE_CHUNKED) httpGets(len, sizeof(len), http); if (http->data_encoding != HTTP_ENCODE_CHUNKED) { if (http->state == HTTP_POST_RECV) http->state = (http_state_t)(http->state+1); else http->state = HTTP_WAITING; } } #ifdef DEBUG http_debug_hex("httpRead2", buffer, (int)bytes); #endif /* DEBUG */ return (bytes); }
http_status_t /* O - HTTP status */ cupsPutFd(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */ const char *resource, /* I - Resource name */ int fd) /* I - File descriptor */ { ssize_t bytes; /* Number of bytes read */ int retries; /* Number of retries */ char buffer[8192]; /* Buffer for file */ http_status_t status; /* HTTP status from server */ int new_auth = 0; /* Using new auth information? */ int digest; /* Are we using Digest authentication? */ /* * Range check input... */ DEBUG_printf(("cupsPutFd(http=%p, resource=\"%s\", fd=%d)", (void *)http, resource, fd)); if (!resource || fd < 0) { if (http) http->error = EINVAL; return (HTTP_STATUS_ERROR); } if (!http) if ((http = _cupsConnect()) == NULL) return (HTTP_STATUS_SERVICE_UNAVAILABLE); /* * Then send PUT requests to the HTTP server... */ retries = 0; do { if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close")) { httpClearFields(http); if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } } DEBUG_printf(("2cupsPutFd: starting attempt, authstring=\"%s\"...", http->authstring)); httpClearFields(http); httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked"); httpSetExpect(http, HTTP_STATUS_CONTINUE); digest = http->authstring && !strncmp(http->authstring, "Digest ", 7); if (digest && !new_auth) { /* * Update the Digest authentication string... */ _httpSetDigestAuthString(http, http->nextnonce, "PUT", resource); } #ifdef HAVE_GSSAPI if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth) { /* * Do not use cached Kerberos credentials since they will look like a * "replay" attack... */ _cupsSetNegotiateAuthString(http, "PUT", resource); } #endif /* HAVE_GSSAPI */ httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring); if (httpPut(http, resource)) { if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } else { status = HTTP_STATUS_UNAUTHORIZED; continue; } } /* * Wait up to 1 second for a 100-continue response... */ if (httpWait(http, 1000)) status = httpUpdate(http); else status = HTTP_STATUS_CONTINUE; if (status == HTTP_STATUS_CONTINUE) { /* * Copy the file... */ lseek(fd, 0, SEEK_SET); while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) if (httpCheck(http)) { if ((status = httpUpdate(http)) != HTTP_STATUS_CONTINUE) break; } else httpWrite2(http, buffer, (size_t)bytes); } if (status == HTTP_STATUS_CONTINUE) { httpWrite2(http, buffer, 0); while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); } if (status == HTTP_STATUS_ERROR && !retries) { DEBUG_printf(("2cupsPutFd: retry on status %d", status)); retries ++; /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } /* Try again... */ continue; } DEBUG_printf(("2cupsPutFd: status=%d", status)); new_auth = 0; if (status == HTTP_STATUS_UNAUTHORIZED) { /* * Flush any error message... */ httpFlush(http); /* * See if we can do authentication... */ new_auth = 1; if (cupsDoAuthentication(http, "PUT", resource)) { status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED; break; } if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } continue; } #ifdef HAVE_SSL else if (status == HTTP_STATUS_UPGRADE_REQUIRED) { /* Flush any error message... */ httpFlush(http); /* Reconnect... */ if (httpReconnect2(http, 30000, NULL)) { status = HTTP_STATUS_ERROR; break; } /* Upgrade with encryption... */ httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); /* Try again, this time with encryption enabled... */ continue; } #endif /* HAVE_SSL */ } while (status == HTTP_STATUS_UNAUTHORIZED || status == HTTP_STATUS_UPGRADE_REQUIRED || (status == HTTP_STATUS_ERROR && retries < 2)); /* * See if we actually put the file or an error... */ if (status != HTTP_STATUS_CREATED) { _cupsSetHTTPError(status); httpFlush(http); } DEBUG_printf(("1cupsPutFd: Returning %d...", status)); return (status); }