1 /*
2  * This file and its contents are licensed under the Apache License 2.0.
3  * Please see the included NOTICE for copyright information and
4  * LICENSE-APACHE for a copy of the license.
5  */
6 #include <postgres.h>
7 
8 #include "http.h"
9 #include "conn.h"
10 
11 static const char *http_error_strings[] = {
12 	[HTTP_ERROR_NONE] = "no HTTP error",
13 	[HTTP_ERROR_WRITE] = "HTTP connection write error",
14 	[HTTP_ERROR_READ] = "HTTP connection read error",
15 	[HTTP_ERROR_CONN_CLOSED] = "HTTP connection closed",
16 	[HTTP_ERROR_REQUEST_BUILD] = "could not build HTTP request",
17 	[HTTP_ERROR_RESPONSE_PARSE] = "could not parse HTTP response",
18 	[HTTP_ERROR_RESPONSE_INCOMPLETE] = "incomplete HTTP response",
19 	[HTTP_ERROR_INVALID_BUFFER_STATE] = "invalid HTTP buffer state",
20 	[HTTP_ERROR_UNKNOWN] = "unknown HTTP error",
21 };
22 
23 static const char *http_version_strings[] = {
24 	[HTTP_VERSION_10] = "HTTP/1.0",
25 	[HTTP_VERSION_11] = "HTTP/1.1",
26 	[HTTP_VERSION_INVALID] = "invalid HTTP version",
27 };
28 
29 const char *
ts_http_strerror(HttpError http_errno)30 ts_http_strerror(HttpError http_errno)
31 {
32 	return http_error_strings[http_errno];
33 }
34 
35 HttpVersion
ts_http_version_from_string(const char * version)36 ts_http_version_from_string(const char *version)
37 {
38 	int i;
39 
40 	for (i = 0; i < HTTP_VERSION_INVALID; i++)
41 		if (pg_strcasecmp(http_version_strings[i], version) == 0)
42 			return i;
43 
44 	return HTTP_VERSION_INVALID;
45 }
46 
47 const char *
ts_http_version_string(HttpVersion version)48 ts_http_version_string(HttpVersion version)
49 {
50 	return http_version_strings[version];
51 }
52 
53 /*
54  * Send an HTTP request and receive the HTTP response on the given connection.
55  *
56  * Returns HTTP_ERROR_NONE (0) on success or a HTTP-specific error on failure.
57  */
58 HttpError
ts_http_send_and_recv(Connection * conn,HttpRequest * req,HttpResponseState * state)59 ts_http_send_and_recv(Connection *conn, HttpRequest *req, HttpResponseState *state)
60 {
61 	const char *built_request;
62 	size_t request_len;
63 	off_t write_off = 0;
64 	HttpError err = HTTP_ERROR_NONE;
65 	int ret;
66 
67 	built_request = ts_http_request_build(req, &request_len);
68 
69 	if (NULL == built_request)
70 		return HTTP_ERROR_REQUEST_BUILD;
71 
72 	while (request_len > 0)
73 	{
74 		ret = ts_connection_write(conn, built_request + write_off, request_len);
75 
76 		if (ret < 0 || ret > request_len)
77 			return HTTP_ERROR_WRITE;
78 
79 		if (ret == 0)
80 			return HTTP_ERROR_CONN_CLOSED;
81 
82 		write_off += ret;
83 		request_len -= ret;
84 	}
85 
86 	while (err == HTTP_ERROR_NONE && !ts_http_response_state_is_done(state))
87 	{
88 		ssize_t remaining = 0;
89 		char *buf = ts_http_response_state_next_buffer(state, &remaining);
90 
91 		if (remaining < 0)
92 			err = HTTP_ERROR_INVALID_BUFFER_STATE;
93 		else if (remaining == 0)
94 			err = HTTP_ERROR_RESPONSE_INCOMPLETE;
95 		else
96 		{
97 			ssize_t bytes_read = ts_connection_read(conn, buf, remaining);
98 
99 			if (bytes_read < 0)
100 				err = HTTP_ERROR_READ;
101 			/* Check for error or closed socket/EOF (ret == 0) */
102 			else if (bytes_read == 0)
103 				err = HTTP_ERROR_CONN_CLOSED;
104 			else if (!ts_http_response_state_parse(state, bytes_read))
105 				err = HTTP_ERROR_RESPONSE_PARSE;
106 		}
107 	}
108 
109 	return err;
110 }
111