| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- #include "HttpSession.h"
- #include "http_parser.h"
- static int on_url(http_parser* parser, const char *at, size_t length);
- static int on_status(http_parser* parser, const char *at, size_t length);
- static int on_header_field(http_parser* parser, const char *at, size_t length);
- static int on_header_value(http_parser* parser, const char *at, size_t length);
- static int on_body(http_parser* parser, const char *at, size_t length);
- static int on_message_begin(http_parser* parser);
- static int on_headers_complete(http_parser* parser);
- static int on_message_complete(http_parser* parser);
- enum http_parser_state {
- HP_START_REQ_OR_RES,
- HP_MESSAGE_BEGIN, HP_URL,
- HP_STATUS,
- HP_HEADER_FIELD,
- HP_HEADER_VALUE,
- HP_HEADERS_COMPLETE,
- HP_BODY,
- HP_MESSAGE_COMPLETE
- };
- class Http1Session : public HttpSession {
- public:
- static http_parser_settings* cbs;
- http_parser parser;
- http_parser_state state;
- HttpPayload* submited;
- HttpPayload* parsed;
- // tmp
- std::string url; // for on_url
- std::string header_field; // for on_header_field
- std::string header_value; // for on_header_value
- std::string sendbuf; // for GetSendData
- void handle_header() {
- if (header_field.size() != 0 && header_value.size() != 0) {
- parsed->headers[header_field] = header_value;
- header_field.clear();
- header_value.clear();
- }
- }
- Http1Session() {
- if (cbs == NULL) {
- cbs = (http_parser_settings*)malloc(sizeof(http_parser_settings));
- http_parser_settings_init(cbs);
- cbs->on_message_begin = on_message_begin;
- cbs->on_url = on_url;
- cbs->on_status = on_status;
- cbs->on_header_field = on_header_field;
- cbs->on_header_value = on_header_value;
- cbs->on_headers_complete = on_headers_complete;
- cbs->on_body = on_body;
- cbs->on_message_complete = on_message_complete;
- }
- http_parser_init(&parser, HTTP_BOTH);
- parser.data = this;
- state = HP_START_REQ_OR_RES;
- submited = NULL;
- parsed = NULL;
- }
- virtual ~Http1Session() {
- }
- virtual int GetSendData(char** data, size_t* len) {
- if (!submited) {
- *data = NULL;
- *len = 0;
- return 0;
- }
- sendbuf = submited->Dump(true, true);
- submited = NULL;
- *data = (char*)sendbuf.data();
- *len = sendbuf.size();
- return sendbuf.size();
- }
- virtual int FeedRecvData(const char* data, size_t len) {
- return http_parser_execute(&parser, cbs, data, len);
- }
- virtual bool WantRecv() {
- return state != HP_MESSAGE_COMPLETE;
- }
- virtual int SubmitRequest(HttpRequest* req) {
- submited = req;
- return 0;
- }
- virtual int SubmitResponse(HttpResponse* res) {
- submited = res;
- return 0;
- }
- virtual int InitRequest(HttpRequest* req) {
- req->Reset();
- parsed = req;
- http_parser_init(&parser, HTTP_REQUEST);
- return 0;
- }
- virtual int InitResponse(HttpResponse* res) {
- res->Reset();
- parsed = res;
- http_parser_init(&parser, HTTP_RESPONSE);
- return 0;
- }
- virtual int GetError() {
- return parser.http_errno;
- }
- virtual const char* StrError(int error) {
- return http_errno_description((enum http_errno)error);
- }
- };
- http_parser_settings* Http1Session::cbs = NULL;
- int on_url(http_parser* parser, const char *at, size_t length) {
- printd("on_url:%.*s\n", (int)length, at);
- Http1Session* hss = (Http1Session*)parser->data;
- hss->state = HP_URL;
- hss->url.insert(hss->url.size(), at, length);
- return 0;
- }
- int on_status(http_parser* parser, const char *at, size_t length) {
- printd("on_status:%.*s\n", (int)length, at);
- Http1Session* hss = (Http1Session*)parser->data;
- hss->state = HP_STATUS;
- return 0;
- }
- int on_header_field(http_parser* parser, const char *at, size_t length) {
- printd("on_header_field:%.*s\n", (int)length, at);
- Http1Session* hss = (Http1Session*)parser->data;
- hss->handle_header();
- hss->state = HP_HEADER_FIELD;
- hss->header_field.insert(hss->header_field.size(), at, length);
- return 0;
- }
- int on_header_value(http_parser* parser, const char *at, size_t length) {
- printd("on_header_value:%.*s""\n", (int)length, at);
- Http1Session* hss = (Http1Session*)parser->data;
- hss->state = HP_HEADER_VALUE;
- hss->header_value.insert(hss->header_value.size(), at, length);
- return 0;
- }
- int on_body(http_parser* parser, const char *at, size_t length) {
- //printd("on_body:%.*s""\n", (int)length, at);
- Http1Session* hss = (Http1Session*)parser->data;
- hss->state = HP_BODY;
- hss->parsed->body.insert(hss->parsed->body.size(), at, length);
- return 0;
- }
- int on_message_begin(http_parser* parser) {
- printd("on_message_begin\n");
- Http1Session* hss = (Http1Session*)parser->data;
- hss->state = HP_MESSAGE_BEGIN;
- return 0;
- }
- int on_headers_complete(http_parser* parser) {
- printd("on_headers_complete\n");
- Http1Session* hss = (Http1Session*)parser->data;
- hss->handle_header();
- auto iter = hss->parsed->headers.find("content-type");
- if (iter != hss->parsed->headers.end()) {
- hss->parsed->content_type = http_content_type_enum(iter->second.c_str());
- }
- hss->parsed->http_major = parser->http_major;
- hss->parsed->http_minor = parser->http_minor;
- if (hss->parsed->type == HTTP_REQUEST) {
- HttpRequest* req = (HttpRequest*)hss->parsed;
- req->method = (http_method)parser->method;
- req->url = hss->url;
- }
- else if (hss->parsed->type == HTTP_RESPONSE) {
- HttpResponse* res = (HttpResponse*)hss->parsed;
- res->status_code = (http_status)parser->status_code;
- }
- hss->state = HP_HEADERS_COMPLETE;
- return 0;
- }
- int on_message_complete(http_parser* parser) {
- printd("on_message_complete\n");
- Http1Session* hss = (Http1Session*)parser->data;
- hss->state = HP_MESSAGE_COMPLETE;
- return 0;
- }
- #ifdef WITH_NGHTTP2
- #include "nghttp2/nghttp2.h"
- #include "http2def.h"
- static nghttp2_nv make_nv(const char* name, const char* value) {
- nghttp2_nv nv;
- nv.name = (uint8_t*)name;
- nv.value = (uint8_t*)value;
- nv.namelen = strlen(name);
- nv.valuelen = strlen(value);
- nv.flags = NGHTTP2_NV_FLAG_NONE;
- return nv;
- }
- static nghttp2_nv make_nv2(const char* name, const char* value,
- int namelen, int valuelen) {
- nghttp2_nv nv;
- nv.name = (uint8_t*)name;
- nv.value = (uint8_t*)value;
- nv.namelen = namelen;
- nv.valuelen = valuelen;
- nv.flags = NGHTTP2_NV_FLAG_NONE;
- return nv;
- }
- static void print_frame_hd(const nghttp2_frame_hd* hd) {
- printd("[frame] length=%d type=%x flags=%x stream_id=%d\n",
- (int)hd->length, (int)hd->type, (int)hd->flags, hd->stream_id);
- }
- static int on_header_callback(nghttp2_session *session,
- const nghttp2_frame *frame,
- const uint8_t *name, size_t namelen,
- const uint8_t *value, size_t valuelen,
- uint8_t flags, void *userdata);
- static int on_data_chunk_recv_callback(nghttp2_session *session,
- uint8_t flags, int32_t stream_id, const uint8_t *data,
- size_t len, void *userdata);
- static int on_frame_recv_callback(nghttp2_session *session,
- const nghttp2_frame *frame, void *userdata);
- /*
- static ssize_t data_source_read_callback(nghttp2_session *session,
- int32_t stream_id, uint8_t *buf, size_t length,
- uint32_t *data_flags, nghttp2_data_source *source, void *userdata);
- */
- enum http2_session_state {
- HSS_SEND_MAGIC,
- HSS_SEND_SETTINGS,
- HSS_SEND_HEADERS,
- HSS_SEND_DATA_FRAME_HD,
- HSS_SEND_DATA
- };
- class Http2Session : public HttpSession {
- public:
- static nghttp2_session_callbacks* cbs;
- nghttp2_session* session;
- http2_session_state state;
- HttpPayload* submited;
- HttpPayload* parsed;
- int error;
- int stream_id;
- int stream_closed;
- unsigned char frame_hdbuf[HTTP2_FRAME_HDLEN];
- Http2Session(http_session_type type) {
- if (cbs == NULL) {
- nghttp2_session_callbacks_new(&cbs);
- nghttp2_session_callbacks_set_on_header_callback(cbs, on_header_callback);
- nghttp2_session_callbacks_set_on_data_chunk_recv_callback(cbs, on_data_chunk_recv_callback);
- nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv_callback);
- }
- if (type == HTTP_CLIENT) {
- nghttp2_session_client_new(&session, cbs, NULL);
- state = HSS_SEND_MAGIC;
- }
- else if (type == HTTP_SERVER) {
- nghttp2_session_server_new(&session, cbs, NULL);
- state = HSS_SEND_SETTINGS;
- }
- nghttp2_session_set_user_data(session, this);
- submited = NULL;
- parsed = NULL;
- stream_id = -1;
- stream_closed = 0;
- nghttp2_settings_entry settings[] = {
- {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}
- };
- nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, settings, ARRAY_SIZE(settings));
- state = HSS_SEND_SETTINGS;
- }
- virtual ~Http2Session() {
- if (session) {
- nghttp2_session_del(session);
- session = NULL;
- }
- }
- virtual int GetSendData(char** data, size_t* len) {
- // HTTP2_MAGIC,HTTP2_SETTINGS,HTTP2_HEADERS
- *len = nghttp2_session_mem_send(session, (const uint8_t**)data);
- if (*len == 0) {
- if (submited) {
- void* content = submited->Content();
- int content_length = submited->ContentLength();
- if (content_length != 0) {
- if (state == HSS_SEND_HEADERS) {
- // HTTP2_DATA_FRAME_HD
- state = HSS_SEND_DATA_FRAME_HD;
- http2_frame_hd hd;
- hd.length = content_length;
- hd.type = HTTP2_DATA;
- hd.flags = HTTP2_FLAG_END_STREAM;
- hd.stream_id = stream_id;
- http2_frame_hd_pack(&hd, frame_hdbuf);
- *data = (char*)frame_hdbuf;
- *len = HTTP2_FRAME_HDLEN;
- }
- else if (state == HSS_SEND_DATA_FRAME_HD) {
- // HTTP2_DATA
- state = HSS_SEND_DATA;
- *data = (char*)content;
- *len = content_length;
- }
- }
- }
- }
- return *len;
- }
- virtual int FeedRecvData(const char* data, size_t len) {
- size_t ret = nghttp2_session_mem_recv(session, (const uint8_t*)data, len);
- if (ret != len) {
- error = ret;
- }
- return (int)ret;
- }
- virtual bool WantRecv() {
- return stream_id == -1 || stream_closed == 0;
- }
- virtual int SubmitRequest(HttpRequest* req) {
- submited = req;
- std::vector<nghttp2_nv> nvs;
- char c_str[256] = {0};
- req->ParseUrl();
- nvs.push_back(make_nv(":method", http_method_str(req->method)));
- nvs.push_back(make_nv(":path", req->path.c_str()));
- nvs.push_back(make_nv(":scheme", req->https ? "https" : "http"));
- if (req->port == 0 ||
- req->port == DEFAULT_HTTP_PORT ||
- req->port == DEFAULT_HTTPS_PORT) {
- nvs.push_back(make_nv(":authority", req->host.c_str()));
- }
- else {
- snprintf(c_str, sizeof(c_str), "%s:%d", req->host.c_str(), req->port);
- nvs.push_back(make_nv(":authority", c_str));
- }
- req->FillContentType();
- req->FillContentLength();
- const char* name;
- const char* value;
- for (auto& header : req->headers) {
- name = header.first.c_str();
- value = header.second.c_str();
- strlower((char*)name);
- if (strcmp(name, "connection") == 0) {
- // HTTP2 use stream
- continue;
- }
- nvs.push_back(make_nv2(name, value, header.first.size(), header.second.size()));
- }
- int flags = NGHTTP2_FLAG_END_HEADERS;
- if (req->ContentLength() == 0) {
- flags |= NGHTTP2_FLAG_END_STREAM;
- }
- stream_id = nghttp2_submit_headers(session, flags, -1, NULL, &nvs[0], nvs.size(), NULL);
- // avoid DATA_SOURCE_COPY, we do not use nghttp2_submit_data
- // nghttp2_data_provider data_prd;
- // data_prd.read_callback = data_source_read_callback;
- //stream_id = nghttp2_submit_request(session, NULL, &nvs[0], nvs.size(), &data_prd, NULL);
- stream_closed = 0;
- state = HSS_SEND_HEADERS;
- return 0;
- }
- virtual int SubmitResponse(HttpResponse* res) {
- submited = res;
- std::vector<nghttp2_nv> nvs;
- char c_str[256] = {0};
- snprintf(c_str, sizeof(c_str), "%d", res->status_code);
- nvs.push_back(make_nv(":status", c_str));
- res->FillContentType();
- res->FillContentLength();
- const char* name;
- const char* value;
- for (auto& header : res->headers) {
- name = header.first.c_str();
- value = header.second.c_str();
- strlower((char*)name);
- if (strcmp(name, "connection") == 0) {
- // HTTP2 use stream
- continue;
- }
- nvs.push_back(make_nv2(name, value, header.first.size(), header.second.size()));
- }
- int flags = NGHTTP2_FLAG_END_HEADERS;
- if (res->ContentLength() == 0) {
- flags |= NGHTTP2_FLAG_END_STREAM;
- }
- if (stream_id == -1) {
- // upgrade
- nghttp2_session_upgrade(session, NULL, 0, NULL);
- stream_id = 1;
- }
- nghttp2_submit_headers(session, flags, stream_id, NULL, &nvs[0], nvs.size(), NULL);
- // avoid DATA_SOURCE_COPY, we do not use nghttp2_submit_data
- // data_prd.read_callback = data_source_read_callback;
- //stream_id = nghttp2_submit_request(session, NULL, &nvs[0], nvs.size(), &data_prd, NULL);
- //nghttp2_submit_response(session, stream_id, &nvs[0], nvs.size(), &data_prd);
- stream_closed = 0;
- state = HSS_SEND_HEADERS;
- return 0;
- }
- virtual int InitResponse(HttpResponse* res) {
- res->Reset();
- res->http_major = 2;
- res->http_minor = 0;
- parsed = res;
- return 0;
- }
- virtual int InitRequest(HttpRequest* req) {
- req->Reset();
- req->http_major = 2;
- req->http_minor = 0;
- parsed = req;
- return 0;
- }
- virtual int GetError() {
- return error;
- }
- virtual const char* StrError(int error) {
- return nghttp2_http2_strerror(error);
- }
- };
- nghttp2_session_callbacks* Http2Session::cbs = NULL;
- int on_header_callback(nghttp2_session *session,
- const nghttp2_frame *frame,
- const uint8_t *_name, size_t namelen,
- const uint8_t *_value, size_t valuelen,
- uint8_t flags, void *userdata) {
- printd("on_header_callback\n");
- print_frame_hd(&frame->hd);
- const char* name = (const char*)_name;
- const char* value = (const char*)_value;
- printd("%s: %s\n", name, value);
- Http2Session* hss = (Http2Session*)userdata;
- if (*name == ':') {
- if (hss->parsed->type == HTTP_REQUEST) {
- // :method :path :scheme :authority
- HttpRequest* req = (HttpRequest*)hss->parsed;
- if (strcmp(name, ":method") == 0) {
- req->method = http_method_enum(value);
- }
- else if (strcmp(name, ":path") == 0) {
- req->url = value;
- }
- else if (strcmp(name, ":scheme") == 0) {
- req->headers["Scheme"] = value;
- }
- else if (strcmp(name, ":authority") == 0) {
- req->headers["Host"] = value;
- }
- }
- else if (hss->parsed->type == HTTP_RESPONSE) {
- HttpResponse* res = (HttpResponse*)hss->parsed;
- if (strcmp(name, ":status") == 0) {
- res->status_code = (http_status)atoi(value);
- }
- }
- }
- else {
- hss->parsed->headers[name] = value;
- if (strcmp(name, "content-type") == 0) {
- hss->parsed->content_type = http_content_type_enum(value);
- }
- }
- return 0;
- }
- int on_data_chunk_recv_callback(nghttp2_session *session,
- uint8_t flags, int32_t stream_id, const uint8_t *data,
- size_t len, void *userdata) {
- printd("on_data_chunk_recv_callback\n");
- printd("stream_id=%d length=%d\n", stream_id, (int)len);
- //printd("%.*s\n", (int)len, data);
- Http2Session* hss = (Http2Session*)userdata;
- hss->parsed->body.insert(hss->parsed->body.size(), (const char*)data, len);
- return 0;
- }
- static int on_frame_recv_callback(nghttp2_session *session,
- const nghttp2_frame *frame, void *userdata) {
- printd("on_frame_recv_callback\n");
- print_frame_hd(&frame->hd);
- Http2Session* hss = (Http2Session*)userdata;
- switch (frame->hd.type) {
- case NGHTTP2_DATA:
- case NGHTTP2_HEADERS:
- hss->stream_id = frame->hd.stream_id;
- if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
- printd("on_stream_closed stream_id=%d\n", hss->stream_id);
- hss->stream_closed = 1;
- }
- break;
- default:
- break;
- }
- return 0;
- }
- #endif
- HttpSession* HttpSession::New(http_session_type type, http_version version) {
- if (version == HTTP_V1) {
- return new Http1Session;
- }
- else if (version == HTTP_V2) {
- #ifdef WITH_NGHTTP2
- return new Http2Session(type);
- #else
- fprintf(stderr, "Please recompile WITH_NGHTTP2!\n");
- #endif
- }
- return NULL;
- }
|