Support for HTTP redirects.

This commit is contained in:
Stephen Heumann 2018-08-01 01:37:22 -05:00
parent 133c7f17b1
commit 179c0afe56
5 changed files with 110 additions and 42 deletions

87
http.c
View File

@ -14,6 +14,7 @@
#include "http.h" #include "http.h"
#include "tcpconnection.h" #include "tcpconnection.h"
#include "strcasecmp.h" #include "strcasecmp.h"
#include "seturl.h"
#define buffTypePointer 0x0000 /* For TCPIPReadTCP() */ #define buffTypePointer 0x0000 /* For TCPIPReadTCP() */
#define buffTypeHandle 0x0001 #define buffTypeHandle 0x0001
@ -70,31 +71,52 @@ Boolean BuildHTTPRequest(Session *sess, char *resourceStr) {
} while (round++ == 0); } while (round++ == 0);
sess->httpRequestRange = sess->httpRequest + rangeOffset; sess->httpRequestRange = sess->httpRequest + rangeOffset;
UpdateRequestRange(sess, sess->desiredStart, sess->desiredEnd);
return TRUE; return TRUE;
} }
void UpdateRequestRange(Session *sess, unsigned long start, unsigned long end) { void UpdateRequestRange(Session *sess, unsigned long start, unsigned long end) {
int count = int count =
snprintf(sess->httpRequestRange, 10+1+10+5, "%lu-%lu\r\n\r\n", start, end); snprintf(sess->httpRequestRange, 10+1+10+5, "%lu-%lu\r\n\r\n", start, end);
sess->desiredStart = start;
sess->desiredEnd = end;
sess->httpRequestLen = sess->httpRequestRange - sess->httpRequest + count; sess->httpRequestLen = sess->httpRequestRange - sess->httpRequest + count;
} }
#pragma debug -1
enum RequestResult DoHTTPRequest(Session *sess) { enum RequestResult DoHTTPRequest(Session *sess) {
top:; top:;
rlrBuff rlrBuff = {0}; rlrBuff rlrBuff = {0};
Word tcpError; Word tcpError;
Boolean wantRedirect = FALSE; Boolean wantRedirect = FALSE, gotRedirect = FALSE;
enum RequestResult result; enum RequestResult result;
sess->responseCode = 0;
/* Send out request */ /* Send out request */
result = NETWORK_ERROR; result = NETWORK_ERROR;
unsigned int netErrors = 0;
do {
if (!sess->tcpLoggedIn || netErrors) {
if (StartTCPConnection(sess) != 0)
goto errorReturn;
}
tcpError = TCPIPWriteTCP(sess->ipid, sess->httpRequest, tcpError = TCPIPWriteTCP(sess->ipid, sess->httpRequest,
sess->httpRequestLen, TRUE, FALSE); sess->httpRequestLen, TRUE, FALSE);
if (tcpError || toolerror()) if (tcpError || toolerror()) {
if (netErrors == 0) {
netErrors++;
continue;
} else {
goto errorReturn; goto errorReturn;
}
}
} while (0);
/* Get response status line & headers */ /* Get response status line & headers */
LongWord startTime = GetTick(); LongWord startTime = GetTick();
@ -158,7 +180,7 @@ top:;
response++; response++;
switch(sess->responseCode) { switch((unsigned int)sess->responseCode) {
/* "Partial content" response, as expected */ /* "Partial content" response, as expected */
case 206: case 206:
break; break;
@ -190,6 +212,12 @@ top:;
while (response < responseEnd - 2) { while (response < responseEnd - 2) {
enum ResponseHeader header = UNKNOWN_HEADER; enum ResponseHeader header = UNKNOWN_HEADER;
if (wantRedirect) {
if (strncasecmp(response, "Location:", 9) == 0) {
response += 9;
header = LOCATION;
}
} else {
if (strncasecmp(response, "Content-Range:", 14) == 0) { if (strncasecmp(response, "Content-Range:", 14) == 0) {
response += 14; response += 14;
header = CONTENT_RANGE; header = CONTENT_RANGE;
@ -202,9 +230,7 @@ top:;
} else if (strncasecmp(response, "Content-Encoding:", 17) == 0) { } else if (strncasecmp(response, "Content-Encoding:", 17) == 0) {
response += 17; response += 17;
header = CONTENT_ENCODING; header = CONTENT_ENCODING;
} else if (strncasecmp(response, "Location:", 9) == 0) { }
response += 9;
header = LOCATION;
} }
while ((*response == ' ' || *response == '\t') && response < responseEnd) while ((*response == ' ' || *response == '\t') && response < responseEnd)
@ -240,7 +266,9 @@ top:;
case CONTENT_LENGTH: case CONTENT_LENGTH:
errno = 0; errno = 0;
sess->contentLength = strtoul(response, &endPtr, 10); sess->contentLength = strtoul(response, &endPtr, 10);
if (errno || sess->contentLength == 0) if (errno)
goto errorReturn;
if (sess->contentLength == 0 && !wantRedirect)
goto errorReturn; goto errorReturn;
response = endPtr; response = endPtr;
break; break;
@ -254,7 +282,23 @@ top:;
} }
break; break;
case LOCATION: /*TODO*/ ; case LOCATION:
endPtr = response;
char c;
while ((c = *endPtr)!=0 && c!='\r' && c!='\n' && c!=' ' && c!='\t')
endPtr++;
if (c == '\0' || c == '\n')
goto errorReturn;
if (wantRedirect) {
*endPtr = '\0';
if (SetURL(sess, response, FALSE, TRUE) != SETURL_SUCCESSFUL) {
result = REDIRECT_ERROR;
goto errorReturn;
}
*endPtr = c;
response = endPtr;
gotRedirect = TRUE;
}
/* Unknown headers: ignored */ /* Unknown headers: ignored */
case UNKNOWN_HEADER: case UNKNOWN_HEADER:
@ -275,14 +319,35 @@ top:;
response++; response++;
} }
/* Wanted redirect, but to Location header found */ /* Wanted redirect: Retry with new location if we got it. */
if (wantRedirect) { if (wantRedirect) {
if (gotRedirect) {
DisposeHandle(rlrBuff.rlrBuffHandle);
goto top;
} else {
result = UNSUPPORTED_RESPONSE; result = UNSUPPORTED_RESPONSE;
goto errorReturn; goto errorReturn;
} }
}
/* See if we got what we wanted */
if (sess->expectedLength != 0 && sess->totalLength != sess->expectedLength) {
result = DIFFERENT_LENGTH;
goto errorReturn;
}
result = NOT_DESIRED_CONTENT;
if (sess->rangeStart != sess->desiredStart)
goto errorReturn;
if (sess->rangeEnd != sess->desiredEnd)
goto errorReturn;
if (sess->contentLength != 0
&& sess->contentLength != (sess->desiredEnd - sess->desiredStart + 1))
goto errorReturn;
result = REQUEST_SUCCESSFUL;
DisposeHandle(rlrBuff.rlrBuffHandle); DisposeHandle(rlrBuff.rlrBuffHandle);
return REQUEST_SUCCESSFUL; return result;
errorReturn: errorReturn:
if (rlrBuff.rlrBuffHandle != NULL) { if (rlrBuff.rlrBuffHandle != NULL) {

3
http.h
View File

@ -12,6 +12,9 @@ enum RequestResult {
EXCESSIVE_REDIRECTS, EXCESSIVE_REDIRECTS,
UNSUPPORTED_RESPONSE, UNSUPPORTED_RESPONSE,
UNSUPPORTED_HEADER_VALUE, UNSUPPORTED_HEADER_VALUE,
REDIRECT_ERROR,
NOT_DESIRED_CONTENT,
DIFFERENT_LENGTH, /* Might be considered successful later */
}; };
Boolean BuildHTTPRequest(Session *sess, char *resourceStr); Boolean BuildHTTPRequest(Session *sess, char *resourceStr);

9
main.c
View File

@ -17,7 +17,7 @@ redirects to:
http://ia800505.us.archive.org/16/items/a2gs_System_1.0_1986_Apple_FW/System_1.0_1986_Apple_FW.2mg http://ia800505.us.archive.org/16/items/a2gs_System_1.0_1986_Apple_FW/System_1.0_1986_Apple_FW.2mg
*/ */
char *defaultURL = "http://ia800505.us.archive.org/16/items/a2gs_System_1.0_1986_Apple_FW/System_1.0_1986_Apple_FW.2mg"; char *defaultURL = "http://archive.org/download/a2gs_System_1.0_1986_Apple_FW/System_1.0_1986_Apple_FW.2mg";
int main(int argc, char **argv) { int main(int argc, char **argv) {
@ -60,14 +60,9 @@ int main(int argc, char **argv) {
printf("=========\n"); printf("=========\n");
printf("\n", i); printf("\n", i);
int err;
if ((err = StartTCPConnection(&sess)) != 0) {
printf("StartTCPConnection error %i\n", err);
goto exit;
}
enum RequestResult requestResult = DoHTTPRequest(&sess); enum RequestResult requestResult = DoHTTPRequest(&sess);
printf("RequestResult %i\n", requestResult); printf("RequestResult %i\n", requestResult);
printf("Response code %lu\n", sess.responseCode);
if (requestResult == REQUEST_SUCCESSFUL) { if (requestResult == REQUEST_SUCCESSFUL) {
printf("rangeStart = %lu\n", sess.rangeStart); printf("rangeStart = %lu\n", sess.rangeStart);

View File

@ -24,7 +24,7 @@ typedef struct Session {
LongWord httpRequestLen; LongWord httpRequestLen;
/* HTTP response code */ /* HTTP response code */
unsigned long responseCode; LongWord responseCode;
/* IP address and TCP port of host */ /* IP address and TCP port of host */
LongWord ipAddr; LongWord ipAddr;
@ -42,6 +42,11 @@ typedef struct Session {
LongWord rangeStart, rangeEnd, totalLength; LongWord rangeStart, rangeEnd, totalLength;
/* Value reported by server in Content-Length header */ /* Value reported by server in Content-Length header */
LongWord contentLength; LongWord contentLength;
/* Desired start/end of range for next request */
LongWord desiredStart, desiredEnd;
/* Expected length of disk image */
LongWord expectedLength;
} Session; } Session;
#endif #endif

View File

@ -1,7 +1,7 @@
#pragma noroot #pragma noroot
#define aspNetworkErr 1 #define NETWORK_ERR 1
#define aspNoRespErr 2 #define NO_RESP_ERR 2
#include <tcpip.h> #include <tcpip.h>
#include <stdlib.h> #include <stdlib.h>
@ -26,15 +26,15 @@ Word StartTCPConnection(Session *sess) {
sess->ipid = sess->ipid =
TCPIPLogin(userid(), sess->ipAddr, sess->port, 0, 0x40); TCPIPLogin(userid(), sess->ipAddr, sess->port, 0, 0x40);
if (toolerror()) if (toolerror())
return aspNetworkErr; return NETWORK_ERR;
tcperr = TCPIPOpenTCP(sess->ipid); tcperr = TCPIPOpenTCP(sess->ipid);
if (toolerror()) { if (toolerror()) {
TCPIPLogout(sess->ipid); TCPIPLogout(sess->ipid);
return aspNetworkErr; return NETWORK_ERR;
} else if (tcperr != tcperrOK) { } else if (tcperr != tcperrOK) {
TCPIPLogout(sess->ipid); TCPIPLogout(sess->ipid);
return aspNoRespErr; return NO_RESP_ERR;
} }
initialTime = GetTick(); initialTime = GetTick();
@ -45,7 +45,7 @@ Word StartTCPConnection(Session *sess) {
if (mySRBuff.srState != TCPSESTABLISHED) { if (mySRBuff.srState != TCPSESTABLISHED) {
TCPIPAbortTCP(sess->ipid); TCPIPAbortTCP(sess->ipid);
TCPIPLogout(sess->ipid); TCPIPLogout(sess->ipid);
return aspNoRespErr; return NO_RESP_ERR;
} }
sess->tcpLoggedIn = TRUE; sess->tcpLoggedIn = TRUE;