#include #include #include #include #include #include "trlitool.h" #include #include #include #include #include #include #include #include using namespace std; static void json_ssl_init() { static bool ssl_init = false; if (!ssl_init){ ssl_init = true; SSL_load_error_strings(); #if OPENSSL_VERSION_MAJOR < 3 ERR_load_BIO_strings(); #endif OpenSSL_add_all_algorithms(); } } static DEBUG_KEY DEBUG_JSON ("json","JSON protocol"); static DEBUG_KEY D_SSL ("ssl","SSL connections"); void REQUEST_JSON::append (const char car) { if (valid){ if (offset + 1 < REQ_BUF_SIZE){ buf[offset++] = car; }else{ valid = false; } } } void REQUEST_JSON::append_comma() { if (!first) append (','); first = false; } void REQUEST_JSON::append (const char *s) { if (valid){ if (offset==0) append ('{'); int nbquote = 0; const char *pt = s; while (*pt != '\0'){ if (*pt == '"' || *pt == '\\') nbquote++; pt++; } int len = (pt-s)+nbquote + 2; if (offset + len < REQ_BUF_SIZE){ char *dst = buf+offset; *dst++ = '"'; pt = s; while (*pt != '\0'){ char car = *pt++; if (car == '"' || car == '\\'){ *dst++ = '\\'; } *dst++ = car; } *dst++ = '"'; offset += len; }else{ valid = false; } } } namespace { struct BIOFreeAll { void operator()(BIO* p) { BIO_free_all(p); } }; } static string Base64Encode(unsigned char *sig, size_t sig_len) { std::unique_ptr b64(BIO_new(BIO_f_base64())); BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL); BIO* sink = BIO_new(BIO_s_mem()); BIO_push(b64.get(), sink); BIO_write(b64.get(), sig,sig_len); BIO_flush(b64.get()); const char* encoded; const long size = BIO_get_mem_data(sink, &encoded); string result; for (long off=0; off size) len = size - off; result += string(encoded+off,len) + "\n"; } return result; } // Assumes no newlines or extra characters in encoded string static vector Base64Decode(const char* encoded) { std::unique_ptr b64(BIO_new(BIO_f_base64())); BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL); BIO* source = BIO_new_mem_buf(encoded, -1); // read-only source BIO_push(b64.get(), source); const int maxlen = strlen(encoded) / 4 * 3 + 1; std::vector decoded(maxlen); const int len = BIO_read(b64.get(), decoded.data(), maxlen); decoded.resize(len); return decoded; } void REQUEST_JSON::append_bob (const void *s, size_t size) { if (valid){ string enc = Base64Encode((unsigned char*)s,size); size_t size = enc.size()+2; if (offset + size < REQ_BUF_SIZE){ buf[offset] = '"'; offset++; memcpy (buf+offset,enc.c_str(),enc.size()); offset += enc.size(); buf[offset] = '"'; offset++; }else{ valid = false; } } } void REQUEST_JSON::append_name (const char *name) { append_comma(); if (name != NULL){ append (name); append (':'); } } /* Add one value */ void REQUEST_JSON::append_val (PARAM_STRING val) { append (val.ptr); } /* Add one value part of many */ void REQUEST_JSON::append_vals (PARAM_STRING val){ append (val.ptr); } void REQUEST_JSON::reset() { offset = 0; offset_read = 0; buf[0] = '\0'; valid = true; first = true; } REQUEST_JSON::REQUEST_JSON() { reset(); } void REQUEST_JSON::add (const char *name, int val) { char tmp[100]; snprintf (tmp,sizeof(tmp),"%d",val); append_name (name); append_val (tmp); } void REQUEST_JSON::add (const char *name, long val) { char tmp[100]; snprintf (tmp,sizeof(tmp),"%ld",val); append_name (name); append_val (tmp); } void REQUEST_JSON::add (const char *name, unsigned val) { char tmp[100]; snprintf (tmp,sizeof(tmp),"%u",val); append_name (name); append_val (tmp); } void REQUEST_JSON::add (const char *name, bool val) { append_name (name); append_val (val ? "1" : "0"); } void REQUEST_JSON::add (const char *name, char *val) { append_name (name); append_val (val); } void REQUEST_JSON::add (const char *name, const char *val) { append_name (name); append_val (val); } void REQUEST_JSON::add (const char *name, PARAM_STRING val) { append_name (name); append_val (val.ptr); } void REQUEST_JSON::add (const char *name, const string &val) { append_name (name); append_val (val.c_str()); } void REQUEST_JSON::add (const char *name, const BOB_TYPE &val) { append_name (name); append_bob (val.getbuffer(),val.getsize()); } void REQUEST_JSON::add (const char *name, const vector> &vals) { append_name (name); append ('['); first = true; for (auto &x:vals){ append_comma(); append ('['); first = true; for (auto &xx:x){ append_comma(); append_vals(xx.c_str()); } append (']'); } append (']'); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); append ('['); first = true; for (auto &x:vals){ append_comma(); append_vals(x.c_str()); } append (']'); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); append ('['); first = true; for (auto x:vals){ append_comma(); append_vals(x); } append (']'); } void REQUEST_JSON::add (const char *name, const PARAM_VECTOR_STRING vals) { append_name (name); append ('['); first = true; for (auto x:vals){ append_comma(); append_vals(x); } append (']'); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); append ('['); first = true; for (auto x:vals){ append_comma(); append_vals(x ? "1" : "0"); } append (']'); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); append ('['); first = true; for (auto x:vals){ append_comma(); char tmp[100]; snprintf (tmp,sizeof(tmp)-1,"%d",x); append_vals(tmp); } append (']'); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); append ('['); first = true; for (auto x:vals){ append_comma(); char tmp[100]; snprintf (tmp,sizeof(tmp)-1,"%u",x); append_vals(tmp); } append (']'); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); append ('['); first = true; for (auto &x:vals){ append_comma(); append_bob(x.getbuffer(),x.getsize()); } append (']'); } void REQUEST_JSON::add_timestamp() { char tmp[100]; long long now = fdpass_getnow(); snprintf (tmp,sizeof(tmp)-1,"%Ld",now); add ("_time_",tmp); } bool REQUEST_JSON::is_valid() const { return valid; } // Put the last tag to end the REQUEST void REQUEST_JSON::complete() { append ('}'); buf[offset] = '\0'; } void REQUEST_JSON::dump() { FILE *fout = fopen ("/tmp/json.log","a"); if (fout != NULL){ fprintf (fout,"buf : %s\n",buf); fprintf (fout,"read: %s\n",buf+offset_read); fclose (fout); } } int REQUEST_JSON::send (BIO *bio) { int ret = -1; if (valid){ ret = BIO_write (bio,buf,offset); }else{ // We send an empty answer anyway REQUEST_JSON req; req.complete(); BIO_write (bio,req.buf,req.offset); } return ret; } int REQUEST_JSON::send (_F_TCPSERVER_V1 *c) { complete(); if (valid){ printf ("%s\n",buf); }else{ // We send an empty answer anyway REQUEST_JSON req; req.complete(); printf ("%s\n",req.buf); } return 0; } // Add a part of a request (received on the socket) // Return -1 if something is wrong // Return 0 if the request is still incomplete // Return 1 if the request is complete, ready to process int REQUEST_JSON::addpart (const char *line, int len) { int ret = -1; if (!valid){ ret = -1; }else{ if (len + offset > REQ_BUF_SIZE){ valid = false; }else{ memcpy(buf+offset,line,len); offset += len; buf[offset] = '\0'; ret = 0; const char *pt = buf; if (*pt != '{'){ ret = -1; }else{ enum LASTOPEN {LAST_BRACE, LAST_BRACK}; vector lasts; bool err = false; while (!err && *pt != '\0'){ const char car = *pt++; if (car == '{'){ lasts.push_back(LAST_BRACE); }else if (car == '['){ lasts.push_back(LAST_BRACK); }else if (car == ']'){ if (lasts.size() == 0 || lasts.back() != LAST_BRACK){ ret = -1; break; } lasts.pop_back(); }else if (car == '}'){ if (lasts.size() == 0 || lasts.back() != LAST_BRACE){ ret = -1; break; } lasts.pop_back(); }else if (car == '"'){ // We have to skip over string as they can contain special characters while (*pt != '\0' && *pt != '"'){ if (*pt == '\\'){ pt++; if (*pt == '\\' || *pt == '"'){ pt++; }else{ // Invalid err = true; break; } }else{ pt++; } } if (*pt == '"'){ pt++; }else{ err = true; break; } } } if (lasts.size()==0){ offset_read=1; // We skip the first { immediatly ret = 1; } } } } return ret; } int REQUEST_JSON::addpart (const char *line) { return addpart (line,strlen(line)); } unsigned REQUEST_JSON::getlength() const { return offset; } const char *REQUEST_JSON::getbuf() const { return buf; } /* Check that the current field in the buffer has this name */ int REQUEST_JSON::checkname (const char *name, const char *&retpt) { retpt = NULL; int ret = -1; char *pt = buf+offset_read; // debug_printf (DEBUG_JSON,"checkname1 name=%s pt=%s\n",name,pt); if (name == NULL){ // Trick to simply unserialise code for USER objects // This code does not need to validate the name of each field received retpt = pt; ret = 0; }else if (*pt == '"'){ pt++; int len = strlen(name); if (strncmp(pt,name,len)==0 && pt[len] == '"'){ pt = str_skip(pt+len+1); if (*pt == ':'){ pt++; offset_read = pt - buf; ret = 0; retpt = pt; } } } // debug_printf (DEBUG_JSON,"checkname2 ret=%d pt=%s\n retpt=%s\n",ret,pt,retpt); return ret; } /* Check that the parsing (getarg()) reached the end of the buffer */ bool REQUEST_JSON::is_all_read() const { bool ret = false; const char *pt = buf+offset_read; pt = str_skip(pt); if (*pt == '}'){ ret = true; pt = str_skip(pt+1); unsigned off = pt - buf; ret = off == offset; } return ret; } /* This is the base function. It checks for a name and return a pointer to the value of the variable */ int REQUEST_JSON::getarg (const char *name, const char *&val) { int ret = -1; val = ""; const char *pt; if (checkname (name,pt) != -1){ pt = str_skip(pt); if (*pt == '"'){ pt++; const char *start = pt; char *dst = (char*)pt; const char *end = buf+offset; while (pt < end){ if (*pt == '\\'){ pt++; if (*pt != '"' && *pt != '\\'){ break; }else{ *dst = *pt; } }else if (*pt == '"'){ ret = 0; pt = str_skip(pt+1); if (*pt == ',') pt++; offset_read = pt - buf; val = start; break; }else if (pt != dst){ *dst = *pt; } dst++; pt++; } // We terminate the string and break the buffer! *dst = '\0'; } } return ret; } int REQUEST_JSON::getarg (const char *name, string &val) { const char *ptval; int ret = getarg (name,ptval); if (ret != -1) val = ptval; return ret; } int REQUEST_JSON::getarg (const char *name, int &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ const char *pt = valstr; if (pt[0] == '-') pt++; if (fdpass_is_all_digit(pt)){ val = atoi(valstr); ret = 0; } } return ret; } int REQUEST_JSON::getarg (const char *name, long &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ const char *pt = valstr; if (pt[0] == '-') pt++; if (fdpass_is_all_digit(pt)){ val = atol(valstr); ret = 0; } } return ret; } int REQUEST_JSON::getarg (const char *name, unsigned long long &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ const char *pt = valstr; if (fdpass_is_all_digit(pt)){ val = atoll(valstr); ret = 0; } } return ret; } int REQUEST_JSON::getarg (const char *name, unsigned &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ const char *pt = valstr; if (fdpass_is_all_digit(pt)){ val = atoi(valstr); ret = 0; } } return ret; } int REQUEST_JSON::getarg (const char *name, bool &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ if (strcmp(valstr,"1")==0){ val = true; ret = 0; }else if (strcmp(valstr,"0")==0){ val = false; ret = 0; } } return ret; } int REQUEST_JSON::getarg (const char *name, BOB_TYPE &val) { int ret = -1; const char *pt; if (checkname(name,pt)!=-1){ const char *end = buf + offset; if (pt + 2 < end){ if (*pt == '"'){ pt++; const char *start = pt; char *dst = (char*)pt; while (pt < end && *pt != '"'){ char car = *pt++; if (car > ' ') *dst++ = car; } if (*pt == '"'){ *dst = '\0'; vector v = Base64Decode(start); val.setbuffer((const void*)v.data(),v.size(),true); pt++; if (*pt == ',') pt++; offset_read = pt - buf; ret = 0; } } } } return ret; } bool REQUEST_JSON::expect (const char car) { bool ret = false; if (offset_read < offset){ const char *pt = buf + offset_read; pt = str_skip(pt); if (*pt == car){ ret = true; pt++; offset_read = pt - buf; } } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; const char *pt; if (checkname(name,pt) != -1 && expect('[')){ pt = buf + offset_read; const char *end = buf + offset; while (pt < end){ offset_read = pt - buf; if (*pt == ']'){ pt = str_skip(pt+1); if (*pt == ',') pt++; ret = 0; offset_read = pt - buf; break; }else if (getarg(NULL,pt)==-1){ break; }else{ vals.push_back(pt); pt = buf + offset_read; } } } if (ret != 0){ vals.clear(); } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; vals.clear(); vector valss; if (getarg (name,valss)!=-1){ for (auto x:valss) vals.push_back(x); ret = 0; } return ret; } int REQUEST_JSON::getarg (const char *name, vector > &vals) { const char *pt; int ret = checkname(name,pt); if (ret != -1){ pt = str_skip(pt); if (*pt == '['){ pt++; offset_read = pt-buf; const char *end = buf + offset; while (pt < end){ if (*pt == ']'){ pt = str_skip(pt+1); if (*pt == ',') pt++; ret = 0; offset_read = pt - buf; break; }else{ vector valss; if (getarg(NULL,valss)==-1){ break; }else{ vals.push_back(valss); pt = buf + offset_read; } } } } } if (ret != 0){ vals.clear(); } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; vector valss; if (getarg (name,valss)!=-1){ ret = 0; for (auto x:valss){ if (strcmp(x,"0")==0){ vals.push_back(false); }else if (strcmp(x,"1")==0){ vals.push_back(true); }else{ ret = -1; break; } } } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; vector valss; if (getarg (name,valss)!=-1){ ret = 0; for (auto x:valss){ const char *pt = x; if (isdigit(*pt) || (pt[0] == '-' && isdigit(pt[1]))){ pt++; while (isdigit(*pt)) pt++; if (*pt == '\0'){ vals.push_back(atoi(x)); }else{ ret = -1; break; } } } } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; vector valss; if (getarg (name,valss)!=-1){ ret = 0; for (auto x:valss){ const char *pt = x; if (isdigit(*pt)){ while (isdigit(*pt)) pt++; if (*pt == '\0'){ vals.push_back(atoi(x)); }else{ ret = -1; break; } } } } return ret; } int REQUEST_JSON_INFO::addpart (const char *line, int len) { return req.addpart(line,len); } int REQUEST_JSON_INFO::addpart (const char *line) { return addpart (line,strlen(line)); } CONNECT_HTTP_INFO::~CONNECT_HTTP_INFO() { close(); } static SSL_CTX * ctx = NULL; CONNECT_HTTP_INFO::CONNECT_HTTP_INFO() { strictmode = true; bio = NULL; use_ssl = false; ssl = NULL; pageapi = "webapi"; if (ctx == NULL){ ctx = SSL_CTX_new(SSLv23_client_method()); static const char *pemfile = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"; if(! SSL_CTX_load_verify_locations(ctx, pemfile,NULL)){ tlmp_error ("Can't load openssl certificates in %s\n",pemfile); } } } void CONNECT_HTTP_INFO::copy (const CONNECT_HTTP_INFO &c) { bio = NULL; ssl = NULL; use_ssl = c.use_ssl; strictmode = c.strictmode; host = c.host; port = c.port; bind = c.bind; pageapi = c.pageapi; } void CONNECT_HTTP_INFO::swap (CONNECT_HTTP_INFO &&c) { std::swap(bio,c.bio); std::swap(ssl,c.ssl); std::swap(use_ssl,c.use_ssl); std::swap(strictmode,c.strictmode); std::swap(host,c.host); std::swap(port,c.port); std::swap(bind,c.bind); std::swap(pageapi,c.pageapi); } CONNECT_HTTP_INFO::CONNECT_HTTP_INFO(const CONNECT_HTTP_INFO &c) { copy (c); } CONNECT_HTTP_INFO::CONNECT_HTTP_INFO(CONNECT_HTTP_INFO &&c) { bio = nullptr; ssl = nullptr; strictmode = true; use_ssl = false; swap (std::forward(c)); } CONNECT_HTTP_INFO &CONNECT_HTTP_INFO::operator =(const CONNECT_HTTP_INFO &c) { close(); ssl = NULL; copy (c); return *this; } CONNECT_HTTP_INFO &CONNECT_HTTP_INFO::operator =(CONNECT_HTTP_INFO &&c) { swap (std::forward(c)); return *this; } static int json_splithttp (const char *line, string &host, string &port, const char *default_port) { int ret = -1; // Line starts after the // const char *pt = strchr(line,':'); if (pt != NULL){ ret = 0; host = string(line,pt-line); port = (pt+1); }else{ ret = 0; host = line; port = default_port; } return ret; } int CONNECT_HTTP_INFO::init (PARAM_STRING serverurl) { int ret = -1; if (strncasecmp(serverurl.ptr,"http://",7)==0){ if (json_splithttp(serverurl.ptr+7,host,port,"80")!=-1){ use_ssl = false; ret = 0; } }else if (strncasecmp(serverurl.ptr,"https://",8)==0){ if (json_splithttp(serverurl.ptr+8,host,port,"443")!=-1){ use_ssl = true; ret = 0; } } return ret; } void CONNECT_HTTP_INFO::close() { if (bio != NULL){ BIO_free_all(bio); bio = NULL; } } /* When called, it will make connection to self sign certificate and won't validate that the hostname in the certificate match the target hostname (in the URL). This is useful for development only. */ void CONNECT_HTTP_INFO::setnonstrictmode() { strictmode = false; } void CONNECT_HTTP_INFO::reconnect() { json_ssl_init(); close(); { const char *default_port = use_ssl ? "443" : "80"; const char *portstr = port.size()==0 ? default_port : port.c_str(); string tmp = string_f("%s:%s",host.c_str(),portstr); if (use_ssl){ bio = BIO_new_ssl_connect(ctx); BIO_get_ssl(bio, & ssl); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); BIO_set_conn_hostname(bio, tmp.c_str()); SSL_set_tlsext_host_name(ssl, host.c_str()); if (strictmode){ X509_VERIFY_PARAM *param = SSL_get0_param(ssl); /* Enable automatic hostname checks */ X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); if (!X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size())) { tlmp_error ("VERIFY PARAM error\n"); } SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL); //SSL_CTX_set_verify_depth(ctx, 2 + 1); } }else{ bio = BIO_new_connect (tmp.c_str()); } if(BIO_do_connect(bio) <= 0){ tlmp_error ("JSON: Failed connection to %s\n",tmp.c_str()); ERR_print_errors_fp(stderr); close(); }else{ if (use_ssl){ int code = SSL_get_verify_result (ssl); debug_printf (D_SSL,"SSL_get_verify_result=%d\n",code); if (code != X509_V_OK){ tlmp_error ("Invalid SSL connection for site %s: code=%d\n",host.c_str(),code); close(); }else if (debug_ison()){ // Debugging X509 *x = SSL_get_peer_certificate(ssl); tlmp_error ("x=%p\n",x); X509_NAME *name = X509_get_subject_name(x); char *buf = X509_NAME_oneline (name,0,0); debug_printf (D_SSL,"SubjectName=%s\n",buf); OPENSSL_free (buf); if (X509_check_host(x, host.c_str(),0,0,NULL)){ debug_printf (D_SSL,"X509 match\n"); }else{ debug_printf (D_SSL,"X509 mismatch\n"); } X509_free(x); } } } } } void CONNECT_HTTP_INFO::setpageapi(PARAM_STRING page) { pageapi = page.ptr; } void CONNECT_HTTP_INFO::reset(REQUEST_JSON &req) { req.reset(); } int CONNECT_HTTP_INFO::send (REQUEST_JSON &req) { int ret = 0; req.complete(); string tmp = string_f("POST /%s.hc HTTP/1.1\r\n" "host: %s\r\n" "Content-Length: %d\r\n" "Content-Type: Text/plain\r\n" "\r\n" "%s\r\n",pageapi.c_str(),host.c_str(),req.getlength()+2,req.getbuf()); int size = tmp.size(); debug_printf (DEBUG_JSON,"write1 bio=%p\n%s\n",bio,tmp.c_str()); if (bio == NULL || BIO_write(bio,tmp.c_str(),size) != size){ reconnect(); //printf ("write2 fd=%d\n",fd); if (bio == NULL || BIO_write(bio,tmp.c_str(),size) != size){ ret = -1; } } if (ret != -1) req.reset(); return ret; } int CONNECT_HTTP_INFO::write (const void *buf, size_t size) { int ret = size; if (bio == NULL || BIO_write(bio,buf,size) != ret){ if (errno == EINTR){ ret = -1; }else{ reconnect(); //printf ("write2 fd=%d\n",fd); if (bio == NULL || BIO_write(bio,buf,size) != ret){ ret = -1; } } } return ret; } int CONNECT_HTTP_INFO::send (PARAM_STRING s) { int ret = 0; int size = strlen(s.ptr); if (write(s.ptr,size) != size) ret = -1; return ret; } static const char *chunked = "Transfer-Encoding: chunked"; static unsigned len_chunked = strlen(chunked); static const char *conclose = "Connection: close"; static unsigned len_conclose = strlen(conclose); static unsigned xtoi (const char *pt) { unsigned ret = 0; while (isxdigit(*pt)){ char car = *pt++; if (isdigit(car)){ ret = (ret<<4) + car - '0'; }else{ ret = (ret<<4) + toupper(car)+10-'A'; } } return ret; } /* Set timeout for receive operation. if seconds == -1, disable timeout. timeout is disabled by default. */ void CONNECT_HTTP_INFO::set_timeout (int seconds) { timeout = seconds; } bool CONNECT_HTTP_INFO::was_timeout () const { return timeout_seen; } int CONNECT_HTTP_INFO::read (void *data, size_t len) { int ret = -1; timeout_seen = false; if (timeout == -1){ ret = BIO_read(bio,data,len); }else{ int fdSocket; if (BIO_get_fd(bio, &fdSocket) < 0){ // failed to get fd. }else{ fd_set fds; struct timeval time_out; FD_ZERO(&fds); FD_SET(fdSocket, &fds); time_out.tv_usec = 0; time_out.tv_sec = timeout; int ok = select(fdSocket + 1, &fds, NULL, NULL, &time_out); if (ok == 0){ // timeout has occurred. timeout_seen = true; }else{ ret = BIO_read(bio,data,len); } } } return ret; } int CONNECT_HTTP_INFO::receive(REQUEST_JSON &req) { glocal REQUEST_JSON *req = &req; glocal bool must_close = false; glocal bool chunk_mode = false; glocal bool header_seen = false; glocal int chunk_size = 0; glocal bool end = false; glocal int ok = 0; req.reset(); STREAMP_BUF buf; int len=-2; char line[REQ_BUF_SIZE+1]; while (!glocal.end && (len=read(line,sizeof(line)-1))>0){ line[len] = '\0'; debug_printf (DEBUG_JSON,"receive2 len=%d\n%s\n",len,line); (buf,line,len); int ret = 0; const char *start = (const char*)buf; const char *pt = start; const char *endpt = pt + len; // printf ("process %d header_seen=%d\n",len,glocal.header_seen); if (!glocal.header_seen){ while (pt < endpt){ if (pt[0] == '\r' && pt[1] == '\n'){ ret = (pt - start) + 2; if (pt == start){ glocal.header_seen = true; }else if (strncasecmp(start,conclose,len_conclose)==0){ glocal.must_close = true; }else if (strncasecmp(start,chunked,len_chunked)==0){ glocal.chunk_mode = true; } break; } pt++; } }else{ if (glocal.chunk_mode){ if (glocal.chunk_size == 0){ while (pt < endpt && isxdigit(*pt)) pt++; if (pt + 2 <= endpt && pt[0] == '\r' && pt[1] == '\n'){ pt += 2; ret = pt-start; if (ret > 2){ glocal.chunk_size = xtoi(start); if (glocal.chunk_size == 0){ glocal.end = true; end = true; } } } }else if (len >= glocal.chunk_size){ ret = glocal.chunk_size; glocal.ok = glocal.req->addpart(pt,ret); glocal.chunk_size = 0; } }else{ ret = len; glocal.ok = glocal.req->addpart(pt,len); glocal.end = glocal.ok != 0; } } //printf ("Use %d: %*.*s",ret,ret,ret,start); return ret; } debug_printf (DEBUG_JSON,"receive3 end=%d len=%d\n",glocal.end,len); //printf ("must_close =%d ok=%d\n",glocal.must_close,glocal.ok); if (glocal.must_close){ BIO_free_all(bio); bio = NULL; } return glocal.ok == 1 ? 0 : -1; } int CONNECT_HTTP_INFO::receive(void *buf, unsigned size) { int ret = -1; int len; if ((len=BIO_read(bio,buf,size))>0){ ret = len; } return ret; }