#include "setup.h"
#include "urldata.h"
#include "sendf.h"
#include "select.h"
#include "progress.h"
#include "speedcheck.h"
#include "pingpong.h"
#include "multiif.h"
#include "non-ascii.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include "curl_memory.h"
#include "memdebug.h"
#ifdef USE_PINGPONG
long Curl_pp_state_timeout(struct pingpong *pp)
{
struct connectdata *conn = pp->conn;
struct SessionHandle *data=conn->data;
long timeout_ms;
long timeout2_ms;
long response_time= (data->set.server_response_timeout)?
data->set.server_response_timeout: pp->response_time;
timeout_ms = response_time -
Curl_tvdiff(Curl_tvnow(), pp->response);
if(data->set.timeout) {
timeout2_ms = data->set.timeout -
Curl_tvdiff(Curl_tvnow(), conn->now);
timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
}
return timeout_ms;
}
CURLcode Curl_pp_multi_statemach(struct pingpong *pp)
{
struct connectdata *conn = pp->conn;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc;
struct SessionHandle *data=conn->data;
CURLcode result = CURLE_OK;
long timeout_ms = Curl_pp_state_timeout(pp);
if(timeout_ms <= 0) {
failf(data, "server response timeout");
return CURLE_OPERATION_TIMEDOUT;
}
rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock,
pp->sendleft?sock:CURL_SOCKET_BAD,
0);
if(rc == -1) {
failf(data, "select/poll error");
return CURLE_OUT_OF_MEMORY;
}
else if(rc != 0)
result = pp->statemach_act(conn);
return result;
}
CURLcode Curl_pp_easy_statemach(struct pingpong *pp)
{
struct connectdata *conn = pp->conn;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc;
long interval_ms;
long timeout_ms = Curl_pp_state_timeout(pp);
struct SessionHandle *data=conn->data;
CURLcode result;
if(timeout_ms <=0 ) {
failf(data, "server response timeout");
return CURLE_OPERATION_TIMEDOUT;
}
interval_ms = 1000;
if(timeout_ms < interval_ms)
interval_ms = timeout_ms;
rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock,
pp->sendleft?sock:CURL_SOCKET_BAD,
interval_ms);
if(Curl_pgrsUpdate(conn))
result = CURLE_ABORTED_BY_CALLBACK;
else
result = Curl_speedcheck(data, Curl_tvnow());
if(result)
;
else if(rc == -1) {
failf(data, "select/poll error");
result = CURLE_OUT_OF_MEMORY;
}
else if(rc)
result = pp->statemach_act(conn);
return result;
}
void Curl_pp_init(struct pingpong *pp)
{
struct connectdata *conn = pp->conn;
pp->nread_resp = 0;
pp->linestart_resp = conn->data->state.buffer;
pp->pending_resp = TRUE;
pp->response = Curl_tvnow();
}
CURLcode Curl_pp_vsendf(struct pingpong *pp,
const char *fmt,
va_list args)
{
ssize_t bytes_written;
size_t write_len;
char *fmt_crlf;
char *s;
CURLcode error;
struct connectdata *conn = pp->conn;
struct SessionHandle *data = conn->data;
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
enum protection_level data_sec = conn->data_prot;
#endif
DEBUGASSERT(pp->sendleft == 0);
DEBUGASSERT(pp->sendsize == 0);
DEBUGASSERT(pp->sendthis == NULL);
fmt_crlf = aprintf("%s\r\n", fmt);
if(!fmt_crlf)
return CURLE_OUT_OF_MEMORY;
s = vaprintf(fmt_crlf, args);
free(fmt_crlf);
if(!s)
return CURLE_OUT_OF_MEMORY;
bytes_written = 0;
write_len = strlen(s);
Curl_pp_init(pp);
error = Curl_convert_to_network(data, s, write_len);
if(error) {
free(s);
return error;
}
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
conn->data_prot = PROT_CMD;
#endif
error = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
&bytes_written);
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
conn->data_prot = data_sec;
#endif
if(error) {
free(s);
return error;
}
if(conn->data->set.verbose)
Curl_debug(conn->data, CURLINFO_HEADER_OUT,
s, (size_t)bytes_written, conn);
if(bytes_written != (ssize_t)write_len) {
pp->sendthis = s;
pp->sendsize = write_len;
pp->sendleft = write_len - bytes_written;
}
else {
free(s);
pp->sendthis = NULL;
pp->sendleft = pp->sendsize = 0;
pp->response = Curl_tvnow();
}
return CURLE_OK;
}
CURLcode Curl_pp_sendf(struct pingpong *pp,
const char *fmt, ...)
{
CURLcode res;
va_list ap;
va_start(ap, fmt);
res = Curl_pp_vsendf(pp, fmt, ap);
va_end(ap);
return res;
}
CURLcode Curl_pp_readresp(curl_socket_t sockfd,
struct pingpong *pp,
int *code,
size_t *size)
{
ssize_t perline;
bool keepon=TRUE;
ssize_t gotbytes;
char *ptr;
struct connectdata *conn = pp->conn;
struct SessionHandle *data = conn->data;
char * const buf = data->state.buffer;
CURLcode result = CURLE_OK;
*code = 0;
*size = 0;
ptr=buf + pp->nread_resp;
perline = (ssize_t)(ptr-pp->linestart_resp);
keepon=TRUE;
while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
if(pp->cache) {
DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
memcpy(ptr, pp->cache, pp->cache_size);
gotbytes = (ssize_t)pp->cache_size;
free(pp->cache);
pp->cache = NULL;
pp->cache_size = 0;
}
else {
int res;
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
enum protection_level prot = conn->data_prot;
conn->data_prot = PROT_CLEAR;
#endif
DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
&gotbytes);
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST);
conn->data_prot = prot;
#endif
if(res == CURLE_AGAIN)
return CURLE_OK;
if((res == CURLE_OK) && (gotbytes > 0))
res = Curl_convert_from_network(data, ptr, gotbytes);
if(CURLE_OK != res) {
result = (CURLcode)res;
keepon = FALSE;
}
}
if(!keepon)
;
else if(gotbytes <= 0) {
keepon = FALSE;
result = CURLE_RECV_ERROR;
failf(data, "response reading failed");
}
else {
ssize_t i;
ssize_t clipamount = 0;
bool restart = FALSE;
data->req.headerbytecount += (long)gotbytes;
pp->nread_resp += gotbytes;
for(i = 0; i < gotbytes; ptr++, i++) {
perline++;
if(*ptr=='\n') {
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
if(!conn->sec_complete)
#endif
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
pp->linestart_resp, (size_t)perline, conn);
result = Curl_client_write(conn, CLIENTWRITE_HEADER,
pp->linestart_resp, perline);
if(result)
return result;
if(pp->endofresp(pp, code)) {
char *meow;
int n;
for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
buf[n] = *meow;
*meow=0;
keepon=FALSE;
pp->linestart_resp = ptr+1;
i++;
*size = pp->nread_resp;
pp->nread_resp = 0;
break;
}
perline=0;
pp->linestart_resp = ptr+1;
}
}
if(!keepon && (i != gotbytes)) {
clipamount = gotbytes - i;
restart = TRUE;
}
else if(keepon) {
if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
infof(data, "Excessive server response line length received, "
"%zd bytes. Stripping\n", gotbytes);
restart = TRUE;
clipamount = 40;
}
else if(pp->nread_resp > BUFSIZE/2) {
clipamount = perline;
restart = TRUE;
}
}
else if(i == gotbytes)
restart = TRUE;
if(clipamount) {
pp->cache_size = clipamount;
pp->cache = malloc(pp->cache_size);
if(pp->cache)
memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
else
return CURLE_OUT_OF_MEMORY;
}
if(restart) {
pp->nread_resp = 0;
ptr = pp->linestart_resp = buf;
perline = 0;
}
}
}
pp->pending_resp = FALSE;
return result;
}
int Curl_pp_getsock(struct pingpong *pp,
curl_socket_t *socks,
int numsocks)
{
struct connectdata *conn = pp->conn;
if(!numsocks)
return GETSOCK_BLANK;
socks[0] = conn->sock[FIRSTSOCKET];
if(pp->sendleft) {
return GETSOCK_WRITESOCK(0);
}
return GETSOCK_READSOCK(0);
}
CURLcode Curl_pp_flushsend(struct pingpong *pp)
{
struct connectdata *conn = pp->conn;
ssize_t written;
CURLcode result = CURLE_OK;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
pp->sendleft, pp->sendleft, &written);
if(result)
return result;
if(written != (ssize_t)pp->sendleft) {
pp->sendleft -= written;
}
else {
free(pp->sendthis);
pp->sendthis=NULL;
pp->sendleft = pp->sendsize = 0;
pp->response = Curl_tvnow();
}
return CURLE_OK;
}
CURLcode Curl_pp_disconnect(struct pingpong *pp)
{
if(pp->cache) {
free(pp->cache);
pp->cache = NULL;
}
return CURLE_OK;
}
#endif