static const char *test_should_keep_alive(void) { struct mg_connection conn; char req1[] = "GET / HTTP/1.1\r\n\r\n"; char req2[] = "GET / HTTP/1.0\r\n\r\n"; char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"; char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"; memset(&conn, 0, sizeof(conn)); ASSERT(parse_http_message(req1, sizeof(req1) - 1, &conn) == sizeof(req1) - 1); ASSERT(should_keep_alive(&conn) != 0); parse_http_message(req2, sizeof(req2) - 1, &conn); ASSERT(should_keep_alive(&conn) == 0); parse_http_message(req3, sizeof(req3) - 1, &conn); ASSERT(should_keep_alive(&conn) == 0); parse_http_message(req4, sizeof(req4) - 1, &conn); ASSERT(should_keep_alive(&conn) != 0); return NULL; }
END_TEST START_TEST(test_should_keep_alive) { /* Adapted from unit_test.c */ /* Copyright (c) 2013-2015 the Civetweb developers */ /* Copyright (c) 2004-2013 Sergey Lyubka */ struct mg_connection conn; struct mg_context ctx; char req1[] = "GET / HTTP/1.1\r\n\r\n"; char req2[] = "GET / HTTP/1.0\r\n\r\n"; char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"; char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"; char yes[] = "yes"; char no[] = "no"; memset(&conn, 0, sizeof(conn)); conn.ctx = &ctx; ck_assert_int_eq(parse_http_message(req1, sizeof(req1), &conn.request_info), sizeof(req1) - 1); ctx.config[ENABLE_KEEP_ALIVE] = no; ck_assert_int_eq(should_keep_alive(&conn), 0); ctx.config[ENABLE_KEEP_ALIVE] = yes; ck_assert_int_eq(should_keep_alive(&conn), 1); conn.must_close = 1; ck_assert_int_eq(should_keep_alive(&conn), 0); conn.must_close = 0; parse_http_message(req2, sizeof(req2), &conn.request_info); ck_assert_int_eq(should_keep_alive(&conn), 0); parse_http_message(req3, sizeof(req3), &conn.request_info); ck_assert_int_eq(should_keep_alive(&conn), 0); parse_http_message(req4, sizeof(req4), &conn.request_info); ck_assert_int_eq(should_keep_alive(&conn), 1); conn.status_code = 401; ck_assert_int_eq(should_keep_alive(&conn), 0); conn.status_code = 200; conn.must_close = 1; ck_assert_int_eq(should_keep_alive(&conn), 0); }
static void test_should_keep_alive(void) { struct mg_connection conn; struct mg_context ctx; char req1[] = "GET / HTTP/1.1\r\n\r\n"; char req2[] = "GET / HTTP/1.0\r\n\r\n"; char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"; char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"; memset(&conn, 0, sizeof(conn)); conn.ctx = &ctx; ASSERT(parse_http_message(req1, sizeof(req1), &conn.request_info) == sizeof(req1) - 1); ctx.config[ENABLE_KEEP_ALIVE] = "no"; ASSERT(should_keep_alive(&conn) == 0); ctx.config[ENABLE_KEEP_ALIVE] = "yes"; ASSERT(should_keep_alive(&conn) == 1); conn.must_close = 1; ASSERT(should_keep_alive(&conn) == 0); conn.must_close = 0; parse_http_message(req2, sizeof(req2), &conn.request_info); ASSERT(should_keep_alive(&conn) == 0); parse_http_message(req3, sizeof(req3), &conn.request_info); ASSERT(should_keep_alive(&conn) == 0); parse_http_message(req4, sizeof(req4), &conn.request_info); ASSERT(should_keep_alive(&conn) == 1); conn.status_code = 401; ASSERT(should_keep_alive(&conn) == 0); conn.status_code = 200; conn.must_close = 1; ASSERT(should_keep_alive(&conn) == 0); }
bool wsgi_call_application(Request* request) { StartResponse* start_response=PyObject_NEW(StartResponse, &StartResponse_Type);//构造了一个自定义的对象 PyObject* request_headers; PyObject* retval; PyObject* first_chunk; PyObject* buf; Py_ssize_t length; //printf("***********************in wsgi_call_application***********\n"); start_response->request = request; /* From now on, `headers` stores the _response_ headers * (passed by the WSGI app) rather than the _request_ headers */ request_headers = request->headers; // 请求头headers转储 request->headers = NULL; /* application(environ, start_response) call */ retval = PyObject_CallFunctionObjArgs( // 构造一个包装了wsgi_app可执行方法 请求头 客户端响应对象 wsgi_app, request_headers, start_response, NULL /* sentinel */ ); Py_DECREF(request_headers); Py_DECREF(start_response); if(retval == NULL) return false; //printf("***********************go retval***********\n"); /* The following code is somewhat magic, so worth an explanation. * * If the application we called was a generator, we have to call .next() on * it before we do anything else because that may execute code that * invokes `start_response` (which might not have been invoked yet). * Think of the following scenario: * * def app(environ, start_response): * start_response('200 Ok', ...) * yield 'Hello World' * * That would make `app` return an iterator (more precisely, a generator). * Unfortunately, `start_response` wouldn't be called until the first item * of that iterator is requested; `start_response` however has to be called * _before_ the wsgi body is sent, because it passes the HTTP headers. * * If the application returned a list this would not be required of course, * but special-handling is painful - especially in C - so here's one generic * way to solve the problem: * * Look into the returned iterator in any case. This allows us to do other * optimizations, for example if the returned value is a list with exactly * one string in it, we can pick the string and throw away the list so bjoern * does not have to come back again and look into the iterator a second time. */ if(PyList_Check(retval) && PyList_GET_SIZE(retval) == 1 && PyString_Check(PyList_GET_ITEM(retval, 0))) { /* Optimize the most common case, a single string in a list: */ PyObject* tmp = PyList_GET_ITEM(retval, 0); //printf("------------wsgi 返回迭代对象\n"); Py_INCREF(tmp); Py_DECREF(retval); retval = tmp; //goto string; /* eeevil */ if(PyString_GET_SIZE(retval)) { first_chunk = retval; } else { Py_DECREF(retval); first_chunk = NULL; } } else if(PyString_Check(retval)) { //printf("------------wsgi 返回字符对象\n"); /* According to PEP 333 strings should be handled like any other iterable, * i.e. sending the response item for item. "item for item" means * "char for char" if you have a string. -- I'm not that stupid. */ //string: if(PyString_GET_SIZE(retval)) { first_chunk = retval; } else { Py_DECREF(retval); first_chunk = NULL; } } else if(FileWrapper_CheckExact(retval)) { //printf("------------wsgi 返回文件对象\n"); request->state.use_sendfile = true; request->iterable = ((FileWrapper*)retval)->file; Py_INCREF(request->iterable); Py_DECREF(retval); request->iterator = NULL; first_chunk = NULL; } else { //printf("------------wsgi 返回其他普通对象\n"); /* Generic iterable (list of length != 1, generator, ...) */ request->iterable = retval; request->iterator = PyObject_GetIter(retval); if(request->iterator == NULL) return false; first_chunk = wsgi_iterable_get_next_chunk(request); if(first_chunk == NULL && PyErr_Occurred()) return false; } if(request->headers == NULL) { /* It is important that this check comes *after* the call to * wsgi_iterable_get_next_chunk(), because in case the WSGI application * was an iterator, there's no chance start_response could be called * before. See above if you don't understand what I say. */ PyErr_SetString( PyExc_RuntimeError, "wsgi application returned before start_response was called" ); Py_DECREF(first_chunk); return false; } if(should_keep_alive(request)) { request->state.chunked_response = request->state.response_length_unknown; request->state.keep_alive = true; } else { request->state.keep_alive = false; } /* Get the headers and concatenate the first body chunk. * In the first place this makes the code more simple because afterwards * we can throw away the first chunk PyObject; but it also is an optimization: * At least for small responses, the complete response could be sent with * one send() call (in server.c:ev_io_on_write) which is a (tiny) performance * booster because less kernel calls means less kernel call overhead. */ buf = PyString_FromStringAndSize(NULL, 1024); length = wsgi_getheaders(request, buf); if(length == 0) { Py_DECREF(first_chunk); Py_DECREF(buf); return false; } if(first_chunk == NULL) { _PyString_Resize(&buf, length); goto out; } if(request->state.chunked_response) { PyObject* new_chunk = wrap_http_chunk_cruft_around(first_chunk); Py_DECREF(first_chunk); assert(PyString_GET_SIZE(new_chunk) >= PyString_GET_SIZE(first_chunk) + 5); first_chunk = new_chunk; } assert(buf); _PyString_Resize(&buf, length + PyString_GET_SIZE(first_chunk)); memcpy(PyString_AS_STRING(buf)+length, PyString_AS_STRING(first_chunk), PyString_GET_SIZE(first_chunk)); Py_DECREF(first_chunk); //printf("&&&&&&&&&&&&&&&&&&& wsgi 结果字符:\n%s\n",buf); out: request->state.wsgi_call_done = true; request->current_chunk = buf; request->current_chunk_p = 0; return true; }