1 /* $Id: ncfetch.cpp 617406 2020-09-30 19:09:56Z sadyrovr $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Mike Dicuccio, Anatoliy Kuznetsov, Dmitry Kazimirov
27 *
28 * File Description:
29 * BLOB (image) fetch from NetCache.
30 * Takes three CGI arguments:
31 * key=NETCACHE_KEY_OR_NETSTORAGE_LOCATOR
32 * fmt=mime/type (default: "image/png")
33 * filename=Example.pdf
34 *
35 */
36
37 #include <ncbi_pch.hpp>
38
39 #include <cgi/cgiapp.hpp>
40 #include <cgi/cgictx.hpp>
41 #include <cgi/cgi_exception.hpp>
42
43 #include <connect/services/netcache_api_expt.hpp>
44 #include <misc/netstorage/netstorage.hpp>
45 #include <connect/services/grid_app_version_info.hpp>
46
47 #include <corelib/reader_writer.hpp>
48 #include <corelib/rwstream.hpp>
49 #include <corelib/ncbiargs.hpp>
50
51 #define GRID_APP_NAME "ncfetch.cgi"
52
53 USING_NCBI_SCOPE;
54
55 #define NETSTORAGE_IO_BUFFER_SIZE (64 * 1024)
56
57 /// NetCache BLOB/image fetch application
58 ///
59 class CNetCacheBlobFetchApp : public CCgiApplication
60 {
61 protected:
62 virtual int ProcessRequest(CCgiContext& ctx);
63 virtual int OnException(std::exception& e, CNcbiOstream& os);
64
65 private:
66 string x_GetInitString(const string& key);
67 };
68
x_GetInitString(const string & key)69 string CNetCacheBlobFetchApp::x_GetInitString(const string& key)
70 {
71 try {
72 if (CNetStorageObjectLoc(CCompoundIDPool(), key).HasSubKey()) {
73 return "mode=direct";
74 }
75 }
76 catch (...) {
77 }
78
79 return GetConfig().Get("ncfetch", "netstorage");
80 }
81
s_WriteHeader(const CCgiRequest & request,CCgiResponse & reply)82 void s_WriteHeader(const CCgiRequest& request, CCgiResponse& reply)
83 {
84 bool is_found;
85
86 string fmt = request.GetEntry("fmt", &is_found);
87 if (fmt.empty() || !is_found)
88 fmt = "image/png";
89
90 string filename(request.GetEntry("filename", &is_found));
91 if (is_found && !filename.empty()) {
92 string is_inline(request.GetEntry("inline", &is_found));
93
94 reply.SetHeaderValue("Content-Disposition",
95 (is_found && NStr::StringToBool(is_inline) ?
96 "inline; filename=" : "attachment; filename=") +
97 filename);
98 }
99
100 reply.SetContentType(fmt);
101 reply.WriteHeader();
102 }
103
ProcessRequest(CCgiContext & ctx)104 int CNetCacheBlobFetchApp::ProcessRequest(CCgiContext& ctx)
105 {
106 const CCgiRequest& request = ctx.GetRequest();
107 CCgiResponse& reply = ctx.GetResponse();
108
109 bool is_found;
110
111 string key = request.GetEntry("key", &is_found);
112 if (key.empty() || !is_found) {
113 NCBI_THROW(CArgException, eNoArg, "CGI entry 'key' is missing");
114 }
115
116 CCombinedNetStorage netstorage(x_GetInitString(key));
117 CNetStorageObject netstorage_object(netstorage.Open(key));
118
119 char buffer[NETSTORAGE_IO_BUFFER_SIZE];
120 size_t total_bytes_written = 0;
121
122 while (!netstorage_object.Eof()) {
123 size_t bytes_read = netstorage_object.Read(buffer, sizeof(buffer));
124
125 if (!bytes_read) continue;
126 if (!total_bytes_written) s_WriteHeader(request, reply);
127
128 reply.out().write(buffer, bytes_read);
129 total_bytes_written += bytes_read;
130 }
131
132 netstorage_object.Close();
133
134 if (!total_bytes_written) s_WriteHeader(request, reply);
135 LOG_POST(Info << "retrieved data: " << total_bytes_written << " bytes");
136
137 return 0;
138 }
139
OnException(std::exception & e,CNcbiOstream & os)140 int CNetCacheBlobFetchApp::OnException(std::exception& e, CNcbiOstream& os)
141 {
142 string status_str;
143 string message;
144
145 if (auto arg_exception = dynamic_cast<CArgException*>(&e)) {
146 status_str = "400 Bad Request";
147 message = arg_exception->GetMsg();
148 SetHTTPStatus(CRequestStatus::e400_BadRequest);
149 } else if (auto nc_exception = dynamic_cast<CNetStorageException*>(&e)) {
150 switch (nc_exception->GetErrCode()) {
151 case CNetStorageException::eAuthError:
152 status_str = "403 Forbidden";
153 message = nc_exception->GetMsg();
154 SetHTTPStatus(CRequestStatus::e403_Forbidden);
155 break;
156 case CNetStorageException::eNotExists:
157 status_str = "404 Not Found";
158 message = nc_exception->GetMsg();
159 SetHTTPStatus(CRequestStatus::e404_NotFound);
160 break;
161 default:
162 return CCgiApplication::OnException(e, os);
163 }
164 } else
165 return CCgiApplication::OnException(e, os);
166
167 // Don't try to write to a broken output
168 if (!os.good()) {
169 return -1;
170 }
171
172 try {
173 // HTTP header
174 os << "Status: " << status_str << HTTP_EOL <<
175 "Content-Type: text/plain" HTTP_EOL HTTP_EOL <<
176 "ERROR: " << status_str << " " HTTP_EOL HTTP_EOL <<
177 NStr::HtmlEncode(message) << HTTP_EOL;
178
179 // Check for problems in sending the response
180 if (!os.good()) {
181 ERR_POST("CNetCacheBlobFetchApp::OnException() "
182 "failed to send error page back to the client");
183 return -1;
184 }
185 }
186 catch (exception& ex) {
187 NCBI_REPORT_EXCEPTION("(CGI) CNetCacheBlobFetchApp::OnException", ex);
188 }
189
190 return 0;
191 }
192
main(int argc,const char * argv[])193 int main(int argc, const char* argv[])
194 {
195 GRID_APP_CHECK_VERSION_ARGS();
196
197 SetSplitLogFile(true);
198 GetDiagContext().SetOldPostFormat(false);
199
200 CNetCacheBlobFetchApp app;
201 return app.AppMain(argc, argv);
202 }
203