From 179c0afe568c4666ad4c3da228413823d86c6ea3 Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Wed, 1 Aug 2018 01:37:22 -0500 Subject: [PATCH] Support for HTTP redirects. --- http.c | 119 +++++++++++++++++++++++++++++++++++++----------- http.h | 3 ++ main.c | 11 ++--- session.h | 7 ++- tcpconnection.c | 12 ++--- 5 files changed, 110 insertions(+), 42 deletions(-) diff --git a/http.c b/http.c index 353347c..e1abe91 100644 --- a/http.c +++ b/http.c @@ -14,6 +14,7 @@ #include "http.h" #include "tcpconnection.h" #include "strcasecmp.h" +#include "seturl.h" #define buffTypePointer 0x0000 /* For TCPIPReadTCP() */ #define buffTypeHandle 0x0001 @@ -70,31 +71,52 @@ Boolean BuildHTTPRequest(Session *sess, char *resourceStr) { } while (round++ == 0); sess->httpRequestRange = sess->httpRequest + rangeOffset; + + UpdateRequestRange(sess, sess->desiredStart, sess->desiredEnd); + return TRUE; } + void UpdateRequestRange(Session *sess, unsigned long start, unsigned long end) { int count = 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; } -#pragma debug -1 enum RequestResult DoHTTPRequest(Session *sess) { top:; rlrBuff rlrBuff = {0}; Word tcpError; - Boolean wantRedirect = FALSE; + Boolean wantRedirect = FALSE, gotRedirect = FALSE; enum RequestResult result; + + sess->responseCode = 0; /* Send out request */ result = NETWORK_ERROR; - tcpError = TCPIPWriteTCP(sess->ipid, sess->httpRequest, - sess->httpRequestLen, TRUE, FALSE); - if (tcpError || toolerror()) - goto errorReturn; + unsigned int netErrors = 0; + do { + if (!sess->tcpLoggedIn || netErrors) { + if (StartTCPConnection(sess) != 0) + goto errorReturn; + } + tcpError = TCPIPWriteTCP(sess->ipid, sess->httpRequest, + sess->httpRequestLen, TRUE, FALSE); + if (tcpError || toolerror()) { + if (netErrors == 0) { + netErrors++; + continue; + } else { + goto errorReturn; + } + } + } while (0); /* Get response status line & headers */ LongWord startTime = GetTick(); @@ -158,7 +180,7 @@ top:; response++; - switch(sess->responseCode) { + switch((unsigned int)sess->responseCode) { /* "Partial content" response, as expected */ case 206: break; @@ -190,21 +212,25 @@ top:; while (response < responseEnd - 2) { enum ResponseHeader header = UNKNOWN_HEADER; - if (strncasecmp(response, "Content-Range:", 14) == 0) { - response += 14; - header = CONTENT_RANGE; - } else if (strncasecmp(response, "Content-Length:", 15) == 0) { - response += 15; - header = CONTENT_LENGTH; - } else if (strncasecmp(response, "Transfer-Encoding:", 18) == 0) { - response += 18; - header = TRANSFER_ENCODING; - } else if (strncasecmp(response, "Content-Encoding:", 17) == 0) { - response += 17; - header = CONTENT_ENCODING; - } else if (strncasecmp(response, "Location:", 9) == 0) { - response += 9; - header = LOCATION; + if (wantRedirect) { + if (strncasecmp(response, "Location:", 9) == 0) { + response += 9; + header = LOCATION; + } + } else { + if (strncasecmp(response, "Content-Range:", 14) == 0) { + response += 14; + header = CONTENT_RANGE; + } else if (strncasecmp(response, "Content-Length:", 15) == 0) { + response += 15; + header = CONTENT_LENGTH; + } else if (strncasecmp(response, "Transfer-Encoding:", 18) == 0) { + response += 18; + header = TRANSFER_ENCODING; + } else if (strncasecmp(response, "Content-Encoding:", 17) == 0) { + response += 17; + header = CONTENT_ENCODING; + } } while ((*response == ' ' || *response == '\t') && response < responseEnd) @@ -240,7 +266,9 @@ top:; case CONTENT_LENGTH: errno = 0; sess->contentLength = strtoul(response, &endPtr, 10); - if (errno || sess->contentLength == 0) + if (errno) + goto errorReturn; + if (sess->contentLength == 0 && !wantRedirect) goto errorReturn; response = endPtr; break; @@ -254,7 +282,23 @@ top:; } 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 */ case UNKNOWN_HEADER: @@ -275,14 +319,35 @@ top:; response++; } - /* Wanted redirect, but to Location header found */ + /* Wanted redirect: Retry with new location if we got it. */ if (wantRedirect) { - result = UNSUPPORTED_RESPONSE; + if (gotRedirect) { + DisposeHandle(rlrBuff.rlrBuffHandle); + goto top; + } else { + result = UNSUPPORTED_RESPONSE; + 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); - return REQUEST_SUCCESSFUL; + return result; errorReturn: if (rlrBuff.rlrBuffHandle != NULL) { diff --git a/http.h b/http.h index 042b8ba..3cd9f08 100644 --- a/http.h +++ b/http.h @@ -12,6 +12,9 @@ enum RequestResult { EXCESSIVE_REDIRECTS, UNSUPPORTED_RESPONSE, UNSUPPORTED_HEADER_VALUE, + REDIRECT_ERROR, + NOT_DESIRED_CONTENT, + DIFFERENT_LENGTH, /* Might be considered successful later */ }; Boolean BuildHTTPRequest(Session *sess, char *resourceStr); diff --git a/main.c b/main.c index a81ed71..236312e 100644 --- a/main.c +++ b/main.c @@ -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 */ -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) { @@ -59,15 +59,10 @@ int main(int argc, char **argv) { } printf("=========\n"); printf("\n", i); - - int err; - if ((err = StartTCPConnection(&sess)) != 0) { - printf("StartTCPConnection error %i\n", err); - goto exit; - } - + enum RequestResult requestResult = DoHTTPRequest(&sess); printf("RequestResult %i\n", requestResult); + printf("Response code %lu\n", sess.responseCode); if (requestResult == REQUEST_SUCCESSFUL) { printf("rangeStart = %lu\n", sess.rangeStart); diff --git a/session.h b/session.h index 527da7e..5e980bc 100644 --- a/session.h +++ b/session.h @@ -24,7 +24,7 @@ typedef struct Session { LongWord httpRequestLen; /* HTTP response code */ - unsigned long responseCode; + LongWord responseCode; /* IP address and TCP port of host */ LongWord ipAddr; @@ -42,6 +42,11 @@ typedef struct Session { LongWord rangeStart, rangeEnd, totalLength; /* Value reported by server in Content-Length header */ LongWord contentLength; + + /* Desired start/end of range for next request */ + LongWord desiredStart, desiredEnd; + /* Expected length of disk image */ + LongWord expectedLength; } Session; #endif diff --git a/tcpconnection.c b/tcpconnection.c index 80150e6..275502c 100644 --- a/tcpconnection.c +++ b/tcpconnection.c @@ -1,7 +1,7 @@ #pragma noroot -#define aspNetworkErr 1 -#define aspNoRespErr 2 +#define NETWORK_ERR 1 +#define NO_RESP_ERR 2 #include #include @@ -26,15 +26,15 @@ Word StartTCPConnection(Session *sess) { sess->ipid = TCPIPLogin(userid(), sess->ipAddr, sess->port, 0, 0x40); if (toolerror()) - return aspNetworkErr; + return NETWORK_ERR; tcperr = TCPIPOpenTCP(sess->ipid); if (toolerror()) { TCPIPLogout(sess->ipid); - return aspNetworkErr; + return NETWORK_ERR; } else if (tcperr != tcperrOK) { TCPIPLogout(sess->ipid); - return aspNoRespErr; + return NO_RESP_ERR; } initialTime = GetTick(); @@ -45,7 +45,7 @@ Word StartTCPConnection(Session *sess) { if (mySRBuff.srState != TCPSESTABLISHED) { TCPIPAbortTCP(sess->ipid); TCPIPLogout(sess->ipid); - return aspNoRespErr; + return NO_RESP_ERR; } sess->tcpLoggedIn = TRUE;