httpd_txrx.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. // Copyright 2018 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <errno.h>
  15. #include <esp_log.h>
  16. #include <esp_err.h>
  17. #include <esp_http_server.h>
  18. #include "esp_httpd_priv.h"
  19. static const char *TAG = "httpd_txrx";
  20. esp_err_t httpd_sess_set_send_override(httpd_handle_t hd, int sockfd, httpd_send_func_t send_func)
  21. {
  22. struct sock_db *sess = httpd_sess_get(hd, sockfd);
  23. if (!sess) {
  24. return ESP_ERR_INVALID_ARG;
  25. }
  26. sess->send_fn = send_func;
  27. return ESP_OK;
  28. }
  29. esp_err_t httpd_sess_set_recv_override(httpd_handle_t hd, int sockfd, httpd_recv_func_t recv_func)
  30. {
  31. struct sock_db *sess = httpd_sess_get(hd, sockfd);
  32. if (!sess) {
  33. return ESP_ERR_INVALID_ARG;
  34. }
  35. sess->recv_fn = recv_func;
  36. return ESP_OK;
  37. }
  38. esp_err_t httpd_sess_set_pending_override(httpd_handle_t hd, int sockfd, httpd_pending_func_t pending_func)
  39. {
  40. struct sock_db *sess = httpd_sess_get(hd, sockfd);
  41. if (!sess) {
  42. return ESP_ERR_INVALID_ARG;
  43. }
  44. sess->pending_fn = pending_func;
  45. return ESP_OK;
  46. }
  47. int httpd_send(httpd_req_t *r, const char *buf, size_t buf_len)
  48. {
  49. if (r == NULL || buf == NULL) {
  50. return HTTPD_SOCK_ERR_INVALID;
  51. }
  52. if (!httpd_valid_req(r)) {
  53. return HTTPD_SOCK_ERR_INVALID;
  54. }
  55. struct httpd_req_aux *ra = r->aux;
  56. int ret = ra->sd->send_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, 0);
  57. if (ret < 0) {
  58. ESP_LOGD(TAG, LOG_FMT("error in send_fn"));
  59. return ret;
  60. }
  61. return ret;
  62. }
  63. static esp_err_t httpd_send_all(httpd_req_t *r, const char *buf, size_t buf_len)
  64. {
  65. struct httpd_req_aux *ra = r->aux;
  66. int ret;
  67. while (buf_len > 0) {
  68. ret = ra->sd->send_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, 0);
  69. if (ret < 0) {
  70. ESP_LOGD(TAG, LOG_FMT("error in send_fn"));
  71. return ESP_FAIL;
  72. }
  73. ESP_LOGD(TAG, LOG_FMT("sent = %d"), ret);
  74. buf += ret;
  75. buf_len -= ret;
  76. }
  77. return ESP_OK;
  78. }
  79. static size_t httpd_recv_pending(httpd_req_t *r, char *buf, size_t buf_len)
  80. {
  81. struct httpd_req_aux *ra = r->aux;
  82. size_t offset = sizeof(ra->sd->pending_data) - ra->sd->pending_len;
  83. /* buf_len must not be greater than remaining_len */
  84. buf_len = MIN(ra->sd->pending_len, buf_len);
  85. memcpy(buf, ra->sd->pending_data + offset, buf_len);
  86. ra->sd->pending_len -= buf_len;
  87. return buf_len;
  88. }
  89. int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t buf_len, bool halt_after_pending)
  90. {
  91. ESP_LOGD(TAG, LOG_FMT("requested length = %d"), buf_len);
  92. size_t pending_len = 0;
  93. struct httpd_req_aux *ra = r->aux;
  94. /* First fetch pending data from local buffer */
  95. if (ra->sd->pending_len > 0) {
  96. ESP_LOGD(TAG, LOG_FMT("pending length = %d"), ra->sd->pending_len);
  97. pending_len = httpd_recv_pending(r, buf, buf_len);
  98. buf += pending_len;
  99. buf_len -= pending_len;
  100. /* If buffer filled then no need to recv.
  101. * If asked to halt after receiving pending data then
  102. * return with received length */
  103. if (!buf_len || halt_after_pending) {
  104. return pending_len;
  105. }
  106. }
  107. /* Receive data of remaining length */
  108. int ret = ra->sd->recv_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, 0);
  109. if (ret < 0) {
  110. ESP_LOGD(TAG, LOG_FMT("error in recv_fn"));
  111. if ((ret == HTTPD_SOCK_ERR_TIMEOUT) && (pending_len != 0)) {
  112. /* If recv() timeout occurred, but pending data is
  113. * present, return length of pending data.
  114. * This behavior is similar to that of socket recv()
  115. * function, which, in case has only partially read the
  116. * requested length, due to timeout, returns with read
  117. * length, rather than error */
  118. return pending_len;
  119. }
  120. return ret;
  121. }
  122. ESP_LOGD(TAG, LOG_FMT("received length = %d"), ret + pending_len);
  123. return ret + pending_len;
  124. }
  125. int httpd_recv(httpd_req_t *r, char *buf, size_t buf_len)
  126. {
  127. return httpd_recv_with_opt(r, buf, buf_len, false);
  128. }
  129. size_t httpd_unrecv(struct httpd_req *r, const char *buf, size_t buf_len)
  130. {
  131. struct httpd_req_aux *ra = r->aux;
  132. /* Truncate if external buf_len is greater than pending_data buffer size */
  133. ra->sd->pending_len = MIN(sizeof(ra->sd->pending_data), buf_len);
  134. /* Copy data into internal pending_data buffer with the exact offset
  135. * such that it is right aligned inside the buffer */
  136. size_t offset = sizeof(ra->sd->pending_data) - ra->sd->pending_len;
  137. memcpy(ra->sd->pending_data + offset, buf, ra->sd->pending_len);
  138. ESP_LOGD(TAG, LOG_FMT("length = %d"), ra->sd->pending_len);
  139. return ra->sd->pending_len;
  140. }
  141. /**
  142. * This API appends an additional header field-value pair in the HTTP response.
  143. * But the header isn't sent out until any of the send APIs is executed.
  144. */
  145. esp_err_t httpd_resp_set_hdr(httpd_req_t *r, const char *field, const char *value)
  146. {
  147. if (r == NULL || field == NULL || value == NULL) {
  148. return ESP_ERR_INVALID_ARG;
  149. }
  150. if (!httpd_valid_req(r)) {
  151. return ESP_ERR_HTTPD_INVALID_REQ;
  152. }
  153. struct httpd_req_aux *ra = r->aux;
  154. struct httpd_data *hd = (struct httpd_data *) r->handle;
  155. /* Number of additional headers is limited */
  156. if (ra->resp_hdrs_count >= hd->config.max_resp_headers) {
  157. return ESP_ERR_HTTPD_RESP_HDR;
  158. }
  159. /* Assign header field-value pair */
  160. ra->resp_hdrs[ra->resp_hdrs_count].field = field;
  161. ra->resp_hdrs[ra->resp_hdrs_count].value = value;
  162. ra->resp_hdrs_count++;
  163. ESP_LOGD(TAG, LOG_FMT("new header = %s: %s"), field, value);
  164. return ESP_OK;
  165. }
  166. /**
  167. * This API sets the status of the HTTP response to the value specified.
  168. * But the status isn't sent out until any of the send APIs is executed.
  169. */
  170. esp_err_t httpd_resp_set_status(httpd_req_t *r, const char *status)
  171. {
  172. if (r == NULL || status == NULL) {
  173. return ESP_ERR_INVALID_ARG;
  174. }
  175. if (!httpd_valid_req(r)) {
  176. return ESP_ERR_HTTPD_INVALID_REQ;
  177. }
  178. struct httpd_req_aux *ra = r->aux;
  179. ra->status = (char *)status;
  180. return ESP_OK;
  181. }
  182. /**
  183. * This API sets the method/type of the HTTP response to the value specified.
  184. * But the method isn't sent out until any of the send APIs is executed.
  185. */
  186. esp_err_t httpd_resp_set_type(httpd_req_t *r, const char *type)
  187. {
  188. if (r == NULL || type == NULL) {
  189. return ESP_ERR_INVALID_ARG;
  190. }
  191. if (!httpd_valid_req(r)) {
  192. return ESP_ERR_HTTPD_INVALID_REQ;
  193. }
  194. struct httpd_req_aux *ra = r->aux;
  195. ra->content_type = (char *)type;
  196. return ESP_OK;
  197. }
  198. esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, ssize_t buf_len)
  199. {
  200. if (r == NULL) {
  201. return ESP_ERR_INVALID_ARG;
  202. }
  203. if (!httpd_valid_req(r)) {
  204. return ESP_ERR_HTTPD_INVALID_REQ;
  205. }
  206. struct httpd_req_aux *ra = r->aux;
  207. const char *httpd_hdr_str = "HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n";
  208. const char *colon_separator = ": ";
  209. const char *cr_lf_seperator = "\r\n";
  210. if (buf_len == HTTPD_RESP_USE_STRLEN) {
  211. buf_len = strlen(buf);
  212. }
  213. /* Request headers are no longer available */
  214. ra->req_hdrs_count = 0;
  215. /* Size of essential headers is limited by scratch buffer size */
  216. if (snprintf(ra->scratch, sizeof(ra->scratch), httpd_hdr_str,
  217. ra->status, ra->content_type, buf_len) >= sizeof(ra->scratch)) {
  218. return ESP_ERR_HTTPD_RESP_HDR;
  219. }
  220. /* Sending essential headers */
  221. if (httpd_send_all(r, ra->scratch, strlen(ra->scratch)) != ESP_OK) {
  222. return ESP_ERR_HTTPD_RESP_SEND;
  223. }
  224. /* Sending additional headers based on set_header */
  225. for (unsigned i = 0; i < ra->resp_hdrs_count; i++) {
  226. /* Send header field */
  227. if (httpd_send_all(r, ra->resp_hdrs[i].field, strlen(ra->resp_hdrs[i].field)) != ESP_OK) {
  228. return ESP_ERR_HTTPD_RESP_SEND;
  229. }
  230. /* Send ': ' */
  231. if (httpd_send_all(r, colon_separator, strlen(colon_separator)) != ESP_OK) {
  232. return ESP_ERR_HTTPD_RESP_SEND;
  233. }
  234. /* Send header value */
  235. if (httpd_send_all(r, ra->resp_hdrs[i].value, strlen(ra->resp_hdrs[i].value)) != ESP_OK) {
  236. return ESP_ERR_HTTPD_RESP_SEND;
  237. }
  238. /* Send CR + LF */
  239. if (httpd_send_all(r, cr_lf_seperator, strlen(cr_lf_seperator)) != ESP_OK) {
  240. return ESP_ERR_HTTPD_RESP_SEND;
  241. }
  242. }
  243. /* End header section */
  244. if (httpd_send_all(r, cr_lf_seperator, strlen(cr_lf_seperator)) != ESP_OK) {
  245. return ESP_ERR_HTTPD_RESP_SEND;
  246. }
  247. /* Sending content */
  248. if (buf && buf_len) {
  249. if (httpd_send_all(r, buf, buf_len) != ESP_OK) {
  250. return ESP_ERR_HTTPD_RESP_SEND;
  251. }
  252. }
  253. return ESP_OK;
  254. }
  255. esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len)
  256. {
  257. if (r == NULL) {
  258. return ESP_ERR_INVALID_ARG;
  259. }
  260. if (!httpd_valid_req(r)) {
  261. return ESP_ERR_HTTPD_INVALID_REQ;
  262. }
  263. if (buf_len == HTTPD_RESP_USE_STRLEN) {
  264. buf_len = strlen(buf);
  265. }
  266. struct httpd_req_aux *ra = r->aux;
  267. const char *httpd_chunked_hdr_str = "HTTP/1.1 %s\r\nContent-Type: %s\r\nTransfer-Encoding: chunked\r\n";
  268. const char *colon_separator = ": ";
  269. const char *cr_lf_seperator = "\r\n";
  270. /* Request headers are no longer available */
  271. ra->req_hdrs_count = 0;
  272. if (!ra->first_chunk_sent) {
  273. /* Size of essential headers is limited by scratch buffer size */
  274. if (snprintf(ra->scratch, sizeof(ra->scratch), httpd_chunked_hdr_str,
  275. ra->status, ra->content_type) >= sizeof(ra->scratch)) {
  276. return ESP_ERR_HTTPD_RESP_HDR;
  277. }
  278. /* Sending essential headers */
  279. if (httpd_send_all(r, ra->scratch, strlen(ra->scratch)) != ESP_OK) {
  280. return ESP_ERR_HTTPD_RESP_SEND;
  281. }
  282. /* Sending additional headers based on set_header */
  283. for (unsigned i = 0; i < ra->resp_hdrs_count; i++) {
  284. /* Send header field */
  285. if (httpd_send_all(r, ra->resp_hdrs[i].field, strlen(ra->resp_hdrs[i].field)) != ESP_OK) {
  286. return ESP_ERR_HTTPD_RESP_SEND;
  287. }
  288. /* Send ': ' */
  289. if (httpd_send_all(r, colon_separator, strlen(colon_separator)) != ESP_OK) {
  290. return ESP_ERR_HTTPD_RESP_SEND;
  291. }
  292. /* Send header value */
  293. if (httpd_send_all(r, ra->resp_hdrs[i].value, strlen(ra->resp_hdrs[i].value)) != ESP_OK) {
  294. return ESP_ERR_HTTPD_RESP_SEND;
  295. }
  296. /* Send CR + LF */
  297. if (httpd_send_all(r, cr_lf_seperator, strlen(cr_lf_seperator)) != ESP_OK) {
  298. return ESP_ERR_HTTPD_RESP_SEND;
  299. }
  300. }
  301. /* End header section */
  302. if (httpd_send_all(r, cr_lf_seperator, strlen(cr_lf_seperator)) != ESP_OK) {
  303. return ESP_ERR_HTTPD_RESP_SEND;
  304. }
  305. ra->first_chunk_sent = true;
  306. }
  307. /* Sending chunked content */
  308. char len_str[10];
  309. snprintf(len_str, sizeof(len_str), "%x\r\n", buf_len);
  310. if (httpd_send_all(r, len_str, strlen(len_str)) != ESP_OK) {
  311. return ESP_ERR_HTTPD_RESP_SEND;
  312. }
  313. if (buf) {
  314. if (httpd_send_all(r, buf, (size_t) buf_len) != ESP_OK) {
  315. return ESP_ERR_HTTPD_RESP_SEND;
  316. }
  317. }
  318. /* Indicate end of chunk */
  319. if (httpd_send_all(r, "\r\n", strlen("\r\n")) != ESP_OK) {
  320. return ESP_ERR_HTTPD_RESP_SEND;
  321. }
  322. return ESP_OK;
  323. }
  324. esp_err_t httpd_resp_send_err(httpd_req_t *req, httpd_err_code_t error, const char *usr_msg)
  325. {
  326. esp_err_t ret;
  327. const char *msg;
  328. const char *status;
  329. switch (error) {
  330. case HTTPD_501_METHOD_NOT_IMPLEMENTED:
  331. status = "501 Method Not Implemented";
  332. msg = "Request method is not supported by server";
  333. break;
  334. case HTTPD_505_VERSION_NOT_SUPPORTED:
  335. status = "505 Version Not Supported";
  336. msg = "HTTP version not supported by server";
  337. break;
  338. case HTTPD_400_BAD_REQUEST:
  339. status = "400 Bad Request";
  340. msg = "Server unable to understand request due to invalid syntax";
  341. break;
  342. case HTTPD_404_NOT_FOUND:
  343. status = "404 Not Found";
  344. msg = "This URI does not exist";
  345. break;
  346. case HTTPD_405_METHOD_NOT_ALLOWED:
  347. status = "405 Method Not Allowed";
  348. msg = "Request method for this URI is not handled by server";
  349. break;
  350. case HTTPD_408_REQ_TIMEOUT:
  351. status = "408 Request Timeout";
  352. msg = "Server closed this connection";
  353. break;
  354. case HTTPD_414_URI_TOO_LONG:
  355. status = "414 URI Too Long";
  356. msg = "URI is too long for server to interpret";
  357. break;
  358. case HTTPD_411_LENGTH_REQUIRED:
  359. status = "411 Length Required";
  360. msg = "Chunked encoding not supported by server";
  361. break;
  362. case HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE:
  363. status = "431 Request Header Fields Too Large";
  364. msg = "Header fields are too long for server to interpret";
  365. break;
  366. case HTTPD_500_INTERNAL_SERVER_ERROR:
  367. default:
  368. status = "500 Internal Server Error";
  369. msg = "Server has encountered an unexpected error";
  370. }
  371. /* If user has provided custom message, override default message */
  372. msg = usr_msg ? usr_msg : msg;
  373. ESP_LOGW(TAG, LOG_FMT("%s - %s"), status, msg);
  374. /* Set error code in HTTP response */
  375. httpd_resp_set_status(req, status);
  376. httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
  377. #ifdef CONFIG_HTTPD_ERR_RESP_NO_DELAY
  378. /* Use TCP_NODELAY option to force socket to send data in buffer
  379. * This ensures that the error message is sent before the socket
  380. * is closed */
  381. struct httpd_req_aux *ra = req->aux;
  382. int nodelay = 1;
  383. if (setsockopt(ra->sd->fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)) < 0) {
  384. /* If failed to turn on TCP_NODELAY, throw warning and continue */
  385. ESP_LOGW(TAG, LOG_FMT("error calling setsockopt : %d"), errno);
  386. nodelay = 0;
  387. }
  388. #endif
  389. /* Send HTTP error message */
  390. ret = httpd_resp_send(req, msg, strlen(msg));
  391. #ifdef CONFIG_HTTPD_ERR_RESP_NO_DELAY
  392. /* If TCP_NODELAY was set successfully above, time to disable it */
  393. if (nodelay == 1) {
  394. nodelay = 0;
  395. if (setsockopt(ra->sd->fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)) < 0) {
  396. /* If failed to turn off TCP_NODELAY, throw error and
  397. * return failure to signal for socket closure */
  398. ESP_LOGE(TAG, LOG_FMT("error calling setsockopt : %d"), errno);
  399. return ESP_ERR_INVALID_STATE;
  400. }
  401. }
  402. #endif
  403. return ret;
  404. }
  405. esp_err_t httpd_register_err_handler(httpd_handle_t handle,
  406. httpd_err_code_t error,
  407. httpd_err_handler_func_t err_handler_fn)
  408. {
  409. if (handle == NULL || error >= HTTPD_ERR_CODE_MAX) {
  410. return ESP_ERR_INVALID_ARG;
  411. }
  412. struct httpd_data *hd = (struct httpd_data *) handle;
  413. hd->err_handler_fns[error] = err_handler_fn;
  414. return ESP_OK;
  415. }
  416. esp_err_t httpd_req_handle_err(httpd_req_t *req, httpd_err_code_t error)
  417. {
  418. struct httpd_data *hd = (struct httpd_data *) req->handle;
  419. esp_err_t ret;
  420. /* Invoke custom error handler if configured */
  421. if (hd->err_handler_fns[error]) {
  422. ret = hd->err_handler_fns[error](req, error);
  423. /* If error code is 500, force return failure
  424. * irrespective of the handler's return value */
  425. ret = (error == HTTPD_500_INTERNAL_SERVER_ERROR ? ESP_FAIL : ret);
  426. } else {
  427. /* If no handler is registered for this error default
  428. * behavior is to send the HTTP error response and
  429. * return failure for closure of underlying socket */
  430. httpd_resp_send_err(req, error, NULL);
  431. ret = ESP_FAIL;
  432. }
  433. return ret;
  434. }
  435. int httpd_req_recv(httpd_req_t *r, char *buf, size_t buf_len)
  436. {
  437. if (r == NULL || buf == NULL) {
  438. return HTTPD_SOCK_ERR_INVALID;
  439. }
  440. if (!httpd_valid_req(r)) {
  441. ESP_LOGW(TAG, LOG_FMT("invalid request"));
  442. return HTTPD_SOCK_ERR_INVALID;
  443. }
  444. struct httpd_req_aux *ra = r->aux;
  445. ESP_LOGD(TAG, LOG_FMT("remaining length = %d"), ra->remaining_len);
  446. if (buf_len > ra->remaining_len) {
  447. buf_len = ra->remaining_len;
  448. }
  449. if (buf_len == 0) {
  450. return buf_len;
  451. }
  452. int ret = httpd_recv(r, buf, buf_len);
  453. if (ret < 0) {
  454. ESP_LOGD(TAG, LOG_FMT("error in httpd_recv"));
  455. return ret;
  456. }
  457. ra->remaining_len -= ret;
  458. ESP_LOGD(TAG, LOG_FMT("received length = %d"), ret);
  459. return ret;
  460. }
  461. int httpd_req_to_sockfd(httpd_req_t *r)
  462. {
  463. if (r == NULL) {
  464. return -1;
  465. }
  466. if (!httpd_valid_req(r)) {
  467. ESP_LOGW(TAG, LOG_FMT("invalid request"));
  468. return -1;
  469. }
  470. struct httpd_req_aux *ra = r->aux;
  471. return ra->sd->fd;
  472. }
  473. static int httpd_sock_err(const char *ctx, int sockfd)
  474. {
  475. int errval;
  476. ESP_LOGW(TAG, LOG_FMT("error in %s : %d"), ctx, errno);
  477. switch(errno) {
  478. case EAGAIN:
  479. case EINTR:
  480. errval = HTTPD_SOCK_ERR_TIMEOUT;
  481. break;
  482. case EINVAL:
  483. case EBADF:
  484. case EFAULT:
  485. case ENOTSOCK:
  486. errval = HTTPD_SOCK_ERR_INVALID;
  487. break;
  488. default:
  489. errval = HTTPD_SOCK_ERR_FAIL;
  490. }
  491. return errval;
  492. }
  493. int httpd_default_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags)
  494. {
  495. (void)hd;
  496. if (buf == NULL) {
  497. return HTTPD_SOCK_ERR_INVALID;
  498. }
  499. int ret = send(sockfd, buf, buf_len, flags);
  500. if (ret < 0) {
  501. return httpd_sock_err("send", sockfd);
  502. }
  503. return ret;
  504. }
  505. int httpd_default_recv(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len, int flags)
  506. {
  507. (void)hd;
  508. if (buf == NULL) {
  509. return HTTPD_SOCK_ERR_INVALID;
  510. }
  511. int ret = recv(sockfd, buf, buf_len, flags);
  512. if (ret < 0) {
  513. return httpd_sock_err("recv", sockfd);
  514. }
  515. return ret;
  516. }