mirror of
https://github.com/sheumann/NetDisk.git
synced 2025-02-20 01:29:05 +00:00
Add code to parse status line and headers of an HTTP response to our request.
This commit is contained in:
parent
7c0a2b8c35
commit
133c7f17b1
244
http.c
244
http.c
@ -3,9 +3,34 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
|
#include <tcpip.h>
|
||||||
|
#include <misctool.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <orca.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
|
#include "tcpconnection.h"
|
||||||
|
#include "strcasecmp.h"
|
||||||
|
|
||||||
|
#define buffTypePointer 0x0000 /* For TCPIPReadTCP() */
|
||||||
|
#define buffTypeHandle 0x0001
|
||||||
|
#define buffTypeNewHandle 0x0002
|
||||||
|
|
||||||
|
#define HTTP_RESPONSE_TIMEOUT 15 /* seconds to wait for response headers */
|
||||||
|
|
||||||
|
#define MAX_REDIRECTS 5
|
||||||
|
|
||||||
|
enum ResponseHeader {
|
||||||
|
UNKNOWN_HEADER = 0,
|
||||||
|
CONTENT_RANGE,
|
||||||
|
CONTENT_LENGTH,
|
||||||
|
TRANSFER_ENCODING,
|
||||||
|
CONTENT_ENCODING,
|
||||||
|
LOCATION,
|
||||||
|
};
|
||||||
|
|
||||||
Boolean BuildHTTPRequest(Session *sess, char *resourceStr) {
|
Boolean BuildHTTPRequest(Session *sess, char *resourceStr) {
|
||||||
int sizeNeeded = 0;
|
int sizeNeeded = 0;
|
||||||
@ -49,5 +74,222 @@ Boolean BuildHTTPRequest(Session *sess, char *resourceStr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UpdateRequestRange(Session *sess, unsigned long start, unsigned long end) {
|
void UpdateRequestRange(Session *sess, unsigned long start, unsigned long end) {
|
||||||
snprintf(sess->httpRequestRange, 10+1+10+5, "%lu-%lu\r\n\r\n", start, end);
|
int count =
|
||||||
|
snprintf(sess->httpRequestRange, 10+1+10+5, "%lu-%lu\r\n\r\n", start, 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;
|
||||||
|
enum RequestResult result;
|
||||||
|
|
||||||
|
/* Send out request */
|
||||||
|
result = NETWORK_ERROR;
|
||||||
|
tcpError = TCPIPWriteTCP(sess->ipid, sess->httpRequest,
|
||||||
|
sess->httpRequestLen, TRUE, FALSE);
|
||||||
|
if (tcpError || toolerror())
|
||||||
|
goto errorReturn;
|
||||||
|
|
||||||
|
/* Get response status line & headers */
|
||||||
|
LongWord startTime = GetTick();
|
||||||
|
do {
|
||||||
|
TCPIPPoll();
|
||||||
|
tcpError = TCPIPReadLineTCP(sess->ipid,
|
||||||
|
(void*)((LongWord)"\p\r\n\r\n" | 0x80000000),
|
||||||
|
buffTypeNewHandle, (Ref)NULL,
|
||||||
|
0xFFFFFF, &rlrBuff);
|
||||||
|
if (tcpError || toolerror())
|
||||||
|
goto errorReturn;
|
||||||
|
} while (rlrBuff.rlrBuffCount == 0
|
||||||
|
&& GetTick() - startTime < HTTP_RESPONSE_TIMEOUT * 60);
|
||||||
|
|
||||||
|
result = NO_RESPONSE;
|
||||||
|
if (!rlrBuff.rlrIsDataFlag)
|
||||||
|
goto errorReturn;
|
||||||
|
|
||||||
|
result = INVALID_RESPONSE;
|
||||||
|
/* Response must be at least long enough for a status line & final CRLF */
|
||||||
|
if (rlrBuff.rlrBuffCount < 8+1+3+1+2+2)
|
||||||
|
goto errorReturn;
|
||||||
|
|
||||||
|
HLock(rlrBuff.rlrBuffHandle);
|
||||||
|
|
||||||
|
char *response = *rlrBuff.rlrBuffHandle;
|
||||||
|
char *responseEnd = response + rlrBuff.rlrBuffCount;
|
||||||
|
/* Make response a C-string. Specifically, it will end "CR LF NUL NUL". */
|
||||||
|
response[rlrBuff.rlrBuffCount - 2] = '\0';
|
||||||
|
response[rlrBuff.rlrBuffCount - 1] = '\0';
|
||||||
|
|
||||||
|
/* Parse status line of HTTP response */
|
||||||
|
char *endPtr;
|
||||||
|
|
||||||
|
if (strncmp(response, "HTTP/1.", 7) != 0)
|
||||||
|
goto errorReturn;
|
||||||
|
response += 7;
|
||||||
|
if (!(*response >= '1' && *response <= '9'))
|
||||||
|
goto errorReturn;
|
||||||
|
response += 1;
|
||||||
|
if (*response != ' ')
|
||||||
|
goto errorReturn;
|
||||||
|
response += 1;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
sess->responseCode = strtoul(response, &endPtr, 10);
|
||||||
|
if (errno || sess->responseCode > 999 || endPtr != response + 3)
|
||||||
|
goto errorReturn;
|
||||||
|
response += 3;
|
||||||
|
|
||||||
|
if (*response != ' ')
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
|
||||||
|
while (*response != '\r' && response < responseEnd)
|
||||||
|
response++;
|
||||||
|
response++;
|
||||||
|
|
||||||
|
if (*response != '\n' && response < responseEnd)
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
|
||||||
|
|
||||||
|
switch(sess->responseCode) {
|
||||||
|
/* "Partial content" response, as expected */
|
||||||
|
case 206:
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Redirect responses */
|
||||||
|
case 300: case 301: case 302: case 307: case 308:
|
||||||
|
if (sess->redirectCount < MAX_REDIRECTS) {
|
||||||
|
sess->redirectCount++;
|
||||||
|
wantRedirect = TRUE;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
result = EXCESSIVE_REDIRECTS;
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = UNSUPPORTED_RESPONSE;
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse response headers */
|
||||||
|
sess->rangeStart = 0;
|
||||||
|
sess->rangeEnd = 0;
|
||||||
|
sess->totalLength = 0;
|
||||||
|
sess->contentLength = 0;
|
||||||
|
|
||||||
|
result = UNSUPPORTED_HEADER_VALUE;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((*response == ' ' || *response == '\t') && response < responseEnd)
|
||||||
|
response++;
|
||||||
|
|
||||||
|
switch (header) {
|
||||||
|
case CONTENT_RANGE:
|
||||||
|
if (strncasecmp(response, "bytes ", 6) != 0)
|
||||||
|
goto errorReturn;
|
||||||
|
response += 6;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
sess->rangeStart = strtoul(response, &response, 10);
|
||||||
|
if (errno)
|
||||||
|
goto errorReturn;
|
||||||
|
if (*response != '-')
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
|
||||||
|
sess->rangeEnd = strtoul(response, &response, 10);
|
||||||
|
if (errno || sess->rangeEnd == 0)
|
||||||
|
goto errorReturn;
|
||||||
|
if (*response != '/')
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
|
||||||
|
/* require numeric total length, not indeterminate '*' */
|
||||||
|
sess->totalLength = strtoul(response, &response, 10);
|
||||||
|
if (errno || sess->totalLength == 0)
|
||||||
|
goto errorReturn;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONTENT_LENGTH:
|
||||||
|
errno = 0;
|
||||||
|
sess->contentLength = strtoul(response, &endPtr, 10);
|
||||||
|
if (errno || sess->contentLength == 0)
|
||||||
|
goto errorReturn;
|
||||||
|
response = endPtr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONTENT_ENCODING:
|
||||||
|
case TRANSFER_ENCODING:
|
||||||
|
if (strcasecmp(response, "identity") == 0) {
|
||||||
|
response += 8;
|
||||||
|
} else {
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOCATION: /*TODO*/ ;
|
||||||
|
|
||||||
|
/* Unknown headers: ignored */
|
||||||
|
case UNKNOWN_HEADER:
|
||||||
|
default:
|
||||||
|
while (*response != '\r' && response < responseEnd)
|
||||||
|
response++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((*response == ' ' || *response == '\t') && response < responseEnd)
|
||||||
|
response++;
|
||||||
|
|
||||||
|
if (*response != '\r')
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
if (*response != '\n')
|
||||||
|
goto errorReturn;
|
||||||
|
response++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wanted redirect, but to Location header found */
|
||||||
|
if (wantRedirect) {
|
||||||
|
result = UNSUPPORTED_RESPONSE;
|
||||||
|
goto errorReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposeHandle(rlrBuff.rlrBuffHandle);
|
||||||
|
return REQUEST_SUCCESSFUL;
|
||||||
|
|
||||||
|
errorReturn:
|
||||||
|
if (rlrBuff.rlrBuffHandle != NULL) {
|
||||||
|
DisposeHandle(rlrBuff.rlrBuffHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error condition on this TCP connection means it can't be reused. */
|
||||||
|
EndTCPConnection(sess);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
11
http.h
11
http.h
@ -4,7 +4,18 @@
|
|||||||
#include <types.h>
|
#include <types.h>
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
|
|
||||||
|
enum RequestResult {
|
||||||
|
REQUEST_SUCCESSFUL = 0,
|
||||||
|
NETWORK_ERROR,
|
||||||
|
NO_RESPONSE,
|
||||||
|
INVALID_RESPONSE,
|
||||||
|
EXCESSIVE_REDIRECTS,
|
||||||
|
UNSUPPORTED_RESPONSE,
|
||||||
|
UNSUPPORTED_HEADER_VALUE,
|
||||||
|
};
|
||||||
|
|
||||||
Boolean BuildHTTPRequest(Session *sess, char *resourceStr);
|
Boolean BuildHTTPRequest(Session *sess, char *resourceStr);
|
||||||
void UpdateRequestRange(Session *sess, unsigned long start, unsigned long end);
|
void UpdateRequestRange(Session *sess, unsigned long start, unsigned long end);
|
||||||
|
enum RequestResult DoHTTPRequest(Session *sess);
|
||||||
|
|
||||||
#endif
|
#endif
|
38
main.c
38
main.c
@ -11,18 +11,12 @@
|
|||||||
#include "seturl.h"
|
#include "seturl.h"
|
||||||
#include "hostname.h"
|
#include "hostname.h"
|
||||||
|
|
||||||
#define buffTypePointer 0x0000 /* For TCPIPReadTCP() */
|
|
||||||
#define buffTypeHandle 0x0001
|
|
||||||
#define buffTypeNewHandle 0x0002
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
http://archive.org/download/a2gs_System_1.0_1986_Apple_FW/System_1.0_1986_Apple_FW.2mg
|
http://archive.org/download/a2gs_System_1.0_1986_Apple_FW/System_1.0_1986_Apple_FW.2mg
|
||||||
redirects to:
|
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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static char delimitStr[] = "\p\r\n\r\n";
|
|
||||||
|
|
||||||
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://ia800505.us.archive.org/16/items/a2gs_System_1.0_1986_Apple_FW/System_1.0_1986_Apple_FW.2mg";
|
||||||
|
|
||||||
|
|
||||||
@ -72,32 +66,16 @@ int main(int argc, char **argv) {
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
TCPIPWriteTCP(sess.ipid, sess.httpRequest, strlen(sess.httpRequest), TRUE, FALSE);
|
enum RequestResult requestResult = DoHTTPRequest(&sess);
|
||||||
|
printf("RequestResult %i\n", requestResult);
|
||||||
|
|
||||||
rlrBuff rlrBuff = {0};
|
if (requestResult == REQUEST_SUCCESSFUL) {
|
||||||
Word tcpError;
|
printf("rangeStart = %lu\n", sess.rangeStart);
|
||||||
do {
|
printf("rangeEnd = %lu\n", sess.rangeEnd);
|
||||||
TCPIPPoll();
|
printf("totalLength = %lu\n", sess.totalLength);
|
||||||
tcpError = TCPIPReadLineTCP(sess.ipid,
|
printf("contentLength = %lu\n", sess.contentLength);
|
||||||
(void*)((LongWord)delimitStr | 0x80000000),
|
|
||||||
buffTypeNewHandle, (Ref)NULL,
|
|
||||||
0xFFFFFF, &rlrBuff);
|
|
||||||
if (tcpError || toolerror()) {
|
|
||||||
printf("tcpError = %u, toolerror = %u\n", tcpError, toolerror());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (rlrBuff.rlrBuffCount == 0);
|
|
||||||
|
|
||||||
printf("Response:\n");
|
|
||||||
printf("=========\n");
|
|
||||||
for (int i = 0; i < rlrBuff.rlrBuffCount; i++) {
|
|
||||||
char ch = ((char*)*(rlrBuff.rlrBuffHandle))[i];
|
|
||||||
if (ch != '\r')
|
|
||||||
putchar(ch);
|
|
||||||
}
|
}
|
||||||
printf("=========\n");
|
|
||||||
printf("Response size = %lu\n", rlrBuff.rlrBuffCount);
|
|
||||||
|
|
||||||
EndTCPConnection(&sess);
|
EndTCPConnection(&sess);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
13
session.h
13
session.h
@ -20,6 +20,11 @@ typedef struct Session {
|
|||||||
char *httpRequest;
|
char *httpRequest;
|
||||||
/* Pointer to the range part within httpRequest */
|
/* Pointer to the range part within httpRequest */
|
||||||
char *httpRequestRange;
|
char *httpRequestRange;
|
||||||
|
/* Length of HTTP request */
|
||||||
|
LongWord httpRequestLen;
|
||||||
|
|
||||||
|
/* HTTP response code */
|
||||||
|
unsigned long responseCode;
|
||||||
|
|
||||||
/* IP address and TCP port of host */
|
/* IP address and TCP port of host */
|
||||||
LongWord ipAddr;
|
LongWord ipAddr;
|
||||||
@ -29,6 +34,14 @@ typedef struct Session {
|
|||||||
char hostName[257];
|
char hostName[257];
|
||||||
/* Time (GetTick) of last DNS lookup */
|
/* Time (GetTick) of last DNS lookup */
|
||||||
LongWord dnsTime;
|
LongWord dnsTime;
|
||||||
|
|
||||||
|
/* Number of redirects followed */
|
||||||
|
Word redirectCount;
|
||||||
|
|
||||||
|
/* Values reported by server in Content-Range header */
|
||||||
|
LongWord rangeStart, rangeEnd, totalLength;
|
||||||
|
/* Value reported by server in Content-Length header */
|
||||||
|
LongWord contentLength;
|
||||||
} Session;
|
} Session;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
2
seturl.c
2
seturl.c
@ -21,7 +21,7 @@ SetURL(Session *sess, char *url, Boolean permissive, Boolean partialOK) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(unsigned int i = 0; url[i] != '\0'; i++) {
|
for(unsigned int i = 0; url[i] != '\0'; i++) {
|
||||||
if (url[i] <= ' ' && url[i] >= 0) {
|
if ((unsigned char)url[i] <= ' ') {
|
||||||
return INVALID_CHARACTER_IN_URL;
|
return INVALID_CHARACTER_IN_URL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@ URLParts ParseURL(char *url) {
|
|||||||
/* Detect relative URL (absolute-path reference only) */
|
/* Detect relative URL (absolute-path reference only) */
|
||||||
if (url[0] == '/') {
|
if (url[0] == '/') {
|
||||||
if (url[1] != '/') {
|
if (url[1] != '/') {
|
||||||
url++;
|
urlParts.path = url + 1;
|
||||||
goto pathPart;
|
return urlParts;
|
||||||
} else {
|
} else {
|
||||||
urlParts.errorFound = 1;
|
urlParts.errorFound = 1;
|
||||||
return urlParts;
|
return urlParts;
|
||||||
@ -90,7 +90,6 @@ URLParts ParseURL(char *url) {
|
|||||||
urlParts.port = sep + 1;
|
urlParts.port = sep + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pathPart:
|
|
||||||
return urlParts;
|
return urlParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user