3 * command gateway for controling swift engine via a TCP connection
5 * Created by Arno Bakker
6 * Copyright 2010-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
15 #include <event2/buffer.h>
16 #include <event2/bufferevent.h>
17 #include <event2/listener.h>
20 using namespace swift;
22 // Send PLAY after receiving 2^layer * chunksize bytes
23 #define CMDGW_MAX_PREBUF_BYTES (256*1024)
26 // Status of the swarm download
27 #define DLSTATUS_HASHCHECKING 2
28 #define DLSTATUS_DOWNLOADING 3
29 #define DLSTATUS_SEEDING 4
31 #define MAX_CMD_MESSAGE 1024
33 #define ERROR_NO_ERROR 0
34 #define ERROR_UNKNOWN_CMD -1
35 #define ERROR_MISS_ARG -2
36 #define ERROR_BAD_ARG -3
38 #define CMDGW_MAX_CLIENT 1024 // Arno: == maximum number of swarms per proc
42 evutil_socket_t cmdsock;
43 int transfer; // swift FD
44 char *contentfilename; // basename of content file
45 bool moreinfo; // whether to report detailed stats (see SETMOREINFO cmd)
46 tint startt; // ARNOSMPTODO: debug speed measurements, remove
47 } cmd_requests[CMDGW_MAX_CLIENT];
50 int cmd_gw_reqs_open = 0;
51 int cmd_gw_reqs_count = 0;
53 struct evconnlistener *cmd_evlistener = NULL;
54 struct evbuffer *cmd_evbuffer = NULL; // Data received on cmd socket : WARNING: one for all cmd sockets
55 Address cmd_gw_httpaddr; // HTTP gateway address for PLAY cmd
58 bool cmd_gw_debug=false;
62 void CmdGwDataCameInCallback(struct bufferevent *bev, void *ctx);
63 bool CmdGwReadLine(evutil_socket_t cmdsock);
64 void CmdGwNewRequestCallback(evutil_socket_t cmdsock, char *line);
68 void CmdGwFreeRequest(cmd_gw_t* req)
70 if (req->contentfilename != NULL)
71 free(req->contentfilename);
72 // Arno, 2012-02-06: Reset, in particular moreinfo flag.
73 memset(req,'\0',sizeof(cmd_gw_t));
77 void CmdGwCloseConnection(evutil_socket_t sock)
79 // Close cmd connection and stop all associated downloads.
80 // Doesn't remove .mhash state or content
86 for(int i=0; i<cmd_gw_reqs_open; i++)
88 cmd_gw_t* req = &cmd_requests[i];
89 if (req->cmdsock==sock)
91 dprintf("%s @%i stopping-on-close transfer %i\n",tintstr(),req->id,req->transfer);
92 swift::Close(req->transfer);
94 // Remove from list and reiterate over it
95 CmdGwFreeRequest(req);
96 *req = cmd_requests[--cmd_gw_reqs_open];
105 cmd_gw_t* CmdGwFindRequestByTransfer (int transfer)
107 for(int i=0; i<cmd_gw_reqs_open; i++)
108 if (cmd_requests[i].transfer==transfer)
109 return cmd_requests+i;
113 cmd_gw_t* CmdGwFindRequestByRootHash(Sha1Hash &want_hash)
115 FileTransfer *ft = NULL;
116 for(int i=0; i<cmd_gw_reqs_open; i++) {
117 cmd_gw_t* req = &cmd_requests[i];
118 ft = FileTransfer::file(req->transfer);
119 Sha1Hash got_hash = ft->root_hash();
120 if (want_hash == got_hash)
127 void CmdGwGotCHECKPOINT(Sha1Hash &want_hash)
129 // Checkpoint the specified download
130 fprintf(stderr,"cmd: GotCHECKPOINT: %s\n",want_hash.hex().c_str());
132 cmd_gw_t* req = CmdGwFindRequestByRootHash(want_hash);
135 FileTransfer *ft = FileTransfer::file(req->transfer);
137 std::string binmap_filename = ft->file().filename();
138 binmap_filename.append(".mbinmap");
139 fprintf(stderr,"cmdgw: GotCHECKPOINT: checkpointing to %s\n", binmap_filename.c_str() );
140 FILE *fp = fopen(binmap_filename.c_str(),"wb");
142 print_error("cannot open mbinmap for writing");
145 if (ft->file().serialize(fp) < 0)
146 print_error("writing to mbinmap");
151 void CmdGwGotREMOVE(Sha1Hash &want_hash, bool removestate, bool removecontent)
153 // Remove the specified download
154 fprintf(stderr,"cmd: GotREMOVE: %s %d %d\n",want_hash.hex().c_str(),removestate,removecontent);
156 cmd_gw_t* req = CmdGwFindRequestByRootHash(want_hash);
159 FileTransfer *ft = FileTransfer::file(req->transfer);
161 fprintf(stderr, "%s @%i remove transfer %i\n",tintstr(),req->id,req->transfer);
162 dprintf("%s @%i remove transfer %i\n",tintstr(),req->id,req->transfer);
163 swift::Close(req->transfer);
165 // Delete content + .mhash from filesystem, if desired
167 remove(req->contentfilename);
171 char *mhashfilename = (char *)malloc(strlen(req->contentfilename)+strlen(".mhash")+1);
172 strcpy(mhashfilename,req->contentfilename);
173 strcat(mhashfilename,".mhash");
175 remove(mhashfilename);
178 // Arno, 2012-01-10: .mbinmap gots to go too.
179 char *mbinmapfilename = (char *)malloc(strlen(req->contentfilename)+strlen(".mbinmap")+1);
180 strcpy(mbinmapfilename,req->contentfilename);
181 strcat(mbinmapfilename,".mbinmap");
183 remove(mbinmapfilename);
184 free(mbinmapfilename);
187 CmdGwFreeRequest(req);
188 *req = cmd_requests[--cmd_gw_reqs_open];
192 void CmdGwGotMAXSPEED(Sha1Hash &want_hash, data_direction_t ddir, double speed)
194 // Set maximum speed on the specified download
195 fprintf(stderr,"cmd: GotMAXSPEED: %s %d %lf\n",want_hash.hex().c_str(),ddir,speed);
197 cmd_gw_t* req = CmdGwFindRequestByRootHash(want_hash);
200 FileTransfer *ft = FileTransfer::file(req->transfer);
201 ft->SetMaxSpeed(ddir,speed);
205 void CmdGwGotSETMOREINFO(Sha1Hash &want_hash, bool enable)
207 cmd_gw_t* req = CmdGwFindRequestByRootHash(want_hash);
210 req->moreinfo = enable;
214 void CmdGwSendINFOHashChecking(cmd_gw_t* req, Sha1Hash root_hash)
216 // Send INFO DLSTATUS_HASHCHECKING message.
218 char cmd[MAX_CMD_MESSAGE];
219 sprintf(cmd,"INFO %s %d %lli/%lli %lf %lf %u %u\r\n",root_hash.hex().c_str(),DLSTATUS_HASHCHECKING,(uint64_t)0,(uint64_t)0,0.0,0.0,0,0);
221 //fprintf(stderr,"cmd: SendINFO: %s", cmd);
222 send(req->cmdsock,cmd,strlen(cmd),0);
226 void CmdGwSendINFO(cmd_gw_t* req, int dlstatus)
228 // Send INFO message.
230 fprintf(stderr,"cmd: SendINFO: %d %d\n", req->transfer, dlstatus );
232 FileTransfer *ft = FileTransfer::file(req->transfer);
234 // Download was removed or closed somehow.
237 Sha1Hash root_hash = ft->root_hash();
239 char cmd[MAX_CMD_MESSAGE];
240 uint64_t size = swift::Size(req->transfer);
241 uint64_t complete = swift::Complete(req->transfer);
242 if (size == complete)
243 dlstatus = DLSTATUS_SEEDING;
245 uint32_t numleech = ft->GetNumLeechers();
246 uint32_t numseeds = ft->GetNumSeeders();
247 sprintf(cmd,"INFO %s %d %lli/%lli %lf %lf %u %u\r\n",root_hash.hex().c_str(),dlstatus,complete,size,ft->GetCurrentSpeed(DDIR_DOWNLOAD),ft->GetCurrentSpeed(DDIR_UPLOAD),numleech,numseeds);
249 //fprintf(stderr,"cmd: SendINFO: %s", cmd);
250 send(req->cmdsock,cmd,strlen(cmd),0);
254 // Send detailed ul/dl stats in JSON format.
256 std::ostringstream oss;
257 oss.setf(std::ios::fixed,std::ios::floatfield);
259 std::set<Channel *>::iterator iter;
260 std::set<Channel *> peerchans = ft->GetChannels();
262 oss << "MOREINFO" << " " << root_hash.hex() << " ";
264 double tss = (double)Channel::Time() / 1000000.0L;
265 oss << "{\"timestamp\":\"" << tss << "\", ";
266 oss << "\"channels\":";
268 for (iter=peerchans.begin(); iter!=peerchans.end(); iter++) {
271 if (iter!=peerchans.begin())
274 oss << "\"ip\": \"" << c->peer().ipv4str() << "\", ";
275 oss << "\"port\": " << c->peer().port() << ", ";
276 oss << "\"raw_bytes_up\": " << c->raw_bytes_up() << ", ";
277 oss << "\"raw_bytes_down\": " << c->raw_bytes_down() << ", ";
278 oss << "\"bytes_up\": " << c->bytes_up() << ", ";
279 oss << "\"bytes_down\": " << c->bytes_down() << " ";
284 oss << "\"raw_bytes_up\": " << Channel::global_raw_bytes_up << ", ";
285 oss << "\"raw_bytes_down\": " << Channel::global_raw_bytes_down << ", ";
286 oss << "\"bytes_up\": " << Channel::global_bytes_up << ", ";
287 oss << "\"bytes_down\": " << Channel::global_bytes_down << " ";
292 std::stringbuf *pbuf=oss.rdbuf();
293 size_t slen = strlen(pbuf->str().c_str());
294 send(req->cmdsock,pbuf->str().c_str(),slen,0);
299 void CmdGwSendPLAY(int transfer)
301 // Send PLAY message to user
303 fprintf(stderr,"cmd: SendPLAY: %d\n", transfer );
305 cmd_gw_t* req = CmdGwFindRequestByTransfer(transfer);
306 Sha1Hash root_hash = FileTransfer::file(transfer)->root_hash();
308 char cmd[MAX_CMD_MESSAGE];
309 // Slightly diff format: roothash as ID after CMD
310 sprintf(cmd,"PLAY %s http://%s/%s\r\n",root_hash.hex().c_str(),cmd_gw_httpaddr.str(),root_hash.hex().c_str());
312 fprintf(stderr,"cmd: SendPlay: %s", cmd);
314 send(req->cmdsock,cmd,strlen(cmd),0);
318 void CmdGwSwiftFirstProgressCallback (int transfer, bin_t bin)
320 // First CMDGW_MAX_PREBUF_BYTES bytes received via swift,
322 // ARNOSMPTODO: bitrate-dependent prebuffering?
324 fprintf(stderr,"cmd: SwiftFirstProgress: %d\n", transfer );
326 swift::RemoveProgressCallback(transfer,&CmdGwSwiftFirstProgressCallback);
328 CmdGwSendPLAY(transfer);
332 void CmdGwSwiftErrorCallback (evutil_socket_t cmdsock)
334 // Error on swift socket callback
336 const char *response = "ERROR Swift Engine Problem\r\n";
337 send(cmdsock,response,strlen(response),0);
339 //swift::close_socket(sock);
344 void CmdGwUpdateDLStateCallback(cmd_gw_t* req)
346 // Periodic callback, tell user INFO
347 CmdGwSendINFO(req,DLSTATUS_DOWNLOADING);
349 // Update speed measurements such that they decrease when DL/UL stops
350 FileTransfer *ft = FileTransfer::file(req->transfer);
356 // DEBUG download speed rate limit
357 double dlspeed = ft->GetCurrentSpeed(DDIR_DOWNLOAD);
359 double dt = max(0.000001,(double)(usec_time() - req->startt)/TINT_SEC);
361 double dt = std::max(0.000001,(double)(usec_time() - req->startt)/TINT_SEC);
363 double exspeed = (double)(swift::Complete(req->transfer)) / dt;
364 fprintf(stderr,"cmd: UpdateDLStateCallback: SPEED %lf == %lf\n", dlspeed, exspeed );
369 void CmdGwUpdateDLStatesCallback()
371 // Called by swift main approximately every second
372 // Loop over all swarms
373 for(int i=0; i<cmd_gw_reqs_open; i++)
375 cmd_gw_t* req = &cmd_requests[i];
376 CmdGwUpdateDLStateCallback(req);
382 void CmdGwDataCameInCallback(struct bufferevent *bev, void *ctx)
384 // Turn TCP stream into lines deliniated by \r\n
385 evutil_socket_t cmdsock = bufferevent_getfd(bev);
387 fprintf(stderr,"CmdGwDataCameIn: ENTER %d\n", cmdsock );
389 struct evbuffer *inputevbuf = bufferevent_get_input(bev);
390 int ret = evbuffer_add_buffer(cmd_evbuffer,inputevbuf);
392 CmdGwCloseConnection(cmdsock);
397 while (CmdGwReadLine(cmdsock))
401 bool CmdGwReadLine(evutil_socket_t cmdsock)
403 // Parse cmd_evbuffer for lines, and call NewRequest when found
406 char *cmd = evbuffer_readln(cmd_evbuffer,&rd, EVBUFFER_EOL_CRLF_STRICT);
409 CmdGwNewRequestCallback(cmdsock,cmd);
417 int CmdGwHandleCommand(evutil_socket_t cmdsock, char *copyline);
419 void CmdGwNewRequestCallback(evutil_socket_t cmdsock, char *line)
421 // New command received from user
424 char *copyline = (char *)malloc(strlen(line)+1);
425 strcpy(copyline,line);
427 int ret = CmdGwHandleCommand(cmdsock,copyline);
429 dprintf("cmd: Error parsing command %s\n", line );
431 if (ret == ERROR_UNKNOWN_CMD)
432 cmd = "ERROR unknown command\r\n";
433 else if (ret == ERROR_MISS_ARG)
434 cmd = "ERROR missing parameter\r\n";
436 cmd = "ERROR bad parameter\r\n";
437 send(cmdsock,cmd,strlen(cmd),0);
438 CmdGwCloseConnection(cmdsock);
445 int CmdGwHandleCommand(evutil_socket_t cmdsock, char *copyline)
447 char *method=NULL,*paramstr = NULL;
448 char * token = strchr(copyline,' '); // split into CMD PARAM
458 fprintf(stderr,"cmd: GOT %s %s\n", method, paramstr);
460 char *savetok = NULL;
461 if (!strcmp(method,"START"))
464 cmd_gw_t* req = cmd_requests + cmd_gw_reqs_open++;
465 req->id = ++cmd_gw_reqs_count;
466 req->cmdsock = cmdsock;
468 //fprintf(stderr,"cmd: START: new request %i\n",req->id);
470 char *url = paramstr;
472 // tswift://tracker/roothash-as-hex$chunksize@duration-in-secs
473 char *trackerstr=NULL,*hashstr=NULL,*durationstr=NULL,*chunksizestr=NULL;
475 bool haschunksize = (bool)(strchr(paramstr,'$') != NULL);
476 bool hasduration = (bool)(strchr(paramstr,'@') != NULL); // FAXME: user@ in tracker URL
478 token = strtok_r(url,"/",&savetok); // tswift://
480 return ERROR_MISS_ARG;
481 token = strtok_r(NULL,"/",&savetok); // tracker:port
483 return ERROR_MISS_ARG;
486 if (haschunksize && hasduration) {
487 token = strtok_r(NULL,"$",&savetok); // roothash
489 return ERROR_BAD_ARG;
492 token = strtok_r(NULL,"@",&savetok); // chunksize
494 return ERROR_BAD_ARG;
495 chunksizestr = token;
497 token = strtok_r(NULL,"",&savetok); // duration
499 return ERROR_BAD_ARG;
502 else if (haschunksize) {
503 token = strtok_r(NULL,"$",&savetok); // roothash
505 return ERROR_BAD_ARG;
508 token = strtok_r(NULL,"",&savetok); // chunksize
510 return ERROR_BAD_ARG;
511 chunksizestr = token;
513 else if (hasduration) {
514 token = strtok_r(NULL,"@",&savetok); // roothash
516 return ERROR_BAD_ARG;
519 token = strtok_r(NULL,"",&savetok); // duration
521 return ERROR_BAD_ARG;
525 token = strtok_r(NULL,"",&savetok); // roothash
527 return ERROR_BAD_ARG;
531 dprintf("cmd: START: parsed tracker %s hash %s dur %s cs %s\n",trackerstr,hashstr,durationstr,chunksizestr);
533 if (strlen(hashstr)!=40) {
534 dprintf("cmd: START: roothash too short %i\n", strlen(hashstr) );
535 return ERROR_BAD_ARG;
537 uint32_t chunksize=SWIFT_DEFAULT_CHUNK_SIZE;
539 int n = sscanf(chunksizestr,"%i",&chunksize);
541 return ERROR_BAD_ARG;
545 int n = sscanf(durationstr,"%i",&duration);
547 return ERROR_BAD_ARG;
550 dprintf("cmd: START: %s with tracker %s chunksize %i duration %i\n",hashstr,trackerstr,chunksize,duration);
552 // FAXME: return duration in HTTPGW
555 trackaddr = Address(trackerstr);
556 if (trackaddr==Address())
558 dprintf("cmd: START: tracker address must be hostname:port, ip:port or just port\n");
559 return ERROR_BAD_ARG;
561 // SetTracker(trackaddr); == set default tracker
563 // initiate transmission
564 Sha1Hash root_hash = Sha1Hash(true,hashstr);
566 // Send INFO DLSTATUS_HASHCHECKING
567 CmdGwSendINFOHashChecking(req,root_hash);
569 // ARNOSMPTODO: disable/interleave hashchecking at startup
570 int transfer = swift::Find(root_hash);
572 transfer = swift::Open(hashstr,root_hash,trackaddr,false,true,chunksize);
575 //FileTransfer::file(transfer)->SetMaxSpeed(DDIR_DOWNLOAD,512*1024);
577 req->transfer = transfer;
578 req->startt = usec_time();
580 // See HashTree::HashTree
581 req->contentfilename = (char *)malloc(strlen(hashstr)+1);
582 strcpy(req->contentfilename,hashstr);
585 fprintf(stderr,"cmd: Already on disk is %lli/%lli\n", swift::Complete(transfer), swift::Size(transfer));
587 // Wait for prebuffering and then send PLAY to user
588 // ARNOSMPTODO: OUTOFORDER: breaks with out-of-order download
589 if (swift::Size(transfer) >= CMDGW_MAX_PREBUF_BYTES)
591 CmdGwSwiftFirstProgressCallback(transfer,bin_t(0,0));
592 CmdGwSendINFO(req, DLSTATUS_DOWNLOADING);
596 int progresslayer = bytes2layer(CMDGW_MAX_PREBUF_BYTES,swift::ChunkSize(transfer));
597 swift::AddProgressCallback(transfer,&CmdGwSwiftFirstProgressCallback,progresslayer);
600 else if (!strcmp(method,"REMOVE"))
602 // REMOVE roothash removestate removecontent\r\n
603 bool removestate = false, removecontent = false;
605 token = strtok_r(paramstr," ",&savetok); //
607 return ERROR_MISS_ARG;
608 char *hashstr = token;
609 token = strtok_r(NULL," ",&savetok); // removestate
611 return ERROR_MISS_ARG;
612 removestate = !strcmp(token,"1");
613 token = strtok_r(NULL,"",&savetok); // removecontent
615 return ERROR_MISS_ARG;
616 removecontent = !strcmp(token,"1");
618 Sha1Hash root_hash = Sha1Hash(true,hashstr);
619 CmdGwGotREMOVE(root_hash,removestate,removecontent);
621 else if (!strcmp(method,"MAXSPEED"))
623 // MAXSPEED roothash direction speed-float-kb/s\r\n
624 data_direction_t ddir;
627 token = strtok_r(paramstr," ",&savetok); //
629 return ERROR_MISS_ARG;
630 char *hashstr = token;
631 token = strtok_r(NULL," ",&savetok); // direction
633 return ERROR_MISS_ARG;
634 ddir = !strcmp(token,"DOWNLOAD") ? DDIR_DOWNLOAD : DDIR_UPLOAD;
635 token = strtok_r(NULL,"",&savetok); // speed
637 return ERROR_MISS_ARG;
638 int n = sscanf(token,"%lf",&speed);
640 dprintf("cmd: MAXSPEED: speed is not a float\n");
641 return ERROR_MISS_ARG;
643 Sha1Hash root_hash = Sha1Hash(true,hashstr);
644 CmdGwGotMAXSPEED(root_hash,ddir,speed*1024.0);
646 else if (!strcmp(method,"CHECKPOINT"))
648 // CHECKPOINT roothash\r\n
649 Sha1Hash root_hash = Sha1Hash(true,paramstr);
650 CmdGwGotCHECKPOINT(root_hash);
652 else if (!strcmp(method,"SETMOREINFO"))
654 // GETMOREINFO roothash toggle\r\n
655 token = strtok_r(paramstr," ",&savetok); //
657 return ERROR_MISS_ARG;
658 char *hashstr = token;
659 token = strtok_r(NULL," ",&savetok); // direction
661 return ERROR_MISS_ARG;
662 bool enable = (bool)!strcmp(token,"1");
663 Sha1Hash root_hash = Sha1Hash(true,hashstr);
664 CmdGwGotSETMOREINFO(root_hash,enable);
666 else if (!strcmp(method,"SHUTDOWN"))
668 CmdGwCloseConnection(cmdsock);
669 // Tell libevent to stop processing events
670 event_base_loopexit(Channel::evbase, NULL);
674 return ERROR_UNKNOWN_CMD;
677 return ERROR_NO_ERROR;
682 void CmdGwEventCameInCallback(struct bufferevent *bev, short events, void *ctx)
684 if (events & BEV_EVENT_ERROR)
685 print_error("cmdgw: Error from bufferevent");
686 if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
688 // Called when error on cmd connection
689 evutil_socket_t cmdsock = bufferevent_getfd(bev);
690 CmdGwCloseConnection(cmdsock);
691 bufferevent_free(bev);
696 void CmdGwNewConnectionCallback(struct evconnlistener *listener,
697 evutil_socket_t fd, struct sockaddr *address, int socklen,
700 // New TCP connection on cmd listen socket
702 fprintf(stderr,"cmd: Got new cmd connection %i\n",fd);
703 dprintf("DBG cmd: Got new cmd connection %i\n",fd);
705 struct event_base *base = evconnlistener_get_base(listener);
706 struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
708 bufferevent_setcb(bev, CmdGwDataCameInCallback, NULL, CmdGwEventCameInCallback, NULL);
709 bufferevent_enable(bev, EV_READ|EV_WRITE);
712 // One buffer for all cmd connections, reset
713 if (cmd_evbuffer != NULL)
714 evbuffer_free(cmd_evbuffer);
715 cmd_evbuffer = evbuffer_new();
719 void CmdGwListenErrorCallback(struct evconnlistener *listener, void *ctx)
721 // libevent got error on cmd listener
722 struct event_base *base = evconnlistener_get_base(listener);
723 int err = EVUTIL_SOCKET_ERROR();
725 sprintf(errmsg, "cmdgw: Got a fatal error %d (%s) on the listener.\n", err, evutil_socket_error_to_string(err));
728 dprintf("%s @0 closed cmd gateway\n",tintstr());
730 evconnlistener_free(cmd_evlistener);
734 bool InstallCmdGateway (struct event_base *evbase,Address cmdaddr,Address httpaddr)
736 // Allocate libevent listener for cmd connections
737 // From http://www.wangafu.net/~nickm/libevent-book/Ref8_listener.html
739 fprintf(stderr,"cmdgw: Creating new listener on addr %s\n", cmdaddr.str() );
741 struct sockaddr_in sin;
742 sin.sin_addr.s_addr = cmdaddr.addr->dests[0].addr;
743 sin.sin_port = cmdaddr.addr->dests[0].port;
744 sin.sin_family = AF_INET;
746 cmd_evlistener = evconnlistener_new_bind(evbase, CmdGwNewConnectionCallback, NULL,
747 LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,
748 (const struct sockaddr *)&sin, sizeof(sin));
749 if (!cmd_evlistener) {
750 print_error("Couldn't create listener");
753 evconnlistener_set_error_cb(cmd_evlistener, CmdGwListenErrorCallback);
755 cmd_gw_httpaddr = httpaddr;
757 cmd_evbuffer = evbuffer_new();