int HTTPServerListen(HTTPServerRef const server, strarg_t const address, strarg_t const port) { if(!server) return 0; assertf(!server->socket->data, "HTTPServer already listening"); int rc; rc = uv_tcp_init(async_loop, server->socket); if(rc < 0) return rc; server->socket->data = server; struct addrinfo const hints = { .ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_NUMERICSERV | AI_PASSIVE, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = 0, // ??? }; struct addrinfo *info; rc = async_getaddrinfo(address, port, &hints, &info); if(rc < 0) { HTTPServerClose(server); return rc; } int bound = 0; rc = 0; for(struct addrinfo *each = info; each; each = each->ai_next) { rc = uv_tcp_bind(server->socket, each->ai_addr, 0); if(rc >= 0) bound++; } uv_freeaddrinfo(info); if(!bound) { HTTPServerClose(server); if(rc < 0) return rc; return UV_EADDRNOTAVAIL; } rc = uv_listen((uv_stream_t *)server->socket, 511, connection_cb); if(rc < 0) { HTTPServerClose(server); return rc; } return 0; } int HTTPServerListenSecure(HTTPServerRef const server, strarg_t const address, strarg_t const port, struct tls **const tlsptr) { if(!server) return 0; int rc = HTTPServerListen(server, address, port); if(rc < 0) return rc; server->secure = *tlsptr; *tlsptr = NULL; return 0; } void HTTPServerClose(HTTPServerRef const server) { if(!server) return; if(!server->socket->data) return; if(server->secure) tls_close(server->secure); tls_free(server->secure); server->secure = NULL; async_close((uv_handle_t *)server->socket); } static void connection(uv_stream_t *const socket) { HTTPServerRef const server = socket->data; HTTPConnectionRef conn; int rc = HTTPConnectionCreateIncomingSecure(socket, server->secure, 0, &conn); if(rc < 0) { fprintf(stderr, "HTTP server connection error %s\n", uv_strerror(rc)); return; } assert(conn); for(;;) { server->listener(server->context, server, conn); rc = HTTPConnectionDrainMessage(conn); if(rc < 0) break; } HTTPConnectionFree(&conn); }
int HTTPConnectionCreateOutgoing(strarg_t const domain, unsigned const flags, HTTPConnectionRef *const out) { str_t host[1023+1]; str_t service[15+1]; host[0] = '\0'; service[0] = '\0'; int matched = sscanf(domain, "%1023[^:]:%15[0-9]", host, service); if(matched < 1) return UV_EINVAL; if('\0' == host[0]) return UV_EINVAL; static struct addrinfo const hints = { .ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_NUMERICSERV, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = 0, // ??? }; struct addrinfo *info = NULL; HTTPConnectionRef conn = NULL; int rc; rc = async_getaddrinfo(host, service[0] ? service : "80", &hints, &info); if(rc < 0) goto cleanup; conn = calloc(1, sizeof(struct HTTPConnection)); if(!conn) rc = UV_ENOMEM; if(rc < 0) goto cleanup; rc = UV_EADDRNOTAVAIL; for(struct addrinfo *each = info; each; each = each->ai_next) { rc = uv_tcp_init(async_loop, conn->stream); if(rc < 0) break; rc = async_tcp_connect(conn->stream, each->ai_addr); if(rc >= 0) break; async_close((uv_handle_t *)conn->stream); } if(rc < 0) goto cleanup; http_parser_init(conn->parser, HTTP_RESPONSE); conn->parser->data = conn; *out = conn; conn = NULL; cleanup: uv_freeaddrinfo(info); info = NULL; HTTPConnectionFree(&conn); return rc; } void HTTPConnectionFree(HTTPConnectionRef *const connptr) { HTTPConnectionRef conn = *connptr; if(!conn) return; async_close((uv_handle_t *)conn->stream); // http_parser does not need to be freed, closed or destroyed. memset(conn->parser, 0, sizeof(*conn->parser)); FREE(&conn->buf); *conn->raw = uv_buf_init(NULL, 0); conn->type = HTTPNothing; *conn->out = uv_buf_init(NULL, 0); conn->flags = 0; assert_zeroed(conn, 1); FREE(connptr); conn = NULL; }