Add Cookie headers when starting an HTTP request
This commit is contained in:
+88
-10
@@ -143,6 +143,67 @@ int http_client_get_builder(HTTP_Client *client,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: test this function
|
||||||
|
static bool is_subdomain(HTTP_String domain, HTTP_String subdomain)
|
||||||
|
{
|
||||||
|
if (http_streq(domain, subdomain))
|
||||||
|
return true; // Exact match
|
||||||
|
|
||||||
|
if (domain.len > subdomain.len)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
HTTP_String subdomain_suffix = {
|
||||||
|
subdomain.ptr + subdomain.len - domain.len,
|
||||||
|
entry.domain.len
|
||||||
|
};
|
||||||
|
if (subdomain_suffix.ptr[-1] != '.' || !http_streq(domain, subdomain_suffix))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test this function
|
||||||
|
static bool is_subpath(HTTP_String path, HTTP_String subpath)
|
||||||
|
{
|
||||||
|
if (path.len > subpath.len)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (subpath.len != path.len && subpath.ptr[path.len] != '/')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
subpath.len = path.len;
|
||||||
|
return http_streq(path, subpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool should_send_cookie(HTTP_CookieJarEntry entry, HTTP_URL url)
|
||||||
|
{
|
||||||
|
if (entry.exact_domain) {
|
||||||
|
// Cookie domain and URL domain must match exactly
|
||||||
|
if (!http_streq(entry.domain, url.authority.host.text))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// The URL's domain must match or be a subdomain of the cookie's domain
|
||||||
|
if (!is_subdomain(entry.domain, url.authority.host.text))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.exact_path) {
|
||||||
|
// Cookie path and URL path must match exactly
|
||||||
|
if (!http_streq(entry.path, url.path))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!is_subpath(entry.path, url.path))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.secure) {
|
||||||
|
if (!http_streq(url.scheme, HTTP_STR("https"))
|
||||||
|
return false; // Cookie was marked as secure but the target URL is not HTTPS
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void http_request_builder_url(HTTP_RequestBuilder builder,
|
void http_request_builder_url(HTTP_RequestBuilder builder,
|
||||||
HTTP_Method method, HTTP_String url)
|
HTTP_Method method, HTTP_String url)
|
||||||
{
|
{
|
||||||
@@ -165,16 +226,15 @@ void http_request_builder_url(HTTP_RequestBuilder builder,
|
|||||||
// Convert method enum to string
|
// Convert method enum to string
|
||||||
const char *method_str;
|
const char *method_str;
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case HTTP_METHOD_GET: method_str = "GET"; break;
|
case HTTP_METHOD_GET: method_str = "GET"; break;
|
||||||
case HTTP_METHOD_HEAD: method_str = "HEAD"; break;
|
case HTTP_METHOD_HEAD: method_str = "HEAD"; break;
|
||||||
case HTTP_METHOD_POST: method_str = "POST"; break;
|
case HTTP_METHOD_POST: method_str = "POST"; break;
|
||||||
case HTTP_METHOD_PUT: method_str = "PUT"; break;
|
case HTTP_METHOD_PUT: method_str = "PUT"; break;
|
||||||
case HTTP_METHOD_DELETE: method_str = "DELETE"; break;
|
case HTTP_METHOD_DELETE: method_str = "DELETE"; break;
|
||||||
case HTTP_METHOD_CONNECT: method_str = "CONNECT"; break;
|
case HTTP_METHOD_CONNECT: method_str = "CONNECT"; break;
|
||||||
case HTTP_METHOD_OPTIONS: method_str = "OPTIONS"; break;
|
case HTTP_METHOD_OPTIONS: method_str = "OPTIONS"; break;
|
||||||
case HTTP_METHOD_TRACE: method_str = "TRACE"; break;
|
case HTTP_METHOD_TRACE: method_str = "TRACE"; break;
|
||||||
case HTTP_METHOD_PATCH: method_str = "PATCH"; break;
|
case HTTP_METHOD_PATCH: method_str = "PATCH"; break;
|
||||||
default: return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build request line: METHOD path HTTP/1.1\r\n
|
// Build request line: METHOD path HTTP/1.1\r\n
|
||||||
@@ -192,8 +252,22 @@ void http_request_builder_url(HTTP_RequestBuilder builder,
|
|||||||
}
|
}
|
||||||
byte_queue_write(&conn->output, "\r\n", 2);
|
byte_queue_write(&conn->output, "\r\n", 2);
|
||||||
|
|
||||||
// TODO: Find all entries from the cookie jar that should
|
// Find all entries from the cookie jar that should
|
||||||
// be sent to this server and append headers for them
|
// be sent to this server and append headers for them
|
||||||
|
HTTP_CookieJar *cookie_jar = &conn->client->cookie_jar;
|
||||||
|
for (int i = 0; i < cookie_jar->count; i++) {
|
||||||
|
HTTP_CookieJarEntry entry = cookie_jar->items[i];
|
||||||
|
if (should_send_cookie(entry, parsed_url)) {
|
||||||
|
// TODO: Adding one header per cookie may cause the number of
|
||||||
|
// headers to increase significantly. Should probably group
|
||||||
|
// 3-4 cookies in the same headers.
|
||||||
|
byte_queue_write(&conn->output, HTTP_STR("Cookie: "));
|
||||||
|
byte_queue_write(&conn->output, entry.name);
|
||||||
|
byte_queue_write(&conn->output, HTTP_STR("="));
|
||||||
|
byte_queue_write(&conn->output, entry.value);
|
||||||
|
byte_queue_write(&conn->output, HTTP_STR("\r\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
conn->state = HTTP_CLIENT_CONN_WAIT_HEADER;
|
conn->state = HTTP_CLIENT_CONN_WAIT_HEADER;
|
||||||
}
|
}
|
||||||
@@ -324,16 +398,20 @@ static void save_cookies(HTTP_CookieJar *cookie_jar, HTTP_Header *headers, int n
|
|||||||
|
|
||||||
if (parsed.have_domain) {
|
if (parsed.have_domain) {
|
||||||
// TODO: Check that the server can set a cookie for this domain
|
// TODO: Check that the server can set a cookie for this domain
|
||||||
|
entry.exact_domain = false;
|
||||||
entry.domain = parsed.domain;
|
entry.domain = parsed.domain;
|
||||||
} else {
|
} else {
|
||||||
// TODO: Set the domain to the specific one used for this interaction
|
// TODO: Set the domain to the specific one used for this interaction
|
||||||
|
entry.exact_domain = true;
|
||||||
entry.domain = xxx;
|
entry.domain = xxx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsed.have_path) {
|
if (parsed.have_path) {
|
||||||
|
antry.exact_path = false;
|
||||||
entry.path = parsed.path;
|
entry.path = parsed.path;
|
||||||
} else {
|
} else {
|
||||||
// TODO: Set the path to the current endpoint minus one level
|
// TODO: Set the path to the current endpoint minus one level
|
||||||
|
entry.exact_path = true;
|
||||||
entry.path = xxx;
|
entry.path = xxx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-1
@@ -12,11 +12,30 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
|
// Cookie name and value
|
||||||
HTTP_String name;
|
HTTP_String name;
|
||||||
HTTP_String value;
|
HTTP_String value;
|
||||||
|
|
||||||
|
// If the "exact_domain" is true, the cookie
|
||||||
|
// can only be sent to the exact domain referred
|
||||||
|
// to by "domain" (which is never empty). If
|
||||||
|
// "exact_domain" is false, then the cookie is
|
||||||
|
// compatible with subdomains.
|
||||||
|
bool exact_domain;
|
||||||
HTTP_String domain;
|
HTTP_String domain;
|
||||||
|
|
||||||
|
// If "exact_path" is set, the cookie is only
|
||||||
|
// compatible with requests to paths that match
|
||||||
|
// "path" exactly. If "exact_path" is not set,
|
||||||
|
// then any path that starts with "path" is
|
||||||
|
// compatible with the cookie.
|
||||||
|
bool exact_path;
|
||||||
HTTP_String path;
|
HTTP_String path;
|
||||||
bool secure;
|
|
||||||
|
// This cookie can only be sent over HTTPS
|
||||||
|
bool secure;
|
||||||
|
|
||||||
} HTTP_CookieJarEntry;
|
} HTTP_CookieJarEntry;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user