-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.c
133 lines (125 loc) · 4.43 KB
/
client.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "utils.c"
// TODO: Switch from writing to stdout to stderr where appropriate
int main(int argc, char* argv[]) {
struct addrinfo hints;
struct addrinfo *addrInfo;
if (argc < 2 || strcmp(argv[1], "--help") == 0) {
printf("One argument required. The host to connect to.");
exit(1);
}
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
// TODO: Could also use inet_conenct instead of what's below to make this shorter.
// It absracts away many of these details.
// TODO: Support https??? Also just support all kinds of ports...
// TODO: Actually parse the argument. In particular this doesn't work for anything
// that has a non '/' request. See below in the GET request and this connection code
// here.
if (getaddrinfo(argv[2], "http", &hints, &addrInfo) != 0) {
perror("Couldn't get address info");
exit(2);
}
struct addrinfo *current;
int socketFd;
int count = 0;
for (current = addrInfo; current != NULL; current = addrInfo->ai_next) {
count = count + 1;
if (count > 100) {
break;
}
if (current->ai_socktype != SOCK_STREAM) {
printf("Socket type isn't stream...");
}
socketFd = socket(current->ai_family, current->ai_socktype, current->ai_protocol);
if (socketFd == -1) {
perror("Socket didn't work");
continue;
}
if (connect(socketFd, current->ai_addr, current->ai_addrlen) != -1) {
break;
}
close(socketFd);
}
if (current == NULL) {
fprintf(stderr, "Could not connect to any socket");
exit(1);
}
freeaddrinfo(addrInfo);
char request[1000];
sprintf(request, "GET / HTTP/1.1\r\nUser-Agent: KyleTest\r\nHost:%s\r\nConnection: close\r\n\r\n", argv[1]);
if (write(socketFd, request, strlen(request)) != strlen(request)) {
perror("Error writing");
exit(3);
}
// Sample of what I would do to pipeline requests...
// If I wanted to handle this particular request I would need to implement redirection handling as it returns a 301
// Note that to get this to work I would have to remove Connection: close from the header above.
// This would mean handling more header metadata (either content-length or Transfer-Encoding: chunked.
// Both of these make the implementation slightly more complicated, though not in a way I'm
// interested in right now.
//char* input2 = "GET /news HTTP/1.1\r\nUser-Agent: KyleTest\r\nHost:www.google.com\r\n\r\n";
//if (write(socketFd, input2, strlen(input2)) != strlen(input2)) {
// perror("Ending sending /news");
// exit(3);
//}
int responseCode = getResponseCode(socketFd);
if (responseCode < 200 || responseCode > 299) {
printf("Don't handle non-2xx response code: %d", responseCode);
}
// Get headers. Note that there are quite a few details about how to parse headers
// which I'm ignoring for now. In particular they can span multiple lines.
int inHeader = 1;
char line[1000];
for (;;) {
int numRead = readLine(socketFd, line, 1000);
if (inHeader) {
// TODO: Should have a command line flag to print the headers.
//printf("Header %s\n: ", line);
}
if (numRead == 0 && inHeader == 1) {
// We got an empty line indicating the end of the headers
inHeader = 0;
} else if (numRead == 0) {
// We're at the end...
break;
}
//printf("%s\n", line);
if (inHeader == 0) {
printf("%s", line);
}
memset(line, 0, numRead);
}
return 0;
}
// Read the first line of the response and parses out the response code.
// Right now this assumes that the response is HTTP/1.1. It doesn't handle
// earlier clients.
// TODO: Just return a three-element array here. That's what we should have...
int getResponseCode(socketFd) {
int bufferSize = 1000 * sizeof(char);
char* line = malloc(bufferSize);
// Get response code line
int numRead = readLine(socketFd, line, bufferSize);
if (numRead == 0) {
printf("Error invalid response");
exit(0);
}
strsep(&line, " ");
// Return code should be the second element
// TODO: Do I need to free any memory here?? What's happening to line??
char* codeStr = strsep(&line, " ");
int returnCode = strtol(codeStr, (char**) NULL, 10);
return returnCode;
}