#开源库下载
下载地址
https://embedthis.com/goahead/download.html
在下载地址中可以看到说明
Source Code Repository
To access the GoAhead source code at GitHub, see: GitHub GoAhead Repository.
To download source code for specific versions, see: GoAhead Releases.
分别是两个地址
github的托管地址 https://github.com/embedthis/goahead
下载之前不同版本的地址 https://github.com/embedthis/goahead/releases
当前能够看到的最新的版本是v3.6.5
并且可以依次往下看到版本有
2017-06-10 v3.6.5
2017-06-10 v3.6.4
2016-06-03 v3.6.3
…
2012-06-28 v2.5.0
我随机下载了 3.3.6 版本;
#开源库编译
$ tar -zxvf goahead-3.3.6.tar.gz
$ cd goahead-3.3.6/
$ make CC=arm-none-linux-gnueabi-gcc ARCH=arm
编译完成后提示
You can now install via "sudo make ARCH=arm CC=arm-none-linux-gnueabi-gcc install" or run GoAhead via: "sudo make run"
To run locally, put linux-arm-default/bin in your path
说明已经编译成功;
进入目录 goahead-3.3.6/build/linux-arm-default/bin
看到了编译出来的嵌入式可执行文件 goahead 已经相关的库文件
ca.crt goahead goahead-test gopass libest.so libgo.so
其中 goahead 可以测试相关的get方法请求;
goahead-test 则有相关的post请求,并且有文件上传的例程;
编译过程中如果需要看到编译语句,可以使用 SHOW=1
$ make CC=arm-none-linux-gnueabi-gcc ARCH=arm SHOW=1
$ make clean CC=arm-none-linux-gnueabi-gcc ARCH=arm SHOW=1
添加断点调试 (-g) :
$ make CC='arm-none-linux-gnueabi-gcc -g' ARCH=arm SHOW=1
可执行程序goahead 是从 goahead-3.3.6/src/goahead.c 的 MAIN() 函数开始执行的;
goahead-test 是从 goahead-3.3.6/test/test.c 的 MAIN() 函数开始;
#开源库使用
在嵌入式 开发板上执行生成的goahead可执行文件
/mnt/test # ./goahead -v web 192.168.1.44:80
src/goahead.c(main)
goahead: 1: This system does not have IPv6 support
goahead: 2: Configuration for Embedthis GoAhead
goahead: 2: ---------------------------------------------
goahead: 2: Version: 3.3.6
goahead: 2: BuildType: Debug
goahead: 2: CPU: arm
goahead: 2: OS: linux
goahead: 2: Host: 192.168.1.44
goahead: 2: Directory: /mnt/test
goahead: 2: Documents: web
goahead: 2: Configure: me -d -q -platform linux-x86-default -configure . -with est -gen make
goahead: 2: ---------------------------------------------
goahead: 2: Started http://192.168.1.44:80
问题1
/mnt/goahead-3.3.6/build/linux-arm-default/bin # ./goahead
goahead: 0: Cannot get host address for host sun5i: errno 11
goahead: 0: Cannot initialize server. Exiting.
修改代码 src/http.c
函数 static int setLocalHost()
#if VXWORKS
...
#elif ECOS
...
#elif TIDSP
...
#elif MACOSX
...
#else
{
struct hostent *hp;
//if ((hp = gethostbyname(host)) == NULL) {
// error("Cannot get host address for host %s: errno %d", host, errno);
// return -1;
// }
//memcpy((char*) &intaddr, (char *) hp->h_addr_list[0], (size_t) hp->h_length);
//ipaddr = inet_ntoa(intaddr);
int sockfd;
struct sockaddr_in sin;
struct ifreq ifr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
return -1;
}
strncpy(ifr.ifr_name, "eth0", IFNAMSIZ); // 指定网卡
ifr.ifr_name[IFNAMSIZ - 1] = 0;
if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) {
return -1;
}
memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
ipaddr=inet_ntoa(sin.sin_addr);
websSetIpAddr(ipaddr);
websSetHost(ipaddr);
}
#endif
问题2
/mnt/goahead-3.3.6/build/linux-arm-default/bin # ./goahead
goahead: 0: Cannot open config file route.txt
goahead: 0: Cannot initialize server. Exiting.
拷贝 src/route.txt 到 build/linux-arm-default/bin/
问题3
/mnt/goahead-3.3.6/build/linux-arm-default/bin # ./goahead
goahead: 0: Cannot open config file auth.txt
goahead: 0: Cannot load auth.txt
拷贝 src/auth.txt 到 build/linux-arm-default/bin/
拷贝网页并进行访问
新建目录 build/linux-arm-default/web/
拷贝 test/web/index.html 到 build/linux-arm-default/web/目录
直接网页上输入开发板的IP地址 192.168.1.123 即可看到
这里我修改了一下网页的显示文字
使用get方法访问数据
修改 src/auth.c 的 loginServiceProc 函数;
static void loginServiceProc(Webs *wp)
{
WebsRoute *route;
assert(wp);
route = wp->route;
assert(route);
printf("%s(%s:%d)%s, %s\n", __FILE__, __FUNCTION__, __LINE__,
websGetVar(wp, "username", ""), websGetVar(wp, "password", ""));
if (websLoginUser(wp, websGetVar(wp, "username", ""), websGetVar(wp, "password", ""))) {
/* If the application defines a referrer session var, redirect to that */
char *referrer;
if ((referrer = websGetSessionVar(wp, "referrer", 0)) != 0) {
websRedirect(wp, referrer);
} else {
websRedirectByStatus(wp, HTTP_CODE_OK);
}
websSetSessionVar(wp, "loginStatus", "ok");
} else {
if (route->askLogin) {
(route->askLogin)(wp);
}
websSetSessionVar(wp, "loginStatus", "failed");
//websRedirectByStatus(wp, HTTP_CODE_UNAUTHORIZED);
websWrite(wp,(char *)"I am test");
// websRedirectByStatus(wp, HTTP_CODE_OK);
websDone(wp);
}
}
在浏览器中访问开发板上的接口
其中这里访问了bbbb/目录,因为修改了 goahead-3.3.6/build/linux-arm-default/bin/route.txt
# For legacy GoAhead applications using /goform
#route uri=/goform handler=action
route uri=/bbbb handler=action
#GET请求和文件上传下载
get请求和文件上传
// $ export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH
// $ arm-none-linux-gnueabi-gcc main.c -L ./ -lgo -lest
// $ ./a.out
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include "goahead.h"
#define wfree(p) if (p) { free(p); } else
static int finished = 0;
static void initPlatform();
static void ShowString(Webs *wp);
static void uploadTest(Webs *wp, char *path, char *query);
int main(){
printf("starting...\n");
char *route = (char*)"route.txt";
char *auth = (char*)"auth.txt";
char *documents = (char*)"web";
initPlatform();
if (websOpen(documents, route) < 0) {
error("Cannot initialize server. Exiting.");
return -1;
}
if (websLoad(auth) < 0) {
error("Cannot load %s", auth);
return -1;
}
websDefineAction("showString", ShowString);
websDefineAction("uploadTest", uploadTest);
char *endpoints = (char*)sclone((char*)"http://*:80,https://*:443");
char *tok;
char *endpoint;
printf("(%d)\n", __LINE__);
for (endpoint = stok(endpoints, ", \t", &tok); endpoint; endpoint = stok(NULL, ", \t,", &tok)) {
printf("(%d)\n", __LINE__);
if (websListen(endpoint) < 0) {
return -1;
}
printf("(%d)\n", __LINE__);
}
printf("(%d)\n", __LINE__);
wfree(endpoints);
/
if (websGetBackground()) {
if (daemon(0, 0) < 0) {
error("Cannot run as daemon");
return -1;
}
}
websServiceEvents(&finished);
printf("Instructed to exit");
websClose();
return 0;
}
static void sigHandler(int signo){
printf("(%s:%d)\n", __FUNCTION__, __LINE__);
finished = 1;
}
static void initPlatform(){
printf("(%s:%d)\n", __FUNCTION__, __LINE__);
signal(SIGTERM, sigHandler);
signal(SIGKILL, sigHandler);
signal(SIGPIPE, SIG_IGN);
}
static void ShowString(Webs *wp){
WebsRoute *route;
char result[64];
assert(wp);
route = wp->route;
assert(route);
sprintf(result, "I am ShowString : %s", websGetVar(wp, "str", ""));
websWrite(wp,result);
websDone(wp);
}
static void uploadTest(Webs *wp, char *path, char *query)
{
WebsKey *s;
WebsUpload *up;
char *upfile;
websSetStatus(wp, 200);
websWriteHeaders(wp, -1, 0);
websWriteHeader(wp, "Content-Type", "text/plain");
websWriteEndHeaders(wp);
if (scaselessmatch(wp->method, "POST")) {
for (s = hashFirst(wp->files); s; s = hashNext(wp->files, s)) {
up = s->content.value.symbol;
websWrite(wp, "TEST : 2017-8-10 14:06:04\r\n");
websWrite(wp, "FILE: %s\r\n", s->name.value.string);
websWrite(wp, "FILENAME=%s\r\n", up->filename);
websWrite(wp, "CLIENT=%s\r\n", up->clientFilename);
websWrite(wp, "TYPE=%s\r\n", up->contentType);
websWrite(wp, "SIZE=%d\r\n", up->size);
//upfile = sfmt("%s/tmp/%s", websGetDocuments(), up->clientFilename);
upfile = sfmt("/tmp/%s", up->clientFilename);
rename(up->filename, upfile);
websWrite(wp, "UPFILE=%s\r\n", upfile);
wfree(upfile);
}
websWrite(wp, "\r\nVARS:\r\n");
for (s = hashFirst(wp->vars); s; s = hashNext(wp->vars, s)) {
websWrite(wp, "%s=%s\r\n", s->name.value.string, s->content.value.string);
}
}
websDone(wp);
}
/*
#
# 其中用的接口来自于 libgo.so
# websOpen
# websLoad
# sclone
# stok
# websListen
# websGetBackground
# websServiceEvents
# websClose
#
*/
文件上传接口
(直接使用自带的 goahead-3.3.6/test/web/upload/upload.html,拷贝到同级目录后使用)
上传完成之后,页面自动跳转显示(文件上传到开发板的 /tmp/ 目录下,由变量 upfile 决定)
TEST : 2017-8-10 14:06:04
FILE: file
FILENAME=/tmp/tmp-0.tmp
CLIENT=test.txt
TYPE=text/plain
SIZE=3
UPFILE=/tmp/test.txt
VARS:
HTTP_CONNECTION=keep-alive
HTTP_CONTENT_LENGTH=481
HTTP_CACHE_CONTROL=max-age=0
FILE_FILENAME_file=/tmp/tmp-0.tmp
UPLOAD_DIR=/tmp
HTTP_ACCEPT_ENCODING=gzip, deflate
HTTP_ORIGIN=http://192.168.1.123
Address=ddd
FILE_SIZE_file=3
HTTP_CONTENT_TYPE=multipart/form-data; boundary=----WebKitFormBoundary5gPnTrFrUlfqmM73
HTTP_REFERER=http://192.168.1.123/upload.html
HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Name=abc
HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.8
MAX_FILE_SIZE=30000
HTTP_UPGRADE_INSECURE_REQUESTS=1
FILE_CLIENT_FILENAME_file=test.txt
HTTP_HOST=192.168.1.123
HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
FILE_CONTENT_TYPE_file=text/plain
文件下载
// 开始下载 192.168.8.41/action/down?video=\UserDev\ip.dat
static void actionDownLoad(Webs *wp, char *path, char *query);
int main(){
// 省略很多
websDefineAction("down", actionDownLoad);
}
static void actionDownLoad(Webs *wp, char *path, char *query){
//(*wp).route->handler->close = (*avolfileClose);
//(*wp).route->handler->service = (*avolfileHandler);
//(*wp).route->handler->service(wp);
WebsHandlerProc service = (*wp).route->handler->service;
(*wp).route->handler->close = (*avolfileClose);
(*wp).route->handler->service =(*avolfileHandler);
(*wp).route->handler->service(wp);
(*wp).route->handler->service= service;
}
static void avolfileClose(){
//wfree(websIndex);
//websIndex = NULL;
//wfree(websDocuments);
//websDocuments = NULL;
}
static bool avolfileHandler(Webs *wp){
WebsFileInfo info;
char *tmp, *date;
ssize nchars;
int code;
char* pathfilename; //带路径的文件名 用于找到对应的文件
char* filenameExt; //文件扩展名 用于 设置 MIME类型
char* filename; //文件名 用于下载后保存的文件名称
char* disposition; //临时保存 附件 标识
assert(websValid(wp));
assert(wp->method);
assert(wp->filename && wp->filename[0]);
// 取download.lua?video=C:\aaa.wmv 中的 C:\aaa.wmv
pathfilename = websGetVar(wp, "video", NULL);
if (pathfilename==NULL)
return true;
//取文件名和扩展名
filename =sclone(getUrlLastSplit(sclone(pathfilename),"\\"));
filenameExt =sclone(getUrlLastSplit(sclone(filename),"."));
if (wp->ext) wfree(wp->ext);
wp->ext=(char*)walloc(1+strlen(filenameExt)+1);
sprintf(wp->ext,".%s",sclone(filenameExt));
free(filenameExt);
filenameExt=NULL;
if (wp->filename) {
wfree(wp->filename);
}
wp->filename=sclone(pathfilename);
if (wp->path) {
wfree(wp->path);
}
wp->path=sclone(pathfilename);
//If the file is a directory, redirect using the nominated default page
if (websPageIsDirectory(wp)) {
nchars = strlen(wp->path);
if (wp->path[nchars - 1] == '/' || wp->path[nchars - 1] == '\\') {
wp->path[--nchars] = '\0';
}
char* websIndex = "testdownload";
tmp = sfmt("%s/%s", wp->path, websIndex);
websRedirect(wp, tmp);
wfree(tmp);
return true;
}
if (websPageOpen(wp, O_RDONLY | O_BINARY, 0666) < 0) {
websError(wp, HTTP_CODE_NOT_FOUND, "Cannot open document for: %s", wp->path);
return true;
}
if (websPageStat(wp, &info) < 0) {
websError(wp, HTTP_CODE_NOT_FOUND, "Cannot stat page for URL");
return true;
}
code = 200;
if (wp->since && info.mtime <= wp->since) {
code = 304;
}
websSetStatus(wp, code);
websWriteHeaders(wp, info.size, 0);
//浏览器下载文件时的文件名
disposition = (char*)walloc(20+strlen(filename)+1);
sprintf(disposition,"attachment;filename=%s",sclone(filename));
websWriteHeader(wp, "Content-Disposition", sclone(disposition));
free(filename);
free(disposition);
filename=NULL;
disposition=NULL;
if ((date = websGetDateString(&info)) != NULL) {
websWriteHeader(wp, "Last-modified", "%s", date);
wfree(date);
}
websWriteEndHeaders(wp);
//All done if the browser did a HEAD request
if (smatch(wp->method, "HEAD")) {
websDone(wp);
return true;
}
websSetBackgroundWriter(wp, fileWriteEvent);
return true;
}
文件下载例程来自:
http://blog.csdn.net/china_boys/article/details/8710450
http://download.csdn.net/download/china_boys/5173588#comment
/demo目录下需要用到的库文件以及配置文件
auth.txt est.h goahead.h js.h
libest.so libgo.so main.c me.h
osdep.h route.txt
上传过大文件的时候,会直接出现找不到接口的网页报错;
修改上传文件的限制:
src/http.c/parseHeaders()
//if (wp->rxLen > ME_GOAHEAD_LIMIT_POST) {
if (wp->rxLen > ME_GOAHEAD_LIMIT_UPLOAD) {
websError(wp, HTTP_CODE_REQUEST_TOO_LARGE | WEBS_CLOSE, "Too big");
return;
}
其中
#define ME_GOAHEAD_LIMIT_POST 16384
#define ME_GOAHEAD_LIMIT_UPLOAD 204800000
这里直接用 ME_GOAHEAD_LIMIT_UPLOAD 替换掉原来的 ME_GOAHEAD_LIMIT_POST;
重新更新应用程序的动态库 libest.so、libgo.so 实现更大文件的上传;
实际运行时别漏掉添加动态库的搜索路径