从零开始一个http服务器(三)
代码地址 : https://github.com/flamedancer/cserver
git checkout step3
运行:
gcc request.h request.c response.h response.c main.c tools/utils.c tools/utils.h && ./a.out
测试:
浏览器打开 :9734/
观察response结构
定义并返回response
测试
观察response结构上一节,我们成功解析了http的request,但是我们在浏览器访问我们的地址:9734/ 还是无法正常显示。这是因为我们没有给浏览器返回它能读懂的信息。这一节我们的目标是让浏览器正确的显示信息。什么样的才是浏览器能读懂的信息呢?不妨我们用telnet来模拟向百度主页发一个http request,来看看百度主页返回的是什么信息。
伪造一个http request的字符串,注意 headers 中的 Host 代表我们要访问的主机地址。
再用telnet连接 并指定80端口(80为http默认端口,telnet默认端口为23), telnet 80
复制黏贴上面我们构造的字符串回车后,你应该能看到如下类似的返回结果:
从HTTP/1.1 200 OK开始就是百度放回给我们的结果。让人惊喜的是这种结构和request很类型,除了第一行外。仔细看看:
第一行为 http版本号 response返回码 response返回结果描述
第二行开始为headers
空行后,接body
定义并返回response根据response的结构有的信息定义我们的结构体.
/* response.h */ #include <stdio.h> #include "tools/utils.h" #include "request.h" struct http_response { char * version; char * code; // 状态返回码 char * desc; // 返回描述 struct Map * headers; char * body; }; void initHttpResponse(struct http_response * response); void doResponse( struct http_request * request, FILE * stream ); void outputToFile( struct http_response * response, FILE * stream );构造我们的response数据, 我们每次都返回相同的数据.
/* response.c */ #include <stdio.h> /* fprintf NULL */ #include <string.h> /* strlen */ #include "response.h" #include "request.h" #include "tools/utils.h" void initHttpResponse(struct http_response * response) { response->version = NULL; response->code = NULL; response->desc = NULL; response->headers = NULL; response->body = NULL; } void doResponse(struct http_request * request, FILE * stream) { struct http_response responseInstance; struct http_response * response = &responseInstance; initHttpResponse(response); response->version = "HTTP/1.1"; response->code = "200"; response->desc = "OK"; char * content = "<html>hello everyone</html>"; char content_len[25]; sprintf(content_len, "%lu", strlen(content)); struct Item * item = newItem( "Content-Length", content_len ); struct Map map_instance; initMap(&map_instance); response->headers = &map_instance; mapPush(response->headers, item); response->body = content; outputToFile(response, stream); // clean releaseMap(request->headers); } void outputToFile(struct http_response * response, FILE * stream) { // output version code desc int r = fprintf(stream, "%s %s %s \r\n", response->version, response->code, response->desc ); // output headers struct Map* map = response->headers; struct List* list; struct Item* item; int print_item_cnt = 0; for(int i=0; i<map->table_len; i++) { list = map->table[i]; if(list == NULL) { continue; } item = list->start; while(item != NULL) { fprintf(stream, "%s: %s\r\n", item->key, item->value ); item = item->next; } } // output body if(response->body != NULL) { fprintf(stream, "\r\n%s", response->body); } }写一个测试用例,将本应向客服端发送的数据输出到stdout
/* test/responseTest.c test cmd: gcc ../request.h ../request.c ../response.h ../response.c ../tools/utils.h ../tools/utils.c responseTest.c && ./a.out */ #include <stdio.h> #include "../request.h" #include "../response.h" int main() { struct http_request request; char data[] = "POST / HTTP/1.1\r\nContent-Length: 3\r\n\r\n111"; struct Map headers; request.headers = &headers; parse_request(&request, data); doResponse(&request, stdout); }