generated from pimoroni/pico-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
HttpConnection.cpp
146 lines (116 loc) · 5.12 KB
/
HttpConnection.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include "lwip/apps/http_client.h"
#include "lwip/tcp.h"
#include "lwip/pbuf.h"
#include "pico/cyw43_arch.h"
#include "libraries/inky_frame/inky_frame.hpp"
#include <iostream>
#include "HttpConnection.hpp"
void http_result_callback(void *arg,
httpc_result_t httpc_result,
u32_t rx_content_len,
u32_t srv_res,
err_t err) {
HttpConnection *this_request = reinterpret_cast<HttpConnection*>(arg);
this_request->http_result_received(httpc_result, rx_content_len, srv_res, err);
}
err_t http_headers_callback(httpc_state_t *connection,
void *arg,
struct pbuf *hdr,
u16_t hdr_len,
u32_t content_len) {
HttpConnection *this_request = reinterpret_cast<HttpConnection*>(arg);
return this_request->http_headers_received(connection, hdr, hdr_len, content_len);
}
err_t http_body_callback(void *arg, struct tcp_pcb *conn, struct pbuf *p, err_t err) {
HttpConnection *this_request = reinterpret_cast<HttpConnection*>(arg);
return this_request->http_body_received(conn, p, err);
}
void HttpConnection::http_result_received(httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err) {
f_close(&file_handle);
completed = true;
}
err_t HttpConnection::http_headers_received(httpc_state_t *connection, pbuf *hdr, u16_t hdr_len, u32_t content_len) {
return ERR_OK;
}
err_t HttpConnection::http_body_received(tcp_pcb *conn, pbuf *p, err_t err) {
u16_t size = p->tot_len;
if (size >= BUFFER_CAPACITY) {
std::cout << "Oh oh, we've exhausted the size of the buffer!" << std::endl;
std::cout << "size: " << size << " BUFFER_CAPACITY: " << BUFFER_CAPACITY << std::endl;
return ERR_MEM;
}
for (pbuf* this_pbuf = p; this_pbuf != nullptr; this_pbuf = this_pbuf->next) {
UINT bw;
FRESULT res =f_write(&file_handle, this_pbuf->payload, this_pbuf->len, &bw);
if (res || bw < this_pbuf->len) return ERR_ABRT; // no point carrying on if we can't write
bytes_downloaded += this_pbuf->len;
}
tcp_recved(conn, p->tot_len);
pbuf_free(p);
return ERR_OK;
}
void HttpConnection::do_request() {
cyw43_arch_lwip_begin();
std::cout << "Doing HTTP request." << std::endl;
completed = false;
httpc_connection_t settings;
settings.result_fn = http_result_callback;
settings.headers_done_fn = http_headers_callback;
ip_addr_t server_addr;
ip4_addr_set_u32(&server_addr, ipaddr_addr(ip_address));
err_t err = httpc_get_file(&server_addr,
port,
path,
&settings,
http_body_callback,
this,
nullptr);
std::cout << "Called httpc_get_file(), returned " << (int)err << std::endl;
cyw43_arch_lwip_end();
bool http_thang_done = false;
while (!http_thang_done) {
cyw43_arch_lwip_begin();
if (completed) {
http_thang_done = true;
}
cyw43_arch_lwip_end();
}
std::cout << "HTTP request done" << std::endl;
std::cout << bytes_downloaded << " bytes downloaded" << std::endl;
}
HttpConnection::HttpConnection(const char *ip_address_param,
int port,
const char *path,
const char *filename)
: port(port),
file_handle(file_handle)
{
// I'm not quite clear why we need to do all this. I'd rather just use std::strings but, for some reason, the HTTP
// call is not sent if we do use strings at this point. Or if we use define ip_address and path as char arrays in
// this class. Or if we don't explicitly add the '\0' to the end of the string (even though the length is
// significantly shorter than the max length...)
//
// So I reckon something screwy is going on with caches or interrupts or memory getting trampled somehow or...
// dunno. But it's seriously annoying and is making me think I should have just stuck with MicroPython!
this->ip_address = new char[IP_V4_MAX_LENGTH];
strlcpy(this->ip_address, ip_address_param, IP_V4_MAX_LENGTH - 1);
this->ip_address[IP_V4_MAX_LENGTH - 1] = '\0';
this->path = new char[PATH_MAX_LENGTH];
strlcpy(this->path, path, PATH_MAX_LENGTH - 1);
this->path[PATH_MAX_LENGTH - 1] = '\0';
this->filename = new char[PATH_MAX_LENGTH];
strlcpy(this->filename, filename, PATH_MAX_LENGTH - 1);
this->filename[PATH_MAX_LENGTH - 1] = '\0';
std::cout << "About to create file handle" << std::endl;
std::cout << "About to open file" << std::endl;
if (f_open(&file_handle, filename, FA_CREATE_ALWAYS | FA_WRITE)) {
std::cout << "ERROR: failed to open " << filename << std::endl;
throw std::runtime_error("Failed to open file");
}
std::cout << "File is open" << std::endl;
}
HttpConnection::~HttpConnection() {
delete[] this->filename;
delete[] this->path;
delete[] this->ip_address;
}