Beantwoord

HTTP Error 400 Bad Request

  • 25 January 2023
  • 13 reacties
  • 154 keer bekeken

Goedemiddag,

Inmiddels ben ik al weer wat uurtjes verder met het stoeien met mijn SIM800L modem en de AT commando's. Ik heb een python script geschreven, waarmee ik de AT commando's geef aan het modem om via TCP een verbinding op te bouwen. In een andere thread had ik gelezen dat een verbinding via de HTTP stack in het modem wat problemen geeft (die ik zelf ook heb ervaren). Inmiddels kom ik met onderstaand script tot een response van de server:

#Python script to connect to KPN Things and send data from Raspberry Pi
from sim800l import SIM800L
import os, time

#Instellen seriële verbinding
sim800l = SIM800L(port='/dev/ttyS0', baudrate=115000, timeout=3.0)
sim800l = SIM800L()

#Algemene informatie ophalen van het modem
print("Date:",
    sim800l.get_date())
print("Operator:",
    sim800l.get_operator())
print("Service provider:",
    sim800l.get_service_provider())
print("Signal strength:",
    sim800l.get_signal_strength(), "%")
print("Temperature:",
    sim800l.get_temperature(), "degrees")
print("MSISDN:",
    sim800l.get_msisdn())
print("Battery Voltage:",
    sim800l.get_battery_voltage(), "V")
print("IMSI:",
    sim800l.get_imsi())
print("ICCID:",
    sim800l.get_ccid())
print("Unit Name:",
    sim800l.get_unit_name())

#Zend AT commando's naar het modem
print('AT commando versturen',
    sim800l.command('AT\n'))
time.sleep(2)
print('functionaliteit instellen',
    sim800l.command('AT+CFUN=1\n'))
time.sleep(2)
print('Controle gereed voor verzenden',
    sim800l.command('AT+CPIN?\n'))
time.sleep(2)
print('Sluit eventuele open verbindingen',
    sim800l.command('AT+CIPSHUT\n'))
time.sleep(2)
print('Enkelvoudige IP verbindingsmodus opzetten',
    sim800l.command('AT+CIPMUX=0\n'))
time.sleep(2)
print('Instellen context',
    sim800l.command('AT+CGACT?\n'))
time.sleep(2)
print('GPRS gekoppeld?',
    sim800l.command('AT+CGATT?\n'))
time.sleep(1)
print('APN instellen',
    sim800l.command('AT+CSTT="kpnthings.iot"\n'))
time.sleep(6)
print('Start de draadloze communicatie',
    sim800l.command('AT+CIICR\n'))
time.sleep(2)
print('OPhalen IP adres',
    sim800l.command('AT+CIFSR\n'))
time.sleep(2)
print('Start de TCP verbinding',
    sim800l.command('AT+CIPSTART="TCP","10.151.236.157",80\n'))
time.sleep(3)
print('Verbinding gemaakt? ', sim800l.check_incoming())
time.sleep(2)
print('Stel de lengte van het bericht in op 259 karakters',
    sim800l.command('AT+CIPSEND=259\n')) #bij losse regels 259
time.sleep(2)
print(sim800l.command('POST /ingestion/m2m/senml/v1 HTTP/1.1'))
print(sim800l.command('Host:m.m'))
print(sim800l.command('Content-Type:application/json'))
print(sim800l.command('Things-Message-Token:fdf8d0e4a1a1481c8639f2daea80071b13dad61050555d3305ad6f65186951ea'))
print(sim800l.command('Content-Length:78'))
print(sim800l.command('[{"bn":"urn:dev:IMEI:867717035410621:","n":"temperature","u":"Cel","v":25.82}]'))
print(sim800l.check_incoming())
time.sleep(3)
print('Verbinding afsluiten',
    sim800l.command('AT+CIPCLOSE=0\n'))
time.sleep(3)
print(sim800l.check_incoming())
time.sleep(5)
print('Communicatie beëindigen',
    sim800l.command('AT+CIPSHUT\n'))
time.sleep(2)

 

Het resultaat is de volgende foutmelding: HTTP/1.1 400 Bad Request

Ik vermoed dat er een fout zit in mijn datapakket. Heeft iemand misschien een idee? Ik heb er inmiddels al weer heel wat uurtjes in zitten…

Help?

icon

