📄 http.c
字号:
static int parse_request_line(int *method, Octstr **url, int *use_version_1_0, Octstr *line){ List *words; Octstr *version; Octstr *method_str; int ret; words = octstr_split_words(line); if (list_len(words) != 3) { list_destroy(words, octstr_destroy_item); return -1; } method_str = list_get(words, 0); *url = list_get(words, 1); version = list_get(words, 2); list_destroy(words, NULL); if (octstr_compare(method_str, octstr_imm("GET")) == 0) *method = HTTP_METHOD_GET; else if (octstr_compare(method_str, octstr_imm("POST")) == 0) *method = HTTP_METHOD_POST; else if (octstr_compare(method_str, octstr_imm("HEAD")) == 0) *method = HTTP_METHOD_HEAD; else goto error; ret = parse_http_version(version); if (ret < 0) goto error; *use_version_1_0 = !ret; octstr_destroy(method_str); octstr_destroy(version); return 0;error: octstr_destroy(method_str); octstr_destroy(*url); octstr_destroy(version); *url = NULL; return -1;}static void receive_request(Connection *conn, void *data){ HTTPClient *client; Octstr *line; int ret; if (run_status != running) { conn_unregister(conn); return; } client = data; for (;;) { switch (client->state) { case reading_request_line: line = conn_read_line(conn); if (line == NULL) { if (conn_eof(conn) || conn_error(conn)) goto error; return; } ret = parse_request_line(&client->method, &client->url, &client->use_version_1_0, line); octstr_destroy(line); if (ret == -1) goto error; /* * RFC2616 (4.3) says we should read a message body if there * is one, even on GET requests. */ client->request = entity_create(expect_body_if_indicated); client->state = reading_request; break; case reading_request: ret = entity_read(client->request, conn); if (ret < 0) goto error; if (ret == 0) { client->state = request_is_being_handled; conn_unregister(conn); port_put_request(client); } return; case sending_reply: /* Implicite conn_unregister() and _destroy */ if (conn_error(conn)) goto error; if (conn_outbuf_len(conn) > 0) return; /* Reply has been sent completely */ if (!client->persistent_conn) { conn_unregister(conn); client_destroy(client); return; } /* Start reading another request */ client_reset(client); break; default: panic(0, "Internal error: HTTPClient state is wrong."); } } error: client_destroy(client);}struct server { int fd; int port; int ssl;};static void server_thread(void *dummy){ struct pollfd tab[MAX_SERVERS]; int ports[MAX_SERVERS]; int ssl[MAX_SERVERS]; long i, j, n, fd; int *portno; struct server *p; struct sockaddr_in addr; int addrlen; Connection *conn; HTTPClient *client; int ret; n = 0; while (run_status == running && keep_servers_open) { if (n == 0 || (n < MAX_SERVERS && list_len(new_server_sockets) > 0)) { p = list_consume(new_server_sockets); if (p == NULL) { debug("gwlib.http", 0, "HTTP: No new servers. Quitting."); break; } tab[n].fd = p->fd; tab[n].events = POLLIN; ports[n] = p->port; ssl[n] = p->ssl; ++n; gw_free(p); } if ((ret = gwthread_poll(tab, n, -1.0)) == -1) { if (errno != EINTR) /* a signal was caught during poll() function */ warning(0, "HTTP: gwthread_poll failed."); continue; } for (i = 0; i < n; ++i) { if (tab[i].revents & POLLIN) { addrlen = sizeof(addr); fd = accept(tab[i].fd, (struct sockaddr *) &addr, &addrlen); if (fd == -1) { error(errno, "HTTP: Error accepting a client."); (void) close(tab[i].fd); port_remove(ports[i]); tab[i].fd = -1; ports[i] = -1; ssl[i] = 0; } else { Octstr *client_ip = host_ip(addr); /* * Be aware that conn_wrap_fd() will return NULL if SSL * handshake has failed, so we only client_create() if * there is an conn. */ if ((conn = conn_wrap_fd(fd, ssl[i]))) { client = client_create(ports[i], conn, client_ip); conn_register(conn, server_fdset, receive_request, client); } else { error(0, "HTTP: unsuccessful SSL handshake for client `%s'", octstr_get_cstr(client_ip)); octstr_destroy(client_ip); } } } } while ((portno = list_extract_first(closed_server_sockets)) != NULL) { for (i = 0; i < n; ++i) { if (ports[i] == *portno) { (void) close(tab[i].fd); port_remove(ports[i]); tab[i].fd = -1; ports[i] = -1; ssl[i] = 0; } } gw_free(portno); } j = 0; for (i = 0; i < n; ++i) { if (tab[i].fd != -1) { tab[j] = tab[i]; ports[j] = ports[i]; ssl[j] = ssl[i]; ++j; } } n = j; } /* make sure we close all ports */ for (i = 0; i < n; ++i) { (void) close(tab[i].fd); port_remove(ports[i]); } server_thread_id = -1;}static void start_server_thread(void){ if (!server_thread_is_running) { /* * To be really certain, we must repeat the test, but use the * lock first. If the test failed, however, we _know_ we've * already initialized. This strategy of double testing avoids * using the lock more than a few times at startup. */ mutex_lock(server_thread_lock); if (!server_thread_is_running) { server_fdset = fdset_create(); server_thread_id = gwthread_create(server_thread, NULL); server_thread_is_running = 1; } mutex_unlock(server_thread_lock); }}int http_open_port_if(int port, int ssl, Octstr *interface){ struct server *p; if (ssl) info(0, "HTTP: Opening SSL server at port %d.", port); else info(0, "HTTP: Opening server at port %d.", port); p = gw_malloc(sizeof(*p)); p->port = port; p->ssl = ssl; p->fd = make_server_socket(port, (interface ? octstr_get_cstr(interface) : NULL)); if (p->fd == -1) { gw_free(p); return -1; } port_add(port); list_produce(new_server_sockets, p); keep_servers_open = 1; start_server_thread(); gwthread_wakeup(server_thread_id); return 0;}int http_open_port(int port, int ssl){ return http_open_port_if(port, ssl, NULL);}void http_close_port(int port){ int *p; p = gw_malloc(sizeof(*p)); *p = port; list_produce(closed_server_sockets, p); gwthread_wakeup(server_thread_id);}void http_close_all_ports(void){ if (server_thread_id != -1) { keep_servers_open = 0; gwthread_wakeup(server_thread_id); gwthread_join_every(server_thread); fdset_destroy(server_fdset); server_fdset = NULL; }}/* * Parse CGI variables from the path given in a GET. Return a list * of HTTPCGIvar pointers. Modify the url so that the variables are * removed. */static List *parse_cgivars(Octstr *url){ HTTPCGIVar *v; List *list; int query, et, equals; Octstr *arg, *args; query = octstr_search_char(url, '?', 0); if (query == -1) return list_create(); args = octstr_copy(url, query + 1, octstr_len(url)); octstr_truncate(url, query); list = list_create(); while (octstr_len(args) > 0) { et = octstr_search_char(args, '&', 0); if (et == -1) et = octstr_len(args); arg = octstr_copy(args, 0, et); octstr_delete(args, 0, et + 1); equals = octstr_search_char(arg, '=', 0); if (equals == -1) equals = octstr_len(arg); v = gw_malloc(sizeof(HTTPCGIVar)); v->name = octstr_copy(arg, 0, equals); v->value = octstr_copy(arg, equals + 1, octstr_len(arg)); octstr_url_decode(v->name); octstr_url_decode(v->value); octstr_destroy(arg); list_append(list, v); } octstr_destroy(args); return list;}HTTPClient *http_accept_request(int port, Octstr **client_ip, Octstr **url, List **headers, Octstr **body, List **cgivars){ HTTPClient *client; client = port_get_request(port); if (client == NULL) { debug("gwlib.http", 0, "HTTP: No clients with requests, quitting."); return NULL; } *client_ip = octstr_duplicate(client->ip); *url = client->url; *headers = client->request->headers; *body = client->request->body; *cgivars = parse_cgivars(client->url); if (client->method != HTTP_METHOD_POST) { octstr_destroy(*body); *body = NULL; } client->persistent_conn = client_is_persistent(client->request->headers, client->use_version_1_0); client->url = NULL; client->request->headers = NULL; client->request->body = NULL; entity_destroy(client->request); client->request = NULL; return client;}/* * The http_send_reply(...) uses this function to determinate the * reason pahrase for a status code. */static const char *http_reason_phrase(int status){ switch (status) { case HTTP_OK: return "OK"; /* 200 */ case HTTP_CREATED: return "Created"; /* 201 */ case HTTP_ACCEPTED: return "Accepted"; /* 202 */ case HTTP_NO_CONTENT: return "No Content"; /* 204 */ case HTTP_RESET_CONTENT: return "Reset Content"; /* 205 */ case HTTP_MOVED_PERMANENTLY: return "Moved Permanently"; /* 301 */ case HTTP_FOUND: return "Found"; /* 302 */ case HTTP_SEE_OTHER: return "See Other"; /* 303 */ case HTTP_NOT_MODIFIED: return "Not Modified"; /* 304 */ case HTTP_TEMPORARY_REDIRECT: return "Temporary Redirect"; /* 307 */ case HTTP_BAD_REQUEST: return "Bad Request"; /* 400 */ case HTTP_UNAUTHORIZED: return "Unauthorized"; /* 401 */ case HTTP_FORBIDDEN: return "Forbidden"; /* 403 */ case HTTP_NOT_FOUND: return "Not Found"; /* 404 */ case HTTP_BAD_METHOD: return "Method Not Allowed"; /* 405 */ case HTTP_NOT_ACCEPTABLE: return "Not Acceptable"; /* 406 */ case HTTP_REQUEST_ENTITY_TOO_LARGE: return "Request Entity Too Large"; /* 413 */ case HTTP_UNSUPPORTED_MEDIA_TYPE: return "Unsupported Media Type"; /* 415 */ case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error"; /* 500 */ case HTTP_NOT_IMPLEMENTED: return "Not Implemented"; /* 501 */ case HTTP_BAD_GATEWAY: return "Bad Gateway"; /* 502 */ } return "Foo";}void http_send_reply(HTTPClient *client, int status, List *headers, Octstr *body){ Octstr *response; long i; int ret; if (client->use_version_1_0) response = octstr_format("HTTP/1.0 %d %s\r\n", status, http_reason_phrase(status)); else response = octstr_format("HTTP/1.1 %d %s\r\n", status, http_reason_phrase(status)); /* identify ourselfs */ octstr_format_append(response, "Server: " GW_NAME "/%s\r\n", GW_VERSION); octstr_format_append(response, "Content-Length: %ld\r\n", octstr_len(body)); /* * RFC2616, sec. 8.1.2.1 says that if the server chooses to close the * connection, it *should* send a coresponding header */ if (!client->use_version_1_0 && !client->persistent_conn) octstr_format_append(response, "Connection: close\r\n"); for (i = 0; i < list_len(headers); ++i) octstr_format_append(response, "%S\r\n", list_get(headers, i)); octstr_format_append(response, "\r\n"); if (body != NULL && client->method != HTTP_METHOD_HEAD) octstr_append(response, body); ret = conn_write(client->conn, response); octstr_destroy(response); /* obey return code of conn_write() */ /* sending response was successful */ if (ret == 0) { /* HTTP/1.0 or 1.1, hence keep-alive or keep-alive */ if (!client->persistent_conn) { client_destroy(client); } else { /* XXX mark this HTTPClient in the keep-alive cleaner thread */ client_reset(client); conn_register(client->conn, ser
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -