PracticeDev/study_cpp/http/HttpRequest.cpp

383 lines
9.8 KiB
C++
Raw Permalink Normal View History

2022-12-20 17:31:11 +08:00
#include <string>
#include <iostream>
#include <fcntl.h>
#include <errno.h>
#include <sys/uio.h> // readv
#include <stdint.h>
#include <endian.h>
#include <unistd.h>
#include <map>
#include <fstream>
#include "HttpRequest.hh"
const std::map<std::string, int>::value_type init_value[] =
{
std::map<std::string, int>::value_type( "GET", HttpRequest::GET),
std::map<std::string, int>::value_type( "POST", HttpRequest::POST)
};
const static std::map<std::string, int> kRequestMethodMap(init_value, init_value + (sizeof init_value / sizeof init_value[0]));
static inline uint16_t hostToNetwork16(uint16_t host16)
{
return htobe16(host16);
}
int sockets::createSocket(sa_family_t family){
// Call "socket()" to create a (family) socket of the specified type.
// But also set it to have the 'close on exec' property (if we can)
int sock;
//CLOEXEC即当调用exec函数成功后文件描述符会自动关闭。
//在以往的内核版本2.6.23以前)中,需要调用 fcntl(fd, F_SETFD, FD_CLOEXEC) 来设置这个属性。
//而新版本2.6.23开始可以在调用open函数的时候通过 flags 参数设置 CLOEXEC 功能,
#ifdef SOCK_CLOEXEC
sock = socket(family, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (sock != -1 || errno != EINVAL) return sock;
// An "errno" of EINVAL likely means that the system wasn't happy with the SOCK_CLOEXEC; fall through and try again without it:
#endif
sock = socket(family, SOCK_STREAM, 0);
#ifdef FD_CLOEXEC
if (sock != -1) fcntl(sock, F_SETFD, FD_CLOEXEC);
#endif
return sock;
}
int sockets::connect(int sockfd, const struct sockaddr* addr)
{
return ::connect(sockfd, addr, sizeof(struct sockaddr));
}
void sockets::fromIpPort(const char* ip, uint16_t port,
struct sockaddr_in* addr)
{
addr->sin_family = AF_INET;
addr->sin_port = hostToNetwork16(port);
if (::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0)
{
LOG_SYSERR << "sockets::fromIpPort";
}
}
ssize_t sockets::read(int sockfd, void *buf, size_t count)
{
return ::read(sockfd, buf, count);
}
ssize_t sockets::readv(int sockfd, const struct iovec *iov, int iovcnt)
{
return ::readv(sockfd, iov, iovcnt);
}
ssize_t sockets::write(int sockfd, const void *buf, size_t count)
{
return ::write(sockfd, buf, count);
}
void sockets::close(int sockfd)
{
if (::close(sockfd) < 0)
{
LOG_SYSERR << "sockets::close";
}
}
void sockets::delaySecond(int sec)
{
struct timeval tv;
tv.tv_sec = sec;
tv.tv_usec = 0;
select(0, NULL, NULL, NULL, &tv);
}
InetAddress::InetAddress(std::string ip, uint16_t port)
{
::bzero(&m_addr, sizeof m_addr);
sockets::fromIpPort(ip.c_str(), port, &m_addr);
}
uint32_t InetAddress::ipNetEndian() const
{
assert(family() == AF_INET);
return m_addr.sin_addr.s_addr;
}
HttpRequest::HttpRequest(std::string httpUrl)
:m_httpUrl(httpUrl)
{
}
HttpRequest::~HttpRequest()
{
}
void HttpRequest::connect()
{
char ip[32] = {0};
while(true)
{
struct hostent* phost = NULL;
phost = gethostbyname(m_httpUrl.domain().c_str());
if (NULL == phost)
{
LOG_ERROR << "HttpUrlToIp(): gethostbyname error : " << errno << " : "<< strerror(errno) << " continue.";
sockets::delaySecond(1);
continue;
}
inet_ntop(phost->h_addrtype, phost->h_addr, ip, sizeof ip);
LOG_DEBUG << "HttpRequest::Connector() gethostbyname Successful";
InetAddress serverAddr = InetAddress(ip, 80);
m_sockfd = sockets::createSocket(serverAddr.family());
if(m_sockfd < 0) LOG_SYSERR << "HttpRequest::connect() : createSocket error";
int ret = sockets::connect(m_sockfd, serverAddr.getSockAddr());
LOG_DEBUG << "sockfd : " << m_sockfd << "sockets::connect ret : " << ret ;
int savedErrno = (ret == 0) ? 0 : errno;
switch (savedErrno)
{
case 0:
case EINPROGRESS:
case EINTR:
case EISCONN:
LOG_INFO << "HttpRequest::connect() sockfd : " << m_sockfd << " Successful";
break;
default :
LOG_ERROR << "Connect Error ";
sockets::delaySecond(1);
continue;
}
break;
}
LOG_DEBUG << "HttpRequest::Connector() end";
}
void HttpRequest::setRequestMethod(const std::string &method)
{
switch(kRequestMethodMap.at(method))
{
case HttpRequest::GET :
m_stream << "GET " << "/" << m_httpUrl.getHttpUrlSubSeg(HttpUrl::URI) << " HTTP/1.1\r\n";
LOG_DEBUG << m_stream.str().c_str();
break;
case HttpRequest::POST :
m_stream << "POST " << "/" << m_httpUrl.getHttpUrlSubSeg(HttpUrl::URI) << " HTTP/1.1\r\n";
LOG_DEBUG << m_stream.str().c_str();
break;
default :
LOG_ERROR << "No such Method : " << method.c_str();
break;
}
m_stream << "Host: " << m_httpUrl.getHttpUrlSubSeg(HttpUrl::HOST) << "\r\n";
}
void HttpRequest::setRequestProperty(const std::string &key, const std::string &value)
{
m_stream << key << ": " << value << "\r\n";
}
void HttpRequest::setRequestBody(const std::string &content)
{
m_stream << content;
}
void HttpRequest::handleRead()
{
assert(!m_haveHandleHead);
ssize_t nread = 0;
ssize_t writtenBytes = 0;
nread = sockets::read(m_sockfd, m_buffer.beginWrite(), kBufferSize);
if(nread < 0) LOG_SYSFATAL << "sockets::read";
m_buffer.hasWritten(nread);
LOG_TRACE << "sockets::read(): nread: " << nread << " remain: " << m_buffer.writableBytes();
size_t remain = kBufferSize - nread;
while(remain > 0)
{
size_t n = sockets::read(m_sockfd, m_buffer.beginWrite(), remain);
if(n < 0) LOG_SYSFATAL << "sockets::read";
m_buffer.hasWritten(n);
if(0 == n)
{
LOG_DEBUG << "sockets::read finish";
break;
}
remain = remain - n;
}
//std::cout << m_buffer.peek();
//for(int i = 0; i < nread; i++) printf("%02x%c",(unsigned char)buffer[i],i==nread - 1 ?'\n':' ');
//LOG_DEBUG << "handleRead Recv Response : \n" << m_buffer.peek();
int headsize = 0;
std::string line;
std::stringstream ss(m_buffer.peek());
std::vector<std::string> v;
getline(ss, line);
//LOG_DEBUG << line;
headsize += line.size() + 1;
SplitString(line, v, " ");
//for(int i = 0; i < v.size(); i++) std::cout << v[i] << std::endl;
m_code = std::stoi(v[1]);
if(v[1] != "200")
{
LOG_ERROR << "Error Http Server Response : " << v[1].c_str();
}
do{
getline(ss, line);
headsize += line.size() + 1; // + 1('\n')
if(!line.empty()) line.erase(line.end()-1); // remove '/r'
//LOG_DEBUG << line;
v.clear();
SplitString(line, v, ":");
if(v.size() == 2){
m_ackProperty[v[0]] = v[1].erase(0,v[1].find_first_not_of(" "));
}
}while(!line.empty());
LOG_DEBUG << "Http Head Size is " << headsize;
std::string res(m_buffer.peek(), headsize);
LOG_DEBUG << "Http Response :\n" << res;
m_buffer.retrieve(headsize);
m_haveHandleHead = true;
}
void HttpRequest::uploadFile(const std::string& file, const std::string& contentEnd)
{
FILE* fp = fopen(file.c_str(), "rb");
if(fp == NULL)
{
LOG_SYSFATAL << "fopen() File :" << file.c_str() << " Errno";
}
bool isEnd = false;
ssize_t writtenBytes = 0;
assert(m_buffer.writableBytes() == Buffer::kInitialSize);
while(!isEnd)
{
ssize_t nread = fread(m_buffer.beginWrite(), 1, kBufferSize, fp);
m_buffer.hasWritten(nread);
while(m_buffer.writableBytes() > 0)
{
LOG_TRACE << "file read(): nread: " << nread << " remain: " << m_buffer.writableBytes();
size_t n = fread(m_buffer.beginWrite(), 1, m_buffer.writableBytes(), fp);
m_buffer.hasWritten(n);
if(0 == n)
{ int err = ferror(fp);
if(err)
{
fprintf(stderr, "fread failed : %s\n", strerror(err));
}
LOG_DEBUG << "sockets::read finish";
isEnd = true;
break;
}
}
ssize_t nwrite = sockets::write(m_sockfd, m_buffer.peek(), m_buffer.readableBytes());
if(nwrite < 0) LOG_SYSFATAL << "sockets::write";
writtenBytes += nwrite;
LOG_TRACE << "sockets::write nread " << m_buffer.readableBytes() << " nwrite " << nwrite << " writtenBytes " << writtenBytes;
m_buffer.retrieve(nwrite);
}
fclose(fp);
m_buffer.retrieveAll();
ssize_t n = sockets::write(m_sockfd, contentEnd.c_str(), contentEnd.size());
if(n < 0) LOG_SYSFATAL << "sockets::write";
}
void HttpRequest::downloadFile(const std::string& file)
{
assert(m_haveHandleHead);
bool isEnd = false;
ssize_t nread = 0;
ssize_t writtenBytes = 0;
bool haveHandleHead = false;
bool isDownFile = false;
std::ofstream output(file, std::ios::binary);
if (!output.is_open()){ // 检查文件是否成功打开
LOG_SYSFATAL << "open file error" << file;
}
output.write(m_buffer.peek(), m_buffer.readableBytes());
writtenBytes += m_buffer.readableBytes();
m_buffer.retrieve(m_buffer.readableBytes());
LOG_DEBUG << "Content-Length : " << getResponseProperty("Content-Length");
while(!isEnd)
{
nread = sockets::read(m_sockfd, m_buffer.beginWrite(), kBufferSize);
if(nread < 0) LOG_SYSFATAL << "sockets::read";
m_buffer.hasWritten(nread);
LOG_TRACE << "sockets::read(): nread: " << nread << " remain: " << m_buffer.writableBytes() << " writtenBytes: " << writtenBytes;
size_t remain = kBufferSize - nread;
while(remain > 0)
{
size_t n = sockets::read(m_sockfd, m_buffer.beginWrite(), remain);
if(n < 0) LOG_SYSFATAL << "sockets::read";
m_buffer.hasWritten(n);
if(0 == n)
{
LOG_DEBUG << "sockets::read finish";
isEnd = true;
break;
}
remain = remain - n;
}
output.write(m_buffer.peek(), m_buffer.readableBytes());
writtenBytes += m_buffer.readableBytes();
m_buffer.retrieve(m_buffer.readableBytes());
}
LOG_DEBUG << " writtenBytes " << writtenBytes;
output.close();
sockets::close(m_sockfd);
}
void HttpRequest::SplitString(const std::string& s, std::vector<std::string>& v, const std::string& c)
{
std::string::size_type pos1, pos2;
pos2 = s.find(c);
pos1 = 0;
while(std::string::npos != pos2)
{
v.push_back(s.substr(pos1, pos2-pos1));
pos1 = pos2 + c.size();
pos2 = s.find(c, pos1);
}
if(pos1 != s.length())
v.push_back(s.substr(pos1));
}