Beste antwoord door Alex Wesemann 5 February 2023, 15:58

Bekijk origineel

13 reacties

Reputatie 7
Badge +6

Goedemiddag @Alex Wesemann,

Ik moet eerlijk zeggen dat ik op het gebied van M2M echt een leek ben, maar ik heb gelijk de specialisten ingeschakeld. 

De terugkoppeling is als volgt: 

Het valt ons op dat er bij de Headers geen spatie tussen de key value staat en als je een HTTP bericht maakt via tcp moet de header afgesloten worden met \r\n\r\n en dan de body pas sturen.

Dit is wat de standaard er over zegt: 

Request : A request message from a client to server includes, within the first line of that message, the method to be applied to the resource, the identifier of the resource, and the protocol version in use.
Request = Request-Line *(( general-header | request-header | entity-header ) CRLF ) CRLF [ message-body ]

Request-Line :
The Request-Line begins with a method token, followed by the Request-URL and the protocol version, and ending the CRLF.The elements are separated by SP characters. No CR or LF is allowed except in the final CRLF sequence.
Request-Line = Method SP Request-URI SP HTTP_Version CRLF
​​​​​​

Beste @Rick S. ,

Dank voor de feedback. Ik heb uiteraard ook niet stilgezeten en heb de boel wat verder geoptimaliseerd. Tevens heb ik de carriage return and line feeds toegevoegd achter iedere regel van zowel header-regels als de message body zoals je collega's hier ook over adviseerden. In het script heb ik nu ook de hashing naar SHA256 opgenomen. Het geheel ziet er nu zo uit en ik ga het vanavond eens testen:

#Python script to connect to KPN Things and send data from Raspberry Pi

from sim800l import SIM800L

from hashlib import sha256

import os, time

 

#Instellen verbinding naar SIM800L modem

sim800l = SIM800L(port='/dev/ttyS0', baudrate=115000, timeout=3.0)

sim800l = SIM800L()

 

#Algemene informatie ophalen van het modem

print("Date:",

    sim800l.get_date())

print("Operator:",

    sim800l.get_operator())

print("Service provider:",

    sim800l.get_service_provider())

print("Signal strength:",

    sim800l.get_signal_strength(), "%")

print("Temperature:",

    sim800l.get_temperature(), "degrees")

print("MSISDN:",

    sim800l.get_msisdn())

print("Battery Voltage:",

    sim800l.get_battery_voltage(), "V")

print("IMSI:",

    sim800l.get_imsi())

print("ICCID:",

    sim800l.get_ccid())

print("Unit Name:",

    sim800l.get_unit_name())

 

#Zend AT commando's naar het modem

print('AT commando versturen',

    sim800l.command('AT\r\n'))

time.sleep(2)

print('functionaliteit instellen',

    sim800l.command('AT+CFUN=1\r\n'))

time.sleep(2)

print('Controle gereed voor verzenden',

    sim800l.command('AT+CPIN?\r\n'))

time.sleep(2)

print('Sluit eventuele open verbindingen',

    sim800l.command('AT+CIPSHUT\r\n'))

time.sleep(2)

print('Enkelvoudige IP verbindingsmodus opzetten',

    sim800l.command('AT+CIPMUX=0\r\n'))

time.sleep(2)

print('Instellen context',

    sim800l.command('AT+CGACT?\r\n'))

time.sleep(2)

print('GPRS gekoppeld?',

    sim800l.command('AT+CGATT?\r\n'))

time.sleep(1)

print('APN instellen',

    sim800l.command('AT+CSTT="kpnthings.iot"\r\n'))

time.sleep(6)

print('Start de draadloze communicatie',

    sim800l.command('AT+CIICR\r\n'))

time.sleep(2)

print('OPhalen IP adres',

    sim800l.command('AT+CIFSR\r\n'))

time.sleep(2)

print('Start de TCP verbinding',

    sim800l.command('AT+CIPSTART="TCP","10.151.236.157",80\r\n'))

time.sleep(3)

print('Verbinding gemaakt? ', sim800l.check_incoming())

time.sleep(2)

 

#Bepalen van het Things-Message-Token

messageBody='[{"bn":"urn:dev:IMEI:867717035410621:","n":"temperature","u":"Cel","v":25.82}]'

shared_secret='4IwOhVfiV?dtGUSSkZe@hr}?n0,&h@:T'

