http.cpp
来自「Shorthand是一个强大的脚本语言」· C++ 代码 · 共 578 行
CPP
578 行
/////////////////////////////////////////////////////////////////////////////
// $Header: /shorthand/src/http.cpp 5 1/09/03 7:14p Arm $
//---------------------------------------------------------------------------
// This file is part of "libAndrix" library - a collection of classes
// and functions developed by Andrei Remenchuk.
//---------------------------------------------------------------------------
// While you may own complete copyright on the project with which you have
// received this file, the author reserves the right to use code contained
// in this very file for any purposes, including publishing and usage in
// any free or commercial software.
//
// You may re-distribute this file or re-use it in your own free or
// commercial software provided that this text is included in the file.
// If you change this file you must include clear notice stating that
// you changed this file and the date of change.
//
// This statement doesn't apply to other files that are part of the same
// package unless otherwise noted.
//---------------------------------------------------------------------------
// (c) 1998-2002 Andrei Remenchuk <andrei@remenchuk.com>
//---------------------------------------------------------------------------
// http.cpp - HTTP client classes and functions
/////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include "http.h"
#include "regexx.h"
StandardHttpStream::StandardHttpStream()
{
#ifdef WIN32
::_setmode(_fileno(stdout), _O_BINARY);
::_setmode(_fileno(stdin), _O_BINARY);
::_setmode(_fileno(stderr), _O_BINARY);
#endif
m_headers_sent = false;
m_status = 200;
m_status_msg = "OK";
m_cookies_grabbed = false;
}
void StandardHttpStream::grab_cookies()
{
string tmp;
const char* cookies = safe_getenv("HTTP_COOKIE", tmp);
//cookies = "cookie1=value1; c3=; cookie2=This is the value (%3b) of cookie called \"Cookie2\"";
if (cookies == NULL) cookies = "";
int length = strlen(cookies);
RX cookies_rx(" *([^=]+) *= *([^;]*);?");
int off = 0;
while((off = cookies_rx.search(cookies, length, off, false)) != -1)
{
string name, value;
cookies_rx.submatch(cookies, 1, name);
cookies_rx.submatch(cookies, 2, value);
string clean_value;
value.urldecode(clean_value);
m_in_cookies.put(name, clean_value.clone());
}
//http_add_header("X-Cookies", cookies ? cookies : "");
m_cookies_grabbed = true;
}
const char* StandardHttpStream::http_get_cookie(const char* name)
{
if (!m_cookies_grabbed) grab_cookies();
string** value = m_in_cookies.get(name);
if (value == NULL || *value == NULL)
return NULL;
else
return (*value)->cstr();
}
int StandardHttpStream::http_read(void* buffer, int size)
{
return ::read(0, buffer, size);
}
bool StandardHttpStream::http_headers_sent()
{
return m_headers_sent;
}
int StandardHttpStream::http_add_header(const char* name, const char* value)
{
m_headers.put(name, new string(value));
return 0;
}
int StandardHttpStream::http_write_chunk(const void* chunk_data, unsigned int chunk_length)
{
if (!m_headers_sent)
{
http_send_headers();
}
int count = 0;
if (chunk_length > 0)
{
count += ::write(1, chunk_data, chunk_length);
}
return count;
}
/**
* Sends HTTP headers to the HTTP output stream, including cookies.
*/
int StandardHttpStream::http_send_headers()
{
int i,n;
if (m_headers.key_exists("Location") && m_status == 200)
{
m_status = 302;
m_status_msg = "Found";
}
//bool have_location = false;
::printf("HTTP/1.1 %d %s\r\n", m_status, m_status_msg.cstr());
//::printf("X-Status-Text: '%s'\r\n", m_status_msg.cstr());
if (!m_headers.key_exists("Content-Type"))
{
m_headers.put("Content-Type", new string("text/html"));
}
m_headers.put("Pragma", new string("no-cache"));
for(i=0,n=m_headers.size(); i<n; i++)
{
string* value;
const char* name = m_headers.key_at(i, value);
printf("%s: %s\r\n", name, value->cstr());
}
for(i=0,n=m_out_cookies.size(); i<n; i++)
{
CHttpCookie* cookie = m_out_cookies.get(i);
if (cookie == NULL || cookie->is_deleted()) continue;
string line; cookie->create_response(line);
printf("%s\r\n", line.cstr());
}
printf("\r\n");
fflush(stdout);
m_headers_sent = true;
return 0;
}
void StandardHttpStream::http_set_response_code(int code, const char* msg)
{
m_status = code;
m_status_msg = msg;
}
int StandardHttpStream::http_set_cookie(const CHttpCookie* cookie)
{
m_out_cookies.add(cookie->clone());
return 0;
}
int StandardHttpStream::http_finalize()
{
if (!m_headers_sent) http_send_headers();
return 0;
}
void CURL::ctor()
{
}
CURL::CURL()
{
ctor();
}
CURL::CURL(const char* spec)
{
ctor();
parse(spec);
}
void CURL::parse(const char* spec)
{
RX rx("((([^:]+):)?//([^/]+))?(:([0-9]+))?(.*)?");
clear();
m_url = spec;
if (!rx.match(spec))
{
m_url = spec;
m_uri = spec;
return;
}
rx.submatch(3, m_protocol);
rx.submatch(4, m_host);
rx.submatch(6, m_port);
rx.submatch(7, m_uri);
if (m_uri.is_empty() && !m_host.is_empty())
{
m_uri = "/";
m_path = "/";
m_directory = "/";
return;
}
m_path = m_uri;
// now parse URI only
// first, see if we can separate query string from it
RX query_rx("([^\\?]+)\\?([^\\?].*)");
if (query_rx.match(m_uri))
{
// got query string: truncate path and extract query
query_rx.submatch(1, m_path);
query_rx.submatch(2, m_query);
}
// now see if we can separate directory from file
RX path_rx("(.+)/([^/]+)");
if (path_rx.match(m_path))
{
path_rx.submatch(1, m_directory);
path_rx.submatch(2, m_file);
}
else if (m_path.begins_with("/"))
{
m_directory = "/";
m_file = m_path.cstr() + 1;
}
else
{
m_directory = "";
m_file = m_path;
}
const char* dot = strrchr(m_host.cstr(), '.');
if (dot != NULL && !isdigit(dot[1]))
{
m_tld.set(dot+1);
}
}
CURL::CURL(const CURL& curl)
{
ctor();
copyfrom(curl);
}
bool CURL::is_empty() const
{
return m_url.is_empty();
}
void CURL::copyfrom(const CURL& curl)
{
m_url = curl.m_url;
m_host = curl.m_host;
m_port = curl.m_port;
m_protocol = curl.m_protocol;
m_uri = curl.m_uri;
m_tld = curl.m_tld;
m_directory = curl.m_directory;
m_file = curl.m_file;
m_query = curl.m_query;
}
void CURL::clear()
{
m_url = "";
m_host = "";
m_port = "";
m_protocol = "";
m_uri = "";
m_tld = "";
m_directory = "";
m_file = "";
m_query = "";
}
const CURL& CURL::operator = (const CURL& url)
{
copyfrom(url);
return *this;
}
/** Reports true if this is HTTPS request */
bool CURL::is_secure() const
{
return m_protocol.ieq("https");
}
/**
* Creates follow-up URL based on the address of this URL
* and specified url which can be partial, in which case it is treated
* as relative to the address of this response object.
* For example, if this URL is from "https://www.my-server.com:3833/",
* and given url is /login.html?, the following URL will be produced:
* "https://www.myserver.com:3833/login.html".
*
* @param url URL or URI relative to this document.
* @param new_url resulting URL is written to this object.
*/
void CURL::followup(const char* url, CURL& new_url) const
{
CURL link(url);
if (link.has_host())
{
new_url = link;
return;
}
string u;
if (!m_protocol.is_empty() && !m_host.is_empty())
{
u.append(m_protocol);
u.append("://");
u.append(m_host);
if (!m_port.is_empty())
{
u.append(":");
u.append(m_port);
}
}
if (link.m_uri.begins_with("/"))
{
// linked is relative to the root of the site - just copy entire URI
u.append(link.m_uri);
}
else
{
// link is relative to the current dir
u.append(m_directory);
if (!m_directory.ieq("/")) u.append("/");
u.append(link.m_file);
if (!link.m_query.is_empty())
{
u.append("?");
u.append(link.m_query);
}
}
new_url.parse(u);
}
//////////////////////////////////////////////////////////////////////////////
// COOKIES
//////////////////////////////////////////////////////////////////////////////
/** Constructs cookie based on external spec */
CHttpCookie::CHttpCookie(const char* spec)
{
ctor();
parse(spec);
}
CHttpCookie::CHttpCookie()
{
ctor();
}
CHttpCookie* CHttpCookie::clone() const
{
CHttpCookie* ck = new CHttpCookie();
ck->copyfrom(*this);
return ck;
}
void CHttpCookie::copyfrom(const CHttpCookie& cookie)
{
m_deleted = cookie.m_deleted;
m_domain = cookie.m_domain;
m_expireable = cookie.m_expireable;
m_expires = cookie.m_expires;
m_name = cookie.m_name;
m_path = cookie.m_path;
m_secure = cookie.m_secure;
m_value = cookie.m_value;
}
void CHttpCookie::ctor()
{
m_deleted = false;
m_secure = false;
m_expireable = false;
}
/**
* Parses cookie from external spec
*/
bool CHttpCookie::parse(const char* spec)
{
int len = strlen(spec);
RX cookie_rx("^(Set-Cookie: *)?([^=]+)=([^;]*); *([^\n]*)$");
if (cookie_rx.match(spec))
{
string attrs;
m_expireable = false;
m_secure = false;
cookie_rx.submatch(2, m_name);
cookie_rx.submatch(3, m_value);
cookie_rx.submatch(4, attrs);
RX attr_rx("([^=]+)=([^;]+)(; *)?"); int off = 0;
while((off = attr_rx.search(attrs, attrs.length(), off)) != -1)
{
string attr_name, attr_value;
attr_rx.submatch(1, attr_name);
attr_rx.submatch(2, attr_value);
attr_value.rtrim();
if (attr_name.ieq("path")) m_path = attr_value;
else if (attr_name.ieq("domain")) m_domain = attr_value;
else if (attr_name.ieq("expires"))
{
m_expires.import_rfc(attr_value);
m_expireable = true;
}
}
return true;
}
else
{
m_name.clear();
m_value.clear();
m_path.clear();
m_domain.clear();
m_expireable = false;
m_secure = false;
return false;
}
}
/** reports whether or not this cookie's domain and path
* matches specified URL */
bool CHttpCookie::matches(const CURL& url)
{
string host = url.host();
string path = url.uri();
if (host.ends_with(m_domain))
{
if (path.begins_with(m_path))
{
return true;
}
else
{
//HTTP_DUMP4("cookie \"%s\" path mismatch: cookie is for '%s'; checking against '%s'\n", m_name.cstr(), m_path.cstr(), path.cstr());
}
}
else
{
//HTTP_DUMP4("cookie \"%s\" domain mismatch: cookie is for '%s'; checking against '%s'\n", m_name.cstr(), m_domain.cstr(), host.cstr());
}
return false;
}
/**
* Reports whether or not the cookie is expired at this very moment
*/
bool CHttpCookie::is_expired()
{
if (!m_expireable) return false;
datetime now;
bool expired = compare_dates(m_expires, now) < 0;
#ifdef HTTP_DEBUG
if (expired)
{
string s_was, s_now;
m_expires.common_print(s_was);
now.common_print(s_now);
http_dump("cookie \"%s\" expired: was until '%s'; now is '%s'\n", m_name.cstr(), s_was.cstr(), s_now.cstr());
}
#endif
return expired;
}
void CHttpCookie::create_response(string& s, bool omit_header_name)
{
string e_value;
const char* p = m_value.cstr();
while(*p)
{
if (*p == ';') e_value.append("%3b");
else e_value.append(*p);
p++;
}
if (!omit_header_name) s.printf("Set-Cookie: ");
else s.clear();
s.appendf("%s=%s", m_name.cstr(), e_value.cstr());
if (m_expireable && m_expires.is_valid())
{
string ts; m_expires.export_rfc(ts);
s.appendf("; expires=%s", ts.cstr());
}
if (!m_path.is_empty())
s.appendf("; path=%s", m_path.cstr());
if (!m_domain.is_empty())
s.appendf("; domain=%s", m_domain.cstr());
if (m_secure)
s.append("; secure");
}
CHttpCookie::CHttpCookie(const char* name, const char* value, const datetime& expires, const char* path, const char* domain, bool secure)
: m_name(name), m_value(value), m_expires(expires), m_path(path), m_domain(domain), m_secure(secure)
{
m_expireable = m_expires.is_valid();
m_path.trim();
m_deleted = false;
}
CHttpCookieJar::CHttpCookieJar()
{
}
CHttpCookie* CHttpCookieJar::find(const char* name, const char* domain)
{
for(int i=0,n=m_size; i<n; i++)
{
CHttpCookie* ck = get(i);
if (ck == NULL || ck->is_deleted() || ck->is_expired()) continue;
if (ck->m_name.ieq(name) && (domain == NULL || ck->m_domain.ieq(domain)))
{
return ck;
}
}
return false;
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?