7 #include "motoroutput.h"
8 #include "util_check.h"
9 #include "util_string.h"
10 #include "util_random.h"
11 #include "util_mimetype.h"
12 #include "util_file.h"
13 #include "util_base64.h"
14 #include "http-iconv.h"
18 #include "filemacro.h"
20 #include <neon/ne_session.h>
21 #include <neon/ne_request.h>
22 #include <neon/ne_utils.h>
23 #include <neon/ne_uri.h>
32 virtual ~NEONInitOnce () {
43 std::cerr << "neon feature:"
44 << (ne_has_support (NE_FEATURE_SSL) ? " FEATURE_SSL" : "")
45 << (ne_has_support (NE_FEATURE_ZLIB) ? " FEATURE_ZLIB" : "")
46 << (ne_has_support (NE_FEATURE_IPV6) ? " FEATURE_IPV6" : "")
47 << (ne_has_support (NE_FEATURE_LFS) ? " FEATURE_LFS" : "")
48 << (ne_has_support (NE_FEATURE_SOCKS) ? " FEATURE_SOCKS" : "")
49 << (ne_has_support (NE_FEATURE_TS_SSL) ? " FEATURE_TS_SSL" : "")
55 NEONInitOnce NeonInit;
57 //============================================================
58 void NeonPostBodyProvider::pushFile (const ustring& name, const ustring& path) {
59 if (name.length () > 0 && isPlainFile (path))
60 postFile.push_back (std::pair<ustring, ustring> (name, path));
63 void NeonPostBodyProvider::makeSeparator () {
64 separator.assign (CharConst ("--------")).append (randomKey ()).append (randomKey ());
67 ustring NeonPostBodyProvider::separatorLine () {
68 return ustring (CharConst ("--")).append (separator).append (uCRLF);
71 ustring NeonPostBodyProvider::separatorHeader () {
72 return ustring (CharConst (kMIME_FORMDATA "; boundary=")).append (separator);
75 ustring NeonPostBodyProvider::textSeparator (const ustring& name) {
77 ans.assign (CharConst ("--")).append (separator).append (uCRLF);
78 ans.append (CharConst (kRES_DISP ": form-data; name=" kQ2)).append (percentEncode (query->cv (name))).append (CharConst (kQ2 kCRLF kCRLF));
82 ustring NeonPostBodyProvider::fileSeparator (const ustring& name, const ustring& filename) {
84 ans.assign (CharConst ("--")).append (separator).append (uCRLF);
85 ans.append (CharConst (kRES_DISP ": form-data; name=" kQ2)).append (percentEncode (query->cv (name))).append (CharConst (kQ2 "; filename=" kQ2)).append (slashEncode (filename)).append (CharConst (kCRLF));
86 ans.append (CharConst (kRES_TYPE ": ")).append (mimetype (getExt (filename))).append (CharConst (kCRLF kCRLF));
90 ustring NeonPostBodyProvider::tailSeparator () {
92 ans.assign (CharConst ("--")).append (separator).append (CharConst ("--" kCRLF));
96 ne_off_t NeonPostBodyProvider::calcLength () {
102 tp = query->queryParam ();
107 ans += textSeparator (to_string (a->car ())).length () + to_string (a->cdr ()).length () + 2;
112 bp = postFile.begin ();
113 ep = postFile.end ();
114 for (; bp < ep; ++ bp) {
115 u = filePart_osSafe ((*bp).second);
116 if (! fileSize (u, s))
118 ans += fileSeparator ((*bp).first, u).length () + s + 2;
120 ans += tailSeparator ().length ();
122 std::cerr << "calcLength(): " << ans << "\n";
127 char* NeonPostBodyProvider::bodyProvider (char* buffer, size_t buflen) {
130 tp = query->queryParam ();
131 bp = postFile.begin ();
132 ep = postFile.end ();
133 if (isCons (tp) || bp < ep) {
142 std::cerr << "state:" << state << ", offset:" << offset << ", buflen:" << buflen << "\n";
147 ans = bodyProviderText (buffer, buflen);
148 } else if (isCons (tp)) {
149 MNode* a = tp->car ();
151 ubuf = textSeparator (to_string (a->car ())) + to_string (a->cdr ()) + uCRLF;
153 std::cerr << "ubuf:" << omitNonAscii (ubuf.substr (0, 8)) << "\n";
155 ans = bodyProviderText (buffer, buflen);
158 ans = bodyProvider (buffer, buflen) - buffer;
163 ans = bodyProviderText (buffer, buflen);
166 } else if (bp < ep) {
167 ustring u = filePart_osSafe ((*bp).second);
168 ubuf = fileSeparator ((*bp).first, u);
170 std::cerr << "ubuf:" << omitNonAscii (ubuf.substr (0, 8)) << "\n";
172 ans = bodyProviderText (buffer, buflen);
177 ans = bodyProvider (buffer, buflen) - buffer;
182 ans = fd.read (buffer, buflen);
184 std::cerr << "read\n";
189 std::cerr << "close\n";
194 ans = bodyProvider (buffer, buflen) - buffer;
198 } else { // offset == 0
199 ustring u = filePart_osSafe ((*bp).second);
201 std::cerr << "open:" << (*bp).second << "\n";
203 if (fd.openRead ((*bp).second.c_str ())) {
204 ans = fd.read (buffer, buflen);
208 std::cerr << "close\n";
212 ans = bodyProvider (buffer, buflen) - buffer;
218 std::cerr << "open failed\n";
222 ans = bodyProvider (buffer, buflen) - buffer;
228 ans = bodyProviderText (buffer, buflen);
232 std::cerr << "ubuf:CRLF\n";
234 ans = bodyProviderText (buffer, buflen);
241 ans = bodyProviderText (buffer, buflen);
245 ubuf = tailSeparator ();
246 ans = bodyProviderText (buffer, buflen);
258 if (ans > 0 && buflen > ans)
259 return bodyProvider (buffer + ans, buflen - ans);
261 std::cerr << "bodyProvider ():" << ans << "\n";
266 ssize_t NeonPostBodyProvider::bodyProviderText (char* buffer, size_t buflen) {
267 ssize_t ans = ubuf.length () - offset;
269 std::cerr << "bodyProviderText: buflen:" << buflen << ", offset:" << offset << ", text:" << omitNonAsciiWord (ubuf.substr (offset, offset + 6)) << "\n";
272 memcpy (buffer, ubuf.data () + offset, ans);
275 memcpy (buffer, ubuf.data () + offset, buflen);
282 //============================================================
283 static int ignoreVerifyFn (void* neon, int failures, const ne_ssl_certificate* cert) {
285 std::cerr << "ignoreVerifyFn ()\n";
290 NeonSession::NeonSession (proto_t _proto, const ustring& host, unsigned int port) {
291 const char* protoName;
304 session = ne_session_create (protoName, host.c_str (), port);
307 if (proto == PROTO_HTTPS)
308 ne_ssl_trust_default_ca (session);
310 std::cerr << "session flag:"
311 << (ne_get_session_flag (session, NE_SESSFLAG_PERSIST) ? " NE_SESSFLAG_PERSIST" : "")
312 << (ne_get_session_flag (session, NE_SESSFLAG_ICYPROTO) ? " NE_SESSFLAG_ICYPROTO" : "")
313 << (ne_get_session_flag (session, NE_SESSFLAG_SSLv2) ? " NE_SESSFLAG_SSLv2" : "")
314 << (ne_get_session_flag (session, NE_SESSFLAG_RFC4918) ? " NE_SESSFLAG_RFC4918" : "")
315 << (ne_get_session_flag (session, NE_SESSFLAG_CONNAUTH) ? " NE_SESSFLAG_CONNAUTH" : "")
316 << (ne_get_session_flag (session, NE_SESSFLAG_TLS_SNI) ? " NE_SESSFLAG_TLS_SNI" : "")
317 << (ne_get_session_flag (session, NE_SESSFLAG_EXPECT100) ? " NE_SESSFLAG_EXPECT100" : "")
322 NeonSession::~NeonSession () {
323 ne_session_destroy (session);
327 void NeonSession::setNoVerify () {
328 if (proto == PROTO_HTTPS)
329 ne_ssl_set_verify (session, ignoreVerifyFn, this);
332 void NeonSession::setProxy (const ustring& host, int port) {
333 if (checkHostname (host) && port > 0 && port < 65536) {
335 std::cerr << "set proxy " << host << ":" << port << "\n";
337 ne_session_proxy (session, host.c_str (), port);
339 throw (ustring (CharConst ("bad proxy host.")));
343 //============================================================
344 void NeonQuery::closeReq () {
347 case MODE_DISPATCHED:
348 ne_discard_response (req);
350 ne_end_request (req);
354 ne_request_destroy (req);
359 void NeonQuery::setIConv (const char* name) {
360 cv_in.reset (new UIConv (kCODE_UTF8, name));
361 cv_out.reset (new UIConv (name, kCODE_UTF8));
364 static ssize_t qbodyProvider (void* userdata, char* buffer, size_t buflen) {
365 NeonPostBodyProvider* obj = (NeonPostBodyProvider*)userdata;
366 return obj->bodyProvider (buffer, buflen) - buffer;
369 void NeonQuery::submit () {
371 ustring uri = filterPath ();
372 ustring getQuery = buildGetQuery ();
375 if (getQuery.length () > 0) {
376 uri.append (CharConst ("?"));
377 uri.append (getQuery);
380 req = ne_request_create (session, methodStr (), uri.c_str ());
385 if (! rawquery.isEmpty ()) {
389 buildQuery (queryParam (), data);
390 setFormType_urlencoded ();
391 ne_set_request_body_buffer (req, data.c_str (), data.size ());
395 ne_add_request_header (req, kRES_TYPE, qbody->separatorHeader ().c_str ());
396 ne_set_request_body_provider (req, qbody->calcLength (), qbodyProvider, qbody.get ());
399 if (! rawquery.isEmpty ()) {
405 int rc = ne_begin_request (req);
407 mode = MODE_DISPATCHED;
409 errorMsg.assign (ne_get_error (session));
411 std::cerr << "error: " << errorMsg << "\n";
416 void NeonQuery::readBody (MotorOutput* out) {
417 if (mode == MODE_DISPATCHED) {
420 while ((s = ne_read_response_block (req, buf, 65536)) > 0) {
421 out->out_raw (buf, s);
423 if (s == 0) { // success
424 mode = MODE_RECEIVED;
425 } else if (s < 0) { // error
426 mode = MODE_RECEIVED;
431 int NeonQuery::getStatus () {
433 const ne_status* st = ne_get_status (req);
440 const char* NeonQuery::methodStr () {
449 return kMETHOD_DELETE;
459 ustring NeonQuery::buildGetQuery () {
462 if (! isNil (queryParamGet ())) {
463 buildQuery (queryParamGet (), ans);
464 } else if (method == METHOD_GET) {
465 if (! rawquery.isEmpty () && rawquery.isText ()) {
466 ans = rawquery.read ();
467 } else if (! isNil (queryParam ())) {
468 buildQuery (queryParam (), ans);
474 void NeonQuery::buildQuery (MNode* e, ustring& out) {
484 out.append (percentEncode (cv (to_string (a->car ()))));
485 if (! isNil (a->cdr ())) {
487 out.append (percentEncode (cv (to_string (a->cdr ()))));
496 ustring NeonQuery::buildMimeSeparator_text (const ustring& name) {
498 ans.assign (qbody->separatorLine ());
499 ans.append (CharConst ("Content-Disposition: form-data; name=" kQ2)).append (percentEncode (cv (name))).append (CharConst (kQ2 kCRLF kCRLF));
503 ustring NeonQuery::buildMimeSeparator_file (const ustring& name, const ustring& filename) {
505 ans.assign (qbody->separatorLine ());
506 ans.append (CharConst ("Content-Disposition: form-data; name=" kQ2)).append (percentEncode (cv (name))).append (CharConst (kQ2 "; filename=" kQ2)).append (slashEncode (filename)).append (CharConst (kQ2 kCRLF));
507 ans.append (CharConst ("Content-Type: ")).append (mimetype (getExt (filename))).append (CharConst (kCRLF kCRLF));
511 ustring NeonQuery::cv (const ustring& src) {
513 return cv_out->cv (src);
519 ustring NeonQuery::rcv (const ustring& src) {
521 return cv_in->cv (src);
527 ustring NeonQuery::getResponseHeader (const char* name) {
528 const char* ans = ne_get_response_header (req, name);
530 return ustring (ans);
535 MNode* NeonQuery::getResponseHeaderAll () {
541 csr = ne_response_header_iterate (req, csr, &name, &val);
543 ans.append (newMNode_cons (newMNode_str (new ustring (name)), newMNode_str (new ustring (val))));
547 return ans.release ();
550 ustring NeonQuery::getResponseCookie (const ustring& name) {
552 for (int i = 0; i < replyCookie.size (); ++ i) {
553 if (replyCookie[i].key == name)
554 return replyCookie[i].value;
559 MNode* NeonQuery::getResponseCookieAll () {
562 for (int i = 0; i < replyCookie.size (); ++ i) {
563 ans.append (newMNode_cons (newMNode_str (new ustring (replyCookie[i].key)), newMNode_str (new ustring (replyCookie[i].value))));
565 return ans.release ();
568 ustring NeonQuery::filterPath () {
569 return percentEncode_path (omitCtrl (path));
572 void NeonQuery::buildCookie () {
573 MNode* e = cookie ();
583 } else if (isCons (a)) {
584 u.assign (cookieencode (to_string (a->car ()))).append (uEq).append (cookieencode (to_string (a->cdr ())));
586 data.append (CharConst (";" kCRLF " ")).append (u);
588 } else if (off > 0) {
589 data.append (CharConst ("; ")).append (u);
590 off += u.length () + 2;
596 throw (cookie ()->dump_string_short () + uErrorBadParam);
601 if (data.length () > 0)
602 ne_add_request_header (req, "Cookie", data.c_str ());
605 void NeonQuery::buildHeader () {
606 MNode* e = header ();
609 static uregex re1 ("[\\x00-\\x1f: ]");
610 static uregex re2 ("[\\x00-\\x1f]");
616 } else if (a->isCons ()) {
617 key = to_string (a->car ());
618 val = to_string (a->cdr ());
619 if (! checkRe (key, re1) && ! checkRe (val, re2) && key.length () + val.length () < 2048) {
620 ne_add_request_header (req, key.c_str (), val.c_str ());
623 throw (cookie ()->dump_string_short () + uErrorBadParam);
628 if (basicID.length () > 0)
629 buildBasicAuthHeader ();
632 void NeonQuery::buildBasicAuthHeader () {
635 idpw.assign (basicID).append (uColon).append (basicPW);
636 auth.assign (CharConst ("Basic ")).append (base64Encode (idpw.begin (), idpw.end ()));
637 ne_add_request_header (req, "Authorization", auth.c_str ());
640 void NeonQuery::setFormType () {
641 if (querytype.length () > 0) {
642 ne_add_request_header(req, "Content-type", querytype.c_str ());
644 setFormType_urlencoded ();
648 void NeonQuery::setFormType_urlencoded () {
649 ne_add_request_header(req, "Content-type", kMIME_URLENCODED);
652 void NeonQuery::setFormType_formdata () {
653 ne_add_request_header(req, "Content-type", kMIME_FORMDATA);
656 void NeonQuery::parseCookie () {
657 if (! replyCookieDone) {
658 replyCookieDone = true;
659 ustring u = getResponseHeader ("set-cookie");
660 uiterator b = u.begin ();
661 uiterator e = u.end ();
662 uregex re1 ("[=,;]");
664 uregex re3 ("^path=|expires=|domain=|secure");
665 uregex re4 ("^..., *[0-9]+[^,;]+");
667 while (b < e && usearch (b, e, m, re1)) { // = | , | ;
669 switch (*m[0].first) {
671 info.key.assign (b, m[0].first);
673 if (usearch (b, e, m, re2)) { // , | ;
674 info.value.assign (b, m[0].first);
675 if (*m[0].first == ';') {
678 if (usearch (b, e, m, re3)) { // path=,expires=,...
679 if (*m[0].first == 'e') {
680 if (usearch (b, e, m, re4)) {
682 if (*(b - 1) == ',') {
688 } else if (usearch (b, e, m, re2)) {
691 if (*m[0].first == ',')
696 } else if (usearch (b, e, m, re2)) {
699 if (*m[0].first == ',')
704 } else if (usearch (b, e, m, re2)) { // , | ;
707 if (*m[0].first == ',')
715 info.value.assign (b, e);
717 replyCookie.push_back (info);
721 skipNextToChar (b, e, ',');
733 void NeonQuery::setRawQuery (FileMacro& fd) {
735 if (rawquery.isText ()) {
736 ne_set_request_body_buffer (req, rawquery.param.c_str (), rawquery.param.size ());
738 ustring src = rawquery.src ();
739 if (src.length () > 0) {
740 if (fd.openRead (src.c_str ())) {
741 ne_set_request_body_fd (req, fd.fd, 0, fd.size ());
747 //============================================================
748 void MLNeon::newSession (MlEnv* mlenv) {
749 session.reset (new NeonSession (proto, host, port));
751 query.reset (new NeonQuery (session->get (), mlenv));
754 //============================================================
761 ($neon [#http | #https] Host Port
762 [:proxy '(HOSTNAME . PORT)] [:proxy-user '(ID . PASSWORD)]
765 [SUBFUNCTION...]) -> LAST VALUE
768 //#MFUNC $neon ml_neon cMLNeonID
769 MNode* ml_neon (MNode* cell, MlEnv* mlenv) {
770 MNode* arg = cell->cdr ();
777 std::vector<MNode*> params;
778 std::vector<MNode*> keywords;
780 static paramList kwlist[] = {
781 {CharConst ("http"), true}, // 0
782 {CharConst ("https"), true}, // 1
783 {CharConst ("proxy"), false}, // 2
784 {CharConst ("proxy-user"), false}, // 3
785 {CharConst ("on-error"), false}, // 4
786 {CharConst ("no-verify"), true}, // 5
790 setParams (arg, 2, ¶ms, kwlist, &keywords, &rest);
791 obj.host = eval_str (params[0], mlenv);
792 obj.port = eval_int (params[1], mlenv);
793 if (evkw_bool (0, b)) // 0:http
794 obj.proto = NeonSession::PROTO_HTTP;
795 if (evkw_bool (1, b)) // 1:https
796 obj.proto = NeonSession::PROTO_HTTPS;
797 if (evkw (2, t)) { // 2:proxy
799 obj.proxyhost = to_string (t ()->car ());
800 obj.proxyport = to_int (t ()->cdr ());
802 throw (t ()->dump_string_short () + uErrorBadParam);
805 if (evkw (3, t)) { // 3:proxy-user
807 obj.proxyid = to_string (t ()->car ());
808 obj.proxypw = to_string (t ()->cdr ());
810 throw (t ()->dump_string_short () + uErrorBadParam);
813 evkw (4, errfn); // 4:on-error
814 evkw_bool (5, obj.fnoverify); // 5:no-verify
816 if (! checkHostname (obj.host))
817 throw (obj.host + ": bad hostname.");
818 if (obj.port <= 0 || obj.port >= 65536)
819 throw (to_ustring (obj.port) + ": bad port number.");
821 //****** proxy-userが未実装
822 obj.newSession (mlenv);
823 if (obj.proxyhost.length () > 0)
824 obj.session->setProxy (obj.proxyhost, obj.proxyport);
826 obj.session->setNoVerify ();
828 mlenv->setMStack (&obj);
830 ans = progn (rest, mlenv);
831 } catch (ustring& msg) {
833 onErrorFn (errfn (), mlenv);
838 mlenv->stopBreak (cell->car ());
840 return ans.release ();
843 static void buildFileList (NeonQuery* query, MNode* postFileSerial, MNode* postFileNamed, MNode* postFileStatic, MlEnv* mlenv) {
853 if ((a = e->car ()) && isCons (a)) {
854 name = to_string (a->car ());
855 val = to_string (a->cdr ());
856 path = mlenv->env->path_store_file (val);
857 query->qbody->pushFile (name, path);
865 if ((a = e->car ()) && isCons (a)) {
866 name = to_string (a->car ());
867 val = to_string (a->cdr ());
868 path = mlenv->env->path_storage_file (val);
869 query->qbody->pushFile (name, path);
877 if ((a = e->car ()) && isCons (a)) {
878 name = to_string (a->car ());
879 val = to_string (a->cdr ());
880 path = mlenv->env->path_static_file (val);
881 query->qbody->pushFile (name, path);
889 ===subfunctions of $neon===
892 static MNode* checkConsList (MNode* e) {
897 if (isCons ((a = e->car ())))
902 return ans.release ();
907 (http-request PATH [#get] [#post] [#put] [#delete] [#head] [#file]
908 [:basic-user '(ID . PASSWORD)]
909 [:query '((NAME . VALUE) ...)] [:get-query '((NAME . VALUE) ...)] [#no-encode]
910 [:post-file-serial '((NAME . FILE) ...)]
911 [:post-file-named '((NAME . FILE) ...)]
912 [:post-file-static '((NAME . FILE) ...)]
913 [:raw-query TEXT] [:raw-file-serial FILE] [:raw-file-named FILE] [:raw-file-static FILE]
914 [:query-type MIMETYPE]
915 [:cookie '((NAME . VALUE) ...)] [:header '((NAME . VALUE) ...)]
916 [#sjis] [#euc-jp] [:iconv NAME]
918 //#SFUNC http-request ml_neon_http_request
919 MNode* ml_neon_http_request (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
920 MNode* arg = cell->cdr ();
921 MLNeon* obj = (MLNeon*)mobj;
922 MNodePtr postFileSerial;
923 MNodePtr postFileNamed;
924 MNodePtr postFileStatic;
927 std::vector<MNode*> params;
928 std::vector<MNode*> keywords;
929 static paramList kwlist[] = {
930 {CharConst ("get"), true}, // 0
931 {CharConst ("post"), true}, // 1
932 {CharConst ("put"), true}, // 2
933 {CharConst ("delete"), true}, // 3
934 {CharConst ("head"), true}, // 4
935 {CharConst ("file"), true}, // 5
936 {CharConst ("basic-user"), false}, // 6
937 {CharConst ("query"), false}, // 7
938 {CharConst ("get-query"), false}, // 8
939 {CharConst ("post-file-serial"), false}, // 9
940 {CharConst ("post-file-named"), false}, // 10
941 {CharConst ("post-file-static"), false}, // 11
942 {CharConst ("raw-query"), false}, // 12
943 {CharConst ("raw-file-serial"), false}, // 13
944 {CharConst ("raw-file-named"), false}, // 14
945 {CharConst ("raw-file-static"), false}, // 15
946 {CharConst ("query-type"), false}, // 16
947 {CharConst ("cookie"), false}, // 17
948 {CharConst ("header"), false}, // 18
949 {CharConst ("sjis"), true}, // 19
950 {CharConst ("euc-jp"), true}, // 20
951 {CharConst ("iconv"), false}, // 21
955 setParams (arg, 1, ¶ms, kwlist, &keywords, NULL);
956 obj->query->path = eval_str (params[0], mlenv);
957 if (evkw_bool (0, f)) // 0:get
958 obj->query->method = NeonQuery::METHOD_GET;
959 if (evkw_bool (1, f)) // 1:post
960 obj->query->method = NeonQuery::METHOD_POST;
961 if (evkw_bool (2, f)) // 2:put
962 obj->query->method = NeonQuery::METHOD_PUT;
963 if (evkw_bool (3, f)) // 3:delete
964 obj->query->method = NeonQuery::METHOD_DELETE;
965 if (evkw_bool (4, f)) // 4:head
966 obj->query->method = NeonQuery::METHOD_HEAD;
967 if (evkw_bool (5, f)) // 5:file / pseudo method
968 obj->query->method = NeonQuery::METHOD_FILE;
969 if (evkw (6, t)) { // 6:basic-user
971 obj->query->basicID = to_string (t ()->car ());
972 obj->query->basicPW = to_string (t ()->cdr ());
974 throw (t ()->dump_string_short () + uErrorBadParam);
977 if (evkw (7, t)) // 7:query
978 obj->query->queryParam = checkConsList (t ());
979 if (evkw (8, t)) // 8:get-query
980 obj->query->queryParamGet = checkConsList (t ());
981 if (evkw (9, t)) // 9:post-file-serial
982 postFileSerial = checkConsList (t ());
983 if (evkw (10, t)) // 10:post-file-named
984 postFileNamed = checkConsList (t ());
985 if (evkw (11, t)) // 11:post-file-static
986 postFileStatic = checkConsList (t ());
987 if (evkw (12, t)) // 12:raw-query
988 obj->query->rawquery.srcText (to_string (t ()));
989 if (evkw (13, t)) // 13:raw-file-serial
990 obj->query->rawquery.srcSerial (to_string (t ()));
991 if (evkw (14, t)) // 14:raw-file-named
992 obj->query->rawquery.srcNamed (to_string (t ()));
993 if (evkw (15, t)) // 15:raw-file-static
994 obj->query->rawquery.srcStatic (to_string (t ()));
995 if (evkw (16, t)) { // 16:query-type
996 obj->query->querytype = to_string (t ());
997 if (!checkASCII (obj->query->querytype))
998 throw (obj->query->querytype + ustring (CharConst (": bad type")));
1000 evkw (17, obj->query->cookie); // 17:cookie
1001 evkw (18, obj->query->header); // 18:header
1002 if (evkw_bool (19, f)) { // 19:sjis
1003 obj->query->setIConv ("SHIFT_JIS");
1004 } else if (evkw_bool (20, f)) { // 20:euc-jp
1005 obj->query->setIConv ("EUC-JP");
1006 } else if (evkw (21, t)) { // 21:iconv
1007 ustring code = to_string (t ());
1008 uregex re ("^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$");
1010 if (usearch (code, m, re)) {
1011 obj->query->setIConv (code.c_str ());
1013 throw (ustring (code).append (CharConst (": unknown encoding.")));
1017 if (obj->query->method == NeonQuery::METHOD_FILE)
1018 buildFileList (obj->query.get (), postFileSerial (), postFileNamed (), postFileStatic (), mlenv);
1020 obj->query->submit ();
1027 (http-status) -> NUMBER
1030 //#SFUNC http-status ml_neon_http_status
1031 MNode* ml_neon_http_status (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1032 MNode* arg = cell->cdr ();
1033 MLNeon* obj = (MLNeon*)mobj;
1036 throw (uErrorWrongNumber);
1038 return newMNode_num (obj->query->getStatus ());
1042 ====http-response====
1043 (http-response) -> STRING
1046 //#SFUNC http-response ml_neon_http_response
1047 MNode* ml_neon_http_response (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1048 MNode* arg = cell->cdr ();
1049 MLNeon* obj = (MLNeon*)mobj;
1053 throw (uErrorWrongNumber);
1055 std::stringstream ostr;
1056 MotorOutputOStream out (&ostr);
1057 obj->query->readBody (&out);
1058 return newMNode_str (new ustring (fixUTF8 (obj->query->rcv (ostr.str ()))));
1062 ====http-response-file====
1063 (http-response-file FILENAME) -> NIL
1066 //#SFUNC http-response-file ml_neon_http_response_file
1067 MNode* ml_neon_http_response_file (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1068 MNode* arg = cell->cdr ();
1069 MLNeon* obj = (MLNeon*)mobj;
1074 throw (uErrorWrongNumber);
1075 name = eval_str (arg->car (), mlenv);
1078 throw (uErrorWrongNumber);
1080 tgt = mlenv->env->path_store_file (name);
1081 tmp.assign (tgt).append (CharConst ("-tmp")).append (to_ustring (getpid ()));
1083 std::ofstream stream;
1084 MotorOutputOStream out (&stream);
1085 stream.open (tmp.c_str (), std::ios_base::out | std::ios_base::binary);
1086 obj->query->readBody (&out);
1088 rename (tmp.c_str (), tgt.c_str ());
1095 ====http-response-output====
1096 (http-response-output [#continue | :continue BOOL]) -> NIL
1099 //#SFUNC http-response-output ml_neon_http_response_output
1100 MNode* ml_neon_http_response_output (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1101 MNode* arg = cell->cdr ();
1102 MLNeon* obj = (MLNeon*)mobj;
1104 std::vector<MNode*> keywords;
1105 static paramList kwlist[] = {
1106 {CharConst ("continue"), true},
1110 setParams (arg, 0, NULL, kwlist, &keywords, NULL);
1111 if (keywords[0] && eval_bool (keywords[0], mlenv))
1114 if (! mlenv->env->responseDone)
1115 mlenv->env->standardResponse_html ();
1116 obj->query->readBody (mlenv->env->output);
1119 mlenv->breakProg ();
1126 (get-cookie NAME) -> STRING
1129 //#SFUNC get-cookie ml_neon_get_cookie
1130 MNode* ml_neon_get_cookie (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1131 MNode* arg = cell->cdr ();
1132 MLNeon* obj = (MLNeon*)mobj;
1136 throw (uErrorWrongNumber);
1137 name = eval_str (arg->car (), mlenv);
1140 throw (uErrorWrongNumber);
1142 ustring ans = obj->query->getResponseCookie (name);
1143 if (ans.length () > 0) {
1144 return newMNode_str (new ustring (ans));
1151 ====get-cookie-all====
1152 (get-cookie-all) -> LIST of (NAME . VALUE)
1155 //#SFUNC get-cookie-all ml_neon_get_cookie_all
1156 MNode* ml_neon_get_cookie_all (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1157 MNode* arg = cell->cdr ();
1158 MLNeon* obj = (MLNeon*)mobj;
1161 throw (uErrorWrongNumber);
1163 return obj->query->getResponseCookieAll ();
1167 ====http-content-type====
1168 (http-content-type) -> STRING
1171 //#SFUNC http-content-type ml_neon_http_content_type
1172 MNode* ml_neon_http_content_type (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1173 MNode* arg = cell->cdr ();
1174 MLNeon* obj = (MLNeon*)mobj;
1175 ustring u = obj->query->getResponseHeader ("content-type");
1177 if (u.length () > 0) {
1181 if (splitChar (b, e, ';', m)) {
1182 return newMNode_str (new ustring (toLower (b, m)));
1184 return newMNode_str (new ustring (toLower (b, e)));
1192 ====http-get-header====
1193 (http-get-header NAME) -> STRING
1196 //#SFUNC http-get-header ml_neon_http_get_header
1197 MNode* ml_neon_http_get_header (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1198 MNode* arg = cell->cdr ();
1199 MLNeon* obj = (MLNeon*)mobj;
1204 throw (uErrorWrongNumber);
1205 name = eval_str (arg->car (), mlenv);
1208 throw (uErrorWrongNumber);
1210 u = obj->query->getResponseHeader (name.c_str ());
1211 if (u.length () > 0) {
1212 return newMNode_str (new ustring (u));
1219 ====http-get-header-all====
1220 (http-get-header-all) -> LIST of (NAME . VALUE)
1223 //#SFUNC http-get-header-all ml_neon_http_get_header_all
1224 MNode* ml_neon_http_get_header_all (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1225 MNode* arg = cell->cdr ();
1226 MLNeon* obj = (MLNeon*)mobj;
1229 throw (uErrorWrongNumber);
1231 return obj->query->getResponseHeaderAll ();