messageToken=messageBody+shared_secret

input_ = input('messageToken')

print('De unhashed Things-Message-Token is %s ' % messageToken)

tmt = sha256(input_.encode('utf-8')).hexdigest()

print('De hashed Things-Message-Token is %s' % tmt)

time.sleep(2)

 

#Lengte bepalen van het totaal aantal karakters dat wordt verzonden

post='POST /ingestion/m2m/senml/v1 HTTP/1.1\r\n\r\n'

host='Host:m.m\r\n\r\n'

ctype='Content-Type:application/json\r\n\r\n'

tmt='%s\r\n\r\n' % tmt

clength='Content-Length:78\r\n\r\n'

json='[{"bn":"urn:dev:IMEI:867717035410621:","n":"temperature","u":"Cel","v":25.82}]\r\n\r\n'

tcpstring=post+host+ctype+tmt+clength+json

print('Dit gaan we versturen: %s' % tcpstring)

length=len(tcpstring)

print('De lengte van de string die verzonden gaat worden is %s karakters.' % str(length))

 

#Samenstellen van het AT+CIPSEND commando

print('Stel de lengte van het bericht in op %s karakters.' % str(length),

    sim800l.command('AT+CIPSEND=%s \r\n' % length))

 

time.sleep(5)

print(sim800l.command('%s\0x1a' % tcpstring))

print(sim800l.check_incoming())

time.sleep(3)

print('Verbinding afsluiten',

    sim800l.command('AT+CIPCLOSE=0\n'))

time.sleep(3)

print(sim800l.check_incoming())

time.sleep(5)

print('Communicatie beëindigen',

    sim800l.command('AT+CIPSHUT\n'))

time.sleep(2)

 

Zodra ik het script heb gedraaid, laat ik weten wat het resultaat was. Ik hoop nu eindelijk eens data in Things te krijgen…

mvrgr,

Alex

Beste @Rick S. 

Afgelopen weekend weer veel tijd gestoken in de juiste format van de string. Is het mogelijk dat iemand controleert waarom onderstaande string niet correct is? Ik zie niet wat ik verkeerd doe:

post='POST /ingestion/m2m/senml/v1 HTTP/1.1\r\n\r\n'

host='Host: m.m'

ctype='Content-Type: application/json'

tmt='Things-Message-Token: b364fbbf0995eb0d638e00da72918a31c3b9e9f04feca0bf741956f6f6fb6b34’ 

clength='Content-Length: 78\r\n\r\n'

json='[{"bn":"urn:dev:IMEI:867717035410621:","n":"temperature","u":"Cel","v":25.82}]\r\n\r\n'

Ik verstuur via een AT+CIPSEND commando de geconcatineerde string: tcpstring=post+host+ctype+tmt+clength+json

Ofwel, ik plak bovenstaande regels aan elkaar in één string. De RFC heb ik geïnterpreteerd als:

Eerste regel van de header met POST method wordt afgesloten met 2x CR+LF

De laatste regel van de header (vóór de body) wordt ook afgesloten met 2x CR+LF.

Tenslotte wordt de body ook afgesloten met 2x CR+LF.

Mijn niet-gehashte string is: [{"bn":"urn:dev:IMEI:867717035410621:","n":"temperature","u":"Cel","v":25.82}]4IwOhVfiV?dtGUSSkZe@hr}?n0,&h@:T

Kan hier iemand naar kijken en me vertellen waar het mis gaat? Verneem graag!

Mvrgr,

Alex

Reputatie 7
Badge +6

Goedemiddag Alex, 

Bedankt voor de uitgebreide toelichting! 
Ik ga dit voorleggen aan de specialisten en kom hier later op terug. 

Dank je @Rick S. 

Ik heb gelijk even het aantal karakters geteld van de string die ik ga versturen naar Things via CIPSEND en dat bedraagt volgens mijn telling 270 karakters. Misschien kun je dit ook nog even voorleggen aan het team? Ik vermoed dat daar ook iets verkeerd gaat.

Ben heel benieuwd en hoop dat we het snel op kunnen lossen. Dan kan ik verder met het aansturen van de sensoren en waarden uitlezen.

Mvrgr, Alex

Hallo @Rick S. 

Is er al iets bekend?

Hoor graag!

Alex

Reputatie 7
Badge +6

Goedemiddag Alex, 

