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