bunch of improvements

This commit is contained in:
Adam D. Ruppe 2020-12-05 23:36:41 -05:00
parent f00019f9ff
commit da1f22af2c
1 changed files with 107 additions and 20 deletions

127
http2.d
View File

@ -175,7 +175,19 @@ string post(string url, string[string] args, string[string] cookies = null) {
/// ///
struct HttpResponse { struct HttpResponse {
int code; /// /++
The HTTP response code, if the response was completed, or some value < 100 if it was aborted or failed.
Code 0 - initial value, nothing happened
Code 1 - you called request.abort
Code 2 - connection refused
Code 3 - connection succeeded, but server disconnected early
Code 4 - server sent corrupted response (or this code has a bug and processed it wrong)
Code 5 - request timed out
Code >= 100 - a HTTP response
+/
int code;
string codeText; /// string codeText; ///
string httpVersion; /// string httpVersion; ///
@ -185,6 +197,15 @@ struct HttpResponse {
string contentType; /// The content type header string contentType; /// The content type header
string location; /// The location header string location; /// The location header
/++
History:
Added December 5, 2020 (version 9.1)
+/
bool wasSuccessful() {
return code >= 200 && code < 400;
}
/// the charset out of content type, if present. `null` if not. /// the charset out of content type, if present. `null` if not.
string contentTypeCharset() { string contentTypeCharset() {
auto idx = contentType.indexOf("charset="); auto idx = contentType.indexOf("charset=");
@ -840,10 +861,17 @@ class HttpRequest {
/// Waits for the request to finish or timeout, whichever comes first. /// Waits for the request to finish or timeout, whichever comes first.
HttpResponse waitForCompletion() { HttpResponse waitForCompletion() {
while(state != State.aborted && state != State.complete) { while(state != State.aborted && state != State.complete) {
if(state == State.unsent) if(state == State.unsent) {
send(); send();
if(auto err = HttpRequest.advanceConnections()) continue;
throw new Exception("waitForCompletion got err " ~ to!string(err)); }
if(auto err = HttpRequest.advanceConnections()) {
switch(err) {
case 1: throw new Exception("HttpRequest.advanceConnections returned 1: all connections timed out");
case 2: throw new Exception("HttpRequest.advanceConnections returned 2: nothing to do");
default: throw new Exception("HttpRequest.advanceConnections got err " ~ to!string(err));
}
}
} }
return responseData; return responseData;
@ -852,7 +880,9 @@ class HttpRequest {
/// Aborts this request. /// Aborts this request.
void abort() { void abort() {
this.state = State.aborted; this.state = State.aborted;
// FIXME this.responseData.code = 1;
this.responseData.codeText = "request.abort called";
// FIXME actually cancel it?
} }
HttpRequestParameters requestParameters; /// HttpRequestParameters requestParameters; ///
@ -982,6 +1012,8 @@ class HttpRequest {
HttpRequest[16] removeFromPending; HttpRequest[16] removeFromPending;
size_t removeFromPendingCount = 0; size_t removeFromPendingCount = 0;
bool hadAbortion;
// are there pending requests? let's try to send them // are there pending requests? let's try to send them
foreach(idx, pc; pending) { foreach(idx, pc; pending) {
if(removeFromPendingCount == removeFromPending.length) if(removeFromPendingCount == removeFromPending.length)
@ -989,10 +1021,26 @@ class HttpRequest {
if(pc.state == HttpRequest.State.aborted) { if(pc.state == HttpRequest.State.aborted) {
removeFromPending[removeFromPendingCount++] = pc; removeFromPending[removeFromPendingCount++] = pc;
hadAbortion = true;
continue; continue;
} }
auto socket = getOpenSocketOnHost(pc.requestParameters.host, pc.requestParameters.port, pc.requestParameters.ssl, pc.requestParameters.unixSocketPath); Socket socket;
try {
socket = getOpenSocketOnHost(pc.requestParameters.host, pc.requestParameters.port, pc.requestParameters.ssl, pc.requestParameters.unixSocketPath);
} catch(SocketException e) {
// connection refused or timed out (I should disambiguate somehow)...
pc.state = HttpRequest.State.aborted;
pc.responseData.code = 2;
pc.responseData.codeText = "connection failed";
hadAbortion = true;
removeFromPending[removeFromPendingCount++] = pc;
continue;
}
if(socket !is null) { if(socket !is null) {
activeRequestOnSocket[socket] = pc; activeRequestOnSocket[socket] = pc;
@ -1024,14 +1072,39 @@ class HttpRequest {
} }
} }
if(!hadOne) if(!hadOne) {
return 1; // automatic timeout, nothing to do if(hadAbortion)
return 0; // something got aborted, that's progress
return 2; // automatic timeout, nothing to do
}
tryAgain: tryAgain:
auto selectGot = Socket.select(readSet, writeSet, null, 10.seconds /* timeout */);
Socket[16] inactive;
int inactiveCount = 0;
void killInactives() {
foreach(s; inactive[0 .. inactiveCount]) {
debug(arsd_http2) writeln("removing socket from active list ", cast(void*) s);
activeRequestOnSocket.remove(s);
}
}
auto selectGot = Socket.select(readSet, writeSet, null, 10.seconds /* timeout */); // FIXME: adjust timeout based on the individual requests
if(selectGot == 0) { /* timeout */ if(selectGot == 0) { /* timeout */
// timeout // FIXME: individual requests should have different time outs...
return 1; foreach(sock, request; activeRequestOnSocket) {
request.state = HttpRequest.State.aborted;
request.responseData.code = 5;
request.responseData.codeText = "Request timed out";
inactive[inactiveCount++] = sock;
sock.close();
loseSocket(request.requestParameters.host, request.requestParameters.port, request.requestParameters.ssl, sock);
}
killInactives();
return 0;
// return 1; was an error to time out but now im making it on the individual request
} else if(selectGot == -1) { /* interrupted */ } else if(selectGot == -1) { /* interrupted */
/* /*
version(Posix) { version(Posix) {
@ -1042,8 +1115,6 @@ class HttpRequest {
*/ */
goto tryAgain; goto tryAgain;
} else { /* ready */ } else { /* ready */
Socket[16] inactive;
int inactiveCount = 0;
foreach(sock, request; activeRequestOnSocket) { foreach(sock, request; activeRequestOnSocket) {
if(readSet.isSet(sock)) { if(readSet.isSet(sock)) {
keep_going: keep_going:
@ -1054,14 +1125,31 @@ class HttpRequest {
} else if(got == 0) { } else if(got == 0) {
// remote side disconnected // remote side disconnected
debug(arsd_http2) writeln("remote disconnect"); debug(arsd_http2) writeln("remote disconnect");
if(request.state != State.complete) if(request.state != State.complete) {
request.state = State.aborted; request.state = State.aborted;
request.responseData.code = 3;
request.responseData.codeText = "server disconnected";
}
inactive[inactiveCount++] = sock; inactive[inactiveCount++] = sock;
sock.close(); sock.close();
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
auto stillAlive = request.handleIncomingData(buffer[0 .. got]); bool stillAlive;
try {
stillAlive = request.handleIncomingData(buffer[0 .. got]);
} catch (Exception e) {
request.state = HttpRequest.State.aborted;
request.responseData.code = 4;
request.responseData.codeText = e.msg;
inactive[inactiveCount++] = sock;
sock.close();
loseSocket(request.requestParameters.host, request.requestParameters.port, request.requestParameters.ssl, sock);
continue;
}
if(!stillAlive || 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); //import std.stdio; writeln(cast(void*) sock, " ", stillAlive, " ", request.state);
@ -1097,13 +1185,10 @@ class HttpRequest {
} }
} }
} }
foreach(s; inactive[0 .. inactiveCount]) {
debug(arsd_http2) writeln("removing socket from active list ", cast(void*) s);
activeRequestOnSocket.remove(s);
}
} }
killInactives();
// we've completed a request, are there any more pending connection? if so, send them now // we've completed a request, are there any more pending connection? if so, send them now
return 0; return 0;
@ -1168,6 +1253,8 @@ class HttpRequest {
auto parts = responseData.statusLine.splitter(" "); auto parts = responseData.statusLine.splitter(" ");
responseData.httpVersion = parts.front; responseData.httpVersion = parts.front;
parts.popFront(); parts.popFront();
if(parts.empty)
throw new Exception("Corrupted response, bad status line");
responseData.code = to!int(parts.front()); responseData.code = to!int(parts.front());
parts.popFront(); parts.popFront();
responseData.codeText = ""; responseData.codeText = "";