auto-follow location header support inside single request object

This commit is contained in:
Adam D. Ruppe 2019-12-06 13:57:03 -05:00
parent 4cf16e9248
commit 1489c4187a
1 changed files with 65 additions and 18 deletions

83
http2.d
View File

@ -590,6 +590,10 @@ struct BasicAuth {
*/ */
class HttpRequest { class HttpRequest {
/// Automatically follow a redirection?
bool followLocation = false;
private static { private static {
// we manage the actual connections. When a request is made on a particular // we manage the actual connections. When a request is made on a particular
// host, we try to reuse connections. We may open more than one connection per // host, we try to reuse connections. We may open more than one connection per
@ -627,7 +631,7 @@ class HttpRequest {
socket = new Socket(AddressFamily.INET, SocketType.STREAM); socket = new Socket(AddressFamily.INET, SocketType.STREAM);
socket.connect(new InternetAddress(host, port)); socket.connect(new InternetAddress(host, port));
debug(arsd_http2) writeln("opening to ", host, ":", port); debug(arsd_http2) writeln("opening to ", host, ":", port, " ", cast(void*) socket);
assert(socket.handle() !is socket_t.init); assert(socket.handle() !is socket_t.init);
return socket; return socket;
} }
@ -774,10 +778,12 @@ class HttpRequest {
loseSocket(request.requestParameters.host, request.requestParameters.port, request.requestParameters.ssl, sock); loseSocket(request.requestParameters.host, request.requestParameters.port, request.requestParameters.ssl, sock);
} else { } else {
// data available // data available
request.handleIncomingData(buffer[0 .. got]); auto stillAlive = request.handleIncomingData(buffer[0 .. got]);
if(request.state == HttpRequest.State.complete || request.state == HttpRequest.State.aborted) { if(!stillAlive || request.state == HttpRequest.State.complete || request.state == HttpRequest.State.aborted) {
//import std.stdio; writeln(cast(void*) sock, " ", stillAlive, " ", request.state);
inactive[inactiveCount++] = sock; inactive[inactiveCount++] = sock;
continue;
// reuse the socket for another pending request, if we can // reuse the socket for another pending request, if we can
} }
} }
@ -799,16 +805,18 @@ class HttpRequest {
if(writeSet.isSet(sock)) { if(writeSet.isSet(sock)) {
assert(request.sendBuffer.length); assert(request.sendBuffer.length);
auto sent = sock.send(request.sendBuffer); auto sent = sock.send(request.sendBuffer);
debug(arsd_http2_verbose) writeln(cast(string) request.sendBuffer); debug(arsd_http2_verbose) writeln(cast(void*) sock, "<send>", cast(string) request.sendBuffer, "</send>");
if(sent <= 0) if(sent <= 0)
throw new Exception("send error " ~ lastSocketError); throw new Exception("send error " ~ lastSocketError);
request.sendBuffer = request.sendBuffer[sent .. $]; request.sendBuffer = request.sendBuffer[sent .. $];
request.state = State.waitingForResponse; if(request.sendBuffer.length == 0) {
request.state = State.waitingForResponse;
}
} }
} }
foreach(s; inactive[0 .. inactiveCount]) { foreach(s; inactive[0 .. inactiveCount]) {
debug(arsd_http2) writeln("removing socket from active list"); debug(arsd_http2) writeln("removing socket from active list ", cast(void*) s);
activeRequestOnSocket.remove(s); activeRequestOnSocket.remove(s);
} }
} }
@ -853,8 +861,9 @@ class HttpRequest {
const(ubyte)[] leftoverDataFromLastTime; const(ubyte)[] leftoverDataFromLastTime;
void handleIncomingData(scope const ubyte[] dataIn) { bool handleIncomingData(scope const ubyte[] dataIn) {
debug(arsd_http2) writeln("handleIncomingData, state: ", state); bool stillAlive = true;
debug(arsd_http2) writeln("handleIncomingData, state: ", state);
if(state == State.waitingForResponse) { if(state == State.waitingForResponse) {
state = State.readingHeaders; state = State.readingHeaders;
headerReadingState = HeaderReadingState.init; headerReadingState = HeaderReadingState.init;
@ -1119,11 +1128,25 @@ class HttpRequest {
responseData.content = cast(ubyte[]) n; responseData.content = cast(ubyte[]) n;
//responseData.content ~= cast(ubyte[]) uncompress.flush(); //responseData.content ~= cast(ubyte[]) uncompress.flush();
} }
state = State.complete; if(followLocation && responseData.location.length) {
responseData.contentText = cast(string) responseData.content; static bool first = true;
// FIXME if(!first) asm { int 3; }
//if(closeSocketWhenComplete) populateFromInfo(Uri(responseData.location), HttpVerb.GET);
//socket.close(); import std.stdio; writeln("redirected to ", responseData.location);
first = false;
responseData = HttpResponse.init;
headerReadingState = HeaderReadingState.init;
bodyReadingState = BodyReadingState.init;
state = State.unsent;
stillAlive = false;
sendPrivate(false);
} else {
state = State.complete;
responseData.contentText = cast(string) responseData.content;
// FIXME
//if(closeSocketWhenComplete)
//socket.close();
}
} }
} }
} }
@ -1132,6 +1155,8 @@ class HttpRequest {
leftoverDataFromLastTime = data.dup; leftoverDataFromLastTime = data.dup;
else else
leftoverDataFromLastTime = null; leftoverDataFromLastTime = null;
return stillAlive;
} }
this() { this() {
@ -1139,7 +1164,15 @@ class HttpRequest {
/// ///
this(Uri where, HttpVerb method) { this(Uri where, HttpVerb method) {
populateFromInfo(where, method);
}
/// Final url after any redirections
string finalUrl;
void populateFromInfo(Uri where, HttpVerb method) {
auto parts = where; auto parts = where;
finalUrl = where.toString();
requestParameters.method = method; requestParameters.method = method;
requestParameters.host = parts.host; requestParameters.host = parts.host;
requestParameters.port = cast(ushort) parts.port; requestParameters.port = cast(ushort) parts.port;
@ -1211,6 +1244,10 @@ class HttpRequest {
/// Sends the request asynchronously. /// Sends the request asynchronously.
void send() { void send() {
sendPrivate(true);
}
private void sendPrivate(bool advance) {
if(state != State.unsent && state != State.aborted) if(state != State.unsent && state != State.aborted)
return; // already sent return; // already sent
string headers; string headers;
@ -1239,15 +1276,26 @@ class HttpRequest {
sendBuffer = cast(ubyte[]) headers ~ requestParameters.bodyData; sendBuffer = cast(ubyte[]) headers ~ requestParameters.bodyData;
// import std.stdio; writeln("******* ", sendBuffer);
responseData = HttpResponse.init; responseData = HttpResponse.init;
responseData.requestParameters = requestParameters; responseData.requestParameters = requestParameters;
bodyBytesSent = 0; bodyBytesSent = 0;
bodyBytesReceived = 0; bodyBytesReceived = 0;
state = State.pendingAvailableConnection; state = State.pendingAvailableConnection;
pending ~= this; bool alreadyPending = false;
foreach(req; pending)
if(req is this) {
alreadyPending = true;
break;
}
if(!alreadyPending) {
pending ~= this;
}
HttpRequest.advanceConnections(); if(advance)
HttpRequest.advanceConnections();
} }
@ -1345,9 +1393,6 @@ class HttpClient {
bool useHttp11 = true; /// bool useHttp11 = true; ///
bool acceptGzip = true; /// bool acceptGzip = true; ///
/// Automatically follow a redirection?
bool followLocation = false; /// NOT IMPLEMENTED
/// ///
@property Uri location() { @property Uri location() {
return currentUrl; return currentUrl;
@ -1362,6 +1407,8 @@ class HttpClient {
currentDomain = where.host; currentDomain = where.host;
auto request = new HttpRequest(currentUrl, method); auto request = new HttpRequest(currentUrl, method);
request.followLocation = true;
request.requestParameters.userAgent = userAgent; request.requestParameters.userAgent = userAgent;
request.requestParameters.authorization = authorization; request.requestParameters.authorization = authorization;