一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。
前言
Content-Type这个头还是比较重要的,在上一章中,我们只能控制浏览器进行下载,而当遇到png、html的文件时,我们希望浏览器进行展示,这部分就是通过Content-Type来控制的,当他的值是application/octet-stream时,浏览器会进行下载,而当是其他类型时,浏览器则根据不同类型,做出不同响应。
这里代码变的逐渐多了起来,我已经上传到github,链接如下
在汇编中,操作这部分还是比较繁琐的,首先我们需要定义一个结构体类型,类似于c中的struct,每个结构体中有一个key和value,key表示后缀名,value是这个后缀名所响应的mime类型,并把所有的实例放在一起,在NASM中,可以如下操作。
struc Media_Item
.key resb 10
.value resb 20
endstruc
SECTION .data
HTML_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'html'
AT Media_Item.value, db 'text/html'
IEND
IMAGE_PNG_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'png'
AT Media_Item.value, db 'image/png'
IEND
IMAGE_JPG_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'jpg'
AT Media_Item.value, db 'image/jpg'
IEND
IMAGE_CSS_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'css'
AT Media_Item.value, db 'text/css'
IEND
IMAGE_JS_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'js'
AT Media_Item.value, db 'text/javascript'
IEND
MediaArray dd HTML_MEDIA,0h,IMAGE_PNG_MEDIA,0h,IMAGE_JPG_MEDIA,IMAGE_CSS_MEDIA,IMAGE_JS_MEDIA,0h
MediaArraySize dd 5
我们规定,一个key是10字节,value是20字节,(虽然用不了这么多),接着把他们放在一起,形成MediaArray,那么如果要获取第2个元素的key,则可以利用(index-1)*30,这个地址开始的20个字节就是他的key,而获取value时,可以利用((index-1)*30)+10,+10意思是跳过10个字节的key,并从这个地址开始的20个字节是他所对应的mime类型。
剩下就是比对请求url中的后缀名和MediaArray中哪个位置的key相同,并返回他的value。
我们把这部分逻辑放入media.asm中。
struc Media_Item
.key resb 10
.value resb 20
endstruc
SECTION .data
HTML_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'html'
AT Media_Item.value, db 'text/html'
IEND
IMAGE_PNG_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'png'
AT Media_Item.value, db 'image/png'
IEND
IMAGE_JPG_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'jpg'
AT Media_Item.value, db 'image/jpg'
IEND
IMAGE_CSS_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'css'
AT Media_Item.value, db 'text/css'
IEND
IMAGE_JS_MEDIA: ISTRUC Media_Item
AT Media_Item.key, db 'js'
AT Media_Item.value, db 'text/javascript'
IEND
MediaArray dd HTML_MEDIA,0h,IMAGE_PNG_MEDIA,0h,IMAGE_JPG_MEDIA,IMAGE_CSS_MEDIA,IMAGE_JS_MEDIA,0h
MediaArraySize dd 5
section .text
getMediaIndexBySuffix:
call getSuffix
mov ebx,0
mov eax,0
nextMediaArray:
mov ecx,[MediaArraySize]
mov edx,[MediaArray] ;;获取数组
push eax
call getMediaKey ;;;根据索引ebx获取当前数组媒体类型
mov esi,suffixBuffer ;;请求的文件后缀
call equalsStr ;;判断请求后缀和当前是否想等
cmp eax,1 ;;如果相等
pop eax
je _finishFind
inc eax ;;数组中下一个
push eax
sub eax ,ecx
pop eax
jz _failFind
jmp nextMediaArray
ret
_failFind:
mov eax,0
ret
_finishFind:
call getMediaValue
mov eax,1
ret
;;更具索引ebx获取媒体key
getMediaKey:
push eax
push ebx
mov edi,[MediaArray] ;;获取数组
mov ebx,30
mul ebx ;;得数保存在eax中
add edi,eax
pop ebx
pop eax
ret
;;更具索引ebx获取媒体类型
getMediaValue:
push ebx
mov edi,[MediaArray] ;;获取数组
mov ebx,30
mul ebx ;;得数保存在eax中
add edi,eax
add edi,10
pop ebx
ret
;;比较esi和edi中的字符串
equalsStr:
push ebx
push ecx
call getStringLength ;;获取
mov ebx,eax
mov ecx,eax
rep cmpsb
pop ecx
pop ebx
je _ok
mov eax,0
ret
_ok:
mov eax,1
ret
;;获取扩展名称,源位于esi中
getSuffix:
call getStringLength
push esi
mov ecx, eax ;;保存长度
_findPoint:
dec eax
cmp eax,-1
jz _finish
cmp byte[esi+eax],46 ;;找到ascii 46 .
jnz _findPoint
mov edi,suffixBuffer
add esi,eax
inc esi
sub ecx,eax
dec ecx
rep movsb
_finish:
pop esi
ret
;;获取字符长度,源位于esi中
getStringLength:
push esi
mov eax,0
dec esi
_loopStr:
inc eax
cmp byte [esi+eax],0
jnz _loopStr
pop esi
dec eax
ret
首先会通过getSuffix获取后缀名,放入suffixBuffer中,思路是先获取请求url的长度,然后从后面开始遍历,在遇到ascii为46的时候,记录下当前长度,并使用总长度-当前位置得出需要在请求url偏移多少字节开始保存后缀名。
有了后缀名就可以进入nextMediaArray查找这个后缀名位于MediaArray那个位置。
接下来就是拼接字符,还记得上一章的utils吗,我们在这里扩展,主要就是在设置完响应大小后,接着拼接Content-Type。
getFileSize:
mov ecx ,statStructBuffer
mov ebx,edx
mov eax,195
int 80h
mov eax,[statStructBuffer+11*4]
ret
loadBaseHeaderToBuffer:
mov edi,headersBuffer
mov esi,headers
mov ecx,72
rep movsb
ret
;;返回一个整数,表示新增的字节数量
setBodyContentLength:
mov ecx,10
mov edx,0
mov ebx,0
loopdiv:
div ecx
push edx ;余数入栈
inc ebx ;;存取结果是几位数
mov edx,0
mov ecx,10
cmp eax,0 ;;商是否为0
jnz loopdiv
mov ecx,ebx
mov eax,0
_loop:
nop
pop edx ;;获取第n位
add edx,48 ;;加上48是ascii码
mov [headersBuffer+32+eax],edx ;;向buffer中增加长度
inc eax ;;偏移
loop _loop
mov edi,headersBuffer
add edi,32
add edi,eax ;;n长度的相应大小
call setResponseMedia ;;设置相应头
mov esi,header_end ;;4个字节的结尾
mov ecx,4
rep movsb
ret
setResponseMedia:
mov esi,header_line
mov ecx,2
rep movsb
mov esi,response_header_content_type ;;复制content-type到buffer中
mov ecx,13
rep movsb
add eax,15
;;到这里buffer中的数据如下,eax是len(285779)+2+13的大小
;;HTTP/1.1 200 OK
;;Content-Length:285779
;;content-type:
;;这个时候只有edi和eax是需要用的
push edi ;;保存buffer
push eax
mov esi,fullPath ;;参数
call getMediaIndexBySuffix ;;获取这个请求的media类型,参数是esi,结果位于edi,如果eax是1
cmp eax,0 ;;如果没找到了这个媒体类型
je setDefaultMedia
copy:
mov edx,edi ;;保存结果到edx中
pop eax
pop edi
call copyMediaToResponse ;;将这个媒体类型复制到buffer中
ret
setDefaultMedia:
mov edi,default_media
jmp copy
ret
copyMediaToResponse:
push eax
mov esi,edx ;;源字符是由getMediaIndexBySuffix获得或者是default_media
call getStringLength ;;获取他的长度
mov ecx,eax
mov ebx,ecx
rep movsb
pop eax
add eax,ebx
ret