You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
558 lines
22 KiB
558 lines
22 KiB
/* libanode: the Anode C reference implementation |
|
* Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com> |
|
* |
|
* This program is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
|
|
|
#include <stdio.h> |
|
#include <netinet/in.h> |
|
#include <sys/socket.h> |
|
#include "http_client.h" |
|
#include "misc.h" |
|
#include "types.h" |
|
|
|
/* How much to increment read buffer at each capacity top? */ |
|
#define ANODE_HTTP_CAPACITY_INCREMENT 4096 |
|
|
|
static void AnodeHttpClient_close_and_fail(struct AnodeHttpClient *client) |
|
{ |
|
if (client->impl.tcp_connection) { |
|
client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection); |
|
client->impl.tcp_connection = (AnodeTransportTcpConnection *)0; |
|
} |
|
|
|
client->response.data_length = 0; |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED; |
|
|
|
if (client->handler) |
|
client->handler(client); |
|
} |
|
|
|
static void AnodeHttpClient_do_initiate_client(struct AnodeHttpClient *client) |
|
{ |
|
const char *method = ""; |
|
long l,i; |
|
|
|
switch(client->method) { |
|
case ANODE_HTTP_GET: method = "GET"; break; |
|
case ANODE_HTTP_HEAD: method = "HEAD"; break; |
|
case ANODE_HTTP_POST: method = "POST"; break; |
|
} |
|
client->impl.outbuf_len = snprintf((char *)client->impl.outbuf,sizeof(client->impl.outbuf), |
|
"%s %s%s%s HTTP/1.1\r\nHost: %s:%d\r\n%s", |
|
method, |
|
client->uri.path, |
|
((client->uri.query[0]) ? "?" : ""), |
|
client->uri.query, |
|
client->uri.host, |
|
((client->uri.port > 0) ? client->uri.port : 80), |
|
((client->keepalive) ? "" : "Connection: close\r\n") |
|
); |
|
if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} |
|
|
|
if (client->method == ANODE_HTTP_POST) { |
|
if ((client->data)&&(client->data_length)) { |
|
client->impl.outbuf_len += snprintf((char *)client->impl.outbuf + client->impl.outbuf_len,sizeof(client->impl.outbuf) - client->impl.outbuf_len, |
|
"Content-Type: %s\r\n", |
|
(client->data_content_type ? client->data_content_type : "application/x-www-form-urlencoded") |
|
); |
|
if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} |
|
client->impl.outbuf_len += snprintf((char *)client->impl.outbuf + client->impl.outbuf_len,sizeof(client->impl.outbuf) - client->impl.outbuf_len, |
|
"Content-Length: %u\r\n", |
|
client->data_length |
|
); |
|
if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} |
|
} else { |
|
client->impl.outbuf_len += snprintf((char *)client->impl.outbuf + client->impl.outbuf_len,sizeof(client->impl.outbuf) - client->impl.outbuf_len, |
|
"Content-Length: 0\r\n" |
|
); |
|
if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
l = AnodeDictionary_write(&(client->headers),(char *)client->impl.outbuf + client->impl.outbuf_len,(long)(sizeof(client->impl.outbuf) - client->impl.outbuf_len - 2),"\r\n",": "); |
|
if (l < 0) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} |
|
|
|
client->impl.outbuf_len += (unsigned int)l; |
|
if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) { /* sanity check */ |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} |
|
|
|
client->impl.outbuf[client->impl.outbuf_len++] = '\r'; |
|
client->impl.outbuf[client->impl.outbuf_len++] = '\n'; |
|
|
|
if ((client->method == ANODE_HTTP_POST)&&(client->data)&&(client->data_length)) { |
|
i = sizeof(client->impl.outbuf) - client->impl.outbuf_len; |
|
if (i > client->data_length) |
|
i = client->data_length; |
|
Anode_memcpy((client->impl.outbuf + client->impl.outbuf_len),client->data,i); |
|
client->impl.request_data_ptr += i; |
|
client->impl.outbuf_len += i; |
|
} |
|
|
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_SEND; |
|
client->impl.transport_engine->tcp_start_writing(client->impl.transport_engine,client->impl.tcp_connection); |
|
} |
|
|
|
static void AnodeHttpClient_tcp_outgoing_connect_handler( |
|
AnodeTransportEngine *transport, |
|
AnodeTransportTcpConnection *connection, |
|
int error_code) |
|
{ |
|
struct AnodeHttpClient *client; |
|
|
|
if (!(client = (struct AnodeHttpClient *)(connection->ptr))) |
|
return; |
|
|
|
if ((client->impl.phase == ANODE_HTTP_REQUEST_PHASE_CONNECT)&&(!client->impl.freed)) { |
|
if (error_code) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_CONNECT_FAILED; |
|
AnodeHttpClient_close_and_fail(client); |
|
} else { |
|
client->impl.tcp_connection = connection; |
|
AnodeHttpClient_do_initiate_client(client); |
|
} |
|
} else transport->tcp_close(transport,connection); |
|
} |
|
|
|
static void AnodeHttpClient_tcp_connection_terminated_handler( |
|
AnodeTransportEngine *transport, |
|
AnodeTransportTcpConnection *connection, |
|
int error_code) |
|
{ |
|
struct AnodeHttpClient *client; |
|
|
|
if (!(client = (struct AnodeHttpClient *)(connection->ptr))) |
|
return; |
|
if (client->impl.freed) |
|
return; |
|
|
|
client->response.data_length = 0; |
|
client->impl.tcp_connection = (AnodeTransportTcpConnection *)0; |
|
if ((client->impl.phase != ANODE_HTTP_REQUEST_PHASE_KEEPALIVE)&&(client->impl.phase != ANODE_HTTP_REQUEST_PHASE_CLOSED)) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_SERVER_CLOSED_CONNECTION; |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED; |
|
AnodeHttpClient_close_and_fail(client); |
|
} else client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED; |
|
} |
|
|
|
static void AnodeHttpClient_tcp_receive_handler( |
|
AnodeTransportEngine *transport, |
|
AnodeTransportTcpConnection *connection, |
|
void *data, |
|
unsigned int data_length) |
|
{ |
|
struct AnodeHttpClient *client; |
|
char *p1,*p2; |
|
unsigned int i; |
|
long l; |
|
|
|
if (!(client = (struct AnodeHttpClient *)(connection->ptr))) |
|
return; |
|
if (client->impl.freed) { |
|
transport->tcp_close(transport,connection); |
|
return; |
|
} |
|
|
|
if (!client->response.data) |
|
client->response.data = malloc(client->impl.response_data_capacity = ANODE_HTTP_CAPACITY_INCREMENT); |
|
|
|
i = 0; |
|
while (i < data_length) { |
|
switch(client->impl.read_mode) { |
|
case ANODE_HTTP_READ_MODE_WAITING: |
|
for(;i<data_length;++i) { |
|
if (((const char *)data)[i] == '\n') { |
|
((char *)client->response.data)[client->response.data_length] = (char)0; |
|
client->response.data_length = 0; |
|
|
|
p1 = (char *)Anode_strchr((char *)client->response.data,' '); |
|
if (!p1) |
|
p1 = (char *)Anode_strchr((char *)client->response.data,'\t'); |
|
if (p1) { |
|
while ((*p1 == ' ')||(*p1 == '\t')) ++p1; |
|
if (!*p1) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} |
|
p2 = p1 + 1; |
|
while (*p2) { |
|
if ((*p2 == ' ')||(*p2 == '\t')||(*p2 == '\r')||(*p2 == '\n')) { |
|
*p2 = (char)0; |
|
break; |
|
} else ++p2; |
|
} |
|
client->response.code = (int)strtol(p1,(char **)0,10); |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_HEADERS; |
|
++i; break; /* Exit inner for() */ |
|
} |
|
} else { |
|
((char *)client->response.data)[client->response.data_length++] = ((const char *)data)[i]; |
|
if (client->response.data_length >= client->impl.response_data_capacity) |
|
client->response.data = realloc(client->response.data,client->impl.response_data_capacity += ANODE_HTTP_CAPACITY_INCREMENT); |
|
} |
|
} |
|
break; |
|
case ANODE_HTTP_READ_MODE_HEADERS: |
|
case ANODE_HTTP_READ_MODE_CHUNKED_FOOTER: |
|
for(;i<data_length;++i) { |
|
if (((const char *)data)[i] == '\n') { |
|
client->impl.header_line_buf[client->impl.header_line_buf_ptr] = (char)0; |
|
client->impl.header_line_buf_ptr = 0; |
|
|
|
if ((!client->impl.header_line_buf[0])||((client->impl.header_line_buf[0] == '\r')&&(!client->impl.header_line_buf[1]))) { |
|
/* If the line is empty (or is empty with \r\n as the |
|
* line terminator), we're at the end. */ |
|
if (client->impl.read_mode == ANODE_HTTP_READ_MODE_CHUNKED_FOOTER) { |
|
/* If this is a chunked footer, we finally end the |
|
* chunked response. */ |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING; |
|
if (client->keepalive) |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE; |
|
else { |
|
client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection); |
|
client->impl.tcp_connection = (AnodeTransportTcpConnection *)0; |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED; |
|
} |
|
if (client->handler) |
|
client->handler(client); |
|
if (client->impl.freed) |
|
return; |
|
} else { |
|
/* Otherwise, this is a regular header block */ |
|
if (client->response.code == 100) { |
|
/* Ignore 100 Continue messages */ |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING; |
|
++i; break; /* Exit inner for() */ |
|
} else if ((client->response.code == 200)&&(client->method != ANODE_HTTP_HEAD)) { |
|
/* Other messages get their headers parsed to determine |
|
* how to read them. */ |
|
p1 = (char *)AnodeDictionary_get(&(client->response.headers),"transfer-encoding"); |
|
if ((p1)&&(Anode_strcaseeq(p1,"chunked"))) { |
|
/* Chunked encoding enters chunked mode */ |
|
client->impl.header_line_buf_ptr = 0; |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE; |
|
++i; break; /* Exit inner for() */ |
|
} else { |
|
/* Else we must have a Content-Length header */ |
|
p1 = (char *)AnodeDictionary_get(&(client->response.headers),"content-length"); |
|
if (!p1) { |
|
/* No chunked or content length is not supported */ |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} else { |
|
/* Enter block read mode with content length */ |
|
l = strtol(p1,(char **)0,10); |
|
if (l <= 0) { |
|
/* Zero length data is all done... */ |
|
client->impl.expecting_response_length = 0; |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING; |
|
if (client->keepalive) |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE; |
|
else { |
|
client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection); |
|
client->impl.tcp_connection = (AnodeTransportTcpConnection *)0; |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED; |
|
} |
|
|
|
if (client->handler) |
|
client->handler(client); |
|
if (client->impl.freed) |
|
return; |
|
|
|
++i; break; /* Exit inner for() */ |
|
} else { |
|
/* Else start reading... */ |
|
client->impl.expecting_response_length = (unsigned int)l; |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_BLOCK; |
|
++i; break; /* Exit inner for() */ |
|
} |
|
} |
|
} |
|
} else { |
|
/* HEAD clients or non-200 codes get headers only */ |
|
client->impl.expecting_response_length = 0; |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING; |
|
if (client->keepalive) |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE; |
|
else { |
|
client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection); |
|
client->impl.tcp_connection = (AnodeTransportTcpConnection *)0; |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED; |
|
} |
|
|
|
if (client->handler) |
|
client->handler(client); |
|
if (client->impl.freed) |
|
return; |
|
|
|
++i; break; /* Exit inner for() */ |
|
} |
|
} |
|
} else { |
|
/* Otherwise this is another header, add to dictionary */ |
|
AnodeDictionary_read( |
|
&(client->response.headers), |
|
client->impl.header_line_buf, |
|
"\r\n", |
|
": \t", |
|
"", |
|
(char)0, |
|
1, |
|
1 |
|
); |
|
} |
|
} else { |
|
client->impl.header_line_buf[client->impl.header_line_buf_ptr++] = ((const char *)data)[i]; |
|
if (client->impl.header_line_buf_ptr >= sizeof(client->impl.header_line_buf)) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} |
|
} |
|
} |
|
break; |
|
case ANODE_HTTP_READ_MODE_BLOCK: |
|
if ((client->response.data_length + client->impl.expecting_response_length) > client->impl.response_data_capacity) |
|
client->response.data = realloc(client->response.data,client->impl.response_data_capacity = (client->response.data_length + client->impl.expecting_response_length)); |
|
|
|
for(;((i<data_length)&&(client->impl.expecting_response_length));++i) { |
|
((char *)client->response.data)[client->response.data_length++] = ((const char *)data)[i]; |
|
--client->impl.expecting_response_length; |
|
} |
|
|
|
if (!client->impl.expecting_response_length) { |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING; |
|
if (client->keepalive) |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE; |
|
else { |
|
client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection); |
|
client->impl.tcp_connection = (AnodeTransportTcpConnection *)0; |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED; |
|
} |
|
|
|
if (client->handler) |
|
client->handler(client); |
|
if (client->impl.freed) |
|
return; |
|
} |
|
break; |
|
case ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE: |
|
for(;i<data_length;++i) { |
|
if (((const char *)data)[i] == '\n') { |
|
client->impl.header_line_buf[client->impl.header_line_buf_ptr] = (char)0; |
|
client->impl.header_line_buf_ptr = 0; |
|
|
|
p1 = client->impl.header_line_buf; |
|
while (*p1) { |
|
if ((*p1 == ';')||(*p1 == ' ')||(*p1 == '\r')||(*p1 == '\n')||(*p1 == '\t')) { |
|
*p1 = (char)0; |
|
break; |
|
} else ++p1; |
|
} |
|
|
|
if (client->impl.header_line_buf[0]) { |
|
l = strtol(client->impl.header_line_buf,(char **)0,16); |
|
if (l <= 0) { |
|
/* Zero length ends chunked and enters footer mode */ |
|
client->impl.expecting_response_length = 0; |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_FOOTER; |
|
} else { |
|
/* Otherwise the next chunk is to be read */ |
|
client->impl.expecting_response_length = (unsigned int)l; |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_DATA; |
|
} |
|
++i; break; /* Exit inner for() */ |
|
} |
|
} else { |
|
client->impl.header_line_buf[client->impl.header_line_buf_ptr++] = ((const char *)data)[i]; |
|
if (client->impl.header_line_buf_ptr >= sizeof(client->impl.header_line_buf)) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE; |
|
AnodeHttpClient_close_and_fail(client); |
|
return; |
|
} |
|
} |
|
} |
|
break; |
|
case ANODE_HTTP_READ_MODE_CHUNKED_DATA: |
|
if ((client->response.data_length + client->impl.expecting_response_length) > client->impl.response_data_capacity) |
|
client->response.data = realloc(client->response.data,client->impl.response_data_capacity = (client->response.data_length + client->impl.expecting_response_length)); |
|
|
|
for(;((i<data_length)&&(client->impl.expecting_response_length));++i) { |
|
((char *)client->response.data)[client->response.data_length++] = ((const char *)data)[i]; |
|
--client->impl.expecting_response_length; |
|
} |
|
|
|
if (!client->impl.expecting_response_length) |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
static void AnodeHttpClient_tcp_available_for_write_handler( |
|
AnodeTransportEngine *transport, |
|
AnodeTransportTcpConnection *connection) |
|
{ |
|
struct AnodeHttpClient *client; |
|
unsigned int i,j; |
|
int n; |
|
|
|
if (!(client = (struct AnodeHttpClient *)(connection->ptr))) |
|
return; |
|
if (client->impl.freed) { |
|
transport->tcp_close(transport,connection); |
|
return; |
|
} |
|
|
|
if (client->impl.phase == ANODE_HTTP_REQUEST_PHASE_SEND) { |
|
n = client->impl.transport_engine->tcp_send(client->impl.transport_engine,client->impl.tcp_connection,(const void *)client->impl.outbuf,(int)client->impl.outbuf_len); |
|
if (n < 0) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_SERVER_CLOSED_CONNECTION; |
|
AnodeHttpClient_close_and_fail(client); |
|
} else if (n > 0) { |
|
for(i=0,j=(client->impl.outbuf_len - (unsigned int)n);i<j;++i) |
|
client->impl.outbuf[i] = client->impl.outbuf[i + (unsigned int)n]; |
|
client->impl.outbuf_len -= (unsigned int)n; |
|
|
|
if ((client->method == ANODE_HTTP_POST)&&(client->data)&&(client->data_length)) { |
|
i = sizeof(client->impl.outbuf) - client->impl.outbuf_len; |
|
j = client->data_length - client->impl.request_data_ptr; |
|
if (i > j) |
|
i = j; |
|
Anode_memcpy((client->impl.outbuf + client->impl.outbuf_len),client->data,i); |
|
client->impl.request_data_ptr += i; |
|
client->impl.outbuf_len += i; |
|
} |
|
|
|
if (!client->impl.outbuf_len) { |
|
client->impl.transport_engine->tcp_stop_writing(client->impl.transport_engine,client->impl.tcp_connection); |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_RECEIVE; |
|
} |
|
} |
|
} else client->impl.transport_engine->tcp_stop_writing(client->impl.transport_engine,client->impl.tcp_connection); |
|
} |
|
|
|
static void AnodeHttpClient_dns_result_handler( |
|
AnodeTransportEngine *transport, |
|
void *ptr, |
|
int error_code, |
|
const char *name, |
|
const AnodeTransportIpAddress *ip_addresses, |
|
unsigned int ip_address_count, |
|
const AnodeAddress *anode_address) |
|
{ |
|
struct AnodeHttpClient *client; |
|
AnodeTransportIpEndpoint to_endpoint; |
|
|
|
if (!(client = (struct AnodeHttpClient *)ptr)) |
|
return; |
|
if (client->impl.freed) |
|
return; |
|
|
|
if ((error_code)||(!ip_address_count)) { |
|
if (client->impl.phase == ANODE_HTTP_REQUEST_PHASE_RESOLVE) { |
|
client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_DNS_RESOLVE_FAILED; |
|
AnodeHttpClient_close_and_fail(client); |
|
} |
|
} else { |
|
client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CONNECT; |
|
Anode_memcpy(&to_endpoint.address,ip_addresses,sizeof(AnodeTransportIpAddress)); |
|
to_endpoint.port = (client->uri.port > 0) ? client->uri.port : 80; |
|
client->impl.transport_engine->tcp_connect( |
|
client->impl.transport_engine, |
|
client, |
|
&AnodeHttpClient_tcp_outgoing_connect_handler, |
|
&AnodeHttpClient_tcp_connection_terminated_handler, |
|
&AnodeHttpClient_tcp_receive_handler, |
|
&AnodeHttpClient_tcp_available_for_write_handler, |
|
&to_endpoint); |
|
} |
|
} |
|
|
|
struct AnodeHttpClient *AnodeHttpClient_new(AnodeTransportEngine *transport_engine) |
|
{ |
|
struct AnodeHttpClient *req = malloc(sizeof(struct AnodeHttpClient)); |
|
Anode_zero(req,sizeof(struct AnodeHttpClient)); |
|
|
|
AnodeDictionary_init(&(req->headers),0); |
|
AnodeDictionary_init(&(req->response.headers),0); |
|
|
|
req->impl.transport_engine = transport_engine; |
|
|
|
return req; |
|
} |
|
|
|
void AnodeHttpClient_send(struct AnodeHttpClient *client) |
|
{ |
|
client->response.code = 0; |
|
client->response.data_length = 0; |
|
AnodeDictionary_clear(&(client->response.headers)); |
|
|
|
client->impl.request_data_ptr = 0; |
|
client->impl.expecting_response_length = 0; |
|
client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING; |
|
client->impl.outbuf_len = 0; |
|
|
|
if (!client->impl.tcp_connection) { |
|
client->impl.transport_engine->dns_resolve( |
|
client->impl.transport_engine, |
|
&AnodeHttpClient_dns_result_handler, |
|
client, |
|
client->uri.host, |
|
ANODE_TRANSPORT_DNS_QUERY_ALWAYS, |
|
ANODE_TRANSPORT_DNS_QUERY_IF_NO_PREVIOUS, |
|
ANODE_TRANSPORT_DNS_QUERY_NEVER); |
|
} else AnodeHttpClient_do_initiate_client(client); |
|
} |
|
|
|
void AnodeHttpClient_free(struct AnodeHttpClient *client) |
|
{ |
|
AnodeDictionary_destroy(&(client->headers)); |
|
AnodeDictionary_destroy(&(client->response.headers)); |
|
|
|
if (client->impl.tcp_connection) { |
|
client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection); |
|
client->impl.tcp_connection = (AnodeTransportTcpConnection *)0; |
|
} |
|
|
|
if (client->response.data) |
|
free(client->response.data); |
|
|
|
client->impl.freed = 1; |
|
client->impl.transport_engine->run_later(client->impl.transport_engine,client,&free); |
|
}
|
|
|