summaryrefslogtreecommitdiff
path: root/src/http.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/http.c')
-rw-r--r--src/http.c791
1 files changed, 791 insertions, 0 deletions
diff --git a/src/http.c b/src/http.c
new file mode 100644
index 0000000..02dedfb
--- /dev/null
+++ b/src/http.c
@@ -0,0 +1,791 @@
+#include "http.h"
+
+SQLITE_EXTENSION_INIT1
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+
+typedef struct http_vtab http_vtab;
+struct http_vtab {
+ sqlite3_vtab base;
+ char* zMethod;
+};
+
+typedef struct http_cursor http_cursor;
+struct http_cursor {
+ sqlite3_vtab_cursor base;
+ sqlite3_int64 iRowid;
+ http_request req;
+ http_response resp;
+};
+
+// If zHeaders contains headers for multiple responses, then this will strip
+// headers from all but the last response.
+void remove_all_but_last_headers(char* zHeaders) {
+ int szHeaders = strlen(zHeaders);
+ char* pLastTerminator = NULL;
+ char* pSearch = zHeaders;
+
+ for (;;) {
+ char* p = strstr(pSearch, "\r\n\r\n");
+ // If there isn't more terminators, we are done here
+ if (!p) {
+ break;
+ }
+ // If it's at the end of zHeaders, we are done here
+ if (szHeaders - (p - zHeaders) == 4) {
+ break;
+ }
+ pSearch = p + 4;
+ pLastTerminator = p;
+ }
+
+ if (pLastTerminator) {
+ pLastTerminator += 4;
+ memmove(zHeaders, pLastTerminator, strlen(pLastTerminator) + 1);
+ }
+}
+
+// Separate the status and header lines
+void separate_status_and_headers(char** ppStatus, char* zHeaders) {
+ char* cr = strchr(zHeaders, '\r');
+ *ppStatus = sqlite3_mprintf("%.*s", cr - zHeaders, zHeaders);
+ memmove(zHeaders, cr + 2, strlen(cr + 2) + 1);
+}
+
+static int httpConnect(sqlite3* db,
+ void* pAux,
+ int argc,
+ const char* const* argv,
+ sqlite3_vtab** ppVtab,
+ char** pzErr) {
+ http_vtab* pNew;
+ int rc;
+
+ rc = sqlite3_declare_vtab(db,
+ "CREATE TABLE x(response_status TEXT, "
+ "response_status_code INT, response_headers TEXT, "
+ "response_body BLOB, request_method TEXT HIDDEN, "
+ "request_url TEXT HIDDEN, "
+ "request_headers TEXT HIDDEN, "
+ "request_body BLOB HIDDEN)");
+
+#define HTTP_COL_RESPONSE_STATUS 0
+#define HTTP_COL_RESPONSE_STATUS_CODE 1
+#define HTTP_COL_RESPONSE_HEADERS 2
+#define HTTP_COL_RESPONSE_BODY 3
+#define HTTP_COL_REQUEST_METHOD 4
+#define HTTP_COL_REQUEST_URL 5
+#define HTTP_COL_REQUEST_HEADERS 6
+#define HTTP_COL_REQUEST_BODY 7
+
+ if (rc == SQLITE_OK) {
+ pNew = sqlite3_malloc(sizeof(*pNew));
+ *ppVtab = (sqlite3_vtab*)pNew;
+ if (pNew == 0)
+ return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ if (sqlite3_stricmp(argv[0], "http_get") == 0) {
+ pNew->zMethod = sqlite3_mprintf("GET");
+ } else if (sqlite3_stricmp(argv[0], "http_post") == 0) {
+ pNew->zMethod = sqlite3_mprintf("POST");
+ }
+ }
+ return rc;
+}
+
+static int httpDisconnect(sqlite3_vtab* pVtab) {
+ http_vtab* p = (http_vtab*)pVtab;
+ sqlite3_free(p->zMethod);
+ sqlite3_free(p);
+ return SQLITE_OK;
+}
+
+static int httpOpen(sqlite3_vtab* p, sqlite3_vtab_cursor** ppCursor) {
+ http_cursor* pCur;
+ http_vtab* pVtab = (http_vtab*)p;
+ pCur = sqlite3_malloc(sizeof(*pCur));
+ if (pCur == 0)
+ return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ if (pVtab->zMethod) {
+ pCur->req.zMethod = sqlite3_mprintf("%s", pVtab->zMethod);
+ }
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+static int httpClose(sqlite3_vtab_cursor* cur) {
+ http_cursor* pCur = (http_cursor*)cur;
+ sqlite3_free(pCur->resp.pBody);
+ sqlite3_free(pCur->req.zUrl);
+ sqlite3_free(pCur->req.zMethod);
+ sqlite3_free(pCur);
+ return SQLITE_OK;
+}
+
+static int httpNext(sqlite3_vtab_cursor* cur) {
+ http_cursor* pCur = (http_cursor*)cur;
+ pCur->iRowid++;
+ return SQLITE_OK;
+}
+
+static int httpColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int i) {
+ http_cursor* pCur = (http_cursor*)cur;
+
+ switch (i) {
+ case HTTP_COL_RESPONSE_STATUS:
+ sqlite3_result_text(ctx, pCur->resp.zStatus, -1, SQLITE_TRANSIENT);
+ break;
+
+ case HTTP_COL_RESPONSE_STATUS_CODE:
+ sqlite3_result_int(ctx, pCur->resp.iStatusCode);
+ break;
+
+ case HTTP_COL_RESPONSE_HEADERS:
+ sqlite3_result_text(ctx, pCur->resp.zHeaders, -1, SQLITE_TRANSIENT);
+ break;
+
+ case HTTP_COL_RESPONSE_BODY:
+ sqlite3_result_blob(ctx, pCur->resp.pBody, pCur->resp.szBody, SQLITE_TRANSIENT);
+ break;
+
+ case HTTP_COL_REQUEST_METHOD:
+ sqlite3_result_text(ctx, pCur->req.zMethod, -1, SQLITE_TRANSIENT);
+ break;
+
+ case HTTP_COL_REQUEST_URL:
+ sqlite3_result_text(ctx, pCur->req.zUrl, -1, SQLITE_TRANSIENT);
+ break;
+
+ case HTTP_COL_REQUEST_HEADERS:
+ if (pCur->req.zHeaders) {
+ sqlite3_result_text(ctx, pCur->req.zHeaders, -1, SQLITE_TRANSIENT);
+ } else {
+ sqlite3_result_null(ctx);
+ }
+ break;
+
+ case HTTP_COL_REQUEST_BODY:
+ if (pCur->req.pBody) {
+ sqlite3_result_blob(ctx, pCur->req.pBody, pCur->req.szBody, SQLITE_TRANSIENT);
+ } else {
+ sqlite3_result_null(ctx);
+ }
+ break;
+ }
+
+ return SQLITE_OK;
+}
+
+static int httpRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) {
+ http_cursor* pCur = (http_cursor*)cur;
+ *pRowid = pCur->iRowid;
+ return SQLITE_OK;
+}
+
+static int httpEof(sqlite3_vtab_cursor* cur) {
+ http_cursor* pCur = (http_cursor*)cur;
+ return pCur->iRowid >= 1;
+}
+
+#define HTTP_FLAG_METHOD 1
+#define HTTP_FLAG_URL 2
+#define HTTP_FLAG_HEADERS 4
+#define HTTP_FLAG_BODY 8
+
+static int httpFilter(sqlite3_vtab_cursor* pVtabCursor,
+ int idxNum,
+ const char* idxStr,
+ int argc,
+ sqlite3_value** argv) {
+ http_cursor* pCur = (http_cursor*)pVtabCursor;
+ http_vtab* pVtab = (http_vtab*)pVtabCursor->pVtab;
+ int bIsDo = pVtab->zMethod == NULL;
+ char* zErrMsg = NULL;
+ int rc;
+ if (bIsDo) {
+ pCur->req.zMethod = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
+ pCur->req.zUrl = sqlite3_mprintf("%s", sqlite3_value_text(argv[1]));
+ if (idxNum & HTTP_FLAG_HEADERS) {
+ pCur->req.zHeaders = sqlite3_mprintf("%s", sqlite3_value_text(argv[2]));
+ }
+ if (idxNum & HTTP_FLAG_BODY) {
+ pCur->req.szBody = sqlite3_value_bytes(argv[3]);
+ pCur->req.pBody = sqlite3_value_blob(argv[3]);
+ }
+ } else {
+ pCur->req.zUrl = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
+ if (idxNum & HTTP_FLAG_HEADERS) {
+ pCur->req.zHeaders = sqlite3_mprintf("%s", sqlite3_value_text(argv[1]));
+ }
+ if (idxNum & HTTP_FLAG_BODY) {
+ pCur->req.szBody = sqlite3_value_bytes(argv[2]);
+ pCur->req.pBody = sqlite3_value_blob(argv[2]);
+ }
+ }
+ rc = http_do_request(&pCur->req, &pCur->resp, &zErrMsg);
+ if (rc != SQLITE_OK) {
+ sqlite3_free(pCur->base.pVtab->zErrMsg);
+ pCur->base.pVtab->zErrMsg = zErrMsg;
+ }
+ return rc;
+}
+
+static int httpBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo) {
+ http_vtab* pTab = (http_vtab*)tab;
+ int bIsDo = pTab->zMethod == NULL;
+ int i;
+ int idxNum = 0;
+ const struct sqlite3_index_constraint* pConstraint;
+
+ pConstraint = pIdxInfo->aConstraint;
+
+ for (i = 0; i < pIdxInfo->nConstraint; ++i, ++pConstraint) {
+ if (!pIdxInfo->aConstraint[i].usable) {
+ return SQLITE_CONSTRAINT;
+ }
+ if (pConstraint->op != SQLITE_INDEX_CONSTRAINT_EQ) {
+ return SQLITE_CONSTRAINT;
+ };
+ switch (pConstraint->iColumn) {
+ case HTTP_COL_REQUEST_METHOD:
+ if (bIsDo) {
+ idxNum |= HTTP_FLAG_METHOD;
+ } else {
+ idxNum |= HTTP_FLAG_URL;
+ }
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ break;
+
+ case HTTP_COL_REQUEST_URL:
+ if (bIsDo) {
+ idxNum |= HTTP_FLAG_URL;
+ } else {
+ idxNum |= HTTP_FLAG_HEADERS;
+ }
+ pIdxInfo->aConstraintUsage[i].argvIndex = 2;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ break;
+
+ case HTTP_COL_REQUEST_HEADERS:
+ if (bIsDo) {
+ idxNum |= HTTP_FLAG_HEADERS;
+ } else {
+ idxNum |= HTTP_FLAG_BODY;
+ }
+ pIdxInfo->aConstraintUsage[i].argvIndex = 3;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ break;
+
+ case HTTP_COL_REQUEST_BODY:
+ if (bIsDo) {
+ idxNum |= HTTP_FLAG_BODY;
+ } else {
+ sqlite3_free(tab->zErrMsg);
+ tab->zErrMsg = sqlite3_mprintf("too many arguments");
+ return SQLITE_ERROR;
+ }
+ pIdxInfo->aConstraintUsage[i].argvIndex = 4;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ break;
+ }
+ }
+
+ if (bIsDo && !(idxNum & HTTP_FLAG_METHOD)) {
+ sqlite3_free(tab->zErrMsg);
+ tab->zErrMsg = sqlite3_mprintf("method missing");
+ return SQLITE_ERROR;
+ }
+
+ if (!(idxNum & HTTP_FLAG_URL)) {
+ sqlite3_free(tab->zErrMsg);
+ tab->zErrMsg = sqlite3_mprintf("url missing");
+ return SQLITE_ERROR;
+ }
+
+ pIdxInfo->estimatedCost = (double)1;
+ pIdxInfo->estimatedRows = 1;
+ pIdxInfo->idxNum = idxNum;
+
+ return SQLITE_OK;
+}
+
+static sqlite3_module httpModule = {
+ /* iVersion */ 0,
+ /* xCreate */ 0,
+ /* xConnect */ httpConnect,
+ /* xBestIndex */ httpBestIndex,
+ /* xDisconnect */ httpDisconnect,
+ /* xDestroy */ 0,
+ /* xOpen */ httpOpen,
+ /* xClose */ httpClose,
+ /* xFilter */ httpFilter,
+ /* xNext */ httpNext,
+ /* xEof */ httpEof,
+ /* xColumn */ httpColumn,
+ /* xRowid */ httpRowid,
+ /* xUpdate */ 0,
+ /* xBegin */ 0,
+ /* xSync */ 0,
+ /* xCommit */ 0,
+ /* xRollback */ 0,
+ /* xFindMethod */ 0,
+ /* xRename */ 0,
+ /* xSavepoint */ 0,
+ /* xRelease */ 0,
+ /* xRollbackTo */ 0,
+ /* xShadowName */ 0,
+};
+
+static void httpSimpleFunc(sqlite3_context* ctx,
+ int argc,
+ sqlite3_value** argv,
+ const char* zTable,
+ const char* zColumn,
+ int bText) {
+ char* zSql;
+ int rc;
+ sqlite3_stmt* pStmt;
+
+ if (argc == 0) {
+ sqlite3_result_error(ctx, "not enough arguments", -1);
+ return;
+ }
+
+ if (argc == 1) {
+ zSql = sqlite3_mprintf(
+ "select \"%w\" from \"%w\"(%Q)", zColumn, zTable, sqlite3_value_text(argv[0]));
+ }
+
+ if (argc == 2) {
+ zSql = sqlite3_mprintf("select \"%w\" from \"%w\"(%Q, %Q)",
+ zColumn,
+ zTable,
+ sqlite3_value_text(argv[0]),
+ sqlite3_value_text(argv[1]));
+ }
+
+ if (argc == 3) {
+ zSql = sqlite3_mprintf("select \"%w\" from \"%w\"(%Q, %Q, %Q)",
+ zColumn,
+ zTable,
+ sqlite3_value_text(argv[0]),
+ sqlite3_value_text(argv[1]),
+ sqlite3_value_text(argv[2]));
+ }
+
+ if (argc >= 4) {
+ zSql = sqlite3_mprintf("select \"%w\" from \"%w\"(%Q, %Q, %Q, %Q)",
+ zColumn,
+ zTable,
+ sqlite3_value_text(argv[0]),
+ sqlite3_value_text(argv[1]),
+ sqlite3_value_text(argv[2]),
+ sqlite3_value_text(argv[2]));
+ }
+
+ if (zSql == 0) {
+ sqlite3_result_error_code(ctx, SQLITE_NOMEM);
+ return;
+ }
+
+ rc = sqlite3_prepare_v2(sqlite3_context_db_handle(ctx), zSql, -1, &pStmt, 0);
+
+ sqlite3_free(zSql);
+
+ if (rc != SQLITE_OK) {
+ sqlite3_result_error_code(ctx, rc);
+ return;
+ }
+
+ rc = sqlite3_step(pStmt);
+
+ if (rc == SQLITE_ROW) {
+ int size = sqlite3_column_bytes(pStmt, 0);
+ if (bText) {
+ sqlite3_result_text(ctx, sqlite3_column_text(pStmt, 0), size, SQLITE_TRANSIENT);
+ } else {
+ sqlite3_result_blob(ctx, sqlite3_column_blob(pStmt, 0), size, SQLITE_TRANSIENT);
+ }
+ sqlite3_finalize(pStmt);
+ return;
+ }
+
+ rc = sqlite3_finalize(pStmt);
+ if (rc != SQLITE_OK) {
+ sqlite3_result_error_code(ctx, rc);
+ }
+}
+
+static void httpGetBodyFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ httpSimpleFunc(ctx, argc, argv, "http_get", "response_body", 0);
+}
+
+static void httpPostBodyFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ httpSimpleFunc(ctx, argc, argv, "http_post", "response_body", 0);
+}
+
+static void httpDoBodyFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ httpSimpleFunc(ctx, argc, argv, "http_do", "response_body", 0);
+}
+
+static void httpGetHeadersFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ httpSimpleFunc(ctx, argc, argv, "http_get", "response_headers", 1);
+}
+
+static void httpPostHeadersFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ httpSimpleFunc(ctx, argc, argv, "http_post", "response_headers", 1);
+}
+
+static void httpDoHeadersFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ httpSimpleFunc(ctx, argc, argv, "http_do", "response_headers", 1);
+}
+
+static void httpHeadersFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ char* zHeaders = NULL;
+ int i;
+ int size = 0;
+ int offset = 0;
+ if (argc < 2) {
+ sqlite3_result_error(ctx, "http_headers: expected at least 2 arguments", -1);
+ return;
+ }
+ if (argc % 2 != 0) {
+ sqlite3_result_error(ctx, "http_headers: even number of arguments expected", -1);
+ return;
+ }
+ for (i = 0; i < argc; i += 2) {
+ size += sqlite3_value_bytes(argv[i]) + 2;
+ size += sqlite3_value_bytes(argv[i + 1]) + 2;
+ }
+ zHeaders = sqlite3_malloc(size);
+ if (!zHeaders) {
+ sqlite3_result_error_code(ctx, SQLITE_NOMEM);
+ return;
+ }
+ for (i = 0; i < argc; i += 2) {
+ memcpy(zHeaders + offset, sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i]));
+ offset += sqlite3_value_bytes(argv[i]);
+
+ memcpy(zHeaders + offset, ": ", 2);
+ offset += 2;
+
+ memcpy(
+ zHeaders + offset, sqlite3_value_text(argv[i + 1]), sqlite3_value_bytes(argv[i + 1]));
+ offset += sqlite3_value_bytes(argv[i + 1]);
+
+ memcpy(zHeaders + offset, "\r\n", 2);
+ offset += 2;
+ }
+ assert(offset == size);
+ sqlite3_result_text(ctx, zHeaders, size, sqlite3_free);
+}
+
+static void httpHeadersHasFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ int zHeadersSize = sqlite3_value_bytes(argv[0]);
+ const char* zHeaders = sqlite3_value_text(argv[0]);
+ int zHeaderSize = sqlite3_value_bytes(argv[1]);
+ const char* zHeader = sqlite3_value_text(argv[1]);
+ while (zHeadersSize > 0) {
+ int iParsed = 0;
+ const char* pName;
+ int iNameSize;
+ int rc = http_next_header(zHeaders, zHeadersSize, &iParsed, &pName, &iNameSize, NULL, NULL);
+ if (rc == SQLITE_ERROR) {
+ sqlite3_result_error(ctx, "http_headers_has: malformed headers", -1);
+ return;
+ }
+ if (rc == SQLITE_DONE) {
+ sqlite3_result_int(ctx, 0);
+ return;
+ }
+ zHeaders += iParsed;
+ zHeadersSize -= iParsed;
+ if (iNameSize == zHeaderSize && sqlite3_strnicmp(zHeader, pName, zHeaderSize) == 0) {
+ sqlite3_result_int(ctx, 1);
+ return;
+ }
+ }
+ sqlite3_result_int(ctx, 0);
+}
+
+static void httpHeadersGetFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ int zHeadersSize = sqlite3_value_bytes(argv[0]);
+ const char* zHeaders = sqlite3_value_text(argv[0]);
+ int zHeaderSize = sqlite3_value_bytes(argv[1]);
+ const char* zHeader = sqlite3_value_text(argv[1]);
+ while (zHeadersSize > 0) {
+ int iParsed = 0;
+ const char* pName;
+ int iNameSize;
+ const char* pValue;
+ int iValueSize;
+ int rc = http_next_header(
+ zHeaders, zHeadersSize, &iParsed, &pName, &iNameSize, &pValue, &iValueSize);
+ if (rc == SQLITE_ERROR) {
+ sqlite3_result_error(ctx, "http_headers_get: malformed headers", -1);
+ return;
+ }
+ if (rc == SQLITE_DONE) {
+ sqlite3_result_int(ctx, 0);
+ return;
+ }
+ zHeaders += iParsed;
+ zHeadersSize -= iParsed;
+ if (iNameSize == zHeaderSize && sqlite3_strnicmp(zHeader, pName, zHeaderSize) == 0) {
+ sqlite3_result_text(ctx, pValue, iValueSize, SQLITE_TRANSIENT);
+ return;
+ }
+ }
+}
+
+#define HTTP_HEADERS_EACH_COL_NAME 0
+#define HTTP_HEADERS_EACH_COL_VALUE 1
+#define HTTP_HEADERS_EACH_COL_HEADERS 2
+
+typedef struct http_headers_each_vtab http_headers_each_vtab;
+struct http_headers_each_vtab {
+ sqlite3_vtab base;
+};
+
+typedef struct http_headers_each_cursor http_headers_each_cursor;
+struct http_headers_each_cursor {
+ sqlite3_vtab_cursor base;
+ sqlite3_int64 iRowid;
+ const char* zHeaders;
+ int zHeadersSize;
+ const char* p;
+ const char* name;
+ int nameSize;
+ const char* value;
+ int valueSize;
+ int available;
+ int done;
+};
+
+static int httpHeadersEachConnect(sqlite3* db,
+ void* pAux,
+ int argc,
+ const char* const* argv,
+ sqlite3_vtab** ppVtab,
+ char** pzErr) {
+ http_headers_each_vtab* pNew;
+ int rc;
+ rc = sqlite3_declare_vtab(db, "CREATE TABLE x(name TEXT, value TEXT, headers TEXT HIDDEN)");
+ if (rc == SQLITE_OK) {
+ pNew = sqlite3_malloc(sizeof(*pNew));
+ *ppVtab = (sqlite3_vtab*)pNew;
+ if (pNew == 0)
+ return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ }
+ return rc;
+}
+
+static int httpHeadersEachDisconnect(sqlite3_vtab* pVtab) {
+ http_headers_each_vtab* p = (http_headers_each_vtab*)pVtab;
+ sqlite3_free(p);
+ return SQLITE_OK;
+}
+
+static int httpHeadersEachOpen(sqlite3_vtab* p, sqlite3_vtab_cursor** ppCursor) {
+ http_headers_each_cursor* pCur;
+ pCur = sqlite3_malloc(sizeof(*pCur));
+ if (pCur == 0)
+ return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+static int httpHeadersEachClose(sqlite3_vtab_cursor* cur) {
+ http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur;
+ sqlite3_free(pCur);
+ return SQLITE_OK;
+}
+
+static int httpHeadersEachNext(sqlite3_vtab_cursor* cur) {
+ http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur;
+ pCur->iRowid++;
+ int nparsed = 0;
+ int rc = http_next_header(pCur->p,
+ pCur->available,
+ &nparsed,
+ &pCur->name,
+ &pCur->nameSize,
+ &pCur->value,
+ &pCur->valueSize);
+ pCur->p += nparsed;
+ pCur->available -= nparsed;
+ if (rc == SQLITE_ERROR) {
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = sqlite3_mprintf("http_headers_each: malformed headers");
+ return SQLITE_ERROR;
+ }
+ pCur->done = rc == SQLITE_DONE;
+ return SQLITE_OK;
+}
+
+static int httpHeadersEachColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int i) {
+ http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur;
+ switch (i) {
+ case HTTP_HEADERS_EACH_COL_NAME:
+ sqlite3_result_text(ctx, pCur->name, pCur->nameSize, SQLITE_TRANSIENT);
+ break;
+
+ case HTTP_HEADERS_EACH_COL_VALUE:
+ sqlite3_result_text(ctx, pCur->value, pCur->valueSize, SQLITE_TRANSIENT);
+ break;
+
+ case HTTP_HEADERS_EACH_COL_HEADERS:
+ sqlite3_result_text(ctx, pCur->zHeaders, pCur->zHeadersSize, SQLITE_TRANSIENT);
+ break;
+
+ default:
+ sqlite3_result_error(ctx, "unknown column", -1);
+ break;
+ }
+ return SQLITE_OK;
+}
+
+static int httpHeadersEachRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) {
+ http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur;
+ *pRowid = pCur->iRowid;
+ return SQLITE_OK;
+}
+
+static int httpHeadersEachEof(sqlite3_vtab_cursor* cur) {
+ http_headers_each_cursor* pCur = (http_headers_each_cursor*)cur;
+ return pCur->done;
+}
+
+static int httpHeadersEachFilter(sqlite3_vtab_cursor* pVtabCursor,
+ int idxNum,
+ const char* idxStr,
+ int argc,
+ sqlite3_value** argv) {
+ http_headers_each_cursor* pCur = (http_headers_each_cursor*)pVtabCursor;
+ pCur->zHeadersSize = sqlite3_value_bytes(argv[0]);
+ pCur->available = pCur->zHeadersSize;
+ pCur->zHeaders = sqlite3_value_text(argv[0]);
+ pCur->p = pCur->zHeaders;
+ int nparsed = 0;
+ int rc = http_next_header(pCur->p,
+ pCur->available,
+ &nparsed,
+ &pCur->name,
+ &pCur->nameSize,
+ &pCur->value,
+ &pCur->valueSize);
+ pCur->p += nparsed;
+ pCur->available -= nparsed;
+ pCur->iRowid = 1;
+ if (rc == SQLITE_ERROR) {
+ sqlite3_free(pVtabCursor->pVtab->zErrMsg);
+ pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf("http_headers_each: malformed headers");
+ return SQLITE_ERROR;
+ }
+ pCur->done = rc == SQLITE_DONE;
+ return SQLITE_OK;
+}
+
+static int httpHeadersEachBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo) {
+ http_vtab* pTab = (http_vtab*)tab;
+ int bHeadersSeen = 0;
+
+ for (int i = 0; i < pIdxInfo->nConstraint; ++i) {
+ if (pIdxInfo->aConstraint[i].iColumn == HTTP_HEADERS_EACH_COL_HEADERS) {
+ if (!pIdxInfo->aConstraint[i].usable) {
+ return SQLITE_CONSTRAINT;
+ }
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ bHeadersSeen = 1;
+ }
+ }
+
+ if (!bHeadersSeen) {
+ sqlite3_free(tab->zErrMsg);
+ tab->zErrMsg = sqlite3_mprintf("headers missing");
+ return SQLITE_ERROR;
+ }
+
+ pIdxInfo->estimatedCost = (double)1;
+ // pIdxInfo->estimatedRows = 1;
+
+ return SQLITE_OK;
+}
+
+static sqlite3_module httpHeadersEachModule = {
+ /* iVersion */ 0,
+ /* xCreate */ 0,
+ /* xConnect */ httpHeadersEachConnect,
+ /* xBestIndex */ httpHeadersEachBestIndex,
+ /* xDisconnect */ httpHeadersEachDisconnect,
+ /* xDestroy */ 0,
+ /* xOpen */ httpHeadersEachOpen,
+ /* xClose */ httpHeadersEachClose,
+ /* xFilter */ httpHeadersEachFilter,
+ /* xNext */ httpHeadersEachNext,
+ /* xEof */ httpHeadersEachEof,
+ /* xColumn */ httpHeadersEachColumn,
+ /* xRowid */ httpHeadersEachRowid,
+ /* xUpdate */ 0,
+ /* xBegin */ 0,
+ /* xSync */ 0,
+ /* xCommit */ 0,
+ /* xRollback */ 0,
+ /* xFindMethod */ 0,
+ /* xRename */ 0,
+ /* xSavepoint */ 0,
+ /* xRelease */ 0,
+ /* xRollbackTo */ 0,
+ /* xShadowName */ 0,
+};
+
+static const struct Func {
+ const char* name;
+ void (*xFunc)(sqlite3_context*, int, sqlite3_value**);
+} funcs[] = {
+ {"http_get_body", httpGetBodyFunc},
+ {"http_post_body", httpPostBodyFunc},
+ {"http_do_body", httpDoBodyFunc},
+ {"http_get_headers", httpGetHeadersFunc},
+ {"http_post_headers", httpPostHeadersFunc},
+ {"http_do_headers", httpDoHeadersFunc},
+ {"http_headers", httpHeadersFunc},
+ {"http_headers_has", httpHeadersHasFunc},
+ {"http_headers_get", httpHeadersGetFunc},
+ {NULL, NULL},
+};
+
+static const struct Module {
+ const char* name;
+ const sqlite3_module* module;
+} modules[] = {
+ {"http_get", &httpModule},
+ {"http_post", &httpModule},
+ {"http_do", &httpModule},
+ {"http_headers_each", &httpHeadersEachModule},
+ {NULL, NULL},
+};
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+ int sqlite3_http_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi) {
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ int i;
+ for (i = 0; funcs[i].name && rc == SQLITE_OK; ++i) {
+ rc = sqlite3_create_function(
+ db, funcs[i].name, -1, SQLITE_UTF8, NULL, funcs[i].xFunc, NULL, NULL);
+ }
+ for (i = 0; modules[i].name && rc == SQLITE_OK; ++i) {
+ rc = sqlite3_create_module(db, modules[i].name, modules[i].module, 0);
+ }
+ return rc;
+}