Ik heb inmiddels een terugkoppeling van de specialisten ontvangen. Zij hebben via een webhook wat testjes gedaan en hebben wat tips. Het lijkt namelijk een bug in de header te zijn. En als de structuur van de header niet aan de voorwaarden voldoet kan de server dit niet accepteren.

Mijn collega's geven aan dat de ‘content-length’ en de ‘content type’ leeg zijn in de header: 


Bij de header van de andere destination is het wel goed gevuld: 

 

Het advies is om de header te gaan debuggen aan de hand van de “print” en te kijken of de output klopt. 

Daarnaast is een andere tip om voor de raspberry ‘PUT’ in plaats van ‘POST’ te gebruiken. 

Wat betreft de vraag over het aantal karakters vragen we ons af waarom je vermoed dat daar iets misgaat? Krijg je daar een foutmelding mee? Wat gaat er mis met het aantal karakters?

Beste @Rick S. 

Dank je voor de uitgebreide toelichting. Ik ga een en ander eens controleren. Ik kom later bij je terug met de resultaten.

groet,

Alex

Beste @Rick S. 

Via Webhook.site vanaf POSTMAN komt de boel nu netjes binnen met de data in de body. Dit ziet er zo uit:


Method: POST / HTTP/1.1
Content-Type: application/json
Content-Length: 76
Things-Message-Token: 1fe66dd9b2f5be707ae9b7864086d9b2aae71eec3ff837ccd0766e92a7b0254f
Host: webhook.site


Body: "{"bn":"urn:dev:IMEI:867717035410621:","n":"temperature","u":"Cel","v":25.82}"

Met als resultaat in Webhook:

 

Nu dacht ik: Als ik nu eerst eens probeer dezelfde post naar https://prod.dm.kpnthings.com stuur vanaf POSTMAN. Uiteraard met de juiste URL en de header aangepast:

 

Method: POST /ingestion/ip/senml/v1 HTTP/1.1
Content-Type: application/json
Content-Length: 76
Things-Message-Token: 1fe66dd9b2f5be707ae9b7864086d9b2aae71eec3ff837ccd0766e92a7b0254f


Body: "{"bn":"urn:dev:IMEI:867717035410621:","n":"temperature","u":"Cel","v":25.82}"

 

