See More

//////////////////////////////////////////////////////////////////////////////// // // // Copyright (C) 2016, goatpig. // // Distributed under the MIT license // // See LICENSE-MIT or https://opensource.org/licenses/MIT // // // //////////////////////////////////////////////////////////////////////////////// #include "StringSockets.h" /////////////////////////////////////////////////////////////////////////////// // // HttpSocket // /////////////////////////////////////////////////////////////////////////////// HttpSocket::HttpSocket(const BinarySocket& obj) : BinarySocket(obj) { stringstream ss; ss << "POST / HTTP/1.1" << "\r\n"; ss << "Host: " << addr_ << "\r\n"; ss << "Content-type: text/html; charset=UTF-8" << "\r\n"; ss << "Content-Length: "; http_header_ = move(ss.str()); } /////////////////////////////////////////////////////////////////////////////// int32_t HttpSocket::makePacket(char** packet, const char* msg) { if (packet == nullptr) return -1; stringstream ss; ss << strlen(msg); ss << "\r\n\r\n"; *packet = new char[strlen(msg) + ss.str().size() + http_header_.size() + 1]; memcpy(*packet, http_header_.c_str(), http_header_.size()); size_t pos = http_header_.size(); memcpy(*packet + pos, ss.str().c_str(), ss.str().size()); pos += ss.str().size(); memcpy(*packet + pos, msg, strlen(msg)); pos += strlen(msg); memset(*packet + pos, 0, 1); return pos; } /////////////////////////////////////////////////////////////////////////////// string HttpSocket::getBody(vector msg) { /*** Always expect text data (null terminated) ***/ //look for double crlf http header end, return everything after that typedef vector::iterator vcIter; //let's use a move iterator, enough copies already as it is string htmlstr( move_iterator(msg.begin()), move_iterator(msg.end())); size_t pos = htmlstr.find("\r\n\r\n"); if (pos == string::npos) { //no html break, check for error marker pos = htmlstr.find("error:"); if (pos != string::npos) throw runtime_error(htmlstr); throw runtime_error("unexpected return value"); } return htmlstr.substr(pos + 4); } /////////////////////////////////////////////////////////////////////////////// string HttpSocket::writeAndRead(const string& msg, SOCKET sockfd) { char* packet = nullptr; auto packetSize = makePacket(&packet, msg.c_str()); typedef vector::iterator vecIterType; packetData packetPtr; while (1) { if (sockfd == SOCK_MAX) sockfd = openSocket(false); if (sockfd == SOCK_MAX) throw SocketError("failed to connect socket"); packetPtr.clear(); try { auto processHttpPacket = [&packetPtr] (const vector& socketData)->bool { auto& httpData = packetPtr.httpData; if (socketData.size() == 0) return true; { httpData.insert( httpData.end(), socketData.begin(), socketData.end()); if (packetPtr.content_length == -1) { //if content_length is -1, we have not read the content-length in the //http header yet, let's find that for (unsigned i = 0; i < httpData.size(); i++) { if (httpData[i] == '\r') { if (httpData.size() - i < 3) break; if (httpData[i + 1] == '\n' && httpData[i + 2] == '\r' && httpData[i + 3] == '\n') { packetPtr.header_len = i + 4; break; } } } if (packetPtr.header_len == 0) throw HttpError("couldn't find http header in response"); string header_str((char*)&httpData[0], packetPtr.header_len); packetPtr.get_content_len(header_str); } if (packetPtr.content_length == -1) throw HttpError("failed to find http header response packet"); //check the total amount of data read matches the advertised //data in the http header } bool done = false; if (httpData.size() >= packetPtr.content_length + packetPtr.header_len) { httpData.resize(packetPtr.content_length + packetPtr.header_len); done = true; } return done; }; BinarySocket::writeAndRead(sockfd, (uint8_t*)packet, packetSize, processHttpPacket); break; } catch (HttpError &e) { LOGERR << "HttpSocket::writeAndRead HttpError: " << e.what(); continue; } catch (SocketError&) { continue; } } closeSocket(sockfd); auto&& retmsg = getBody(move(packetPtr.httpData)); return retmsg; } /////////////////////////////////////////////////////////////////////////////// // // FcgiSocket // /////////////////////////////////////////////////////////////////////////////// FcgiSocket::FcgiSocket(const HttpSocket& obj) : HttpSocket(obj) {} /////////////////////////////////////////////////////////////////////////////// string FcgiSocket::writeAndRead(const string& msg, SOCKET sockfd) { auto&& fcgiMsg = FcgiMessage::makePacket(msg.c_str()); auto serdata = fcgiMsg.serialize(); auto serdatalength = fcgiMsg.getSerializedDataLength(); packetStruct packetPtr; packetPtr.fcgiid = fcgiMsg.id(); while (1) { packetPtr.clear(); if (sockfd == SOCK_MAX) sockfd = openSocket(false); if (sockfd == SOCK_MAX) throw SocketError("can't connect socket"); try { auto processFcgiPacket = [&packetPtr]( const vector& socketData)->bool { if (socketData.size() == 0) return true; packetPtr.fcgidata.insert( packetPtr.fcgidata.end(), socketData.begin(), socketData.end()); while (packetPtr.ptroffset + FCGI_HEADER_LEN <= packetPtr.fcgidata.size()) { //grab fcgi header auto* fcgiheader = &packetPtr.fcgidata[packetPtr.ptroffset]; packetPtr.ptroffset += FCGI_HEADER_LEN; //make sure fcgi version and request id match if (fcgiheader[0] != FCGI_VERSION_1) throw FcgiError("unexpected fcgi header version"); uint16_t requestid; requestid = (uint8_t)fcgiheader[3] + (uint8_t)fcgiheader[2] * 256; if (requestid != packetPtr.fcgiid) throw FcgiError("request id mismatch"); //check packet type bool abortParse = false; switch (fcgiheader[1]) { case FCGI_END_REQUEST: { packetPtr.endpacket++; break; } case FCGI_STDOUT: { //get packetsize and padding uint16_t packetsize = 0, padding; packetsize |= (uint8_t)fcgiheader[5]; packetsize |= (uint16_t)(fcgiheader[4] << 8); padding = (uint8_t)fcgiheader[6]; if (packetsize > 0) { //do not process this fcgi packet if we dont have enough //data in the read buffer to cover the advertized length if (packetsize + padding + packetPtr.ptroffset > packetPtr.fcgidata.size()) { packetPtr.ptroffset -= FCGI_HEADER_LEN; abortParse = true; break; } //extract http data packetPtr.httpData.insert(packetPtr.httpData.end(), packetPtr.fcgidata.begin() + packetPtr.ptroffset, packetPtr.fcgidata.begin() + packetPtr.ptroffset + packetsize); //advance index to next header packetPtr.ptroffset += packetsize + padding; } break; } case FCGI_ABORT_REQUEST: throw FcgiError("received FCGI_ABORT_REQUEST packet"); //we do not handle the other request types as a client default: throw FcgiError("unknown fcgi header request byte"); } if (packetPtr.endpacket >= 1 || abortParse) break; } if (packetPtr.endpacket >= 1) return true; return false; }; BinarySocket::writeAndRead(sockfd, serdata, serdatalength, processFcgiPacket); //if we got this far we're all good break; } catch (FcgiError &e) { LOGERR << "FcgiSocket::writeAndRead FcgiError: " << e.what(); } catch (future_error& e) { cout << e.what(); int abc = 0; } closeSocket(sockfd); } closeSocket(sockfd); fcgiMsg.clear(); return HttpSocket::getBody(move(packetPtr.httpData)); }