mirror of https://github.com/adamdruppe/arsd.git
bunch of improvements
This commit is contained in:
parent
f00019f9ff
commit
da1f22af2c
127
http2.d
127
http2.d
|
@ -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 = "";
|
||||||
|
|
Loading…
Reference in New Issue