De inhoud ziet er hetzelfde uit als de inhoud van het bericht dat ik via TCP ga versturen, behalve dat ik de host-regel niet hoefde op te nemen volgens jullie handleiding (https://docs.kpnthings.com/dm/connectivity/internet):

Achter het Things-Message-Token staan 2x CR/LF om de header te scheiden van de Body.

Gek genoeg krijg ik een 403 Forbidden fout. in POSTMAN 

In mijn stukje Python software heb ik bovenstaande script nog niet verwerkt. Dat doe ik vanavond en dan weet ik meer.

Maar, het belangrijkste lijkt me om te weten of mijn Header + Body kloppen. Kun je dit zo voorleggen aan het team, Rick?

Hoor het graag!

groet,

Alex

@Rick S.

Ik zie trouwens dat ik een fout heb gemaakt in de body die via internet wordt verzonden. Hier moet natuurlijk staan: 

{"bn":"urn:dev:DVNUUID:867717035410621:","n":"temperature","u":"Cel","v":25.82}

Het Things-Message-Token verandert natuurlijk mee en wordt: 57df2196b24f39d6dd56ee9013788fd3001e77005d63a60f940c0bde88dd73a6

Overigens krijg ik ook dan nog steeds een 403 - Forbidden foutmelding. 

Excuus!

Alex

@Rick S.

Ik heb het voor elkaar!!!!

Dit is het werkende script!

from hashlib import sha256
import os, time
import serial

ser = serial.Serial()
ser.port = "/dev/ttyS0"
ser.baudrate = 115200
ser.open()

APN = "kpnthings.iot"
HOST = "10.151.236.157"
PORT = 80

# Voor er geschreven wordt, eerst de buffer voor de zekerheid leegmaken
if ser.inWaiting() > 0:
    ser.flushInput()

# Stel de time-out in op een redelijk aantal seconden
ser.timeout = 2.

# AT Commando's om modem voor te bereiden op TCP transactie
ser.write(b"ATZ\r")
msg = ser.read(1024)
print(msg)

ser.write(b"AT+CFUN=1\r")
msg = ser.read(1024)
print(msg)

ser.write(b"AT+CPIN?\r")
msg = ser.read(1024)
print(msg)

ser.write(b"AT+CIPSHUT\r")
msg = ser.read(1024)
print(msg)

ser.write(b"AT+CIPMUX=0\r")
msg = ser.read(1024)
print(msg)

ser.write(b"AT+CGACT?\r")
msg = ser.read(1024)
print(msg)

ser.write(b"AT+CGATT?\r")
msg = ser.read(1024)
print(msg)

ser.write(b'AT+CSTT="kpnthings.iot"\r')
msg = ser.read(1024)
print(msg)
time.sleep(4)

ser.write(b"AT+CIICR\r")
msg = ser.read(1024)
print(msg)
time.sleep(2)

ser.write(b"AT+CIFSR\r")
msg = ser.read(1024)
print(msg)
time.sleep(2)

ser.write(b"AT+CIFSR\r")
msg = ser.read(1024)
print(msg)

#Bepalen van het Things-Message-Token
messageBody='[{"bn":"urn:dev:IMEI:867717035410621:","n":"temperature","u":"Cel","v":25.82}]'
shared_secret='4IwOhVfiV?dtGUSSkZe@hr}?n0,&h@:T'
messageToken = messageBody+shared_secret
print('De unhashed Things-Message-Token is %s ' % messageToken)
tmt = sha256(messageToken.encode('utf-8')).hexdigest()
print('De hashed Things-Message-Token is %s' % tmt)
time.sleep(2)

#Lengte bepalen van het totaal aantal karakters dat wordt verzonden
post='POST /ingestion/m2m/senml/v1 HTTP/1.1'
host='Host: m.m'
ctype='Content-Type: application/json'
tmt='Things-Message-Token: %s' % tmt
contentl = len(messageBody)
clength='Content-Length: %s' % str(contentl)
json=messageBody
tcpstring = post + '\r\n' + host + '\r\n' + ctype + '\r\n' + clength + '\r\n' + tmt + '\r\n\r\n' + json #We tellen voor \r en \n 2 bytes
print('Dit gaan we versturen: %s' % tcpstring)
length=len(tcpstring)
print('De lengte van de string die verzonden gaat worden is %s karakters.' % str(length))

ser.write(b'AT+CIPSTART="TCP","10.151.236.157",80\r\n')
msg = ser.read(1024)
print(msg)
time.sleep(3)

cipsendcmd = 'AT+CIPSEND=%s' %str(length) + '\r\n'
ser.write(cipsendcmd.encode())
msg = ser.read(1024)
print(msg)
time.sleep(3)

ser.write(tcpstring.encode()) #Geen CR/LF!!!
msg = ser.read(1024)
print(msg)
time.sleep(2)

ser.write(b"AT+CIPSHUT\r")
msg = ser.read(1024)
print(msg)
time.sleep(2)

ser.write(b"AT+CIPSTATUS\r")
msg = ser.read(1024)
print(msg)
time.sleep(2)

 

Nog niet helemaal netjes opgeschoond, maar dat ga ik nu hierna doen. Heb zitten prutsen met het wel of niet meesturen van het “Method” commando bij de POST/PUT opdracht. Moet dus zonder Method zijn.

Wil je je collega's bedanken voor het meekijken en meedenken. Ik ga verder met het opbouwen van de meetopstelling.

Mvrgr,

Alex

Reputatie 7
Badge +6

Goedemorgen Alex, 

Wat goed om te lezen dat het gelukt is! 
Na jouw berichten van donderdag heb ik mijn collega's ingeschakeld om mee op onderzoek te gaan. Ik kreeg bericht dat er een developer mee zou gaan puzzelen, maar ik had vrijdagmiddag nog geen terugkoppeling ontvangen. Ze waren er dus nog niet helemaal uit. 

Daarnaast (nogmaals) super fijn dat je het hier ook zo uitgebreid omschreven hebt. Dat is natuurlijk ook heel handig voor andere gebruikers die hier mogelijk tegen aan (gaan) lopen. Bedankt daarvoor! 

Als we je nog met andere zaken kunnen helpen horen we het uiteraard graag! 

Graag gedaan @Rick S. ! En zoals gezegd, dank voor de hulp.

Mvrgr, Alex

Reageer