Add following redirects to redbean Fetch (#226)

This commit is contained in:
Paul Kulchenko 2021-08-06 04:48:46 -07:00 committed by GitHub
parent fd76fa0016
commit b142ea7176
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 9 deletions

View file

@ -545,7 +545,19 @@ FUNCTIONS
provided, then a GET request is sent. If both URL and body parameters
are specified, then a POST request is sent. If any other method needs
to be specified (for example, PUT or DELETE), then passing a table as
the second value allows setting method and body values.
the second value allows setting method and body values as well other
options:
- followredirect (default = true): forces temporary and permanent
redirects to be followed. This behavior can be disabled by
passing `false`.
- maxredirects (default = 5): sets the number of allowed redirects
to minimize looping due to misconfigured servers. When the number
is exceeded, the result of the last redirect is returned.
When the redirect is being followed, the same method and body values
are being sent in all cases except when 303 status is returned. In
that case the method is set to GET and the body is removed before the
redirect is followed. Note that if these (method/body) values are
provided as table fields, they will be modified in place.
FormatHttpDateTime(seconds:int) → rfc1123:str
Converts UNIX timestamp to an RFC1123 string that looks like this:

View file

@ -3703,6 +3703,8 @@ static int LuaFetch(lua_State *L) {
char *conlenhdr = "";
size_t urlarglen, requestlen, paylen, bodylen;
size_t g, i, n, hdrsize;
int numredirects = 0, maxredirects = 5;
bool followredirect = true;
struct addrinfo hints = {.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
@ -3719,6 +3721,12 @@ static int LuaFetch(lua_State *L) {
lua_getfield(L, 2, "method");
// use GET by default if no method is provided
method = strtoupper(luaL_optstring(L, -1, kHttpMethod[kHttpGet]));
lua_getfield(L, 2, "followredirect");
if (lua_isboolean(L, -1)) followredirect = lua_toboolean(L, -1);
lua_getfield(L, 2, "maxredirects");
maxredirects = luaL_optinteger(L, -1, maxredirects);
lua_getfield(L, 2, "numredirects");
numredirects = luaL_optinteger(L, -1, numredirects);
lua_settop(L, 2); // drop all added elements to keep the stack balanced
} else if (lua_isnoneornil(L, 2)) {
body = "";
@ -3995,15 +4003,49 @@ static int LuaFetch(lua_State *L) {
Finished:
if (paylen && logbodies) LogBody("received", inbuf.p + hdrsize, paylen);
LOGF("FETCH HTTP%02d %d %s %`'.*s", msg.version, msg.status, urlarg,
LOGF("FETCH %s HTTP%02d %d %s %`'.*s", method, msg.version, msg.status, urlarg,
HeaderLength(kHttpServer), HeaderData(kHttpServer));
lua_pushinteger(L, msg.status);
LuaPushHeaders(L, &msg, inbuf.p);
lua_pushlstring(L, inbuf.p + hdrsize, paylen);
DestroyHttpMessage(&msg);
free(inbuf.p);
close(sock);
return 3;
if (followredirect && HasHeader(kHttpLocation) &&
(msg.status == 301 || msg.status == 308 || // permanent redirects
msg.status == 302 || msg.status == 307 || // temporary redirects
msg.status == 303 // see other; non-GET changes to GET, body lost
) && numredirects < maxredirects) {
// if 303, then remove body and set method to GET
if (msg.status == 303) {
body = "";
bodylen = 0;
method = kHttpMethod[kHttpGet];
}
// create table if needed
if (!lua_istable(L, 2)) {
lua_settop(L, 1); // pop body if present
lua_createtable(L, 0, 3); // body, method, numredirects
}
lua_pushlstring(L, body, bodylen);
lua_setfield(L, -2, "body");
lua_pushstring(L, method);
lua_setfield(L, -2, "method");
lua_pushinteger(L, numredirects + 1);
lua_setfield(L, -2, "numredirects");
// replace URL with Location header
lua_pushlstring(L, HeaderData(kHttpLocation), HeaderLength(kHttpLocation));
lua_replace(L, -3);
DestroyHttpMessage(&msg);
free(inbuf.p);
close(sock);
return LuaFetch(L);
} else {
lua_pushinteger(L, msg.status);
LuaPushHeaders(L, &msg, inbuf.p);
lua_pushlstring(L, inbuf.p + hdrsize, paylen);
DestroyHttpMessage(&msg);
free(inbuf.p);
close(sock);
return 3;
}
TransportError:
close(sock);
free(inbuf.p);