void fcgi_send_file_revisions(void) { const sx_hashfs_volume_t *vol; const sx_hashfs_file_t *file; int comma = 0; rc_ty s; s = sx_hashfs_volume_by_name(hashfs, volume, &vol); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); s = sx_hashfs_revision_first(hashfs, vol, path, &file, 0); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); CGI_PUTS("Content-type: application/json\r\n\r\n{\"fileRevisions\":{"); do { CGI_PRINTF("%s\"%s\":{\"blockSize\":%d,\"fileSize\":", comma ? "," : "", file->revision, file->block_size); CGI_PUTLL(file->file_size); CGI_PRINTF(",\"createdAt\":%u}", file->created_at); comma = 1; s = sx_hashfs_revision_next(hashfs, 0); } while(s == OK); if(s == ITER_NO_MORE) CGI_PUTS("}}"); }
void fcgi_send_file(void) { sx_hashfs_file_t filedata; const sx_hash_t *hash; sx_nodelist_t *nodes; sx_hash_t etag; int comma = 0; rc_ty s = sx_hashfs_getfile_begin(hashfs, volume, path, get_arg("rev"), &filedata, &etag); if(s != OK) quit_errnum(s == ENOENT ? 404 : 500); if(is_object_fresh(&etag, 'F', filedata.created_at)) { sx_hashfs_getfile_end(hashfs); return; } CGI_PRINTF("Content-type: application/json\r\n\r\n{\"blockSize\":%d,\"fileSize\":", filedata.block_size); CGI_PUTLL(filedata.file_size); CGI_PRINTF(",\"createdAt\":%u,\"fileRevision\":\"%s\",\"fileData\":[", filedata.created_at, filedata.revision); while((s = sx_hashfs_getfile_block(hashfs, &hash, &nodes)) == OK) { if(comma) CGI_PUTC(','); else comma |= 1; CGI_PUTC('{'); send_qstring_hash(hash); CGI_PUTC(':'); /* Nodes are in NL_PREVNEXT order and MUST NOT be randomized * (see comments in sx_hashfs_getfile_block) */ send_nodes(nodes); CGI_PUTC('}'); sx_nodelist_delete(nodes); } sx_hashfs_getfile_end(hashfs); CGI_PUTS("]"); if(s != ITER_NO_MORE) quit_itererr("Failed to list file blocks", s); CGI_PUTS("}"); }
static void print_html(int status, const char *title, int errnum, const char *errmsg) { if (status) CGI_PRINTF("Status: %d\r\n", status); CGI_PRINTF("Content-Type: text/html\r\n\r\n"); CGI_PRINTF(HTML_1, errmsg ? 653: 600); CGI_PUTS(HTML_2); CGI_PUTS(LOGO); CGI_PUTS(HTML_3); if (errmsg) CGI_PRINTF("<h2>Error %d: %s</h2>", errnum, errmsg); unsigned int version; const sx_uuid_t *cluster_uuid = sx_hashfs_uuid(hashfs); const sx_uuid_t *dist_uuid = sx_hashfs_distinfo(hashfs, &version, NULL); sx_uuid_t node_uuid; CGI_PRINTF(HTML_4, cluster_uuid->string, sx_hashfs_self_uuid(hashfs, &node_uuid) == OK ? node_uuid.string : "<not assigned yet>", dist_uuid ? dist_uuid->string : "<not defined yet>", dist_uuid ? version : 0 ); }
static void print_html(int status, const char *title, int errnum, const char *errmsg) { if (status) CGI_PRINTF("Status: %d\r\n", status); CGI_PRINTF("Content-Type: text/html\r\n\r\n\ <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\ <html>\ <head><title>%s</title></head>", title); CGI_PUTS("<body style='background-color:#9fcdea'>\ <div style='position:absolute; top:50%; height:20em; margin-top: -10em; text-align:center; width: 90%;'>\ <img src=\""); CGI_PUTS(LOGO1);/* too large to put into printf */ CGI_PUTS(LOGO2); CGI_PUTS("\"/>"); if (errmsg) CGI_PRINTF("<h2>Error %d</h2><pre>%s</pre>", errnum, errmsg); CGI_PUTS("\ <h2>\ This is an <a href=\"http://www.skylable.com/products/sx\">"SERVER_NAME" node</a>\ </h2>\ </div>\ <div style=\"position: absolute; bottom:1em; right: 1em; border: #ddd 1px solid; font: 10px normal; color: #07c;\">"); unsigned int version; const sx_uuid_t *cluster_uuid = sx_hashfs_uuid(hashfs); const sx_uuid_t *dist_uuid = sx_hashfs_distinfo(hashfs, &version, NULL); sx_uuid_t node_uuid; CGI_PRINTF("Cluster UUID: %s<br>Node UUID: %s<br>Distribution: ", cluster_uuid->string, sx_hashfs_self_uuid(hashfs, &node_uuid) == OK ? node_uuid.string : "<not assigned yet>"); if(dist_uuid) CGI_PRINTF("%s(%u)", dist_uuid->string, version); else CGI_PUTS("<not defined yet>"); CGI_PUTS("\ </div>\ </body>\ </html>"); }
static void send_error_helper(const char sep, int errnum, const char *message) { sx_uuid_t node_uuid; CGI_PRINTF("%c\"ErrorMessage\":", sep); json_send_qstring(message ? message : ""); if (errnum == 500 || errnum == 400) { if (message) WARN("HTTP %d: %s", errnum, message); CGI_PUTS(",\"ErrorDetails\":"); json_send_qstring(msg_log_end()); } CGI_PUTS(",\"NodeId\":"); json_send_qstring(sx_hashfs_self_uuid(hashfs, &node_uuid)==OK ? node_uuid.string : "<UNKNOWN>"); CGI_PUTS(",\"ErrorId\":"); json_send_qstring(msg_get_id()); CGI_PUTC('}'); }
static void send_error_helper(const char sep, int errnum, const char *message) { CGI_PRINTF("%c\"ErrorMessage\":", sep); json_send_qstring(message); if (errnum == 500 || errnum == 400) { if (message) WARN("HTTP %d: %s", errnum, message); CGI_PUTS(",\"ErrorDetails\":"); json_send_qstring(msg_log_end()); } CGI_PUTS(",\"NodeId\":"); const sx_node_t *me = sx_hashfs_self(hashfs); json_send_qstring(me ? sx_node_uuid(me)->string : "<UNKNOWN>"); CGI_PUTS(",\"ErrorId\":"); json_send_qstring(msg_get_id()); CGI_PUTC('}'); }
void send_error(int errnum, const char *message) { if(!message || !*message) message = http_err_str(errnum); if(!is_sky()) { print_html(errnum, "Error", errnum, message); } else { CGI_PRINTF("Status: %d\r\nContent-Type: application/json\r\n", errnum); if (verb == VERB_HEAD) { /* workaround for old curl: it would close (instead of reusing) * the connection if a HEAD doesn't have Content-Length/chunked * encoding. And then we run out of ports due to too many * TIME_WAIT connections */ CGI_PUTS("Content-Length: 0\r\n\r\n"); } else { CGI_PUTS("\r\n"); send_error_helper('{',errnum,message); } } }
void send_server_info(void) { last_flush = time(NULL); CGI_PRINTF("Server: Skylable SX\r\nSX-Cluster: %s (%s)%s\r\nSX-API-Version: %u\r\nVary: Accept-Encoding\r\n", src_version(), sx_hashfs_uuid(hashfs)->string, sx_hashfs_uses_secure_proto(hashfs) ? " ssl" : "", SRC_API_VERSION); }
void send_server_info(void) { last_flush = time(NULL); CGI_PRINTF("Server: Skylable SX\r\nSX-Cluster: %s (%s)\r\nVary: Accept-Encoding\r\n", src_version(), sx_hashfs_uuid(hashfs)->string); }
static void create_or_extend_tempfile(const sx_hashfs_volume_t *vol, const char *filename, int extending) { const struct jparse_actions acts = { JPACTS_INT64( JPACT(cb_newfile_size, JPKEY("fileSize")), JPACT(cb_newfile_seq, JPKEY("extendSeq")) ), JPACTS_STRING( JPACT(cb_newfile_block, JPKEY("fileData"), JPANYITM), JPACT(cb_newfile_addmeta, JPKEY("fileMeta"), JPANYKEY) ), JPACTS_NULL( JPACT(cb_newfile_delmeta, JPKEY("fileMeta"), JPANYKEY) ) }; struct cb_newfile_ctx yctx; hash_presence_ctx_t ctx; const char *token; jparse_t *J; int len; rc_ty s; yctx.filesize = -1; yctx.seq = -1; yctx.rc = EINVAL; J = sxi_jparse_create(&acts, &yctx, 0); if(!J) { sx_hashfs_putfile_end(hashfs); quit_errmsg(503, "Cannot create JSON parser"); } while((len = get_body_chunk(hashbuf, sizeof(hashbuf))) > 0) if(sxi_jparse_digest(J, hashbuf, len)) break; if(len || sxi_jparse_done(J)) { send_error(rc2http(yctx.rc), sxi_jparse_geterr(J)); sx_hashfs_putfile_end(hashfs); sxi_jparse_destroy(J); return; } sxi_jparse_destroy(J); auth_complete(); quit_unless_authed(); s = sx_hashfs_putfile_gettoken(hashfs, user, yctx.filesize, yctx.seq, &token, hash_presence_callback, &ctx); if (s != OK) { sx_hashfs_putfile_end(hashfs); if(!*msg_get_reason()) msg_set_reason("Cannot obtain upload token: %s", rc2str(s)); quit_errmsg((s == ENOSPC) ? 413 : rc2http(s), msg_get_reason()); } CGI_PRINTF("Content-type: application/json\r\n\r\n{\"uploadToken\":"); json_send_qstring(extending ? path : token); CGI_PUTS(",\"uploadData\":{"); ctx.h = hashfs; ctx.comma = 0; while((s = sx_hashfs_putfile_getblock(hashfs)) == OK) { /* Nothing to do here, API does a little bit of work at a time by design * We can stick keepalives in here if we ever need to */ } sx_hashfs_putfile_end(hashfs); CGI_PUTS("}"); if(s != ITER_NO_MORE) { quit_itererr("Failed to send file blocks", s); } CGI_PUTS("}"); }