Files
linguist/samples/Game Maker Language/faucet-http.gml
2014-01-19 15:10:28 +00:00

1470 lines
55 KiB
Plaintext

#define __http_init
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Creates global.__HttpClient
// real __http_init()
global.__HttpClient = object_add();
object_set_persistent(global.__HttpClient, true);
#define __http_split
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// real __http_split(string text, delimeter delimeter, real limit)
// Splits string into items
// text - string comma-separated values
// delimeter - delimeter to split by
// limit if non-zero, maximum times to split text
// When limited, the remaining text will be left as the last item.
// E.g. splitting "1,2,3,4,5" with delimeter "," and limit 2 yields this list:
// "1", "2", "3,4,5"
// return value - ds_list containing strings of values
var text, delimeter, limit;
text = argument0;
delimeter = argument1;
limit = argument2;
var list, count;
list = ds_list_create();
count = 0;
while (string_pos(delimeter, text) != 0)
{
ds_list_add(list, string_copy(text, 1, string_pos(delimeter,text) - 1));
text = string_copy(text, string_pos(delimeter, text) + string_length(delimeter), string_length(text) - string_pos(delimeter, text));
count += 1;
if (limit and count == limit)
break;
}
ds_list_add(list, text);
return list;
#define __http_parse_url
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Parses a URL into its components
// real __http_parse_url(string url)
// Return value is a ds_map containing keys for the different URL parts: (or -1 on failure)
// "url" - the URL which was passed in
// "scheme" - the URL scheme (e.g. "http")
// "host" - the hostname (e.g. "example.com" or "127.0.0.1")
// "port" - the port (e.g. 8000) - this is a real, unlike the others
// "abs_path" - the absolute path (e.g. "/" or "/index.html")
// "query" - the query string (e.g. "a=b&c=3")
// Parts which are not included will not be in the map
// e.g. http://example.com will not have the "port", "path" or "query" keys
// This will *only* work properly for URLs of format:
// scheme ":" "//" host [ ":" port ] [ abs_path [ "?" query ]]"
// where [] denotes an optional component
// file: URLs will *not* work as they lack the authority (host:port) component
// It will not work correctly for IPv6 host values
var url;
url = argument0;
var map;
map = ds_map_create();
ds_map_add(map, 'url', url);
// before scheme
var colonPos;
// Find colon for end of scheme
colonPos = string_pos(':', url);
// No colon - bad URL
if (colonPos == 0)
return -1;
ds_map_add(map, 'scheme', string_copy(url, 1, colonPos - 1));
url = string_copy(url, colonPos + 1, string_length(url) - colonPos);
// before double slash
// remove slashes (yes this will screw up file:// but who cares)
while (string_char_at(url, 1) == '/')
url = string_copy(url, 2, string_length(url) - 1);
// before hostname
var slashPos, colonPos;
// Find slash for beginning of path
slashPos = string_pos('/', url);
// No slash ahead - http://host format with no ending slash
if (slashPos == 0)
{
// Find : for beginning of port
colonPos = string_pos(':', url);
}
else
{
// Find : for beginning of port prior to /
colonPos = string_pos(':', string_copy(url, 1, slashPos - 1));
}
// No colon - no port
if (colonPos == 0)
{
// There was no slash
if (slashPos == 0)
{
ds_map_add(map, 'host', url);
return map;
}
// There was a slash
else
{
ds_map_add(map, 'host', string_copy(url, 1, slashPos - 1));
url = string_copy(url, slashPos, string_length(url) - slashPos + 1);
}
}
// There's a colon - port specified
else
{
// There was no slash
if (slashPos == 0)
{
ds_map_add(map, 'host', string_copy(url, 1, colonPos - 1));
ds_map_add(map, 'port', real(string_copy(url, colonPos + 1, string_length(url) - colonPos)));
return map;
}
// There was a slash
else
{
ds_map_add(map, 'host', string_copy(url, 1, colonPos - 1));
url = string_copy(url, colonPos + 1, string_length(url) - colonPos);
slashPos = string_pos('/', url);
ds_map_add(map, 'port', real(string_copy(url, 1, slashPos - 1)));
url = string_copy(url, slashPos, string_length(url) - slashPos + 1);
}
}
// before path
var queryPos;
queryPos = string_pos('?', url);
// There's no ? - no query
if (queryPos == 0)
{
ds_map_add(map, 'abs_path', url);
return map;
}
else
{
ds_map_add(map, 'abs_path', string_copy(url, 1, queryPos - 1));
ds_map_add(map, 'query', string_copy(url, queryPos + 1, string_length(url) - queryPos));
return map;
}
// Return -1 upon unlikely error
ds_map_destroy(map);
return -1;
#define __http_resolve_url
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Takes a base URL and a URL reference and applies it to the base URL
// Returns resulting absolute URL
// string __http_resolve_url(string baseUrl, string refUrl)
// Return value is a string containing the new absolute URL, or "" on failure
// Works only for restricted URL syntax as understood by by http_resolve_url
// The sole restriction of which is that only scheme://authority/path URLs work
// This notably excludes file: URLs which lack the authority component
// As described by RFC3986:
// URI-reference = URI / relative-ref
// relative-ref = relative-part [ "?" query ] [ "#" fragment ]
// relative-part = "//" authority path-abempty
// / path-absolute
// / path-noscheme
// / path-empty
// However http_resolve_url does *not* deal with fragments
// Algorithm based on that of section 5.2.2 of RFC 3986
var baseUrl, refUrl;
baseUrl = argument0;
refUrl = argument1;
// Parse base URL
var urlParts;
urlParts = __http_parse_url(baseUrl);
if (urlParts == -1)
return '';
// Try to parse reference URL
var refUrlParts, canParseRefUrl;
refUrlParts = __http_parse_url(refUrl);
canParseRefUrl = (refUrlParts != -1);
if (refUrlParts != -1)
ds_map_destroy(refUrlParts);
var result;
result = '';
// Parsing of reference URL succeeded - it's absolute and we're done
if (canParseRefUrl)
{
result = refUrl;
}
// Begins with '//' - scheme-relative URL
else if (string_copy(refUrl, 1, 2) == '//' and string_length(refUrl) > 2)
{
result = ds_map_find_value(urlParts, 'scheme') + ':' + refUrl;
}
// Is or begins with '/' - absolute path relative URL
else if (((string_char_at(refUrl, 1) == '/' and string_length(refUrl) > 1) or refUrl == '/')
// Doesn't begin with ':' and is not blank - relative path relative URL
or (string_char_at(refUrl, 1) != ':' and string_length(refUrl) > 0))
{
// Find '?' for query
var queryPos;
queryPos = string_pos('?', refUrl);
// No query
if (queryPos == 0)
{
refUrl = __http_resolve_path(ds_map_find_value(urlParts, 'abs_path'), refUrl);
ds_map_replace(urlParts, 'abs_path', refUrl);
if (ds_map_exists(urlParts, 'query'))
ds_map_delete(urlParts, 'query');
}
// Query exists, split
else
{
var path, query;
path = string_copy(refUrl, 1, queryPos - 1);
query = string_copy(refUrl, queryPos + 1, string_length(relUrl) - queryPos);
path = __http_resolve_path(ds_map_find_value(urlParts, 'abs_path'), path);
ds_map_replace(urlParts, 'abs_path', path);
if (ds_map_exists(urlParts, 'query'))
ds_map_replace(urlParts, 'query', query);
else
ds_map_add(urlParts, 'query', query);
}
result = __http_construct_url(urlParts);
}
ds_map_destroy(urlParts);
return result;
#define __http_resolve_path
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Takes a base path and a path reference and applies it to the base path
// Returns resulting absolute path
// string __http_resolve_path(string basePath, string refPath)
// Return value is a string containing the new absolute path
// Deals with UNIX-style / paths, not Windows-style \ paths
// Can be used to clean up .. and . in non-absolute paths too ('' as basePath)
var basePath, refPath;
basePath = argument0;
refPath = argument1;
// refPath begins with '/' (is absolute), we can ignore all of basePath
if (string_char_at(refPath, 1) == '/')
{
basePath = refPath;
refPath = '';
}
var parts, refParts;
parts = __http_split(basePath, '/', 0);
refParts = __http_split(refPath, '/', 0);
if (refPath != '')
{
// Find last part of base path
var lastPart;
lastPart = ds_list_find_value(parts, ds_list_size(parts) - 1);
// If it's not blank (points to a file), remove it
if (lastPart != '')
{
ds_list_delete(parts, ds_list_size(parts) - 1);
}
// Concatenate refParts to end of parts
var i;
for (i = 0; i < ds_list_size(refParts); i += 1)
ds_list_add(parts, ds_list_find_value(refParts, i));
}
// We now don't need refParts any more
ds_list_destroy(refParts);
// Deal with '..' and '.'
for (i = 0; i < ds_list_size(parts); i += 1)
{
var part;
part = ds_list_find_value(parts, i);
if (part == '.')
{
if (i == 1 or i == ds_list_size(parts) - 1)
ds_list_replace(parts, i, '');
else
ds_list_delete(parts, i);
i -= 1;
continue;
}
else if (part == '..')
{
if (i > 1)
{
ds_list_delete(parts, i - 1);
ds_list_delete(part, i);
i -= 2;
}
else
{
ds_list_replace(parts, i, '');
i -= 1;
}
continue;
}
else if (part == '' and i != 0 and i != ds_list_size(parts) - 1)
{
ds_list_delete(parts, i);
i -= 1;
continue;
}
}
// Reconstruct path from parts
var path;
path = '';
for (i = 0; i < ds_list_size(parts); i += 1)
{
if (i != 0)
path += '/';
path += ds_list_find_value(parts, i);
}
ds_map_destroy(parts);
return path;
#define __http_parse_hex
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Takes a lowercase hexadecimal string and returns its integer value
// real __http_parse_hex(string hexString)
// Return value is the whole number value (or -1 if invalid)
// Only works for whole numbers (non-fractional numbers >= 0) and lowercase hex
var hexString;
hexString = argument0;
var result, hexValues;
result = 0;
hexValues = "0123456789abcdef";
var i;
for (i = 1; i <= string_length(hexString); i += 1) {
result *= 16;
var digit;
digit = string_pos(string_char_at(hexString, i), hexValues) - 1;
if (digit == -1)
return -1;
result += digit;
}
return result;
#define __http_construct_url
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Constructs an URL from its components (as http_parse_url would return)
// string __http_construct_url(real parts)
// Return value is the string of the constructed URL
// Keys of parts map:
// "scheme" - the URL scheme (e.g. "http")
// "host" - the hostname (e.g. "example.com" or "127.0.0.1")
// "port" - the port (e.g. 8000) - this is a real, unlike the others
// "abs_path" - the absolute path (e.g. "/" or "/index.html")
// "query" - the query string (e.g. "a=b&c=3")
// Parts which are omitted will be omitted in the URL
// e.g. http://example.com lacks "port", "path" or "query" keys
// This will *only* work properly for URLs of format:
// scheme ":" "//" host [ ":" port ] [ abs_path [ "?" query ]]"
// where [] denotes an optional component
// file: URLs will *not* work as they lack the authority (host:port) component
// Should work correctly for IPv6 host values, but bare in mind parse_url won't
var parts;
parts = argument0;
var url;
url = '';
url += ds_map_find_value(parts, 'scheme');
url += '://';
url += ds_map_find_value(parts, 'host');
if (ds_map_exists(parts, 'port'))
url += ':' + string(ds_map_find_value(parts, 'port'));
if (ds_map_exists(parts, 'abs_path'))
{
url += ds_map_find_value(parts, 'abs_path');
if (ds_map_exists(parts, 'query'))
url += '?' + ds_map_find_value(parts, 'query');
}
return url;
#define __http_prepare_request
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Internal function - prepares request
// void __http_prepare_request(real client, string url, real headers)
// client - HttpClient object to prepare
// url - URL to send GET request to
// headers - ds_map of extra headers to send, -1 if none
var client, url, headers;
client = argument0;
url = argument1;
headers = argument2;
var parsed;
parsed = __http_parse_url(url);
if (parsed == -1)
show_error("Error when making HTTP GET request - can't parse URL: " + url, true);
if (!ds_map_exists(parsed, 'port'))
ds_map_add(parsed, 'port', 80);
if (!ds_map_exists(parsed, 'abs_path'))
ds_map_add(parsed, 'abs_path', '/');
with (client)
{
destroyed = false;
CR = chr(13);
LF = chr(10);
CRLF = CR + LF;
socket = tcp_connect(ds_map_find_value(parsed, 'host'), ds_map_find_value(parsed, 'port'));
state = 0;
errored = false;
error = '';
linebuf = '';
line = 0;
statusCode = -1;
reasonPhrase = '';
responseBody = buffer_create();
responseBodySize = -1;
responseBodyProgress = -1;
responseHeaders = ds_map_create();
requestUrl = url;
requestHeaders = headers;
// Request = Request-Line ; Section 5.1
// *(( general-header ; Section 4.5
// | request-header ; Section 5.3
// | entity-header ) CRLF) ; Section 7.1
// CRLF
// [ message-body ] ; Section 4.3
// "The Request-Line begins with a method token, followed by the
// Request-URI and the protocol version, and ending with CRLF. The
// elements are separated by SP characters. No CR or LF is allowed
// except in the final CRLF sequence."
if (ds_map_exists(parsed, 'query'))
write_string(socket, 'GET ' + ds_map_find_value(parsed, 'abs_path') + '?' + ds_map_find_value(parsed, 'query') + ' HTTP/1.1' + CRLF);
else
write_string(socket, 'GET ' + ds_map_find_value(parsed, 'abs_path') + ' HTTP/1.1' + CRLF);
// "A client MUST include a Host header field in all HTTP/1.1 request
// messages."
// "A "host" without any trailing port information implies the default
// port for the service requested (e.g., "80" for an HTTP URL)."
if (ds_map_find_value(parsed, 'port') == 80)
write_string(socket, 'Host: ' + ds_map_find_value(parsed, 'host') + CRLF);
else
write_string(socket, 'Host: ' + ds_map_find_value(parsed, 'host')
+ ':' + string(ds_map_find_value(parsed, 'port')) + CRLF);
// "An HTTP/1.1 server MAY assume that a HTTP/1.1 client intends to
// maintain a persistent connection unless a Connection header including
// the connection-token "close" was sent in the request."
write_string(socket, 'Connection: close' + CRLF);
// "If no Accept-Encoding field is present in a request, the server MAY
// assume that the client will accept any content coding."
write_string(socket, 'Accept-Encoding:' + CRLF);
// If headers specified
if (headers != -1)
{
var key;
// Iterate over headers map
for (key = ds_map_find_first(headers); is_string(key); key = ds_map_find_next(headers, key))
{
write_string(socket, key + ': ' + ds_map_find_value(headers, key) + CRLF);
}
}
// Send extra CRLF to terminate request
write_string(socket, CRLF);
socket_send(socket);
ds_map_destroy(parsed);
}
#define __http_parse_header
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Internal function - parses header
// real __http_parse_header(string linebuf, real line)
// Returns false if it errored (caller should return and destroy)
var linebuf, line;
linebuf = argument0;
line = argument1;
// "HTTP/1.1 header field values can be folded onto multiple lines if the
// continuation line begins with a space or horizontal tab."
if ((string_char_at(linebuf, 1) == ' ' or ord(string_char_at(linebuf, 1)) == 9))
{
if (line == 1)
{
errored = true;
error = "First header line of response can't be a continuation, right?";
return false;
}
headerValue = ds_map_find_value(responseHeaders, string_lower(headerName))
+ string_copy(linebuf, 2, string_length(linebuf) - 1);
}
// "Each header field consists
// of a name followed by a colon (":") and the field value. Field names
// are case-insensitive. The field value MAY be preceded by any amount
// of LWS, though a single SP is preferred."
else
{
var colonPos;
colonPos = string_pos(':', linebuf);
if (colonPos == 0)
{
errored = true;
error = "No colon in a header line of response";
return false;
}
headerName = string_copy(linebuf, 1, colonPos - 1);
headerValue = string_copy(linebuf, colonPos + 1, string_length(linebuf) - colonPos);
// "The field-content does not include any leading or trailing LWS:
// linear white space occurring before the first non-whitespace
// character of the field-value or after the last non-whitespace
// character of the field-value. Such leading or trailing LWS MAY be
// removed without changing the semantics of the field value."
while (string_char_at(headerValue, 1) == ' ' or ord(string_char_at(headerValue, 1)) == 9)
headerValue = string_copy(headerValue, 2, string_length(headerValue) - 1);
}
ds_map_add(responseHeaders, string_lower(headerName), headerValue);
if (string_lower(headerName) == 'content-length')
{
responseBodySize = real(headerValue);
responseBodyProgress = 0;
}
return true;
#define __http_client_step
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Steps the HTTP client (needs to be called each step or so)
var client;
client = argument0;
with (client)
{
if (errored)
exit;
// Socket error
if (socket_has_error(socket))
{
errored = true;
error = "Socket error: " + socket_error(socket);
return __http_client_destroy();
}
var available;
available = tcp_receive_available(socket);
switch (state)
{
// Receiving lines
case 0:
if (!available && tcp_eof(socket))
{
errored = true;
error = "Unexpected EOF when receiving headers/status code";
return __http_client_destroy();
}
var bytesRead, c;
for (bytesRead = 1; bytesRead <= available; bytesRead += 1)
{
c = read_string(socket, 1);
// Reached end of line
// "HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all
// protocol elements except the entity-body (see appendix 19.3 for
// tolerant applications)."
if (c == LF and string_char_at(linebuf, string_length(linebuf)) == CR)
{
// Strip trailing CR
linebuf = string_copy(linebuf, 1, string_length(linebuf) - 1);
// First line - status code
if (line == 0)
{
// "The first line of a Response message is the Status-Line, consisting
// of the protocol version followed by a numeric status code and its
// associated textual phrase, with each element separated by SP
// characters. No CR or LF is allowed except in the final CRLF sequence."
var httpVer, spacePos;
spacePos = string_pos(' ', linebuf);
if (spacePos == 0)
{
errored = true;
error = "No space in first line of response";
return __http_client_destroy();
}
httpVer = string_copy(linebuf, 1, spacePos);
linebuf = string_copy(linebuf, spacePos + 1, string_length(linebuf) - spacePos);
spacePos = string_pos(' ', linebuf);
if (spacePos == 0)
{
errored = true;
error = "No second space in first line of response";
return __http_client_destroy();
}
statusCode = real(string_copy(linebuf, 1, spacePos));
reasonPhrase = string_copy(linebuf, spacePos + 1, string_length(linebuf) - spacePos);
}
// Other line
else
{
// Blank line, end of response headers
if (linebuf == '')
{
state = 1;
// write remainder
write_buffer_part(responseBody, socket, available - bytesRead);
responseBodyProgress = available - bytesRead;
break;
}
// Header
else
{
if (!__http_parse_header(linebuf, line))
return __http_client_destroy();
}
}
linebuf = '';
line += 1;
}
else
linebuf += c;
}
break;
// Receiving response body
case 1:
write_buffer(responseBody, socket);
responseBodyProgress += available;
if (tcp_eof(socket))
{
if (ds_map_exists(responseHeaders, 'transfer-encoding'))
{
if (ds_map_find_value(responseHeaders, 'transfer-encoding') == 'chunked')
{
// Chunked transfer, let's decode it
var actualResponseBody, actualResponseSize;
actualResponseBody = buffer_create();
actualResponseBodySize = 0;
// Parse chunks
// chunk = chunk-size [ chunk-extension ] CRLF
// chunk-data CRLF
// chunk-size = 1*HEX
while (buffer_bytes_left(responseBody))
{
var chunkSize, c;
chunkSize = '';
// Read chunk size byte by byte
while (buffer_bytes_left(responseBody))
{
c = read_string(responseBody, 1);
if (c == CR or c == ';')
break;
else
chunkSize += c;
}
// We found a semicolon - beginning of chunk-extension
if (c == ';')
{
// skip all extension stuff
while (buffer_bytes_left(responseBody) && c != CR)
{
c = read_string(responseBody, 1);
}
}
// Reached end of header
if (c == CR)
{
c += read_string(responseBody, 1);
// Doesn't end in CRLF
if (c != CRLF)
{
errored = true;
error = 'header of chunk in chunked transfer did not end in CRLF';
buffer_destroy(actualResponseBody);
return __http_client_destroy();
}
// chunk-size is empty - something's up!
if (chunkSize == '')
{
errored = true;
error = 'empty chunk-size in a chunked transfer';
buffer_destroy(actualResponseBody);
return __http_client_destroy();
}
chunkSize = __http_parse_hex(chunkSize);
// Parsing of size failed - not hex?
if (chunkSize == -1)
{
errored = true;
error = 'chunk-size was not hexadecimal in a chunked transfer';
buffer_destroy(actualResponseBody);
return __http_client_destroy();
}
// Is the chunk bigger than the remaining response?
if (chunkSize + 2 > buffer_bytes_left(responseBody))
{
errored = true;
error = 'chunk-size was greater than remaining data in a chunked transfer';
buffer_destroy(actualResponseBody);
return __http_client_destroy();
}
// OK, everything's good, read the chunk
write_buffer_part(actualResponseBody, responseBody, chunkSize);
actualResponseBodySize += chunkSize;
// Check for CRLF
if (read_string(responseBody, 2) != CRLF)
{
errored = true;
error = 'chunk did not end in CRLF in a chunked transfer';
return __http_client_destroy();
}
}
else
{
errored = true;
error = 'did not find CR after reading chunk header in a chunked transfer, Faucet HTTP bug?';
return __http_client_destroy();
}
// if the chunk size is zero, then it was the last chunk
if (chunkSize == 0
// trailer headers will be present
and ds_map_exists(responseHeaders, 'trailer'))
{
// Parse header lines
var line;
line = 1;
while (buffer_bytes_left(responseBody))
{
var linebuf;
linebuf = '';
while (buffer_bytes_left(responseBody))
{
c = read_string(responseBody, 1);
if (c != CR)
linebuf += c;
else
break;
}
c += read_string(responseBody, 1);
if (c != CRLF)
{
errored = true;
error = 'trailer header did not end in CRLF in a chunked transfer';
return __http_client_destroy();
}
if (!__http_parse_header(linebuf, line))
return __http_client_destroy();
line += 1;
}
}
}
responseBodySize = actualResponseBodySize;
buffer_destroy(responseBody);
responseBody = actualResponseBody;
}
else
{
errored = true;
error = 'Unsupported Transfer-Encoding: "' + ds_map_find_value(responseHaders, 'transfer-encoding') + '"';
return __http_client_destroy();
}
}
else if (responseBodySize != -1)
{
if (responseBodyProgress < responseBodySize)
{
errored = true;
error = "Unexpected EOF, response body size is less than expected";
return __http_client_destroy();
}
}
// 301 Moved Permanently/302 Found/303 See Other/307 Moved Temporarily
if (statusCode == 301 or statusCode == 302 or statusCode == 303 or statusCode == 307)
{
if (ds_map_exists(responseHeaders, 'location'))
{
var location, resolved;
location = ds_map_find_value(responseHeaders, 'location');
resolved = __http_resolve_url(requestUrl, location);
// Resolving URL didn't fail and it's http://
if (resolved != '' and string_copy(resolved, 1, 7) == 'http://')
{
// Restart request
__http_client_destroy();
__http_prepare_request(client, resolved, requestHeaders);
}
else
{
errored = true;
error = "301, 302, 303 or 307 response with invalid or unsupported Location URL ('" + location + "') - can't redirect";
return __http_client_destroy();
}
exit;
}
else
{
errored = true;
error = "301, 302, 303 or 307 response without Location header - can't redirect";
return __http_client_destroy();
}
}
else
state = 2;
}
break;
// Done.
case 2:
break;
}
}
#define __http_client_destroy
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Clears up contents of an httpClient prior to destruction or after error
if (!destroyed) {
socket_destroy(socket);
buffer_destroy(responseBody);
ds_map_destroy(responseHeaders);
}
destroyed = true;
#define http_new_get
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Makes a GET HTTP request
// real http_new_get(string url)
// url - URL to send GET request to
// Return value is an HttpClient instance that can be passed to
// fct_http_request_status etc.
// (errors on failure to parse URL)
var url, client;
url = argument0;
if (!variable_global_exists('__HttpClient'))
__http_init();
client = instance_create(0, 0, global.__HttpClient);
__http_prepare_request(client, url, -1);
return client;
#define http_new_get_ex
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Makes a GET HTTP request with custom headers
// real http_new_get_ex(string url, real headers)
// url - URL to send GET request to
// headers - ds_map of extra headers to send
// Return value is an HttpClient instance that can be passed to
// fct_http_request_status etc.
// (errors on failure to parse URL)
var url, headers, client;
url = argument0;
headers = argument1;
if (!variable_global_exists('__HttpClient'))
__http_init();
client = instance_create(0, 0, global.__HttpClient);
__http_prepare_request(client, url, headers);
return client;
#define http_step
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Steps the HTTP client. This is what makes everything actually happen.
// Call it each step. Returns whether or not the request has finished.
// real http_step(real client)
// client - HttpClient object
// Return value is either:
// 0 - In progress
// 1 - Done or Errored
// Example usage:
// req = http_new_get("http://example.com/x.txt");
// while (http_step(req)) {}
// if (http_status_code(req) != 200) {
// // Errored!
// } else {
// // Hasn't errored, do stuff here.
// }
var client;
client = argument0;
__http_client_step(client);
if (client.errored || client.state == 2)
return 1;
else
return 0;
#define http_status_code
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Gets the status code
// real http_status_code(real client)
// client - HttpClient object
// Return value is either:
// * 0, if the request has not yet finished
// * a negative value, if there was an internal Faucet HTTP error
// * a positive value, the status code of the HTTP request
// "The Status-Code element is a 3-digit integer result code of the
// attempt to understand and satisfy the request. These codes are fully
// defined in section 10. The Reason-Phrase is intended to give a short
// textual description of the Status-Code. The Status-Code is intended
// for use by automata and the Reason-Phrase is intended for the human
// user. The client is not required to examine or display the Reason-
// Phrase."
// See also: http_reason_phrase, gets the Reason-Phrase
var client;
client = argument0;
if (client.errored)
return -1;
else if (client.state == 2)
return client.statusCode;
else
return 0;
#define http_reason_phrase
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Gets the reason phrase
// string http_reason_phrase(real client)
// client - HttpClient object
// Return value is either:
// * "", if the request has not yet finished
// * an internal Faucet HTTP error message, if there was one
// * the reason phrase of the HTTP request
// "The Status-Code element is a 3-digit integer result code of the
// attempt to understand and satisfy the request. These codes are fully
// defined in section 10. The Reason-Phrase is intended to give a short
// textual description of the Status-Code. The Status-Code is intended
// for use by automata and the Reason-Phrase is intended for the human
// user. The client is not required to examine or display the Reason-
// Phrase."
// See also: http_status_code, gets the Status-Code
var client;
client = argument0;
if (client.errored)
return client.error;
else if (client.state == 2)
return client.reasonPhrase;
else
return "";
#define http_response_body
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Gets the response body returned by an HTTP request as a buffer
// real http_response_body(real client)
// client - HttpClient object
// Return value is a buffer if client hasn't errored and is finished
var client;
client = argument0;
return client.responseBody;
#define http_response_body_size
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Gets the size of response body returned by an HTTP request
// real http_response_body_size(real client)
// client - HttpClient object
// Return value is the size in bytes, or -1 if we don't know or don't know yet
// Call this each time you use the size - it may have changed in the case of redirect
var client;
client = argument0;
return client.responseBodySize;
#define http_response_body_progress
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Gets the size of response body returned by an HTTP request which is so far downloaded
// real http_response_body_progress(real client)
// client - HttpClient object
// Return value is the size in bytes, or -1 if we haven't started yet or client has errored
var client;
client = argument0;
return client.responseBodyProgress;
#define http_response_headers
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Gets the response headers returned by an HTTP request as a ds_map
// real http_response_headers(real client)
// client - HttpClient object
// Return value is a ds_map if client hasn't errored and is finished
// All headers will have lowercase keys
// The ds_map is owned by the HttpClient, do not use ds_map_destroy() yourself
// Call when the request has finished - otherwise may be incomplete or missing
var client;
client = argument0;
return client.responseHeaders;
#define http_destroy
// ***
// This function forms part of Faucet HTTP v1.0
// https://github.com/TazeTSchnitzel/Faucet-HTTP-Extension
//
// Copyright (c) 2013-2014, Andrea Faulds <ajf@ajf.me>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// ***
// Cleans up HttpClient
// void http_destroy(real client)
// client - HttpClient object
var client;
client = argument0;
with (client)
{
__http_client_destroy();
instance_destroy();
}